diff options
| author | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2017-01-05 11:41:16 -0500 |
|---|---|---|
| committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2017-01-05 11:41:16 -0500 |
| commit | c8d204b38a558d74fafb6915e2593602b7f4b823 (patch) | |
| tree | d4e076845e4d6dfbef70b1f4740e8fa246958059 | |
| parent | 29fc1aa454d0603493b47a8e2410ae6e9ab20258 (diff) | |
| parent | ef079936d3cd09e63612834fe2698eeada0d8e3f (diff) | |
Merge tag 'usb-serial-4.10-rc3' of git://git.kernel.org/pub/scm/linux/kernel/git/johan/usb-serial into usb-linus
Johan writes:
USB-serial fixes for v4.10-rc3
These fixes address a number of long-standing issues in various
USB-serial drivers which would lead to crashes should a malicious device
lack the expected endpoints.
Included are also a few related fixes, and a couple of unrelated ones
that were found during my survey (e.g. a memleak and a
sleep-while-atomic).
A compiler warning revealed an error-handling issue in the new f81534
driver which is also fixed.
Signed-off-by: Johan Hovold <johan@kernel.org>
| -rw-r--r-- | drivers/usb/serial/cyberjack.c | 10 | ||||
| -rw-r--r-- | drivers/usb/serial/f81534.c | 8 | ||||
| -rw-r--r-- | drivers/usb/serial/garmin_gps.c | 1 | ||||
| -rw-r--r-- | drivers/usb/serial/io_edgeport.c | 5 | ||||
| -rw-r--r-- | drivers/usb/serial/io_ti.c | 22 | ||||
| -rw-r--r-- | drivers/usb/serial/iuu_phoenix.c | 11 | ||||
| -rw-r--r-- | drivers/usb/serial/keyspan_pda.c | 14 | ||||
| -rw-r--r-- | drivers/usb/serial/kobil_sct.c | 12 | ||||
| -rw-r--r-- | drivers/usb/serial/mos7720.c | 56 | ||||
| -rw-r--r-- | drivers/usb/serial/mos7840.c | 24 | ||||
| -rw-r--r-- | drivers/usb/serial/omninet.c | 13 | ||||
| -rw-r--r-- | drivers/usb/serial/oti6858.c | 16 | ||||
| -rw-r--r-- | drivers/usb/serial/pl2303.c | 8 | ||||
| -rw-r--r-- | drivers/usb/serial/quatech2.c | 4 | ||||
| -rw-r--r-- | drivers/usb/serial/spcp8x5.c | 14 | ||||
| -rw-r--r-- | drivers/usb/serial/ti_usb_3410_5052.c | 7 |
16 files changed, 167 insertions, 58 deletions
diff --git a/drivers/usb/serial/cyberjack.c b/drivers/usb/serial/cyberjack.c index 5f17a3b9916d..80260b08398b 100644 --- a/drivers/usb/serial/cyberjack.c +++ b/drivers/usb/serial/cyberjack.c | |||
| @@ -50,6 +50,7 @@ | |||
| 50 | #define CYBERJACK_PRODUCT_ID 0x0100 | 50 | #define CYBERJACK_PRODUCT_ID 0x0100 |
| 51 | 51 | ||
| 52 | /* Function prototypes */ | 52 | /* Function prototypes */ |
| 53 | static int cyberjack_attach(struct usb_serial *serial); | ||
| 53 | static int cyberjack_port_probe(struct usb_serial_port *port); | 54 | static int cyberjack_port_probe(struct usb_serial_port *port); |
| 54 | static int cyberjack_port_remove(struct usb_serial_port *port); | 55 | static int cyberjack_port_remove(struct usb_serial_port *port); |
| 55 | static int cyberjack_open(struct tty_struct *tty, | 56 | static int cyberjack_open(struct tty_struct *tty, |
| @@ -77,6 +78,7 @@ static struct usb_serial_driver cyberjack_device = { | |||
| 77 | .description = "Reiner SCT Cyberjack USB card reader", | 78 | .description = "Reiner SCT Cyberjack USB card reader", |
| 78 | .id_table = id_table, | 79 | .id_table = id_table, |
| 79 | .num_ports = 1, | 80 | .num_ports = 1, |
| 81 | .attach = cyberjack_attach, | ||
| 80 | .port_probe = cyberjack_port_probe, | 82 | .port_probe = cyberjack_port_probe, |
| 81 | .port_remove = cyberjack_port_remove, | 83 | .port_remove = cyberjack_port_remove, |
| 82 | .open = cyberjack_open, | 84 | .open = cyberjack_open, |
| @@ -100,6 +102,14 @@ struct cyberjack_private { | |||
| 100 | short wrsent; /* Data already sent */ | 102 | short wrsent; /* Data already sent */ |
| 101 | }; | 103 | }; |
| 102 | 104 | ||
| 105 | static int cyberjack_attach(struct usb_serial *serial) | ||
| 106 | { | ||
| 107 | if (serial->num_bulk_out < serial->num_ports) | ||
| 108 | return -ENODEV; | ||
| 109 | |||
| 110 | return 0; | ||
| 111 | } | ||
| 112 | |||
| 103 | static int cyberjack_port_probe(struct usb_serial_port *port) | 113 | static int cyberjack_port_probe(struct usb_serial_port *port) |
| 104 | { | 114 | { |
| 105 | struct cyberjack_private *priv; | 115 | struct cyberjack_private *priv; |
diff --git a/drivers/usb/serial/f81534.c b/drivers/usb/serial/f81534.c index 8282a6a18fee..22f23a429a95 100644 --- a/drivers/usb/serial/f81534.c +++ b/drivers/usb/serial/f81534.c | |||
| @@ -1237,6 +1237,7 @@ static int f81534_attach(struct usb_serial *serial) | |||
| 1237 | static int f81534_port_probe(struct usb_serial_port *port) | 1237 | static int f81534_port_probe(struct usb_serial_port *port) |
| 1238 | { | 1238 | { |
| 1239 | struct f81534_port_private *port_priv; | 1239 | struct f81534_port_private *port_priv; |
| 1240 | int ret; | ||
| 1240 | 1241 | ||
| 1241 | port_priv = devm_kzalloc(&port->dev, sizeof(*port_priv), GFP_KERNEL); | 1242 | port_priv = devm_kzalloc(&port->dev, sizeof(*port_priv), GFP_KERNEL); |
| 1242 | if (!port_priv) | 1243 | if (!port_priv) |
| @@ -1246,10 +1247,11 @@ static int f81534_port_probe(struct usb_serial_port *port) | |||
| 1246 | mutex_init(&port_priv->mcr_mutex); | 1247 | mutex_init(&port_priv->mcr_mutex); |
| 1247 | 1248 | ||
| 1248 | /* Assign logic-to-phy mapping */ | 1249 | /* Assign logic-to-phy mapping */ |
| 1249 | port_priv->phy_num = f81534_logic_to_phy_port(port->serial, port); | 1250 | ret = f81534_logic_to_phy_port(port->serial, port); |
| 1250 | if (port_priv->phy_num < 0 || port_priv->phy_num >= F81534_NUM_PORT) | 1251 | if (ret < 0) |
| 1251 | return -ENODEV; | 1252 | return ret; |
| 1252 | 1253 | ||
| 1254 | port_priv->phy_num = ret; | ||
| 1253 | usb_set_serial_port_data(port, port_priv); | 1255 | usb_set_serial_port_data(port, port_priv); |
| 1254 | dev_dbg(&port->dev, "%s: port_number: %d, phy_num: %d\n", __func__, | 1256 | dev_dbg(&port->dev, "%s: port_number: %d, phy_num: %d\n", __func__, |
| 1255 | port->port_number, port_priv->phy_num); | 1257 | port->port_number, port_priv->phy_num); |
diff --git a/drivers/usb/serial/garmin_gps.c b/drivers/usb/serial/garmin_gps.c index 97cabf803c2f..b2f2e87aed94 100644 --- a/drivers/usb/serial/garmin_gps.c +++ b/drivers/usb/serial/garmin_gps.c | |||
| @@ -1043,6 +1043,7 @@ static int garmin_write_bulk(struct usb_serial_port *port, | |||
| 1043 | "%s - usb_submit_urb(write bulk) failed with status = %d\n", | 1043 | "%s - usb_submit_urb(write bulk) failed with status = %d\n", |
| 1044 | __func__, status); | 1044 | __func__, status); |
| 1045 | count = status; | 1045 | count = status; |
| 1046 | kfree(buffer); | ||
| 1046 | } | 1047 | } |
| 1047 | 1048 | ||
| 1048 | /* we are done with this urb, so let the host driver | 1049 | /* we are done with this urb, so let the host driver |
diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c index dcc0c58aaad5..d50e5773483f 100644 --- a/drivers/usb/serial/io_edgeport.c +++ b/drivers/usb/serial/io_edgeport.c | |||
| @@ -2751,6 +2751,11 @@ static int edge_startup(struct usb_serial *serial) | |||
| 2751 | EDGE_COMPATIBILITY_MASK1, | 2751 | EDGE_COMPATIBILITY_MASK1, |
| 2752 | EDGE_COMPATIBILITY_MASK2 }; | 2752 | EDGE_COMPATIBILITY_MASK2 }; |
| 2753 | 2753 | ||
| 2754 | if (serial->num_bulk_in < 1 || serial->num_interrupt_in < 1) { | ||
| 2755 | dev_err(&serial->interface->dev, "missing endpoints\n"); | ||
| 2756 | return -ENODEV; | ||
| 2757 | } | ||
| 2758 | |||
| 2754 | dev = serial->dev; | 2759 | dev = serial->dev; |
| 2755 | 2760 | ||
| 2756 | /* create our private serial structure */ | 2761 | /* create our private serial structure */ |
diff --git a/drivers/usb/serial/io_ti.c b/drivers/usb/serial/io_ti.c index c339163698eb..9a0db2965fbb 100644 --- a/drivers/usb/serial/io_ti.c +++ b/drivers/usb/serial/io_ti.c | |||
| @@ -1499,8 +1499,7 @@ static int do_boot_mode(struct edgeport_serial *serial, | |||
| 1499 | 1499 | ||
| 1500 | dev_dbg(dev, "%s - Download successful -- Device rebooting...\n", __func__); | 1500 | dev_dbg(dev, "%s - Download successful -- Device rebooting...\n", __func__); |
| 1501 | 1501 | ||
| 1502 | /* return an error on purpose */ | 1502 | return 1; |
| 1503 | return -ENODEV; | ||
| 1504 | } | 1503 | } |
| 1505 | 1504 | ||
| 1506 | stayinbootmode: | 1505 | stayinbootmode: |
| @@ -1508,7 +1507,7 @@ stayinbootmode: | |||
| 1508 | dev_dbg(dev, "%s - STAYING IN BOOT MODE\n", __func__); | 1507 | dev_dbg(dev, "%s - STAYING IN BOOT MODE\n", __func__); |
| 1509 | serial->product_info.TiMode = TI_MODE_BOOT; | 1508 | serial->product_info.TiMode = TI_MODE_BOOT; |
| 1510 | 1509 | ||
| 1511 | return 0; | 1510 | return 1; |
| 1512 | } | 1511 | } |
| 1513 | 1512 | ||
| 1514 | static int ti_do_config(struct edgeport_port *port, int feature, int on) | 1513 | static int ti_do_config(struct edgeport_port *port, int feature, int on) |
| @@ -2546,6 +2545,13 @@ static int edge_startup(struct usb_serial *serial) | |||
| 2546 | int status; | 2545 | int status; |
| 2547 | u16 product_id; | 2546 | u16 product_id; |
| 2548 | 2547 | ||
| 2548 | /* Make sure we have the required endpoints when in download mode. */ | ||
| 2549 | if (serial->interface->cur_altsetting->desc.bNumEndpoints > 1) { | ||
| 2550 | if (serial->num_bulk_in < serial->num_ports || | ||
| 2551 | serial->num_bulk_out < serial->num_ports) | ||
| 2552 | return -ENODEV; | ||
| 2553 | } | ||
| 2554 | |||
| 2549 | /* create our private serial structure */ | 2555 | /* create our private serial structure */ |
| 2550 | edge_serial = kzalloc(sizeof(struct edgeport_serial), GFP_KERNEL); | 2556 | edge_serial = kzalloc(sizeof(struct edgeport_serial), GFP_KERNEL); |
| 2551 | if (!edge_serial) | 2557 | if (!edge_serial) |
| @@ -2553,14 +2559,18 @@ static int edge_startup(struct usb_serial *serial) | |||
| 2553 | 2559 | ||
| 2554 | mutex_init(&edge_serial->es_lock); | 2560 | mutex_init(&edge_serial->es_lock); |
| 2555 | edge_serial->serial = serial; | 2561 | edge_serial->serial = serial; |
| 2562 | INIT_DELAYED_WORK(&edge_serial->heartbeat_work, edge_heartbeat_work); | ||
| 2556 | usb_set_serial_data(serial, edge_serial); | 2563 | usb_set_serial_data(serial, edge_serial); |
| 2557 | 2564 | ||
| 2558 | status = download_fw(edge_serial); | 2565 | status = download_fw(edge_serial); |
| 2559 | if (status) { | 2566 | if (status < 0) { |
| 2560 | kfree(edge_serial); | 2567 | kfree(edge_serial); |
| 2561 | return status; | 2568 | return status; |
| 2562 | } | 2569 | } |
| 2563 | 2570 | ||
| 2571 | if (status > 0) | ||
| 2572 | return 1; /* bind but do not register any ports */ | ||
| 2573 | |||
| 2564 | product_id = le16_to_cpu( | 2574 | product_id = le16_to_cpu( |
| 2565 | edge_serial->serial->dev->descriptor.idProduct); | 2575 | edge_serial->serial->dev->descriptor.idProduct); |
| 2566 | 2576 | ||
| @@ -2572,7 +2582,6 @@ static int edge_startup(struct usb_serial *serial) | |||
| 2572 | } | 2582 | } |
| 2573 | } | 2583 | } |
| 2574 | 2584 | ||
| 2575 | INIT_DELAYED_WORK(&edge_serial->heartbeat_work, edge_heartbeat_work); | ||
| 2576 | edge_heartbeat_schedule(edge_serial); | 2585 | edge_heartbeat_schedule(edge_serial); |
| 2577 | 2586 | ||
| 2578 | return 0; | 2587 | return 0; |
| @@ -2580,6 +2589,9 @@ static int edge_startup(struct usb_serial *serial) | |||
| 2580 | 2589 | ||
| 2581 | static void edge_disconnect(struct usb_serial *serial) | 2590 | static void edge_disconnect(struct usb_serial *serial) |
| 2582 | { | 2591 | { |
| 2592 | struct edgeport_serial *edge_serial = usb_get_serial_data(serial); | ||
| 2593 | |||
| 2594 | cancel_delayed_work_sync(&edge_serial->heartbeat_work); | ||
| 2583 | } | 2595 | } |
| 2584 | 2596 | ||
| 2585 | static void edge_release(struct usb_serial *serial) | 2597 | static void edge_release(struct usb_serial *serial) |
diff --git a/drivers/usb/serial/iuu_phoenix.c b/drivers/usb/serial/iuu_phoenix.c index 344b4eea4bd5..d57fb5199218 100644 --- a/drivers/usb/serial/iuu_phoenix.c +++ b/drivers/usb/serial/iuu_phoenix.c | |||
| @@ -68,6 +68,16 @@ struct iuu_private { | |||
| 68 | u32 clk; | 68 | u32 clk; |
| 69 | }; | 69 | }; |
| 70 | 70 | ||
| 71 | static int iuu_attach(struct usb_serial *serial) | ||
| 72 | { | ||
| 73 | unsigned char num_ports = serial->num_ports; | ||
| 74 | |||
| 75 | if (serial->num_bulk_in < num_ports || serial->num_bulk_out < num_ports) | ||
| 76 | return -ENODEV; | ||
| 77 | |||
| 78 | return 0; | ||
| 79 | } | ||
| 80 | |||
| 71 | static int iuu_port_probe(struct usb_serial_port *port) | 81 | static int iuu_port_probe(struct usb_serial_port *port) |
| 72 | { | 82 | { |
| 73 | struct iuu_private *priv; | 83 | struct iuu_private *priv; |
| @@ -1196,6 +1206,7 @@ static struct usb_serial_driver iuu_device = { | |||
| 1196 | .tiocmset = iuu_tiocmset, | 1206 | .tiocmset = iuu_tiocmset, |
| 1197 | .set_termios = iuu_set_termios, | 1207 | .set_termios = iuu_set_termios, |
| 1198 | .init_termios = iuu_init_termios, | 1208 | .init_termios = iuu_init_termios, |
| 1209 | .attach = iuu_attach, | ||
| 1199 | .port_probe = iuu_port_probe, | 1210 | .port_probe = iuu_port_probe, |
| 1200 | .port_remove = iuu_port_remove, | 1211 | .port_remove = iuu_port_remove, |
| 1201 | }; | 1212 | }; |
diff --git a/drivers/usb/serial/keyspan_pda.c b/drivers/usb/serial/keyspan_pda.c index e49ad0c63ad8..83523fcf6fb9 100644 --- a/drivers/usb/serial/keyspan_pda.c +++ b/drivers/usb/serial/keyspan_pda.c | |||
| @@ -699,6 +699,19 @@ MODULE_FIRMWARE("keyspan_pda/keyspan_pda.fw"); | |||
| 699 | MODULE_FIRMWARE("keyspan_pda/xircom_pgs.fw"); | 699 | MODULE_FIRMWARE("keyspan_pda/xircom_pgs.fw"); |
| 700 | #endif | 700 | #endif |
| 701 | 701 | ||
| 702 | static int keyspan_pda_attach(struct usb_serial *serial) | ||
| 703 | { | ||
| 704 | unsigned char num_ports = serial->num_ports; | ||
| 705 | |||
| 706 | if (serial->num_bulk_out < num_ports || | ||
| 707 | serial->num_interrupt_in < num_ports) { | ||
| 708 | dev_err(&serial->interface->dev, "missing endpoints\n"); | ||
| 709 | return -ENODEV; | ||
| 710 | } | ||
| 711 | |||
| 712 | return 0; | ||
| 713 | } | ||
| 714 | |||
| 702 | static int keyspan_pda_port_probe(struct usb_serial_port *port) | 715 | static int keyspan_pda_port_probe(struct usb_serial_port *port) |
| 703 | { | 716 | { |
| 704 | 717 | ||
| @@ -776,6 +789,7 @@ static struct usb_serial_driver keyspan_pda_device = { | |||
| 776 | .break_ctl = keyspan_pda_break_ctl, | 789 | .break_ctl = keyspan_pda_break_ctl, |
| 777 | .tiocmget = keyspan_pda_tiocmget, | 790 | .tiocmget = keyspan_pda_tiocmget, |
| 778 | .tiocmset = keyspan_pda_tiocmset, | 791 | .tiocmset = keyspan_pda_tiocmset, |
| 792 | .attach = keyspan_pda_attach, | ||
| 779 | .port_probe = keyspan_pda_port_probe, | 793 | .port_probe = keyspan_pda_port_probe, |
| 780 | .port_remove = keyspan_pda_port_remove, | 794 | .port_remove = keyspan_pda_port_remove, |
| 781 | }; | 795 | }; |
diff --git a/drivers/usb/serial/kobil_sct.c b/drivers/usb/serial/kobil_sct.c index 2363654cafc9..813035f51fe7 100644 --- a/drivers/usb/serial/kobil_sct.c +++ b/drivers/usb/serial/kobil_sct.c | |||
| @@ -51,6 +51,7 @@ | |||
| 51 | 51 | ||
| 52 | 52 | ||
| 53 | /* Function prototypes */ | 53 | /* Function prototypes */ |
| 54 | static int kobil_attach(struct usb_serial *serial); | ||
| 54 | static int kobil_port_probe(struct usb_serial_port *probe); | 55 | static int kobil_port_probe(struct usb_serial_port *probe); |
| 55 | static int kobil_port_remove(struct usb_serial_port *probe); | 56 | static int kobil_port_remove(struct usb_serial_port *probe); |
| 56 | static int kobil_open(struct tty_struct *tty, struct usb_serial_port *port); | 57 | static int kobil_open(struct tty_struct *tty, struct usb_serial_port *port); |
| @@ -86,6 +87,7 @@ static struct usb_serial_driver kobil_device = { | |||
| 86 | .description = "KOBIL USB smart card terminal", | 87 | .description = "KOBIL USB smart card terminal", |
| 87 | .id_table = id_table, | 88 | .id_table = id_table, |
| 88 | .num_ports = 1, | 89 | .num_ports = 1, |
| 90 | .attach = kobil_attach, | ||
| 89 | .port_probe = kobil_port_probe, | 91 | .port_probe = kobil_port_probe, |
| 90 | .port_remove = kobil_port_remove, | 92 | .port_remove = kobil_port_remove, |
| 91 | .ioctl = kobil_ioctl, | 93 | .ioctl = kobil_ioctl, |
| @@ -113,6 +115,16 @@ struct kobil_private { | |||
| 113 | }; | 115 | }; |
| 114 | 116 | ||
| 115 | 117 | ||
| 118 | static int kobil_attach(struct usb_serial *serial) | ||
| 119 | { | ||
| 120 | if (serial->num_interrupt_out < serial->num_ports) { | ||
| 121 | dev_err(&serial->interface->dev, "missing interrupt-out endpoint\n"); | ||
| 122 | return -ENODEV; | ||
| 123 | } | ||
| 124 | |||
| 125 | return 0; | ||
| 126 | } | ||
| 127 | |||
| 116 | static int kobil_port_probe(struct usb_serial_port *port) | 128 | static int kobil_port_probe(struct usb_serial_port *port) |
| 117 | { | 129 | { |
| 118 | struct usb_serial *serial = port->serial; | 130 | struct usb_serial *serial = port->serial; |
diff --git a/drivers/usb/serial/mos7720.c b/drivers/usb/serial/mos7720.c index d52caa03679c..91bc170b408a 100644 --- a/drivers/usb/serial/mos7720.c +++ b/drivers/usb/serial/mos7720.c | |||
| @@ -65,8 +65,6 @@ struct moschip_port { | |||
| 65 | struct urb *write_urb_pool[NUM_URBS]; | 65 | struct urb *write_urb_pool[NUM_URBS]; |
| 66 | }; | 66 | }; |
| 67 | 67 | ||
| 68 | static struct usb_serial_driver moschip7720_2port_driver; | ||
| 69 | |||
| 70 | #define USB_VENDOR_ID_MOSCHIP 0x9710 | 68 | #define USB_VENDOR_ID_MOSCHIP 0x9710 |
| 71 | #define MOSCHIP_DEVICE_ID_7720 0x7720 | 69 | #define MOSCHIP_DEVICE_ID_7720 0x7720 |
| 72 | #define MOSCHIP_DEVICE_ID_7715 0x7715 | 70 | #define MOSCHIP_DEVICE_ID_7715 0x7715 |
| @@ -970,25 +968,6 @@ static void mos7720_bulk_out_data_callback(struct urb *urb) | |||
| 970 | tty_port_tty_wakeup(&mos7720_port->port->port); | 968 | tty_port_tty_wakeup(&mos7720_port->port->port); |
| 971 | } | 969 | } |
| 972 | 970 | ||
| 973 | /* | ||
| 974 | * mos77xx_probe | ||
| 975 | * this function installs the appropriate read interrupt endpoint callback | ||
| 976 | * depending on whether the device is a 7720 or 7715, thus avoiding costly | ||
| 977 | * run-time checks in the high-frequency callback routine itself. | ||
| 978 | */ | ||
| 979 | static int mos77xx_probe(struct usb_serial *serial, | ||
| 980 | const struct usb_device_id *id) | ||
| 981 | { | ||
| 982 | if (id->idProduct == MOSCHIP_DEVICE_ID_7715) | ||
| 983 | moschip7720_2port_driver.read_int_callback = | ||
| 984 | mos7715_interrupt_callback; | ||
| 985 | else | ||
| 986 | moschip7720_2port_driver.read_int_callback = | ||
| 987 | mos7720_interrupt_callback; | ||
| 988 | |||
| 989 | return 0; | ||
| 990 | } | ||
| 991 | |||
| 992 | static int mos77xx_calc_num_ports(struct usb_serial *serial) | 971 | static int mos77xx_calc_num_ports(struct usb_serial *serial) |
| 993 | { | 972 | { |
| 994 | u16 product = le16_to_cpu(serial->dev->descriptor.idProduct); | 973 | u16 product = le16_to_cpu(serial->dev->descriptor.idProduct); |
| @@ -1917,6 +1896,11 @@ static int mos7720_startup(struct usb_serial *serial) | |||
| 1917 | u16 product; | 1896 | u16 product; |
| 1918 | int ret_val; | 1897 | int ret_val; |
| 1919 | 1898 | ||
| 1899 | if (serial->num_bulk_in < 2 || serial->num_bulk_out < 2) { | ||
| 1900 | dev_err(&serial->interface->dev, "missing bulk endpoints\n"); | ||
| 1901 | return -ENODEV; | ||
| 1902 | } | ||
| 1903 | |||
| 1920 | product = le16_to_cpu(serial->dev->descriptor.idProduct); | 1904 | product = le16_to_cpu(serial->dev->descriptor.idProduct); |
| 1921 | dev = serial->dev; | 1905 | dev = serial->dev; |
| 1922 | 1906 | ||
| @@ -1941,19 +1925,18 @@ static int mos7720_startup(struct usb_serial *serial) | |||
| 1941 | tmp->interrupt_in_endpointAddress; | 1925 | tmp->interrupt_in_endpointAddress; |
| 1942 | serial->port[1]->interrupt_in_urb = NULL; | 1926 | serial->port[1]->interrupt_in_urb = NULL; |
| 1943 | serial->port[1]->interrupt_in_buffer = NULL; | 1927 | serial->port[1]->interrupt_in_buffer = NULL; |
| 1928 | |||
| 1929 | if (serial->port[0]->interrupt_in_urb) { | ||
| 1930 | struct urb *urb = serial->port[0]->interrupt_in_urb; | ||
| 1931 | |||
| 1932 | urb->complete = mos7715_interrupt_callback; | ||
| 1933 | } | ||
| 1944 | } | 1934 | } |
| 1945 | 1935 | ||
| 1946 | /* setting configuration feature to one */ | 1936 | /* setting configuration feature to one */ |
| 1947 | usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), | 1937 | usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), |
| 1948 | (__u8)0x03, 0x00, 0x01, 0x00, NULL, 0x00, 5000); | 1938 | (__u8)0x03, 0x00, 0x01, 0x00, NULL, 0x00, 5000); |
| 1949 | 1939 | ||
| 1950 | /* start the interrupt urb */ | ||
| 1951 | ret_val = usb_submit_urb(serial->port[0]->interrupt_in_urb, GFP_KERNEL); | ||
| 1952 | if (ret_val) | ||
| 1953 | dev_err(&dev->dev, | ||
| 1954 | "%s - Error %d submitting control urb\n", | ||
| 1955 | __func__, ret_val); | ||
| 1956 | |||
| 1957 | #ifdef CONFIG_USB_SERIAL_MOS7715_PARPORT | 1940 | #ifdef CONFIG_USB_SERIAL_MOS7715_PARPORT |
| 1958 | if (product == MOSCHIP_DEVICE_ID_7715) { | 1941 | if (product == MOSCHIP_DEVICE_ID_7715) { |
| 1959 | ret_val = mos7715_parport_init(serial); | 1942 | ret_val = mos7715_parport_init(serial); |
| @@ -1961,6 +1944,13 @@ static int mos7720_startup(struct usb_serial *serial) | |||
| 1961 | return ret_val; | 1944 | return ret_val; |
| 1962 | } | 1945 | } |
| 1963 | #endif | 1946 | #endif |
| 1947 | /* start the interrupt urb */ | ||
| 1948 | ret_val = usb_submit_urb(serial->port[0]->interrupt_in_urb, GFP_KERNEL); | ||
| 1949 | if (ret_val) { | ||
| 1950 | dev_err(&dev->dev, "failed to submit interrupt urb: %d\n", | ||
| 1951 | ret_val); | ||
| 1952 | } | ||
| 1953 | |||
| 1964 | /* LSR For Port 1 */ | 1954 | /* LSR For Port 1 */ |
| 1965 | read_mos_reg(serial, 0, MOS7720_LSR, &data); | 1955 | read_mos_reg(serial, 0, MOS7720_LSR, &data); |
| 1966 | dev_dbg(&dev->dev, "LSR:%x\n", data); | 1956 | dev_dbg(&dev->dev, "LSR:%x\n", data); |
| @@ -1970,6 +1960,8 @@ static int mos7720_startup(struct usb_serial *serial) | |||
| 1970 | 1960 | ||
| 1971 | static void mos7720_release(struct usb_serial *serial) | 1961 | static void mos7720_release(struct usb_serial *serial) |
| 1972 | { | 1962 | { |
| 1963 | usb_kill_urb(serial->port[0]->interrupt_in_urb); | ||
| 1964 | |||
| 1973 | #ifdef CONFIG_USB_SERIAL_MOS7715_PARPORT | 1965 | #ifdef CONFIG_USB_SERIAL_MOS7715_PARPORT |
| 1974 | /* close the parallel port */ | 1966 | /* close the parallel port */ |
| 1975 | 1967 | ||
| @@ -2019,11 +2011,6 @@ static int mos7720_port_probe(struct usb_serial_port *port) | |||
| 2019 | if (!mos7720_port) | 2011 | if (!mos7720_port) |
| 2020 | return -ENOMEM; | 2012 | return -ENOMEM; |
| 2021 | 2013 | ||
| 2022 | /* Initialize all port interrupt end point to port 0 int endpoint. | ||
| 2023 | * Our device has only one interrupt endpoint common to all ports. | ||
| 2024 | */ | ||
| 2025 | port->interrupt_in_endpointAddress = | ||
| 2026 | port->serial->port[0]->interrupt_in_endpointAddress; | ||
| 2027 | mos7720_port->port = port; | 2014 | mos7720_port->port = port; |
| 2028 | 2015 | ||
| 2029 | usb_set_serial_port_data(port, mos7720_port); | 2016 | usb_set_serial_port_data(port, mos7720_port); |
| @@ -2053,7 +2040,6 @@ static struct usb_serial_driver moschip7720_2port_driver = { | |||
| 2053 | .close = mos7720_close, | 2040 | .close = mos7720_close, |
| 2054 | .throttle = mos7720_throttle, | 2041 | .throttle = mos7720_throttle, |
| 2055 | .unthrottle = mos7720_unthrottle, | 2042 | .unthrottle = mos7720_unthrottle, |
| 2056 | .probe = mos77xx_probe, | ||
| 2057 | .attach = mos7720_startup, | 2043 | .attach = mos7720_startup, |
| 2058 | .release = mos7720_release, | 2044 | .release = mos7720_release, |
| 2059 | .port_probe = mos7720_port_probe, | 2045 | .port_probe = mos7720_port_probe, |
| @@ -2067,7 +2053,7 @@ static struct usb_serial_driver moschip7720_2port_driver = { | |||
| 2067 | .chars_in_buffer = mos7720_chars_in_buffer, | 2053 | .chars_in_buffer = mos7720_chars_in_buffer, |
| 2068 | .break_ctl = mos7720_break, | 2054 | .break_ctl = mos7720_break, |
| 2069 | .read_bulk_callback = mos7720_bulk_in_callback, | 2055 | .read_bulk_callback = mos7720_bulk_in_callback, |
| 2070 | .read_int_callback = NULL /* dynamically assigned in probe() */ | 2056 | .read_int_callback = mos7720_interrupt_callback, |
| 2071 | }; | 2057 | }; |
| 2072 | 2058 | ||
| 2073 | static struct usb_serial_driver * const serial_drivers[] = { | 2059 | static struct usb_serial_driver * const serial_drivers[] = { |
diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c index 9a220b8e810f..ea27fb23967a 100644 --- a/drivers/usb/serial/mos7840.c +++ b/drivers/usb/serial/mos7840.c | |||
| @@ -214,7 +214,6 @@ MODULE_DEVICE_TABLE(usb, id_table); | |||
| 214 | 214 | ||
| 215 | struct moschip_port { | 215 | struct moschip_port { |
| 216 | int port_num; /*Actual port number in the device(1,2,etc) */ | 216 | int port_num; /*Actual port number in the device(1,2,etc) */ |
| 217 | struct urb *write_urb; /* write URB for this port */ | ||
| 218 | struct urb *read_urb; /* read URB for this port */ | 217 | struct urb *read_urb; /* read URB for this port */ |
| 219 | __u8 shadowLCR; /* last LCR value received */ | 218 | __u8 shadowLCR; /* last LCR value received */ |
| 220 | __u8 shadowMCR; /* last MCR value received */ | 219 | __u8 shadowMCR; /* last MCR value received */ |
| @@ -1037,9 +1036,7 @@ static int mos7840_open(struct tty_struct *tty, struct usb_serial_port *port) | |||
| 1037 | serial, | 1036 | serial, |
| 1038 | serial->port[0]->interrupt_in_urb->interval); | 1037 | serial->port[0]->interrupt_in_urb->interval); |
| 1039 | 1038 | ||
| 1040 | /* start interrupt read for mos7840 * | 1039 | /* start interrupt read for mos7840 */ |
| 1041 | * will continue as long as mos7840 is connected */ | ||
| 1042 | |||
| 1043 | response = | 1040 | response = |
| 1044 | usb_submit_urb(serial->port[0]->interrupt_in_urb, | 1041 | usb_submit_urb(serial->port[0]->interrupt_in_urb, |
| 1045 | GFP_KERNEL); | 1042 | GFP_KERNEL); |
| @@ -1186,7 +1183,6 @@ static void mos7840_close(struct usb_serial_port *port) | |||
| 1186 | } | 1183 | } |
| 1187 | } | 1184 | } |
| 1188 | 1185 | ||
| 1189 | usb_kill_urb(mos7840_port->write_urb); | ||
| 1190 | usb_kill_urb(mos7840_port->read_urb); | 1186 | usb_kill_urb(mos7840_port->read_urb); |
| 1191 | mos7840_port->read_urb_busy = false; | 1187 | mos7840_port->read_urb_busy = false; |
| 1192 | 1188 | ||
| @@ -1199,12 +1195,6 @@ static void mos7840_close(struct usb_serial_port *port) | |||
| 1199 | } | 1195 | } |
| 1200 | } | 1196 | } |
| 1201 | 1197 | ||
| 1202 | if (mos7840_port->write_urb) { | ||
| 1203 | /* if this urb had a transfer buffer already (old tx) free it */ | ||
| 1204 | kfree(mos7840_port->write_urb->transfer_buffer); | ||
| 1205 | usb_free_urb(mos7840_port->write_urb); | ||
| 1206 | } | ||
| 1207 | |||
| 1208 | Data = 0x0; | 1198 | Data = 0x0; |
| 1209 | mos7840_set_uart_reg(port, MODEM_CONTROL_REGISTER, Data); | 1199 | mos7840_set_uart_reg(port, MODEM_CONTROL_REGISTER, Data); |
| 1210 | 1200 | ||
| @@ -2113,6 +2103,17 @@ static int mos7840_calc_num_ports(struct usb_serial *serial) | |||
| 2113 | return mos7840_num_ports; | 2103 | return mos7840_num_ports; |
| 2114 | } | 2104 | } |
| 2115 | 2105 | ||
| 2106 | static int mos7840_attach(struct usb_serial *serial) | ||
| 2107 | { | ||
| 2108 | if (serial->num_bulk_in < serial->num_ports || | ||
| 2109 | serial->num_bulk_out < serial->num_ports) { | ||
| 2110 | dev_err(&serial->interface->dev, "missing endpoints\n"); | ||
| 2111 | return -ENODEV; | ||
| 2112 | } | ||
| 2113 | |||
| 2114 | return 0; | ||
| 2115 | } | ||
| 2116 | |||
| 2116 | static int mos7840_port_probe(struct usb_serial_port *port) | 2117 | static int mos7840_port_probe(struct usb_serial_port *port) |
| 2117 | { | 2118 | { |
| 2118 | struct usb_serial *serial = port->serial; | 2119 | struct usb_serial *serial = port->serial; |
| @@ -2388,6 +2389,7 @@ static struct usb_serial_driver moschip7840_4port_device = { | |||
| 2388 | .tiocmset = mos7840_tiocmset, | 2389 | .tiocmset = mos7840_tiocmset, |
| 2389 | .tiocmiwait = usb_serial_generic_tiocmiwait, | 2390 | .tiocmiwait = usb_serial_generic_tiocmiwait, |
| 2390 | .get_icount = usb_serial_generic_get_icount, | 2391 | .get_icount = usb_serial_generic_get_icount, |
| 2392 | .attach = mos7840_attach, | ||
| 2391 | .port_probe = mos7840_port_probe, | 2393 | .port_probe = mos7840_port_probe, |
| 2392 | .port_remove = mos7840_port_remove, | 2394 | .port_remove = mos7840_port_remove, |
| 2393 | .read_bulk_callback = mos7840_bulk_in_callback, | 2395 | .read_bulk_callback = mos7840_bulk_in_callback, |
diff --git a/drivers/usb/serial/omninet.c b/drivers/usb/serial/omninet.c index f6c6900bccf0..a180b17d2432 100644 --- a/drivers/usb/serial/omninet.c +++ b/drivers/usb/serial/omninet.c | |||
| @@ -38,6 +38,7 @@ static int omninet_write(struct tty_struct *tty, struct usb_serial_port *port, | |||
| 38 | const unsigned char *buf, int count); | 38 | const unsigned char *buf, int count); |
| 39 | static int omninet_write_room(struct tty_struct *tty); | 39 | static int omninet_write_room(struct tty_struct *tty); |
| 40 | static void omninet_disconnect(struct usb_serial *serial); | 40 | static void omninet_disconnect(struct usb_serial *serial); |
| 41 | static int omninet_attach(struct usb_serial *serial); | ||
| 41 | static int omninet_port_probe(struct usb_serial_port *port); | 42 | static int omninet_port_probe(struct usb_serial_port *port); |
| 42 | static int omninet_port_remove(struct usb_serial_port *port); | 43 | static int omninet_port_remove(struct usb_serial_port *port); |
| 43 | 44 | ||
| @@ -56,6 +57,7 @@ static struct usb_serial_driver zyxel_omninet_device = { | |||
| 56 | .description = "ZyXEL - omni.net lcd plus usb", | 57 | .description = "ZyXEL - omni.net lcd plus usb", |
| 57 | .id_table = id_table, | 58 | .id_table = id_table, |
| 58 | .num_ports = 1, | 59 | .num_ports = 1, |
| 60 | .attach = omninet_attach, | ||
| 59 | .port_probe = omninet_port_probe, | 61 | .port_probe = omninet_port_probe, |
| 60 | .port_remove = omninet_port_remove, | 62 | .port_remove = omninet_port_remove, |
| 61 | .open = omninet_open, | 63 | .open = omninet_open, |
| @@ -104,6 +106,17 @@ struct omninet_data { | |||
| 104 | __u8 od_outseq; /* Sequence number for bulk_out URBs */ | 106 | __u8 od_outseq; /* Sequence number for bulk_out URBs */ |
| 105 | }; | 107 | }; |
| 106 | 108 | ||
| 109 | static int omninet_attach(struct usb_serial *serial) | ||
| 110 | { | ||
| 111 | /* The second bulk-out endpoint is used for writing. */ | ||
| 112 | if (serial->num_bulk_out < 2) { | ||
| 113 | dev_err(&serial->interface->dev, "missing endpoints\n"); | ||
| 114 | return -ENODEV; | ||
| 115 | } | ||
| 116 | |||
| 117 | return 0; | ||
| 118 | } | ||
| 119 | |||
| 107 | static int omninet_port_probe(struct usb_serial_port *port) | 120 | static int omninet_port_probe(struct usb_serial_port *port) |
| 108 | { | 121 | { |
| 109 | struct omninet_data *od; | 122 | struct omninet_data *od; |
diff --git a/drivers/usb/serial/oti6858.c b/drivers/usb/serial/oti6858.c index a4b88bc038b6..b8bf52bf7a94 100644 --- a/drivers/usb/serial/oti6858.c +++ b/drivers/usb/serial/oti6858.c | |||
| @@ -134,6 +134,7 @@ static int oti6858_chars_in_buffer(struct tty_struct *tty); | |||
| 134 | static int oti6858_tiocmget(struct tty_struct *tty); | 134 | static int oti6858_tiocmget(struct tty_struct *tty); |
| 135 | static int oti6858_tiocmset(struct tty_struct *tty, | 135 | static int oti6858_tiocmset(struct tty_struct *tty, |
| 136 | unsigned int set, unsigned int clear); | 136 | unsigned int set, unsigned int clear); |
| 137 | static int oti6858_attach(struct usb_serial *serial); | ||
| 137 | static int oti6858_port_probe(struct usb_serial_port *port); | 138 | static int oti6858_port_probe(struct usb_serial_port *port); |
| 138 | static int oti6858_port_remove(struct usb_serial_port *port); | 139 | static int oti6858_port_remove(struct usb_serial_port *port); |
| 139 | 140 | ||
| @@ -158,6 +159,7 @@ static struct usb_serial_driver oti6858_device = { | |||
| 158 | .write_bulk_callback = oti6858_write_bulk_callback, | 159 | .write_bulk_callback = oti6858_write_bulk_callback, |
| 159 | .write_room = oti6858_write_room, | 160 | .write_room = oti6858_write_room, |
| 160 | .chars_in_buffer = oti6858_chars_in_buffer, | 161 | .chars_in_buffer = oti6858_chars_in_buffer, |
| 162 | .attach = oti6858_attach, | ||
| 161 | .port_probe = oti6858_port_probe, | 163 | .port_probe = oti6858_port_probe, |
| 162 | .port_remove = oti6858_port_remove, | 164 | .port_remove = oti6858_port_remove, |
| 163 | }; | 165 | }; |
| @@ -324,6 +326,20 @@ static void send_data(struct work_struct *work) | |||
| 324 | usb_serial_port_softint(port); | 326 | usb_serial_port_softint(port); |
| 325 | } | 327 | } |
| 326 | 328 | ||
| 329 | static int oti6858_attach(struct usb_serial *serial) | ||
| 330 | { | ||
| 331 | unsigned char num_ports = serial->num_ports; | ||
| 332 | |||
| 333 | if (serial->num_bulk_in < num_ports || | ||
| 334 | serial->num_bulk_out < num_ports || | ||
| 335 | serial->num_interrupt_in < num_ports) { | ||
| 336 | dev_err(&serial->interface->dev, "missing endpoints\n"); | ||
| 337 | return -ENODEV; | ||
| 338 | } | ||
| 339 | |||
| 340 | return 0; | ||
| 341 | } | ||
| 342 | |||
| 327 | static int oti6858_port_probe(struct usb_serial_port *port) | 343 | static int oti6858_port_probe(struct usb_serial_port *port) |
| 328 | { | 344 | { |
| 329 | struct oti6858_private *priv; | 345 | struct oti6858_private *priv; |
diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c index ae682e4eeaef..46fca6b75846 100644 --- a/drivers/usb/serial/pl2303.c +++ b/drivers/usb/serial/pl2303.c | |||
| @@ -220,9 +220,17 @@ static int pl2303_probe(struct usb_serial *serial, | |||
| 220 | static int pl2303_startup(struct usb_serial *serial) | 220 | static int pl2303_startup(struct usb_serial *serial) |
| 221 | { | 221 | { |
| 222 | struct pl2303_serial_private *spriv; | 222 | struct pl2303_serial_private *spriv; |
| 223 | unsigned char num_ports = serial->num_ports; | ||
| 223 | enum pl2303_type type = TYPE_01; | 224 | enum pl2303_type type = TYPE_01; |
| 224 | unsigned char *buf; | 225 | unsigned char *buf; |
| 225 | 226 | ||
| 227 | if (serial->num_bulk_in < num_ports || | ||
| 228 | serial->num_bulk_out < num_ports || | ||
| 229 | serial->num_interrupt_in < num_ports) { | ||
| 230 | dev_err(&serial->interface->dev, "missing endpoints\n"); | ||
| 231 | return -ENODEV; | ||
| 232 | } | ||
| 233 | |||
| 226 | spriv = kzalloc(sizeof(*spriv), GFP_KERNEL); | 234 | spriv = kzalloc(sizeof(*spriv), GFP_KERNEL); |
| 227 | if (!spriv) | 235 | if (!spriv) |
| 228 | return -ENOMEM; | 236 | return -ENOMEM; |
diff --git a/drivers/usb/serial/quatech2.c b/drivers/usb/serial/quatech2.c index 659cb8606bd9..5709cc93b083 100644 --- a/drivers/usb/serial/quatech2.c +++ b/drivers/usb/serial/quatech2.c | |||
| @@ -408,16 +408,12 @@ static void qt2_close(struct usb_serial_port *port) | |||
| 408 | { | 408 | { |
| 409 | struct usb_serial *serial; | 409 | struct usb_serial *serial; |
| 410 | struct qt2_port_private *port_priv; | 410 | struct qt2_port_private *port_priv; |
| 411 | unsigned long flags; | ||
| 412 | int i; | 411 | int i; |
| 413 | 412 | ||
| 414 | serial = port->serial; | 413 | serial = port->serial; |
| 415 | port_priv = usb_get_serial_port_data(port); | 414 | port_priv = usb_get_serial_port_data(port); |
| 416 | 415 | ||
| 417 | spin_lock_irqsave(&port_priv->urb_lock, flags); | ||
| 418 | usb_kill_urb(port_priv->write_urb); | 416 | usb_kill_urb(port_priv->write_urb); |
| 419 | port_priv->urb_in_use = false; | ||
| 420 | spin_unlock_irqrestore(&port_priv->urb_lock, flags); | ||
| 421 | 417 | ||
| 422 | /* flush the port transmit buffer */ | 418 | /* flush the port transmit buffer */ |
| 423 | i = usb_control_msg(serial->dev, | 419 | i = usb_control_msg(serial->dev, |
diff --git a/drivers/usb/serial/spcp8x5.c b/drivers/usb/serial/spcp8x5.c index ef0dbf0703c5..475e6c31b266 100644 --- a/drivers/usb/serial/spcp8x5.c +++ b/drivers/usb/serial/spcp8x5.c | |||
| @@ -154,6 +154,19 @@ static int spcp8x5_probe(struct usb_serial *serial, | |||
| 154 | return 0; | 154 | return 0; |
| 155 | } | 155 | } |
| 156 | 156 | ||
| 157 | static int spcp8x5_attach(struct usb_serial *serial) | ||
| 158 | { | ||
| 159 | unsigned char num_ports = serial->num_ports; | ||
| 160 | |||
| 161 | if (serial->num_bulk_in < num_ports || | ||
| 162 | serial->num_bulk_out < num_ports) { | ||
| 163 | dev_err(&serial->interface->dev, "missing endpoints\n"); | ||
| 164 | return -ENODEV; | ||
| 165 | } | ||
| 166 | |||
| 167 | return 0; | ||
| 168 | } | ||
| 169 | |||
| 157 | static int spcp8x5_port_probe(struct usb_serial_port *port) | 170 | static int spcp8x5_port_probe(struct usb_serial_port *port) |
| 158 | { | 171 | { |
| 159 | const struct usb_device_id *id = usb_get_serial_data(port->serial); | 172 | const struct usb_device_id *id = usb_get_serial_data(port->serial); |
| @@ -477,6 +490,7 @@ static struct usb_serial_driver spcp8x5_device = { | |||
| 477 | .tiocmget = spcp8x5_tiocmget, | 490 | .tiocmget = spcp8x5_tiocmget, |
| 478 | .tiocmset = spcp8x5_tiocmset, | 491 | .tiocmset = spcp8x5_tiocmset, |
| 479 | .probe = spcp8x5_probe, | 492 | .probe = spcp8x5_probe, |
| 493 | .attach = spcp8x5_attach, | ||
| 480 | .port_probe = spcp8x5_port_probe, | 494 | .port_probe = spcp8x5_port_probe, |
| 481 | .port_remove = spcp8x5_port_remove, | 495 | .port_remove = spcp8x5_port_remove, |
| 482 | }; | 496 | }; |
diff --git a/drivers/usb/serial/ti_usb_3410_5052.c b/drivers/usb/serial/ti_usb_3410_5052.c index 8db9d071d940..64b85b8dedf3 100644 --- a/drivers/usb/serial/ti_usb_3410_5052.c +++ b/drivers/usb/serial/ti_usb_3410_5052.c | |||
| @@ -579,6 +579,13 @@ static int ti_startup(struct usb_serial *serial) | |||
| 579 | goto free_tdev; | 579 | goto free_tdev; |
| 580 | } | 580 | } |
| 581 | 581 | ||
| 582 | if (serial->num_bulk_in < serial->num_ports || | ||
| 583 | serial->num_bulk_out < serial->num_ports) { | ||
| 584 | dev_err(&serial->interface->dev, "missing endpoints\n"); | ||
| 585 | status = -ENODEV; | ||
| 586 | goto free_tdev; | ||
| 587 | } | ||
| 588 | |||
| 582 | return 0; | 589 | return 0; |
| 583 | 590 | ||
| 584 | free_tdev: | 591 | free_tdev: |
