diff options
author | Oliver Neukum <oliver@neukum.org> | 2009-09-04 17:19:53 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2009-09-23 09:46:39 -0400 |
commit | e6929a9020acbeb04d9a3ad9a88234c15be808fd (patch) | |
tree | cc720f3426c9dc7b0d4d6b4e2fd259fcf0487495 /drivers/usb | |
parent | ad45f1dc836cb175e9aeea927837dd48039d652c (diff) |
USB: support for autosuspend in sierra while online
This implements support for autosuspend in the sierra driver while online.
Remote wakeup is used for reception. Transmission is facilitated with a queue
and the asynchronous autopm mechanism. To prevent races a private flag
for opened ports and a counter of running transmissions needs to be added.
Signed-off-by: Oliver Neukum <oliver@neukum.org>
Tested-by: Elina Pasheva <epasheva@sierrawireless.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb')
-rw-r--r-- | drivers/usb/serial/sierra.c | 157 |
1 files changed, 152 insertions, 5 deletions
diff --git a/drivers/usb/serial/sierra.c b/drivers/usb/serial/sierra.c index 55391bbe1230..68fa0e43b781 100644 --- a/drivers/usb/serial/sierra.c +++ b/drivers/usb/serial/sierra.c | |||
@@ -51,6 +51,12 @@ struct sierra_iface_info { | |||
51 | const u8 *ifaceinfo; /* pointer to the array holding the numbers */ | 51 | const u8 *ifaceinfo; /* pointer to the array holding the numbers */ |
52 | }; | 52 | }; |
53 | 53 | ||
54 | struct sierra_intf_private { | ||
55 | spinlock_t susp_lock; | ||
56 | unsigned int suspended:1; | ||
57 | int in_flight; | ||
58 | }; | ||
59 | |||
54 | static int sierra_set_power_state(struct usb_device *udev, __u16 swiState) | 60 | static int sierra_set_power_state(struct usb_device *udev, __u16 swiState) |
55 | { | 61 | { |
56 | int result; | 62 | int result; |
@@ -144,6 +150,7 @@ static int sierra_probe(struct usb_serial *serial, | |||
144 | { | 150 | { |
145 | int result = 0; | 151 | int result = 0; |
146 | struct usb_device *udev; | 152 | struct usb_device *udev; |
153 | struct sierra_intf_private *data; | ||
147 | u8 ifnum; | 154 | u8 ifnum; |
148 | 155 | ||
149 | udev = serial->dev; | 156 | udev = serial->dev; |
@@ -171,6 +178,11 @@ static int sierra_probe(struct usb_serial *serial, | |||
171 | return -ENODEV; | 178 | return -ENODEV; |
172 | } | 179 | } |
173 | 180 | ||
181 | data = serial->private = kzalloc(sizeof(struct sierra_intf_private), GFP_KERNEL); | ||
182 | if (!data) | ||
183 | return -ENOMEM; | ||
184 | spin_lock_init(&data->susp_lock); | ||
185 | |||
174 | return result; | 186 | return result; |
175 | } | 187 | } |
176 | 188 | ||
@@ -261,13 +273,18 @@ static struct usb_driver sierra_driver = { | |||
261 | .name = "sierra", | 273 | .name = "sierra", |
262 | .probe = usb_serial_probe, | 274 | .probe = usb_serial_probe, |
263 | .disconnect = usb_serial_disconnect, | 275 | .disconnect = usb_serial_disconnect, |
276 | .suspend = usb_serial_suspend, | ||
277 | .resume = usb_serial_resume, | ||
264 | .id_table = id_table, | 278 | .id_table = id_table, |
265 | .no_dynamic_id = 1, | 279 | .no_dynamic_id = 1, |
280 | .supports_autosuspend = 1, | ||
266 | }; | 281 | }; |
267 | 282 | ||
268 | struct sierra_port_private { | 283 | struct sierra_port_private { |
269 | spinlock_t lock; /* lock the structure */ | 284 | spinlock_t lock; /* lock the structure */ |
270 | int outstanding_urbs; /* number of out urbs in flight */ | 285 | int outstanding_urbs; /* number of out urbs in flight */ |
286 | struct usb_anchor active; | ||
287 | struct usb_anchor delayed; | ||
271 | 288 | ||
272 | /* Input endpoints and buffers for this port */ | 289 | /* Input endpoints and buffers for this port */ |
273 | struct urb *in_urbs[N_IN_URB]; | 290 | struct urb *in_urbs[N_IN_URB]; |
@@ -279,6 +296,8 @@ struct sierra_port_private { | |||
279 | int dsr_state; | 296 | int dsr_state; |
280 | int dcd_state; | 297 | int dcd_state; |
281 | int ri_state; | 298 | int ri_state; |
299 | |||
300 | unsigned int opened:1; | ||
282 | }; | 301 | }; |
283 | 302 | ||
284 | static int sierra_send_setup(struct usb_serial_port *port) | 303 | static int sierra_send_setup(struct usb_serial_port *port) |
@@ -390,21 +409,25 @@ static void sierra_outdat_callback(struct urb *urb) | |||
390 | { | 409 | { |
391 | struct usb_serial_port *port = urb->context; | 410 | struct usb_serial_port *port = urb->context; |
392 | struct sierra_port_private *portdata = usb_get_serial_port_data(port); | 411 | struct sierra_port_private *portdata = usb_get_serial_port_data(port); |
412 | struct sierra_intf_private *intfdata; | ||
393 | int status = urb->status; | 413 | int status = urb->status; |
394 | unsigned long flags; | ||
395 | 414 | ||
396 | dev_dbg(&port->dev, "%s - port %d\n", __func__, port->number); | 415 | dev_dbg(&port->dev, "%s - port %d\n", __func__, port->number); |
416 | intfdata = port->serial->private; | ||
397 | 417 | ||
398 | /* free up the transfer buffer, as usb_free_urb() does not do this */ | 418 | /* free up the transfer buffer, as usb_free_urb() does not do this */ |
399 | kfree(urb->transfer_buffer); | 419 | kfree(urb->transfer_buffer); |
400 | 420 | usb_autopm_put_interface_async(port->serial->interface); | |
401 | if (status) | 421 | if (status) |
402 | dev_dbg(&port->dev, "%s - nonzero write bulk status " | 422 | dev_dbg(&port->dev, "%s - nonzero write bulk status " |
403 | "received: %d\n", __func__, status); | 423 | "received: %d\n", __func__, status); |
404 | 424 | ||
405 | spin_lock_irqsave(&portdata->lock, flags); | 425 | spin_lock(&portdata->lock); |
406 | --portdata->outstanding_urbs; | 426 | --portdata->outstanding_urbs; |
407 | spin_unlock_irqrestore(&portdata->lock, flags); | 427 | spin_unlock(&portdata->lock); |
428 | spin_lock(&intfdata->susp_lock); | ||
429 | --intfdata->in_flight; | ||
430 | spin_unlock(&intfdata->susp_lock); | ||
408 | 431 | ||
409 | usb_serial_port_softint(port); | 432 | usb_serial_port_softint(port); |
410 | } | 433 | } |
@@ -414,6 +437,7 @@ static int sierra_write(struct tty_struct *tty, struct usb_serial_port *port, | |||
414 | const unsigned char *buf, int count) | 437 | const unsigned char *buf, int count) |
415 | { | 438 | { |
416 | struct sierra_port_private *portdata = usb_get_serial_port_data(port); | 439 | struct sierra_port_private *portdata = usb_get_serial_port_data(port); |
440 | struct sierra_intf_private *intfdata; | ||
417 | struct usb_serial *serial = port->serial; | 441 | struct usb_serial *serial = port->serial; |
418 | unsigned long flags; | 442 | unsigned long flags; |
419 | unsigned char *buffer; | 443 | unsigned char *buffer; |
@@ -426,9 +450,9 @@ static int sierra_write(struct tty_struct *tty, struct usb_serial_port *port, | |||
426 | return 0; | 450 | return 0; |
427 | 451 | ||
428 | portdata = usb_get_serial_port_data(port); | 452 | portdata = usb_get_serial_port_data(port); |
453 | intfdata = serial->private; | ||
429 | 454 | ||
430 | dev_dbg(&port->dev, "%s: write (%zd bytes)\n", __func__, writesize); | 455 | dev_dbg(&port->dev, "%s: write (%zd bytes)\n", __func__, writesize); |
431 | |||
432 | spin_lock_irqsave(&portdata->lock, flags); | 456 | spin_lock_irqsave(&portdata->lock, flags); |
433 | dev_dbg(&port->dev, "%s - outstanding_urbs: %d\n", __func__, | 457 | dev_dbg(&port->dev, "%s - outstanding_urbs: %d\n", __func__, |
434 | portdata->outstanding_urbs); | 458 | portdata->outstanding_urbs); |
@@ -442,6 +466,14 @@ static int sierra_write(struct tty_struct *tty, struct usb_serial_port *port, | |||
442 | portdata->outstanding_urbs); | 466 | portdata->outstanding_urbs); |
443 | spin_unlock_irqrestore(&portdata->lock, flags); | 467 | spin_unlock_irqrestore(&portdata->lock, flags); |
444 | 468 | ||
469 | retval = usb_autopm_get_interface_async(serial->interface); | ||
470 | if (retval < 0) { | ||
471 | spin_lock_irqsave(&portdata->lock, flags); | ||
472 | portdata->outstanding_urbs--; | ||
473 | spin_unlock_irqrestore(&portdata->lock, flags); | ||
474 | goto error_simple; | ||
475 | } | ||
476 | |||
445 | buffer = kmalloc(writesize, GFP_ATOMIC); | 477 | buffer = kmalloc(writesize, GFP_ATOMIC); |
446 | if (!buffer) { | 478 | if (!buffer) { |
447 | dev_err(&port->dev, "out of memory\n"); | 479 | dev_err(&port->dev, "out of memory\n"); |
@@ -468,14 +500,29 @@ static int sierra_write(struct tty_struct *tty, struct usb_serial_port *port, | |||
468 | /* Handle the need to send a zero length packet */ | 500 | /* Handle the need to send a zero length packet */ |
469 | urb->transfer_flags |= URB_ZERO_PACKET; | 501 | urb->transfer_flags |= URB_ZERO_PACKET; |
470 | 502 | ||
503 | spin_lock_irqsave(&intfdata->susp_lock, flags); | ||
504 | |||
505 | if (intfdata->suspended) { | ||
506 | usb_anchor_urb(urb, &portdata->delayed); | ||
507 | spin_unlock_irqrestore(&intfdata->susp_lock, flags); | ||
508 | goto skip_power; | ||
509 | } else { | ||
510 | usb_anchor_urb(urb, &portdata->active); | ||
511 | } | ||
471 | /* send it down the pipe */ | 512 | /* send it down the pipe */ |
472 | retval = usb_submit_urb(urb, GFP_ATOMIC); | 513 | retval = usb_submit_urb(urb, GFP_ATOMIC); |
473 | if (retval) { | 514 | if (retval) { |
515 | usb_unanchor_urb(urb); | ||
516 | spin_unlock_irqrestore(&intfdata->susp_lock, flags); | ||
474 | dev_err(&port->dev, "%s - usb_submit_urb(write bulk) failed " | 517 | dev_err(&port->dev, "%s - usb_submit_urb(write bulk) failed " |
475 | "with status = %d\n", __func__, retval); | 518 | "with status = %d\n", __func__, retval); |
476 | goto error; | 519 | goto error; |
520 | } else { | ||
521 | intfdata->in_flight++; | ||
522 | spin_unlock_irqrestore(&intfdata->susp_lock, flags); | ||
477 | } | 523 | } |
478 | 524 | ||
525 | skip_power: | ||
479 | /* we are done with this urb, so let the host driver | 526 | /* we are done with this urb, so let the host driver |
480 | * really free it when it is finished with it */ | 527 | * really free it when it is finished with it */ |
481 | usb_free_urb(urb); | 528 | usb_free_urb(urb); |
@@ -491,6 +538,8 @@ error_no_buffer: | |||
491 | dev_dbg(&port->dev, "%s - 2. outstanding_urbs: %d\n", __func__, | 538 | dev_dbg(&port->dev, "%s - 2. outstanding_urbs: %d\n", __func__, |
492 | portdata->outstanding_urbs); | 539 | portdata->outstanding_urbs); |
493 | spin_unlock_irqrestore(&portdata->lock, flags); | 540 | spin_unlock_irqrestore(&portdata->lock, flags); |
541 | usb_autopm_put_interface_async(serial->interface); | ||
542 | error_simple: | ||
494 | return retval; | 543 | return retval; |
495 | } | 544 | } |
496 | 545 | ||
@@ -530,6 +579,7 @@ static void sierra_indat_callback(struct urb *urb) | |||
530 | 579 | ||
531 | /* Resubmit urb so we continue receiving */ | 580 | /* Resubmit urb so we continue receiving */ |
532 | if (port->port.count && status != -ESHUTDOWN && status != -EPERM) { | 581 | if (port->port.count && status != -ESHUTDOWN && status != -EPERM) { |
582 | usb_mark_last_busy(port->serial->dev); | ||
533 | err = usb_submit_urb(urb, GFP_ATOMIC); | 583 | err = usb_submit_urb(urb, GFP_ATOMIC); |
534 | if (err) | 584 | if (err) |
535 | dev_err(&port->dev, "resubmit read urb failed." | 585 | dev_err(&port->dev, "resubmit read urb failed." |
@@ -591,6 +641,7 @@ static void sierra_instat_callback(struct urb *urb) | |||
591 | 641 | ||
592 | /* Resubmit urb so we continue receiving IRQ data */ | 642 | /* Resubmit urb so we continue receiving IRQ data */ |
593 | if (port->port.count && status != -ESHUTDOWN && status != -ENOENT) { | 643 | if (port->port.count && status != -ESHUTDOWN && status != -ENOENT) { |
644 | usb_mark_last_busy(serial->dev); | ||
594 | urb->dev = serial->dev; | 645 | urb->dev = serial->dev; |
595 | err = usb_submit_urb(urb, GFP_ATOMIC); | 646 | err = usb_submit_urb(urb, GFP_ATOMIC); |
596 | if (err) | 647 | if (err) |
@@ -711,6 +762,8 @@ static void sierra_close(struct usb_serial_port *port) | |||
711 | int i; | 762 | int i; |
712 | struct usb_serial *serial = port->serial; | 763 | struct usb_serial *serial = port->serial; |
713 | struct sierra_port_private *portdata; | 764 | struct sierra_port_private *portdata; |
765 | struct sierra_intf_private *intfdata = port->serial->private; | ||
766 | |||
714 | 767 | ||
715 | dev_dbg(&port->dev, "%s\n", __func__); | 768 | dev_dbg(&port->dev, "%s\n", __func__); |
716 | portdata = usb_get_serial_port_data(port); | 769 | portdata = usb_get_serial_port_data(port); |
@@ -723,6 +776,10 @@ static void sierra_close(struct usb_serial_port *port) | |||
723 | if (!serial->disconnected) | 776 | if (!serial->disconnected) |
724 | sierra_send_setup(port); | 777 | sierra_send_setup(port); |
725 | mutex_unlock(&serial->disc_mutex); | 778 | mutex_unlock(&serial->disc_mutex); |
779 | spin_lock_irq(&intfdata->susp_lock); | ||
780 | portdata->opened = 0; | ||
781 | spin_unlock_irq(&intfdata->susp_lock); | ||
782 | |||
726 | 783 | ||
727 | /* Stop reading urbs */ | 784 | /* Stop reading urbs */ |
728 | sierra_stop_rx_urbs(port); | 785 | sierra_stop_rx_urbs(port); |
@@ -731,6 +788,8 @@ static void sierra_close(struct usb_serial_port *port) | |||
731 | sierra_release_urb(portdata->in_urbs[i]); | 788 | sierra_release_urb(portdata->in_urbs[i]); |
732 | portdata->in_urbs[i] = NULL; | 789 | portdata->in_urbs[i] = NULL; |
733 | } | 790 | } |
791 | usb_autopm_get_interface(serial->interface); | ||
792 | serial->interface->needs_remote_wakeup = 0; | ||
734 | } | 793 | } |
735 | } | 794 | } |
736 | 795 | ||
@@ -738,6 +797,7 @@ static int sierra_open(struct tty_struct *tty, struct usb_serial_port *port) | |||
738 | { | 797 | { |
739 | struct sierra_port_private *portdata; | 798 | struct sierra_port_private *portdata; |
740 | struct usb_serial *serial = port->serial; | 799 | struct usb_serial *serial = port->serial; |
800 | struct sierra_intf_private *intfdata = serial->private; | ||
741 | int i; | 801 | int i; |
742 | int err; | 802 | int err; |
743 | int endpoint; | 803 | int endpoint; |
@@ -771,6 +831,12 @@ static int sierra_open(struct tty_struct *tty, struct usb_serial_port *port) | |||
771 | } | 831 | } |
772 | sierra_send_setup(port); | 832 | sierra_send_setup(port); |
773 | 833 | ||
834 | serial->interface->needs_remote_wakeup = 1; | ||
835 | spin_lock_irq(&intfdata->susp_lock); | ||
836 | portdata->opened = 1; | ||
837 | spin_unlock_irq(&intfdata->susp_lock); | ||
838 | usb_autopm_put_interface(serial->interface); | ||
839 | |||
774 | return 0; | 840 | return 0; |
775 | } | 841 | } |
776 | 842 | ||
@@ -818,6 +884,8 @@ static int sierra_startup(struct usb_serial *serial) | |||
818 | return -ENOMEM; | 884 | return -ENOMEM; |
819 | } | 885 | } |
820 | spin_lock_init(&portdata->lock); | 886 | spin_lock_init(&portdata->lock); |
887 | init_usb_anchor(&portdata->active); | ||
888 | init_usb_anchor(&portdata->delayed); | ||
821 | /* Set the port private data pointer */ | 889 | /* Set the port private data pointer */ |
822 | usb_set_serial_port_data(port, portdata); | 890 | usb_set_serial_port_data(port, portdata); |
823 | } | 891 | } |
@@ -844,6 +912,83 @@ static void sierra_release(struct usb_serial *serial) | |||
844 | } | 912 | } |
845 | } | 913 | } |
846 | 914 | ||
915 | static void stop_read_write_urbs(struct usb_serial *serial) | ||
916 | { | ||
917 | int i, j; | ||
918 | struct usb_serial_port *port; | ||
919 | struct sierra_port_private *portdata; | ||
920 | |||
921 | /* Stop reading/writing urbs */ | ||
922 | for (i = 0; i < serial->num_ports; ++i) { | ||
923 | port = serial->port[i]; | ||
924 | portdata = usb_get_serial_port_data(port); | ||
925 | for (j = 0; j < N_IN_URB; j++) | ||
926 | usb_kill_urb(portdata->in_urbs[j]); | ||
927 | usb_kill_anchored_urbs(&portdata->active); | ||
928 | } | ||
929 | } | ||
930 | |||
931 | static int sierra_suspend(struct usb_serial *serial, pm_message_t message) | ||
932 | { | ||
933 | struct sierra_intf_private *intfdata; | ||
934 | int b; | ||
935 | |||
936 | if (serial->dev->auto_pm) { | ||
937 | intfdata = serial->private; | ||
938 | spin_lock_irq(&intfdata->susp_lock); | ||
939 | b = intfdata->in_flight; | ||
940 | |||
941 | if (b) { | ||
942 | spin_unlock_irq(&intfdata->susp_lock); | ||
943 | return -EBUSY; | ||
944 | } else { | ||
945 | intfdata->suspended = 1; | ||
946 | spin_unlock_irq(&intfdata->susp_lock); | ||
947 | } | ||
948 | } | ||
949 | stop_read_write_urbs(serial); | ||
950 | |||
951 | return 0; | ||
952 | } | ||
953 | |||
954 | static int sierra_resume(struct usb_serial *serial) | ||
955 | { | ||
956 | struct usb_serial_port *port; | ||
957 | struct sierra_intf_private *intfdata = serial->private; | ||
958 | struct sierra_port_private *portdata; | ||
959 | struct urb *urb; | ||
960 | int ec = 0; | ||
961 | int i, err; | ||
962 | |||
963 | spin_lock_irq(&intfdata->susp_lock); | ||
964 | for (i = 0; i < serial->num_ports; i++) { | ||
965 | port = serial->port[i]; | ||
966 | portdata = usb_get_serial_port_data(port); | ||
967 | |||
968 | while ((urb = usb_get_from_anchor(&portdata->delayed))) { | ||
969 | usb_anchor_urb(urb, &portdata->active); | ||
970 | intfdata->in_flight++; | ||
971 | err = usb_submit_urb(urb, GFP_ATOMIC); | ||
972 | if (err < 0) { | ||
973 | intfdata->in_flight--; | ||
974 | usb_unanchor_urb(urb); | ||
975 | usb_scuttle_anchored_urbs(&portdata->delayed); | ||
976 | break; | ||
977 | } | ||
978 | } | ||
979 | |||
980 | if (portdata->opened) { | ||
981 | err = sierra_submit_rx_urbs(port, GFP_ATOMIC); | ||
982 | if (err) | ||
983 | ec++; | ||
984 | } | ||
985 | } | ||
986 | intfdata->suspended = 0; | ||
987 | spin_unlock_irq(&intfdata->susp_lock); | ||
988 | |||
989 | return ec ? -EIO : 0; | ||
990 | } | ||
991 | |||
847 | static struct usb_serial_driver sierra_device = { | 992 | static struct usb_serial_driver sierra_device = { |
848 | .driver = { | 993 | .driver = { |
849 | .owner = THIS_MODULE, | 994 | .owner = THIS_MODULE, |
@@ -864,6 +1009,8 @@ static struct usb_serial_driver sierra_device = { | |||
864 | .tiocmset = sierra_tiocmset, | 1009 | .tiocmset = sierra_tiocmset, |
865 | .attach = sierra_startup, | 1010 | .attach = sierra_startup, |
866 | .release = sierra_release, | 1011 | .release = sierra_release, |
1012 | .suspend = sierra_suspend, | ||
1013 | .resume = sierra_resume, | ||
867 | .read_int_callback = sierra_instat_callback, | 1014 | .read_int_callback = sierra_instat_callback, |
868 | }; | 1015 | }; |
869 | 1016 | ||