diff options
author | Oliver Neukum <oneukum@suse.de> | 2007-05-24 07:52:51 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2007-07-12 19:34:28 -0400 |
commit | 08a2b3b610a734a6d6e4ff0455eec1241966fcb3 (patch) | |
tree | 773e6f9444ebdcca009a07b80ad5716b34f9cf6e /drivers | |
parent | 403dfb58c3134a339e415fba9f6d45b51c6ee357 (diff) |
USB: whiteheat driver update
this is an update of the whiteheat driver. It fixes:
- switch from spinlocks to mutexes to prevent sleeping with a spinlock held
- locking to stop races with disconnect
- error handling for commands that time out
Signed-off-by: Oliver Neukum <oneukum@suse.de>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/usb/serial/whiteheat.c | 92 |
1 files changed, 52 insertions, 40 deletions
diff --git a/drivers/usb/serial/whiteheat.c b/drivers/usb/serial/whiteheat.c index 27c5f8f9a2d5..8fd976d728ed 100644 --- a/drivers/usb/serial/whiteheat.c +++ b/drivers/usb/serial/whiteheat.c | |||
@@ -74,6 +74,7 @@ | |||
74 | #include <linux/tty_flip.h> | 74 | #include <linux/tty_flip.h> |
75 | #include <linux/module.h> | 75 | #include <linux/module.h> |
76 | #include <linux/spinlock.h> | 76 | #include <linux/spinlock.h> |
77 | #include <linux/mutex.h> | ||
77 | #include <asm/uaccess.h> | 78 | #include <asm/uaccess.h> |
78 | #include <asm/termbits.h> | 79 | #include <asm/termbits.h> |
79 | #include <linux/usb.h> | 80 | #include <linux/usb.h> |
@@ -203,7 +204,7 @@ static struct usb_serial_driver whiteheat_device = { | |||
203 | 204 | ||
204 | 205 | ||
205 | struct whiteheat_command_private { | 206 | struct whiteheat_command_private { |
206 | spinlock_t lock; | 207 | struct mutex mutex; |
207 | __u8 port_running; | 208 | __u8 port_running; |
208 | __u8 command_finished; | 209 | __u8 command_finished; |
209 | wait_queue_head_t wait_command; /* for handling sleeping while waiting for a command to finish */ | 210 | wait_queue_head_t wait_command; /* for handling sleeping while waiting for a command to finish */ |
@@ -232,6 +233,7 @@ struct whiteheat_private { | |||
232 | struct usb_serial_port *port; | 233 | struct usb_serial_port *port; |
233 | struct list_head tx_urbs_free; | 234 | struct list_head tx_urbs_free; |
234 | struct list_head tx_urbs_submitted; | 235 | struct list_head tx_urbs_submitted; |
236 | struct mutex deathwarrant; | ||
235 | }; | 237 | }; |
236 | 238 | ||
237 | 239 | ||
@@ -425,6 +427,7 @@ static int whiteheat_attach (struct usb_serial *serial) | |||
425 | } | 427 | } |
426 | 428 | ||
427 | spin_lock_init(&info->lock); | 429 | spin_lock_init(&info->lock); |
430 | mutex_init(&info->deathwarrant); | ||
428 | info->flags = 0; | 431 | info->flags = 0; |
429 | info->mcr = 0; | 432 | info->mcr = 0; |
430 | INIT_WORK(&info->rx_work, rx_data_softint); | 433 | INIT_WORK(&info->rx_work, rx_data_softint); |
@@ -495,7 +498,7 @@ static int whiteheat_attach (struct usb_serial *serial) | |||
495 | goto no_command_private; | 498 | goto no_command_private; |
496 | } | 499 | } |
497 | 500 | ||
498 | spin_lock_init(&command_info->lock); | 501 | mutex_init(&command_info->mutex); |
499 | command_info->port_running = 0; | 502 | command_info->port_running = 0; |
500 | init_waitqueue_head(&command_info->wait_command); | 503 | init_waitqueue_head(&command_info->wait_command); |
501 | usb_set_serial_port_data(command_port, command_info); | 504 | usb_set_serial_port_data(command_port, command_info); |
@@ -654,7 +657,6 @@ static void whiteheat_close(struct usb_serial_port *port, struct file * filp) | |||
654 | struct urb *urb; | 657 | struct urb *urb; |
655 | struct list_head *tmp; | 658 | struct list_head *tmp; |
656 | struct list_head *tmp2; | 659 | struct list_head *tmp2; |
657 | unsigned long flags; | ||
658 | 660 | ||
659 | dbg("%s - port %d", __FUNCTION__, port->number); | 661 | dbg("%s - port %d", __FUNCTION__, port->number); |
660 | 662 | ||
@@ -683,24 +685,32 @@ static void whiteheat_close(struct usb_serial_port *port, struct file * filp) | |||
683 | 685 | ||
684 | firm_close(port); | 686 | firm_close(port); |
685 | 687 | ||
688 | printk(KERN_ERR"Before processing rx_urbs_submitted.\n"); | ||
686 | /* shutdown our bulk reads and writes */ | 689 | /* shutdown our bulk reads and writes */ |
687 | spin_lock_irqsave(&info->lock, flags); | 690 | mutex_lock(&info->deathwarrant); |
691 | spin_lock_irq(&info->lock); | ||
688 | list_for_each_safe(tmp, tmp2, &info->rx_urbs_submitted) { | 692 | list_for_each_safe(tmp, tmp2, &info->rx_urbs_submitted) { |
689 | wrap = list_entry(tmp, struct whiteheat_urb_wrap, list); | 693 | wrap = list_entry(tmp, struct whiteheat_urb_wrap, list); |
690 | urb = wrap->urb; | 694 | urb = wrap->urb; |
695 | list_del(tmp); | ||
696 | spin_unlock_irq(&info->lock); | ||
691 | usb_kill_urb(urb); | 697 | usb_kill_urb(urb); |
692 | list_move(tmp, &info->rx_urbs_free); | 698 | spin_lock_irq(&info->lock); |
699 | list_add(tmp, &info->rx_urbs_free); | ||
693 | } | 700 | } |
694 | list_for_each_safe(tmp, tmp2, &info->rx_urb_q) | 701 | list_for_each_safe(tmp, tmp2, &info->rx_urb_q) |
695 | list_move(tmp, &info->rx_urbs_free); | 702 | list_move(tmp, &info->rx_urbs_free); |
696 | |||
697 | list_for_each_safe(tmp, tmp2, &info->tx_urbs_submitted) { | 703 | list_for_each_safe(tmp, tmp2, &info->tx_urbs_submitted) { |
698 | wrap = list_entry(tmp, struct whiteheat_urb_wrap, list); | 704 | wrap = list_entry(tmp, struct whiteheat_urb_wrap, list); |
699 | urb = wrap->urb; | 705 | urb = wrap->urb; |
706 | list_del(tmp); | ||
707 | spin_unlock_irq(&info->lock); | ||
700 | usb_kill_urb(urb); | 708 | usb_kill_urb(urb); |
701 | list_move(tmp, &info->tx_urbs_free); | 709 | spin_lock_irq(&info->lock); |
710 | list_add(tmp, &info->tx_urbs_free); | ||
702 | } | 711 | } |
703 | spin_unlock_irqrestore(&info->lock, flags); | 712 | spin_unlock_irq(&info->lock); |
713 | mutex_unlock(&info->deathwarrant); | ||
704 | 714 | ||
705 | stop_command_port(port->serial); | 715 | stop_command_port(port->serial); |
706 | 716 | ||
@@ -872,7 +882,7 @@ static int whiteheat_ioctl (struct usb_serial_port *port, struct file * file, un | |||
872 | } | 882 | } |
873 | 883 | ||
874 | 884 | ||
875 | static void whiteheat_set_termios (struct usb_serial_port *port, struct ktermios *old_termios) | 885 | static void whiteheat_set_termios(struct usb_serial_port *port, struct ktermios *old_termios) |
876 | { | 886 | { |
877 | dbg("%s -port %d", __FUNCTION__, port->number); | 887 | dbg("%s -port %d", __FUNCTION__, port->number); |
878 | 888 | ||
@@ -920,7 +930,7 @@ static int whiteheat_chars_in_buffer(struct usb_serial_port *port) | |||
920 | spin_unlock_irqrestore(&info->lock, flags); | 930 | spin_unlock_irqrestore(&info->lock, flags); |
921 | 931 | ||
922 | dbg ("%s - returns %d", __FUNCTION__, chars); | 932 | dbg ("%s - returns %d", __FUNCTION__, chars); |
923 | return (chars); | 933 | return chars; |
924 | } | 934 | } |
925 | 935 | ||
926 | 936 | ||
@@ -962,7 +972,7 @@ static void whiteheat_unthrottle (struct usb_serial_port *port) | |||
962 | /***************************************************************************** | 972 | /***************************************************************************** |
963 | * Connect Tech's White Heat callback routines | 973 | * Connect Tech's White Heat callback routines |
964 | *****************************************************************************/ | 974 | *****************************************************************************/ |
965 | static void command_port_write_callback (struct urb *urb) | 975 | static void command_port_write_callback(struct urb *urb) |
966 | { | 976 | { |
967 | dbg("%s", __FUNCTION__); | 977 | dbg("%s", __FUNCTION__); |
968 | 978 | ||
@@ -973,43 +983,43 @@ static void command_port_write_callback (struct urb *urb) | |||
973 | } | 983 | } |
974 | 984 | ||
975 | 985 | ||
976 | static void command_port_read_callback (struct urb *urb) | 986 | static void command_port_read_callback(struct urb *urb) |
977 | { | 987 | { |
978 | struct usb_serial_port *command_port = (struct usb_serial_port *)urb->context; | 988 | struct usb_serial_port *command_port = (struct usb_serial_port *)urb->context; |
979 | struct whiteheat_command_private *command_info; | 989 | struct whiteheat_command_private *command_info; |
980 | unsigned char *data = urb->transfer_buffer; | 990 | unsigned char *data = urb->transfer_buffer; |
981 | int result; | 991 | int result; |
982 | unsigned long flags; | ||
983 | 992 | ||
984 | dbg("%s", __FUNCTION__); | 993 | dbg("%s", __FUNCTION__); |
985 | 994 | ||
995 | command_info = usb_get_serial_port_data(command_port); | ||
996 | if (!command_info) { | ||
997 | dbg ("%s - command_info is NULL, exiting.", __FUNCTION__); | ||
998 | return; | ||
999 | } | ||
986 | if (urb->status) { | 1000 | if (urb->status) { |
987 | dbg("%s - nonzero urb status: %d", __FUNCTION__, urb->status); | 1001 | dbg("%s - nonzero urb status: %d", __FUNCTION__, urb->status); |
1002 | if (urb->status != -ENOENT) | ||
1003 | command_info->command_finished = WHITEHEAT_CMD_FAILURE; | ||
1004 | wake_up(&command_info->wait_command); | ||
988 | return; | 1005 | return; |
989 | } | 1006 | } |
990 | 1007 | ||
991 | usb_serial_debug_data(debug, &command_port->dev, __FUNCTION__, urb->actual_length, data); | 1008 | usb_serial_debug_data(debug, &command_port->dev, __FUNCTION__, urb->actual_length, data); |
992 | 1009 | ||
993 | command_info = usb_get_serial_port_data(command_port); | ||
994 | if (!command_info) { | ||
995 | dbg ("%s - command_info is NULL, exiting.", __FUNCTION__); | ||
996 | return; | ||
997 | } | ||
998 | spin_lock_irqsave(&command_info->lock, flags); | ||
999 | |||
1000 | if (data[0] == WHITEHEAT_CMD_COMPLETE) { | 1010 | if (data[0] == WHITEHEAT_CMD_COMPLETE) { |
1001 | command_info->command_finished = WHITEHEAT_CMD_COMPLETE; | 1011 | command_info->command_finished = WHITEHEAT_CMD_COMPLETE; |
1002 | wake_up_interruptible(&command_info->wait_command); | 1012 | wake_up(&command_info->wait_command); |
1003 | } else if (data[0] == WHITEHEAT_CMD_FAILURE) { | 1013 | } else if (data[0] == WHITEHEAT_CMD_FAILURE) { |
1004 | command_info->command_finished = WHITEHEAT_CMD_FAILURE; | 1014 | command_info->command_finished = WHITEHEAT_CMD_FAILURE; |
1005 | wake_up_interruptible(&command_info->wait_command); | 1015 | wake_up(&command_info->wait_command); |
1006 | } else if (data[0] == WHITEHEAT_EVENT) { | 1016 | } else if (data[0] == WHITEHEAT_EVENT) { |
1007 | /* These are unsolicited reports from the firmware, hence no waiting command to wakeup */ | 1017 | /* These are unsolicited reports from the firmware, hence no waiting command to wakeup */ |
1008 | dbg("%s - event received", __FUNCTION__); | 1018 | dbg("%s - event received", __FUNCTION__); |
1009 | } else if (data[0] == WHITEHEAT_GET_DTR_RTS) { | 1019 | } else if (data[0] == WHITEHEAT_GET_DTR_RTS) { |
1010 | memcpy(command_info->result_buffer, &data[1], urb->actual_length - 1); | 1020 | memcpy(command_info->result_buffer, &data[1], urb->actual_length - 1); |
1011 | command_info->command_finished = WHITEHEAT_CMD_COMPLETE; | 1021 | command_info->command_finished = WHITEHEAT_CMD_COMPLETE; |
1012 | wake_up_interruptible(&command_info->wait_command); | 1022 | wake_up(&command_info->wait_command); |
1013 | } else { | 1023 | } else { |
1014 | dbg("%s - bad reply from firmware", __FUNCTION__); | 1024 | dbg("%s - bad reply from firmware", __FUNCTION__); |
1015 | } | 1025 | } |
@@ -1017,7 +1027,6 @@ static void command_port_read_callback (struct urb *urb) | |||
1017 | /* Continue trying to always read */ | 1027 | /* Continue trying to always read */ |
1018 | command_port->read_urb->dev = command_port->serial->dev; | 1028 | command_port->read_urb->dev = command_port->serial->dev; |
1019 | result = usb_submit_urb(command_port->read_urb, GFP_ATOMIC); | 1029 | result = usb_submit_urb(command_port->read_urb, GFP_ATOMIC); |
1020 | spin_unlock_irqrestore(&command_info->lock, flags); | ||
1021 | if (result) | 1030 | if (result) |
1022 | dbg("%s - failed resubmitting read urb, error %d", __FUNCTION__, result); | 1031 | dbg("%s - failed resubmitting read urb, error %d", __FUNCTION__, result); |
1023 | } | 1032 | } |
@@ -1095,20 +1104,20 @@ static void whiteheat_write_callback(struct urb *urb) | |||
1095 | /***************************************************************************** | 1104 | /***************************************************************************** |
1096 | * Connect Tech's White Heat firmware interface | 1105 | * Connect Tech's White Heat firmware interface |
1097 | *****************************************************************************/ | 1106 | *****************************************************************************/ |
1098 | static int firm_send_command (struct usb_serial_port *port, __u8 command, __u8 *data, __u8 datasize) | 1107 | static int firm_send_command(struct usb_serial_port *port, __u8 command, __u8 *data, __u8 datasize) |
1099 | { | 1108 | { |
1100 | struct usb_serial_port *command_port; | 1109 | struct usb_serial_port *command_port; |
1101 | struct whiteheat_command_private *command_info; | 1110 | struct whiteheat_command_private *command_info; |
1102 | struct whiteheat_private *info; | 1111 | struct whiteheat_private *info; |
1103 | __u8 *transfer_buffer; | 1112 | __u8 *transfer_buffer; |
1104 | int retval = 0; | 1113 | int retval = 0; |
1105 | unsigned long flags; | 1114 | int t; |
1106 | 1115 | ||
1107 | dbg("%s - command %d", __FUNCTION__, command); | 1116 | dbg("%s - command %d", __FUNCTION__, command); |
1108 | 1117 | ||
1109 | command_port = port->serial->port[COMMAND_PORT]; | 1118 | command_port = port->serial->port[COMMAND_PORT]; |
1110 | command_info = usb_get_serial_port_data(command_port); | 1119 | command_info = usb_get_serial_port_data(command_port); |
1111 | spin_lock_irqsave(&command_info->lock, flags); | 1120 | mutex_lock(&command_info->mutex); |
1112 | command_info->command_finished = false; | 1121 | command_info->command_finished = false; |
1113 | 1122 | ||
1114 | transfer_buffer = (__u8 *)command_port->write_urb->transfer_buffer; | 1123 | transfer_buffer = (__u8 *)command_port->write_urb->transfer_buffer; |
@@ -1116,18 +1125,17 @@ static int firm_send_command (struct usb_serial_port *port, __u8 command, __u8 * | |||
1116 | memcpy (&transfer_buffer[1], data, datasize); | 1125 | memcpy (&transfer_buffer[1], data, datasize); |
1117 | command_port->write_urb->transfer_buffer_length = datasize + 1; | 1126 | command_port->write_urb->transfer_buffer_length = datasize + 1; |
1118 | command_port->write_urb->dev = port->serial->dev; | 1127 | command_port->write_urb->dev = port->serial->dev; |
1119 | retval = usb_submit_urb (command_port->write_urb, GFP_KERNEL); | 1128 | retval = usb_submit_urb (command_port->write_urb, GFP_NOIO); |
1120 | if (retval) { | 1129 | if (retval) { |
1121 | dbg("%s - submit urb failed", __FUNCTION__); | 1130 | dbg("%s - submit urb failed", __FUNCTION__); |
1122 | goto exit; | 1131 | goto exit; |
1123 | } | 1132 | } |
1124 | spin_unlock_irqrestore(&command_info->lock, flags); | ||
1125 | 1133 | ||
1126 | /* wait for the command to complete */ | 1134 | /* wait for the command to complete */ |
1127 | wait_event_interruptible_timeout(command_info->wait_command, | 1135 | t = wait_event_timeout(command_info->wait_command, |
1128 | (bool)command_info->command_finished, COMMAND_TIMEOUT); | 1136 | (bool)command_info->command_finished, COMMAND_TIMEOUT); |
1129 | 1137 | if (!t) | |
1130 | spin_lock_irqsave(&command_info->lock, flags); | 1138 | usb_kill_urb(command_port->write_urb); |
1131 | 1139 | ||
1132 | if (command_info->command_finished == false) { | 1140 | if (command_info->command_finished == false) { |
1133 | dbg("%s - command timed out.", __FUNCTION__); | 1141 | dbg("%s - command timed out.", __FUNCTION__); |
@@ -1152,7 +1160,7 @@ static int firm_send_command (struct usb_serial_port *port, __u8 command, __u8 * | |||
1152 | } | 1160 | } |
1153 | 1161 | ||
1154 | exit: | 1162 | exit: |
1155 | spin_unlock_irqrestore(&command_info->lock, flags); | 1163 | mutex_unlock(&command_info->mutex); |
1156 | return retval; | 1164 | return retval; |
1157 | } | 1165 | } |
1158 | 1166 | ||
@@ -1305,12 +1313,11 @@ static int start_command_port(struct usb_serial *serial) | |||
1305 | { | 1313 | { |
1306 | struct usb_serial_port *command_port; | 1314 | struct usb_serial_port *command_port; |
1307 | struct whiteheat_command_private *command_info; | 1315 | struct whiteheat_command_private *command_info; |
1308 | unsigned long flags; | ||
1309 | int retval = 0; | 1316 | int retval = 0; |
1310 | 1317 | ||
1311 | command_port = serial->port[COMMAND_PORT]; | 1318 | command_port = serial->port[COMMAND_PORT]; |
1312 | command_info = usb_get_serial_port_data(command_port); | 1319 | command_info = usb_get_serial_port_data(command_port); |
1313 | spin_lock_irqsave(&command_info->lock, flags); | 1320 | mutex_lock(&command_info->mutex); |
1314 | if (!command_info->port_running) { | 1321 | if (!command_info->port_running) { |
1315 | /* Work around HCD bugs */ | 1322 | /* Work around HCD bugs */ |
1316 | usb_clear_halt(serial->dev, command_port->read_urb->pipe); | 1323 | usb_clear_halt(serial->dev, command_port->read_urb->pipe); |
@@ -1325,7 +1332,7 @@ static int start_command_port(struct usb_serial *serial) | |||
1325 | command_info->port_running++; | 1332 | command_info->port_running++; |
1326 | 1333 | ||
1327 | exit: | 1334 | exit: |
1328 | spin_unlock_irqrestore(&command_info->lock, flags); | 1335 | mutex_unlock(&command_info->mutex); |
1329 | return retval; | 1336 | return retval; |
1330 | } | 1337 | } |
1331 | 1338 | ||
@@ -1334,15 +1341,14 @@ static void stop_command_port(struct usb_serial *serial) | |||
1334 | { | 1341 | { |
1335 | struct usb_serial_port *command_port; | 1342 | struct usb_serial_port *command_port; |
1336 | struct whiteheat_command_private *command_info; | 1343 | struct whiteheat_command_private *command_info; |
1337 | unsigned long flags; | ||
1338 | 1344 | ||
1339 | command_port = serial->port[COMMAND_PORT]; | 1345 | command_port = serial->port[COMMAND_PORT]; |
1340 | command_info = usb_get_serial_port_data(command_port); | 1346 | command_info = usb_get_serial_port_data(command_port); |
1341 | spin_lock_irqsave(&command_info->lock, flags); | 1347 | mutex_lock(&command_info->mutex); |
1342 | command_info->port_running--; | 1348 | command_info->port_running--; |
1343 | if (!command_info->port_running) | 1349 | if (!command_info->port_running) |
1344 | usb_kill_urb(command_port->read_urb); | 1350 | usb_kill_urb(command_port->read_urb); |
1345 | spin_unlock_irqrestore(&command_info->lock, flags); | 1351 | mutex_unlock(&command_info->mutex); |
1346 | } | 1352 | } |
1347 | 1353 | ||
1348 | 1354 | ||
@@ -1363,17 +1369,23 @@ static int start_port_read(struct usb_serial_port *port) | |||
1363 | wrap = list_entry(tmp, struct whiteheat_urb_wrap, list); | 1369 | wrap = list_entry(tmp, struct whiteheat_urb_wrap, list); |
1364 | urb = wrap->urb; | 1370 | urb = wrap->urb; |
1365 | urb->dev = port->serial->dev; | 1371 | urb->dev = port->serial->dev; |
1372 | spin_unlock_irqrestore(&info->lock, flags); | ||
1366 | retval = usb_submit_urb(urb, GFP_KERNEL); | 1373 | retval = usb_submit_urb(urb, GFP_KERNEL); |
1367 | if (retval) { | 1374 | if (retval) { |
1375 | spin_lock_irqsave(&info->lock, flags); | ||
1368 | list_add(tmp, &info->rx_urbs_free); | 1376 | list_add(tmp, &info->rx_urbs_free); |
1369 | list_for_each_safe(tmp, tmp2, &info->rx_urbs_submitted) { | 1377 | list_for_each_safe(tmp, tmp2, &info->rx_urbs_submitted) { |
1370 | wrap = list_entry(tmp, struct whiteheat_urb_wrap, list); | 1378 | wrap = list_entry(tmp, struct whiteheat_urb_wrap, list); |
1371 | urb = wrap->urb; | 1379 | urb = wrap->urb; |
1380 | list_del(tmp); | ||
1381 | spin_unlock_irqrestore(&info->lock, flags); | ||
1372 | usb_kill_urb(urb); | 1382 | usb_kill_urb(urb); |
1373 | list_move(tmp, &info->rx_urbs_free); | 1383 | spin_lock_irqsave(&info->lock, flags); |
1384 | list_add(tmp, &info->rx_urbs_free); | ||
1374 | } | 1385 | } |
1375 | break; | 1386 | break; |
1376 | } | 1387 | } |
1388 | spin_lock_irqsave(&info->lock, flags); | ||
1377 | list_add(tmp, &info->rx_urbs_submitted); | 1389 | list_add(tmp, &info->rx_urbs_submitted); |
1378 | } | 1390 | } |
1379 | 1391 | ||