diff options
author | Jan Dumon <j.dumon@option.com> | 2010-01-04 23:52:13 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2010-01-07 03:43:45 -0500 |
commit | 68a351c501ad22077a969df157cd13367cb43a40 (patch) | |
tree | 57bf6392e60e4d8de89c62d7d5c2d67295a43b36 /drivers/net | |
parent | f4763e96c08ea0790750603999e5b3158c3b50d4 (diff) |
hso: Attempt to recover from usb bus errors
Attempt to reset the usb device when we receive usb bus errors.
Signed-off-by: Jan Dumon <j.dumon@option.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net')
-rw-r--r-- | drivers/net/usb/hso.c | 54 |
1 files changed, 44 insertions, 10 deletions
diff --git a/drivers/net/usb/hso.c b/drivers/net/usb/hso.c index fb1c5ac55c01..7482d0d5e278 100644 --- a/drivers/net/usb/hso.c +++ b/drivers/net/usb/hso.c | |||
@@ -286,6 +286,7 @@ struct hso_device { | |||
286 | u8 usb_gone; | 286 | u8 usb_gone; |
287 | struct work_struct async_get_intf; | 287 | struct work_struct async_get_intf; |
288 | struct work_struct async_put_intf; | 288 | struct work_struct async_put_intf; |
289 | struct work_struct reset_device; | ||
289 | 290 | ||
290 | struct usb_device *usb; | 291 | struct usb_device *usb; |
291 | struct usb_interface *interface; | 292 | struct usb_interface *interface; |
@@ -332,7 +333,8 @@ static void hso_kick_transmit(struct hso_serial *serial); | |||
332 | /* Helper functions */ | 333 | /* Helper functions */ |
333 | static int hso_mux_submit_intr_urb(struct hso_shared_int *mux_int, | 334 | static int hso_mux_submit_intr_urb(struct hso_shared_int *mux_int, |
334 | struct usb_device *usb, gfp_t gfp); | 335 | struct usb_device *usb, gfp_t gfp); |
335 | static void log_usb_status(int status, const char *function); | 336 | static void handle_usb_error(int status, const char *function, |
337 | struct hso_device *hso_dev); | ||
336 | static struct usb_endpoint_descriptor *hso_get_ep(struct usb_interface *intf, | 338 | static struct usb_endpoint_descriptor *hso_get_ep(struct usb_interface *intf, |
337 | int type, int dir); | 339 | int type, int dir); |
338 | static int hso_get_mux_ports(struct usb_interface *intf, unsigned char *ports); | 340 | static int hso_get_mux_ports(struct usb_interface *intf, unsigned char *ports); |
@@ -350,6 +352,7 @@ static void async_put_intf(struct work_struct *data); | |||
350 | static int hso_put_activity(struct hso_device *hso_dev); | 352 | static int hso_put_activity(struct hso_device *hso_dev); |
351 | static int hso_get_activity(struct hso_device *hso_dev); | 353 | static int hso_get_activity(struct hso_device *hso_dev); |
352 | static void tiocmget_intr_callback(struct urb *urb); | 354 | static void tiocmget_intr_callback(struct urb *urb); |
355 | static void reset_device(struct work_struct *data); | ||
353 | /*****************************************************************************/ | 356 | /*****************************************************************************/ |
354 | /* Helping functions */ | 357 | /* Helping functions */ |
355 | /*****************************************************************************/ | 358 | /*****************************************************************************/ |
@@ -664,8 +667,8 @@ static void set_serial_by_index(unsigned index, struct hso_serial *serial) | |||
664 | spin_unlock_irqrestore(&serial_table_lock, flags); | 667 | spin_unlock_irqrestore(&serial_table_lock, flags); |
665 | } | 668 | } |
666 | 669 | ||
667 | /* log a meaningful explanation of an USB status */ | 670 | static void handle_usb_error(int status, const char *function, |
668 | static void log_usb_status(int status, const char *function) | 671 | struct hso_device *hso_dev) |
669 | { | 672 | { |
670 | char *explanation; | 673 | char *explanation; |
671 | 674 | ||
@@ -694,10 +697,20 @@ static void log_usb_status(int status, const char *function) | |||
694 | case -EMSGSIZE: | 697 | case -EMSGSIZE: |
695 | explanation = "internal error"; | 698 | explanation = "internal error"; |
696 | break; | 699 | break; |
700 | case -EILSEQ: | ||
701 | case -EPROTO: | ||
702 | case -ETIME: | ||
703 | case -ETIMEDOUT: | ||
704 | explanation = "protocol error"; | ||
705 | if (hso_dev) | ||
706 | schedule_work(&hso_dev->reset_device); | ||
707 | break; | ||
697 | default: | 708 | default: |
698 | explanation = "unknown status"; | 709 | explanation = "unknown status"; |
699 | break; | 710 | break; |
700 | } | 711 | } |
712 | |||
713 | /* log a meaningful explanation of an USB status */ | ||
701 | D1("%s: received USB status - %s (%d)", function, explanation, status); | 714 | D1("%s: received USB status - %s (%d)", function, explanation, status); |
702 | } | 715 | } |
703 | 716 | ||
@@ -771,7 +784,7 @@ static void write_bulk_callback(struct urb *urb) | |||
771 | /* log status, but don't act on it, we don't need to resubmit anything | 784 | /* log status, but don't act on it, we don't need to resubmit anything |
772 | * anyhow */ | 785 | * anyhow */ |
773 | if (status) | 786 | if (status) |
774 | log_usb_status(status, __func__); | 787 | handle_usb_error(status, __func__, odev->parent); |
775 | 788 | ||
776 | hso_put_activity(odev->parent); | 789 | hso_put_activity(odev->parent); |
777 | 790 | ||
@@ -1007,7 +1020,7 @@ static void read_bulk_callback(struct urb *urb) | |||
1007 | 1020 | ||
1008 | /* is al ok? (Filip: Who's Al ?) */ | 1021 | /* is al ok? (Filip: Who's Al ?) */ |
1009 | if (status) { | 1022 | if (status) { |
1010 | log_usb_status(status, __func__); | 1023 | handle_usb_error(status, __func__, odev->parent); |
1011 | return; | 1024 | return; |
1012 | } | 1025 | } |
1013 | 1026 | ||
@@ -1217,7 +1230,7 @@ static void hso_std_serial_read_bulk_callback(struct urb *urb) | |||
1217 | D1("serial == NULL"); | 1230 | D1("serial == NULL"); |
1218 | return; | 1231 | return; |
1219 | } else if (status) { | 1232 | } else if (status) { |
1220 | log_usb_status(status, __func__); | 1233 | handle_usb_error(status, __func__, serial->parent); |
1221 | return; | 1234 | return; |
1222 | } | 1235 | } |
1223 | 1236 | ||
@@ -1523,7 +1536,7 @@ static void tiocmget_intr_callback(struct urb *urb) | |||
1523 | if (!serial) | 1536 | if (!serial) |
1524 | return; | 1537 | return; |
1525 | if (status) { | 1538 | if (status) { |
1526 | log_usb_status(status, __func__); | 1539 | handle_usb_error(status, __func__, serial->parent); |
1527 | return; | 1540 | return; |
1528 | } | 1541 | } |
1529 | tiocmget = serial->tiocmget; | 1542 | tiocmget = serial->tiocmget; |
@@ -1898,7 +1911,7 @@ static void intr_callback(struct urb *urb) | |||
1898 | 1911 | ||
1899 | /* status check */ | 1912 | /* status check */ |
1900 | if (status) { | 1913 | if (status) { |
1901 | log_usb_status(status, __func__); | 1914 | handle_usb_error(status, __func__, NULL); |
1902 | return; | 1915 | return; |
1903 | } | 1916 | } |
1904 | D4("\n--- Got intr callback 0x%02X ---", status); | 1917 | D4("\n--- Got intr callback 0x%02X ---", status); |
@@ -1968,7 +1981,7 @@ static void hso_std_serial_write_bulk_callback(struct urb *urb) | |||
1968 | tty = tty_kref_get(serial->tty); | 1981 | tty = tty_kref_get(serial->tty); |
1969 | spin_unlock(&serial->serial_lock); | 1982 | spin_unlock(&serial->serial_lock); |
1970 | if (status) { | 1983 | if (status) { |
1971 | log_usb_status(status, __func__); | 1984 | handle_usb_error(status, __func__, serial->parent); |
1972 | tty_kref_put(tty); | 1985 | tty_kref_put(tty); |
1973 | return; | 1986 | return; |
1974 | } | 1987 | } |
@@ -2024,7 +2037,7 @@ static void ctrl_callback(struct urb *urb) | |||
2024 | tty = tty_kref_get(serial->tty); | 2037 | tty = tty_kref_get(serial->tty); |
2025 | spin_unlock(&serial->serial_lock); | 2038 | spin_unlock(&serial->serial_lock); |
2026 | if (status) { | 2039 | if (status) { |
2027 | log_usb_status(status, __func__); | 2040 | handle_usb_error(status, __func__, serial->parent); |
2028 | tty_kref_put(tty); | 2041 | tty_kref_put(tty); |
2029 | return; | 2042 | return; |
2030 | } | 2043 | } |
@@ -2401,6 +2414,7 @@ static struct hso_device *hso_create_device(struct usb_interface *intf, | |||
2401 | 2414 | ||
2402 | INIT_WORK(&hso_dev->async_get_intf, async_get_intf); | 2415 | INIT_WORK(&hso_dev->async_get_intf, async_get_intf); |
2403 | INIT_WORK(&hso_dev->async_put_intf, async_put_intf); | 2416 | INIT_WORK(&hso_dev->async_put_intf, async_put_intf); |
2417 | INIT_WORK(&hso_dev->reset_device, reset_device); | ||
2404 | 2418 | ||
2405 | return hso_dev; | 2419 | return hso_dev; |
2406 | } | 2420 | } |
@@ -3143,6 +3157,26 @@ out: | |||
3143 | return result; | 3157 | return result; |
3144 | } | 3158 | } |
3145 | 3159 | ||
3160 | static void reset_device(struct work_struct *data) | ||
3161 | { | ||
3162 | struct hso_device *hso_dev = | ||
3163 | container_of(data, struct hso_device, reset_device); | ||
3164 | struct usb_device *usb = hso_dev->usb; | ||
3165 | int result; | ||
3166 | |||
3167 | if (hso_dev->usb_gone) { | ||
3168 | D1("No reset during disconnect\n"); | ||
3169 | } else { | ||
3170 | result = usb_lock_device_for_reset(usb, hso_dev->interface); | ||
3171 | if (result < 0) | ||
3172 | D1("unable to lock device for reset: %d\n", result); | ||
3173 | else { | ||
3174 | usb_reset_device(usb); | ||
3175 | usb_unlock_device(usb); | ||
3176 | } | ||
3177 | } | ||
3178 | } | ||
3179 | |||
3146 | static void hso_serial_ref_free(struct kref *ref) | 3180 | static void hso_serial_ref_free(struct kref *ref) |
3147 | { | 3181 | { |
3148 | struct hso_device *hso_dev = container_of(ref, struct hso_device, ref); | 3182 | struct hso_device *hso_dev = container_of(ref, struct hso_device, ref); |