aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb
diff options
context:
space:
mode:
authorJohan Hovold <jhovold@gmail.com>2013-07-26 05:55:19 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2013-07-26 17:14:09 -0400
commit05cf0dec5ccc696a7636c84b265b477173498156 (patch)
tree83abe5bd4e48655518d3f6d3d9614c41be2ebe94 /drivers/usb
parent40c24f2893ba0ba7df485871f6aac0c197ceef5b (diff)
USB: mos7840: fix race in led handling
Fix race in LED handling introduced by commit 0eafe4de ("USB: serial: mos7840: add support for MCS7810 devices") which reused the port control urb for manipulating the LED without making sure that the urb is not already in use. This could lead to the control urb being manipulated while in flight. Fix by adding a dedicated LED urb and ctrlrequest along with a LED-busy flag to handle concurrency. Cc: stable@vger.kernel.org Signed-off-by: Johan Hovold <jhovold@gmail.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/usb')
-rw-r--r--drivers/usb/serial/mos7840.c59
1 files changed, 37 insertions, 22 deletions
diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c
index e95d91434785..c10fc15bf851 100644
--- a/drivers/usb/serial/mos7840.c
+++ b/drivers/usb/serial/mos7840.c
@@ -185,6 +185,7 @@
185 185
186enum mos7840_flag { 186enum mos7840_flag {
187 MOS7840_FLAG_CTRL_BUSY, 187 MOS7840_FLAG_CTRL_BUSY,
188 MOS7840_FLAG_LED_BUSY,
188}; 189};
189 190
190static const struct usb_device_id id_table[] = { 191static const struct usb_device_id id_table[] = {
@@ -240,9 +241,10 @@ struct moschip_port {
240 241
241 /* For device(s) with LED indicator */ 242 /* For device(s) with LED indicator */
242 bool has_led; 243 bool has_led;
243 bool led_flag;
244 struct timer_list led_timer1; /* Timer for LED on */ 244 struct timer_list led_timer1; /* Timer for LED on */
245 struct timer_list led_timer2; /* Timer for LED off */ 245 struct timer_list led_timer2; /* Timer for LED off */
246 struct urb *led_urb;
247 struct usb_ctrlrequest *led_dr;
246 248
247 unsigned long flags; 249 unsigned long flags;
248}; 250};
@@ -535,7 +537,7 @@ static void mos7840_set_led_async(struct moschip_port *mcs, __u16 wval,
535 __u16 reg) 537 __u16 reg)
536{ 538{
537 struct usb_device *dev = mcs->port->serial->dev; 539 struct usb_device *dev = mcs->port->serial->dev;
538 struct usb_ctrlrequest *dr = mcs->dr; 540 struct usb_ctrlrequest *dr = mcs->led_dr;
539 541
540 dr->bRequestType = MCS_WR_RTYPE; 542 dr->bRequestType = MCS_WR_RTYPE;
541 dr->bRequest = MCS_WRREQ; 543 dr->bRequest = MCS_WRREQ;
@@ -543,10 +545,10 @@ static void mos7840_set_led_async(struct moschip_port *mcs, __u16 wval,
543 dr->wIndex = cpu_to_le16(reg); 545 dr->wIndex = cpu_to_le16(reg);
544 dr->wLength = cpu_to_le16(0); 546 dr->wLength = cpu_to_le16(0);
545 547
546 usb_fill_control_urb(mcs->control_urb, dev, usb_sndctrlpipe(dev, 0), 548 usb_fill_control_urb(mcs->led_urb, dev, usb_sndctrlpipe(dev, 0),
547 (unsigned char *)dr, NULL, 0, mos7840_set_led_callback, NULL); 549 (unsigned char *)dr, NULL, 0, mos7840_set_led_callback, NULL);
548 550
549 usb_submit_urb(mcs->control_urb, GFP_ATOMIC); 551 usb_submit_urb(mcs->led_urb, GFP_ATOMIC);
550} 552}
551 553
552static void mos7840_set_led_sync(struct usb_serial_port *port, __u16 reg, 554static void mos7840_set_led_sync(struct usb_serial_port *port, __u16 reg,
@@ -572,7 +574,19 @@ static void mos7840_led_flag_off(unsigned long arg)
572{ 574{
573 struct moschip_port *mcs = (struct moschip_port *) arg; 575 struct moschip_port *mcs = (struct moschip_port *) arg;
574 576
575 mcs->led_flag = false; 577 clear_bit_unlock(MOS7840_FLAG_LED_BUSY, &mcs->flags);
578}
579
580static void mos7840_led_activity(struct usb_serial_port *port)
581{
582 struct moschip_port *mos7840_port = usb_get_serial_port_data(port);
583
584 if (test_and_set_bit_lock(MOS7840_FLAG_LED_BUSY, &mos7840_port->flags))
585 return;
586
587 mos7840_set_led_async(mos7840_port, 0x0301, MODEM_CONTROL_REGISTER);
588 mod_timer(&mos7840_port->led_timer1,
589 jiffies + msecs_to_jiffies(LED_ON_MS));
576} 590}
577 591
578/***************************************************************************** 592/*****************************************************************************
@@ -770,14 +784,8 @@ static void mos7840_bulk_in_callback(struct urb *urb)
770 return; 784 return;
771 } 785 }
772 786
773 /* Turn on LED */ 787 if (mos7840_port->has_led)
774 if (mos7840_port->has_led && !mos7840_port->led_flag) { 788 mos7840_led_activity(port);
775 mos7840_port->led_flag = true;
776 mos7840_set_led_async(mos7840_port, 0x0301,
777 MODEM_CONTROL_REGISTER);
778 mod_timer(&mos7840_port->led_timer1,
779 jiffies + msecs_to_jiffies(LED_ON_MS));
780 }
781 789
782 mos7840_port->read_urb_busy = true; 790 mos7840_port->read_urb_busy = true;
783 retval = usb_submit_urb(mos7840_port->read_urb, GFP_ATOMIC); 791 retval = usb_submit_urb(mos7840_port->read_urb, GFP_ATOMIC);
@@ -1454,13 +1462,8 @@ static int mos7840_write(struct tty_struct *tty, struct usb_serial_port *port,
1454 data1 = urb->transfer_buffer; 1462 data1 = urb->transfer_buffer;
1455 dev_dbg(&port->dev, "bulkout endpoint is %d\n", port->bulk_out_endpointAddress); 1463 dev_dbg(&port->dev, "bulkout endpoint is %d\n", port->bulk_out_endpointAddress);
1456 1464
1457 /* Turn on LED */ 1465 if (mos7840_port->has_led)
1458 if (mos7840_port->has_led && !mos7840_port->led_flag) { 1466 mos7840_led_activity(port);
1459 mos7840_port->led_flag = true;
1460 mos7840_set_led_sync(port, MODEM_CONTROL_REGISTER, 0x0301);
1461 mod_timer(&mos7840_port->led_timer1,
1462 jiffies + msecs_to_jiffies(LED_ON_MS));
1463 }
1464 1467
1465 /* send it down the pipe */ 1468 /* send it down the pipe */
1466 status = usb_submit_urb(urb, GFP_ATOMIC); 1469 status = usb_submit_urb(urb, GFP_ATOMIC);
@@ -2412,6 +2415,14 @@ static int mos7840_port_probe(struct usb_serial_port *port)
2412 if (device_type == MOSCHIP_DEVICE_ID_7810) { 2415 if (device_type == MOSCHIP_DEVICE_ID_7810) {
2413 mos7840_port->has_led = true; 2416 mos7840_port->has_led = true;
2414 2417
2418 mos7840_port->led_urb = usb_alloc_urb(0, GFP_KERNEL);
2419 mos7840_port->led_dr = kmalloc(sizeof(*mos7840_port->led_dr),
2420 GFP_KERNEL);
2421 if (!mos7840_port->led_urb || !mos7840_port->led_dr) {
2422 status = -ENOMEM;
2423 goto error;
2424 }
2425
2415 init_timer(&mos7840_port->led_timer1); 2426 init_timer(&mos7840_port->led_timer1);
2416 mos7840_port->led_timer1.function = mos7840_led_off; 2427 mos7840_port->led_timer1.function = mos7840_led_off;
2417 mos7840_port->led_timer1.expires = 2428 mos7840_port->led_timer1.expires =
@@ -2424,8 +2435,6 @@ static int mos7840_port_probe(struct usb_serial_port *port)
2424 jiffies + msecs_to_jiffies(LED_OFF_MS); 2435 jiffies + msecs_to_jiffies(LED_OFF_MS);
2425 mos7840_port->led_timer2.data = (unsigned long)mos7840_port; 2436 mos7840_port->led_timer2.data = (unsigned long)mos7840_port;
2426 2437
2427 mos7840_port->led_flag = false;
2428
2429 /* Turn off LED */ 2438 /* Turn off LED */
2430 mos7840_set_led_sync(port, MODEM_CONTROL_REGISTER, 0x0300); 2439 mos7840_set_led_sync(port, MODEM_CONTROL_REGISTER, 0x0300);
2431 } 2440 }
@@ -2447,6 +2456,8 @@ out:
2447 } 2456 }
2448 return 0; 2457 return 0;
2449error: 2458error:
2459 kfree(mos7840_port->led_dr);
2460 usb_free_urb(mos7840_port->led_urb);
2450 kfree(mos7840_port->dr); 2461 kfree(mos7840_port->dr);
2451 kfree(mos7840_port->ctrl_buf); 2462 kfree(mos7840_port->ctrl_buf);
2452 usb_free_urb(mos7840_port->control_urb); 2463 usb_free_urb(mos7840_port->control_urb);
@@ -2467,6 +2478,10 @@ static int mos7840_port_remove(struct usb_serial_port *port)
2467 2478
2468 del_timer_sync(&mos7840_port->led_timer1); 2479 del_timer_sync(&mos7840_port->led_timer1);
2469 del_timer_sync(&mos7840_port->led_timer2); 2480 del_timer_sync(&mos7840_port->led_timer2);
2481
2482 usb_kill_urb(mos7840_port->led_urb);
2483 usb_free_urb(mos7840_port->led_urb);
2484 kfree(mos7840_port->led_dr);
2470 } 2485 }
2471 usb_kill_urb(mos7840_port->control_urb); 2486 usb_kill_urb(mos7840_port->control_urb);
2472 usb_free_urb(mos7840_port->control_urb); 2487 usb_free_urb(mos7840_port->control_urb);