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 | |
| 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>
| -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); |
