diff options
author | Oliver Neukum <oliver@neukum.org> | 2009-01-27 11:21:40 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2009-03-24 19:20:29 -0400 |
commit | 4901b2c34ecb6fc45909228ad269c8126efe4401 (patch) | |
tree | dd7fe1bd0bb4b86859eaba4bcb27b5302e51d134 | |
parent | b633d28e2c5fbe1c8d163892644f57df04aa1421 (diff) |
USB: suspend/resume support for option driver
This patch implements suspend and resume methods for the
option driver. With my hardware I can even suspend the system
and keep up a connection for a short time.
Signed-off-by: Oliver Neukum <oneukum@suse.de>
Signed-Off-By: Matthias Urlichs <smurf@smurf.noris.de>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
-rw-r--r-- | drivers/usb/serial/option.c | 86 |
1 files changed, 80 insertions, 6 deletions
diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index 61ebddc48497..d560c0b54e6e 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c | |||
@@ -62,6 +62,8 @@ static int option_tiocmget(struct tty_struct *tty, struct file *file); | |||
62 | static int option_tiocmset(struct tty_struct *tty, struct file *file, | 62 | static int option_tiocmset(struct tty_struct *tty, struct file *file, |
63 | unsigned int set, unsigned int clear); | 63 | unsigned int set, unsigned int clear); |
64 | static int option_send_setup(struct tty_struct *tty, struct usb_serial_port *port); | 64 | static int option_send_setup(struct tty_struct *tty, struct usb_serial_port *port); |
65 | static int option_suspend(struct usb_serial *serial, pm_message_t message); | ||
66 | static int option_resume(struct usb_serial *serial); | ||
65 | 67 | ||
66 | /* Vendor and product IDs */ | 68 | /* Vendor and product IDs */ |
67 | #define OPTION_VENDOR_ID 0x0AF0 | 69 | #define OPTION_VENDOR_ID 0x0AF0 |
@@ -523,6 +525,8 @@ static struct usb_driver option_driver = { | |||
523 | .name = "option", | 525 | .name = "option", |
524 | .probe = usb_serial_probe, | 526 | .probe = usb_serial_probe, |
525 | .disconnect = usb_serial_disconnect, | 527 | .disconnect = usb_serial_disconnect, |
528 | .suspend = usb_serial_suspend, | ||
529 | .resume = usb_serial_resume, | ||
526 | .id_table = option_ids, | 530 | .id_table = option_ids, |
527 | .no_dynamic_id = 1, | 531 | .no_dynamic_id = 1, |
528 | }; | 532 | }; |
@@ -551,6 +555,8 @@ static struct usb_serial_driver option_1port_device = { | |||
551 | .attach = option_startup, | 555 | .attach = option_startup, |
552 | .shutdown = option_shutdown, | 556 | .shutdown = option_shutdown, |
553 | .read_int_callback = option_instat_callback, | 557 | .read_int_callback = option_instat_callback, |
558 | .suspend = option_suspend, | ||
559 | .resume = option_resume, | ||
554 | }; | 560 | }; |
555 | 561 | ||
556 | static int debug; | 562 | static int debug; |
@@ -821,10 +827,10 @@ static void option_instat_callback(struct urb *urb) | |||
821 | req_pkt->bRequestType, req_pkt->bRequest); | 827 | req_pkt->bRequestType, req_pkt->bRequest); |
822 | } | 828 | } |
823 | } else | 829 | } else |
824 | dbg("%s: error %d", __func__, status); | 830 | err("%s: error %d", __func__, status); |
825 | 831 | ||
826 | /* Resubmit urb so we continue receiving IRQ data */ | 832 | /* Resubmit urb so we continue receiving IRQ data */ |
827 | if (status != -ESHUTDOWN) { | 833 | if (status != -ESHUTDOWN && status != -ENOENT) { |
828 | urb->dev = serial->dev; | 834 | urb->dev = serial->dev; |
829 | err = usb_submit_urb(urb, GFP_ATOMIC); | 835 | err = usb_submit_urb(urb, GFP_ATOMIC); |
830 | if (err) | 836 | if (err) |
@@ -843,7 +849,6 @@ static int option_write_room(struct tty_struct *tty) | |||
843 | 849 | ||
844 | portdata = usb_get_serial_port_data(port); | 850 | portdata = usb_get_serial_port_data(port); |
845 | 851 | ||
846 | |||
847 | for (i = 0; i < N_OUT_URB; i++) { | 852 | for (i = 0; i < N_OUT_URB; i++) { |
848 | this_urb = portdata->out_urbs[i]; | 853 | this_urb = portdata->out_urbs[i]; |
849 | if (this_urb && !test_bit(i, &portdata->out_busy)) | 854 | if (this_urb && !test_bit(i, &portdata->out_busy)) |
@@ -1105,14 +1110,12 @@ bail_out_error: | |||
1105 | return 1; | 1110 | return 1; |
1106 | } | 1111 | } |
1107 | 1112 | ||
1108 | static void option_shutdown(struct usb_serial *serial) | 1113 | static void stop_read_write_urbs(struct usb_serial *serial) |
1109 | { | 1114 | { |
1110 | int i, j; | 1115 | int i, j; |
1111 | struct usb_serial_port *port; | 1116 | struct usb_serial_port *port; |
1112 | struct option_port_private *portdata; | 1117 | struct option_port_private *portdata; |
1113 | 1118 | ||
1114 | dbg("%s", __func__); | ||
1115 | |||
1116 | /* Stop reading/writing urbs */ | 1119 | /* Stop reading/writing urbs */ |
1117 | for (i = 0; i < serial->num_ports; ++i) { | 1120 | for (i = 0; i < serial->num_ports; ++i) { |
1118 | port = serial->port[i]; | 1121 | port = serial->port[i]; |
@@ -1122,6 +1125,17 @@ static void option_shutdown(struct usb_serial *serial) | |||
1122 | for (j = 0; j < N_OUT_URB; j++) | 1125 | for (j = 0; j < N_OUT_URB; j++) |
1123 | usb_kill_urb(portdata->out_urbs[j]); | 1126 | usb_kill_urb(portdata->out_urbs[j]); |
1124 | } | 1127 | } |
1128 | } | ||
1129 | |||
1130 | static void option_shutdown(struct usb_serial *serial) | ||
1131 | { | ||
1132 | int i, j; | ||
1133 | struct usb_serial_port *port; | ||
1134 | struct option_port_private *portdata; | ||
1135 | |||
1136 | dbg("%s", __func__); | ||
1137 | |||
1138 | stop_read_write_urbs(serial); | ||
1125 | 1139 | ||
1126 | /* Now free them */ | 1140 | /* Now free them */ |
1127 | for (i = 0; i < serial->num_ports; ++i) { | 1141 | for (i = 0; i < serial->num_ports; ++i) { |
@@ -1152,6 +1166,66 @@ static void option_shutdown(struct usb_serial *serial) | |||
1152 | } | 1166 | } |
1153 | } | 1167 | } |
1154 | 1168 | ||
1169 | static int option_suspend(struct usb_serial *serial, pm_message_t message) | ||
1170 | { | ||
1171 | dbg("%s entered", __func__); | ||
1172 | stop_read_write_urbs(serial); | ||
1173 | |||
1174 | return 0; | ||
1175 | } | ||
1176 | |||
1177 | static int option_resume(struct usb_serial *serial) | ||
1178 | { | ||
1179 | int err, i, j; | ||
1180 | struct usb_serial_port *port; | ||
1181 | struct urb *urb; | ||
1182 | struct option_port_private *portdata; | ||
1183 | |||
1184 | dbg("%s entered", __func__); | ||
1185 | /* get the interrupt URBs resubmitted unconditionally */ | ||
1186 | for (i = 0; i < serial->num_ports; i++) { | ||
1187 | port = serial->port[i]; | ||
1188 | if (!port->interrupt_in_urb) { | ||
1189 | dbg("%s: No interrupt URB for port %d\n", __func__, i); | ||
1190 | continue; | ||
1191 | } | ||
1192 | port->interrupt_in_urb->dev = serial->dev; | ||
1193 | err = usb_submit_urb(port->interrupt_in_urb, GFP_NOIO); | ||
1194 | dbg("Submitted interrupt URB for port %d (result %d)", i, err); | ||
1195 | if (err < 0) { | ||
1196 | err("%s: Error %d for interrupt URB of port%d", | ||
1197 | __func__, err, i); | ||
1198 | return err; | ||
1199 | } | ||
1200 | } | ||
1201 | |||
1202 | for (i = 0; i < serial->num_ports; i++) { | ||
1203 | /* walk all ports */ | ||
1204 | port = serial->port[i]; | ||
1205 | portdata = usb_get_serial_port_data(port); | ||
1206 | mutex_lock(&port->mutex); | ||
1207 | |||
1208 | /* skip closed ports */ | ||
1209 | if (!port->port.count) { | ||
1210 | mutex_unlock(&port->mutex); | ||
1211 | continue; | ||
1212 | } | ||
1213 | |||
1214 | for (j = 0; j < N_IN_URB; j++) { | ||
1215 | urb = portdata->in_urbs[j]; | ||
1216 | err = usb_submit_urb(urb, GFP_NOIO); | ||
1217 | if (err < 0) { | ||
1218 | mutex_unlock(&port->mutex); | ||
1219 | err("%s: Error %d for bulk URB %d", | ||
1220 | __func__, err, i); | ||
1221 | return err; | ||
1222 | } | ||
1223 | } | ||
1224 | mutex_unlock(&port->mutex); | ||
1225 | } | ||
1226 | return 0; | ||
1227 | } | ||
1228 | |||
1155 | MODULE_AUTHOR(DRIVER_AUTHOR); | 1229 | MODULE_AUTHOR(DRIVER_AUTHOR); |
1156 | MODULE_DESCRIPTION(DRIVER_DESC); | 1230 | MODULE_DESCRIPTION(DRIVER_DESC); |
1157 | MODULE_VERSION(DRIVER_VERSION); | 1231 | MODULE_VERSION(DRIVER_VERSION); |