]>
Commit | Line | Data |
---|---|---|
5fd54ace | 1 | // SPDX-License-Identifier: GPL-2.0 |
0d456194 MG |
2 | /* |
3 | USB Driver layer for GSM modems | |
4 | ||
5 | Copyright (C) 2005 Matthias Urlichs <smurf@smurf.noris.de> | |
6 | ||
0d456194 MG |
7 | Portions copied from the Keyspan driver by Hugh Blemings <hugh@blemings.org> |
8 | ||
9 | History: see the git log. | |
10 | ||
11 | Work sponsored by: Sigos GmbH, Germany <info@sigos.de> | |
12 | ||
13 | This driver exists because the "normal" serial driver doesn't work too well | |
14 | with GSM modems. Issues: | |
15 | - data loss -- one single Receive URB is not nearly enough | |
16 | - controlling the baud rate doesn't make sense | |
17 | */ | |
18 | ||
0d456194 MG |
19 | #define DRIVER_AUTHOR "Matthias Urlichs <smurf@smurf.noris.de>" |
20 | #define DRIVER_DESC "USB Driver for GSM modems" | |
21 | ||
22 | #include <linux/kernel.h> | |
23 | #include <linux/jiffies.h> | |
24 | #include <linux/errno.h> | |
25 | #include <linux/slab.h> | |
26 | #include <linux/tty.h> | |
27 | #include <linux/tty_flip.h> | |
28 | #include <linux/module.h> | |
29 | #include <linux/bitops.h> | |
66921edd | 30 | #include <linux/uaccess.h> |
0d456194 MG |
31 | #include <linux/usb.h> |
32 | #include <linux/usb/serial.h> | |
02303f73 | 33 | #include <linux/serial.h> |
0d456194 MG |
34 | #include "usb-wwan.h" |
35 | ||
669e729f DW |
36 | /* |
37 | * Generate DTR/RTS signals on the port using the SET_CONTROL_LINE_STATE request | |
38 | * in CDC ACM. | |
39 | */ | |
40 | static int usb_wwan_send_setup(struct usb_serial_port *port) | |
41 | { | |
42 | struct usb_serial *serial = port->serial; | |
43 | struct usb_wwan_port_private *portdata; | |
44 | int val = 0; | |
45 | int ifnum; | |
46 | int res; | |
47 | ||
48 | portdata = usb_get_serial_port_data(port); | |
49 | ||
50 | if (portdata->dtr_state) | |
51 | val |= 0x01; | |
52 | if (portdata->rts_state) | |
53 | val |= 0x02; | |
54 | ||
55 | ifnum = serial->interface->cur_altsetting->desc.bInterfaceNumber; | |
56 | ||
57 | res = usb_autopm_get_interface(serial->interface); | |
58 | if (res) | |
59 | return res; | |
60 | ||
61 | res = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), | |
62 | 0x22, 0x21, val, ifnum, NULL, 0, | |
63 | USB_CTRL_SET_TIMEOUT); | |
64 | ||
65 | usb_autopm_put_interface(port->serial->interface); | |
66 | ||
67 | return res; | |
68 | } | |
69 | ||
0d456194 MG |
70 | void usb_wwan_dtr_rts(struct usb_serial_port *port, int on) |
71 | { | |
0d456194 | 72 | struct usb_wwan_port_private *portdata; |
0d456194 MG |
73 | struct usb_wwan_intf_private *intfdata; |
74 | ||
37357ca5 | 75 | intfdata = usb_get_serial_data(port->serial); |
0d456194 | 76 | |
669e729f | 77 | if (!intfdata->use_send_setup) |
0d456194 MG |
78 | return; |
79 | ||
80 | portdata = usb_get_serial_port_data(port); | |
b2ca6990 | 81 | /* FIXME: locking */ |
0d456194 MG |
82 | portdata->rts_state = on; |
83 | portdata->dtr_state = on; | |
b2ca6990 | 84 | |
669e729f | 85 | usb_wwan_send_setup(port); |
0d456194 MG |
86 | } |
87 | EXPORT_SYMBOL(usb_wwan_dtr_rts); | |
88 | ||
60b33c13 | 89 | int usb_wwan_tiocmget(struct tty_struct *tty) |
0d456194 MG |
90 | { |
91 | struct usb_serial_port *port = tty->driver_data; | |
92 | unsigned int value; | |
93 | struct usb_wwan_port_private *portdata; | |
94 | ||
95 | portdata = usb_get_serial_port_data(port); | |
96 | ||
97 | value = ((portdata->rts_state) ? TIOCM_RTS : 0) | | |
98 | ((portdata->dtr_state) ? TIOCM_DTR : 0) | | |
99 | ((portdata->cts_state) ? TIOCM_CTS : 0) | | |
100 | ((portdata->dsr_state) ? TIOCM_DSR : 0) | | |
101 | ((portdata->dcd_state) ? TIOCM_CAR : 0) | | |
102 | ((portdata->ri_state) ? TIOCM_RNG : 0); | |
103 | ||
104 | return value; | |
105 | } | |
106 | EXPORT_SYMBOL(usb_wwan_tiocmget); | |
107 | ||
20b9d177 | 108 | int usb_wwan_tiocmset(struct tty_struct *tty, |
0d456194 MG |
109 | unsigned int set, unsigned int clear) |
110 | { | |
111 | struct usb_serial_port *port = tty->driver_data; | |
112 | struct usb_wwan_port_private *portdata; | |
113 | struct usb_wwan_intf_private *intfdata; | |
114 | ||
115 | portdata = usb_get_serial_port_data(port); | |
37357ca5 | 116 | intfdata = usb_get_serial_data(port->serial); |
0d456194 | 117 | |
669e729f | 118 | if (!intfdata->use_send_setup) |
0d456194 MG |
119 | return -EINVAL; |
120 | ||
121 | /* FIXME: what locks portdata fields ? */ | |
122 | if (set & TIOCM_RTS) | |
123 | portdata->rts_state = 1; | |
124 | if (set & TIOCM_DTR) | |
125 | portdata->dtr_state = 1; | |
126 | ||
127 | if (clear & TIOCM_RTS) | |
128 | portdata->rts_state = 0; | |
129 | if (clear & TIOCM_DTR) | |
130 | portdata->dtr_state = 0; | |
669e729f | 131 | return usb_wwan_send_setup(port); |
0d456194 MG |
132 | } |
133 | EXPORT_SYMBOL(usb_wwan_tiocmset); | |
134 | ||
02303f73 DW |
135 | static int get_serial_info(struct usb_serial_port *port, |
136 | struct serial_struct __user *retinfo) | |
137 | { | |
138 | struct serial_struct tmp; | |
139 | ||
02303f73 | 140 | memset(&tmp, 0, sizeof(tmp)); |
e5b1e206 | 141 | tmp.line = port->minor; |
1143832e | 142 | tmp.port = port->port_number; |
02303f73 DW |
143 | tmp.baud_base = tty_get_baud_rate(port->port.tty); |
144 | tmp.close_delay = port->port.close_delay / 10; | |
145 | tmp.closing_wait = port->port.closing_wait == ASYNC_CLOSING_WAIT_NONE ? | |
146 | ASYNC_CLOSING_WAIT_NONE : | |
147 | port->port.closing_wait / 10; | |
148 | ||
149 | if (copy_to_user(retinfo, &tmp, sizeof(*retinfo))) | |
150 | return -EFAULT; | |
151 | return 0; | |
152 | } | |
153 | ||
154 | static int set_serial_info(struct usb_serial_port *port, | |
155 | struct serial_struct __user *newinfo) | |
156 | { | |
157 | struct serial_struct new_serial; | |
158 | unsigned int closing_wait, close_delay; | |
159 | int retval = 0; | |
160 | ||
161 | if (copy_from_user(&new_serial, newinfo, sizeof(new_serial))) | |
162 | return -EFAULT; | |
163 | ||
164 | close_delay = new_serial.close_delay * 10; | |
165 | closing_wait = new_serial.closing_wait == ASYNC_CLOSING_WAIT_NONE ? | |
166 | ASYNC_CLOSING_WAIT_NONE : new_serial.closing_wait * 10; | |
167 | ||
168 | mutex_lock(&port->port.mutex); | |
169 | ||
170 | if (!capable(CAP_SYS_ADMIN)) { | |
171 | if ((close_delay != port->port.close_delay) || | |
172 | (closing_wait != port->port.closing_wait)) | |
173 | retval = -EPERM; | |
174 | else | |
175 | retval = -EOPNOTSUPP; | |
176 | } else { | |
177 | port->port.close_delay = close_delay; | |
178 | port->port.closing_wait = closing_wait; | |
179 | } | |
180 | ||
181 | mutex_unlock(&port->port.mutex); | |
182 | return retval; | |
183 | } | |
184 | ||
00a0d0d6 | 185 | int usb_wwan_ioctl(struct tty_struct *tty, |
02303f73 DW |
186 | unsigned int cmd, unsigned long arg) |
187 | { | |
188 | struct usb_serial_port *port = tty->driver_data; | |
189 | ||
a80be97d | 190 | dev_dbg(&port->dev, "%s cmd 0x%04x\n", __func__, cmd); |
02303f73 DW |
191 | |
192 | switch (cmd) { | |
193 | case TIOCGSERIAL: | |
194 | return get_serial_info(port, | |
195 | (struct serial_struct __user *) arg); | |
196 | case TIOCSSERIAL: | |
197 | return set_serial_info(port, | |
198 | (struct serial_struct __user *) arg); | |
199 | default: | |
200 | break; | |
201 | } | |
202 | ||
a80be97d | 203 | dev_dbg(&port->dev, "%s arg not supported\n", __func__); |
02303f73 DW |
204 | |
205 | return -ENOIOCTLCMD; | |
206 | } | |
207 | EXPORT_SYMBOL(usb_wwan_ioctl); | |
208 | ||
0d456194 MG |
209 | int usb_wwan_write(struct tty_struct *tty, struct usb_serial_port *port, |
210 | const unsigned char *buf, int count) | |
211 | { | |
212 | struct usb_wwan_port_private *portdata; | |
213 | struct usb_wwan_intf_private *intfdata; | |
214 | int i; | |
215 | int left, todo; | |
216 | struct urb *this_urb = NULL; /* spurious */ | |
217 | int err; | |
218 | unsigned long flags; | |
219 | ||
220 | portdata = usb_get_serial_port_data(port); | |
37357ca5 | 221 | intfdata = usb_get_serial_data(port->serial); |
0d456194 | 222 | |
a80be97d | 223 | dev_dbg(&port->dev, "%s: write (%d chars)\n", __func__, count); |
0d456194 MG |
224 | |
225 | i = 0; | |
226 | left = count; | |
227 | for (i = 0; left > 0 && i < N_OUT_URB; i++) { | |
228 | todo = left; | |
229 | if (todo > OUT_BUFLEN) | |
230 | todo = OUT_BUFLEN; | |
231 | ||
232 | this_urb = portdata->out_urbs[i]; | |
233 | if (test_and_set_bit(i, &portdata->out_busy)) { | |
234 | if (time_before(jiffies, | |
235 | portdata->tx_start_time[i] + 10 * HZ)) | |
236 | continue; | |
237 | usb_unlink_urb(this_urb); | |
238 | continue; | |
239 | } | |
a80be97d GKH |
240 | dev_dbg(&port->dev, "%s: endpoint %d buf %d\n", __func__, |
241 | usb_pipeendpoint(this_urb->pipe), i); | |
0d456194 MG |
242 | |
243 | err = usb_autopm_get_interface_async(port->serial->interface); | |
db090473 | 244 | if (err < 0) { |
245 | clear_bit(i, &portdata->out_busy); | |
0d456194 | 246 | break; |
db090473 | 247 | } |
0d456194 MG |
248 | |
249 | /* send the data */ | |
250 | memcpy(this_urb->transfer_buffer, buf, todo); | |
251 | this_urb->transfer_buffer_length = todo; | |
252 | ||
253 | spin_lock_irqsave(&intfdata->susp_lock, flags); | |
254 | if (intfdata->suspended) { | |
255 | usb_anchor_urb(this_urb, &portdata->delayed); | |
256 | spin_unlock_irqrestore(&intfdata->susp_lock, flags); | |
257 | } else { | |
258 | intfdata->in_flight++; | |
259 | spin_unlock_irqrestore(&intfdata->susp_lock, flags); | |
260 | err = usb_submit_urb(this_urb, GFP_ATOMIC); | |
261 | if (err) { | |
8bb7ec65 JH |
262 | dev_err(&port->dev, |
263 | "%s: submit urb %d failed: %d\n", | |
264 | __func__, i, err); | |
0d456194 MG |
265 | clear_bit(i, &portdata->out_busy); |
266 | spin_lock_irqsave(&intfdata->susp_lock, flags); | |
267 | intfdata->in_flight--; | |
268 | spin_unlock_irqrestore(&intfdata->susp_lock, | |
269 | flags); | |
3d06bf15 | 270 | usb_autopm_put_interface_async(port->serial->interface); |
433508ae | 271 | break; |
0d456194 MG |
272 | } |
273 | } | |
274 | ||
275 | portdata->tx_start_time[i] = jiffies; | |
276 | buf += todo; | |
277 | left -= todo; | |
278 | } | |
279 | ||
280 | count -= left; | |
a80be97d | 281 | dev_dbg(&port->dev, "%s: wrote (did %d)\n", __func__, count); |
0d456194 MG |
282 | return count; |
283 | } | |
284 | EXPORT_SYMBOL(usb_wwan_write); | |
285 | ||
286 | static void usb_wwan_indat_callback(struct urb *urb) | |
287 | { | |
288 | int err; | |
289 | int endpoint; | |
290 | struct usb_serial_port *port; | |
a80be97d | 291 | struct device *dev; |
0d456194 MG |
292 | unsigned char *data = urb->transfer_buffer; |
293 | int status = urb->status; | |
294 | ||
0d456194 MG |
295 | endpoint = usb_pipeendpoint(urb->pipe); |
296 | port = urb->context; | |
a80be97d | 297 | dev = &port->dev; |
0d456194 MG |
298 | |
299 | if (status) { | |
a80be97d GKH |
300 | dev_dbg(dev, "%s: nonzero status: %d on endpoint %02x.\n", |
301 | __func__, status, endpoint); | |
0d456194 | 302 | } else { |
2e124b4a JS |
303 | if (urb->actual_length) { |
304 | tty_insert_flip_string(&port->port, data, | |
305 | urb->actual_length); | |
306 | tty_flip_buffer_push(&port->port); | |
307 | } else | |
308 | dev_dbg(dev, "%s: empty read urb received\n", __func__); | |
6c1ee66a MB |
309 | } |
310 | /* Resubmit urb so we continue receiving */ | |
311 | err = usb_submit_urb(urb, GFP_ATOMIC); | |
312 | if (err) { | |
d2958d1b | 313 | if (err != -EPERM && err != -ENODEV) { |
6c1ee66a MB |
314 | dev_err(dev, "%s: resubmit read urb failed. (%d)\n", |
315 | __func__, err); | |
316 | /* busy also in error unless we are killed */ | |
ec42899c | 317 | usb_mark_last_busy(port->serial->dev); |
0d456194 | 318 | } |
6c1ee66a MB |
319 | } else { |
320 | usb_mark_last_busy(port->serial->dev); | |
0d456194 | 321 | } |
0d456194 MG |
322 | } |
323 | ||
324 | static void usb_wwan_outdat_callback(struct urb *urb) | |
325 | { | |
326 | struct usb_serial_port *port; | |
327 | struct usb_wwan_port_private *portdata; | |
328 | struct usb_wwan_intf_private *intfdata; | |
329 | int i; | |
330 | ||
0d456194 | 331 | port = urb->context; |
37357ca5 | 332 | intfdata = usb_get_serial_data(port->serial); |
0d456194 MG |
333 | |
334 | usb_serial_port_softint(port); | |
335 | usb_autopm_put_interface_async(port->serial->interface); | |
336 | portdata = usb_get_serial_port_data(port); | |
337 | spin_lock(&intfdata->susp_lock); | |
338 | intfdata->in_flight--; | |
339 | spin_unlock(&intfdata->susp_lock); | |
340 | ||
341 | for (i = 0; i < N_OUT_URB; ++i) { | |
342 | if (portdata->out_urbs[i] == urb) { | |
4e857c58 | 343 | smp_mb__before_atomic(); |
0d456194 MG |
344 | clear_bit(i, &portdata->out_busy); |
345 | break; | |
346 | } | |
347 | } | |
348 | } | |
349 | ||
350 | int usb_wwan_write_room(struct tty_struct *tty) | |
351 | { | |
352 | struct usb_serial_port *port = tty->driver_data; | |
353 | struct usb_wwan_port_private *portdata; | |
354 | int i; | |
355 | int data_len = 0; | |
356 | struct urb *this_urb; | |
357 | ||
358 | portdata = usb_get_serial_port_data(port); | |
359 | ||
360 | for (i = 0; i < N_OUT_URB; i++) { | |
361 | this_urb = portdata->out_urbs[i]; | |
362 | if (this_urb && !test_bit(i, &portdata->out_busy)) | |
363 | data_len += OUT_BUFLEN; | |
364 | } | |
365 | ||
a80be97d | 366 | dev_dbg(&port->dev, "%s: %d\n", __func__, data_len); |
0d456194 MG |
367 | return data_len; |
368 | } | |
369 | EXPORT_SYMBOL(usb_wwan_write_room); | |
370 | ||
371 | int usb_wwan_chars_in_buffer(struct tty_struct *tty) | |
372 | { | |
373 | struct usb_serial_port *port = tty->driver_data; | |
374 | struct usb_wwan_port_private *portdata; | |
375 | int i; | |
376 | int data_len = 0; | |
377 | struct urb *this_urb; | |
378 | ||
379 | portdata = usb_get_serial_port_data(port); | |
380 | ||
381 | for (i = 0; i < N_OUT_URB; i++) { | |
382 | this_urb = portdata->out_urbs[i]; | |
383 | /* FIXME: This locking is insufficient as this_urb may | |
384 | go unused during the test */ | |
385 | if (this_urb && test_bit(i, &portdata->out_busy)) | |
386 | data_len += this_urb->transfer_buffer_length; | |
387 | } | |
a80be97d | 388 | dev_dbg(&port->dev, "%s: %d\n", __func__, data_len); |
0d456194 MG |
389 | return data_len; |
390 | } | |
391 | EXPORT_SYMBOL(usb_wwan_chars_in_buffer); | |
392 | ||
393 | int usb_wwan_open(struct tty_struct *tty, struct usb_serial_port *port) | |
394 | { | |
395 | struct usb_wwan_port_private *portdata; | |
396 | struct usb_wwan_intf_private *intfdata; | |
397 | struct usb_serial *serial = port->serial; | |
398 | int i, err; | |
399 | struct urb *urb; | |
400 | ||
401 | portdata = usb_get_serial_port_data(port); | |
37357ca5 | 402 | intfdata = usb_get_serial_data(serial); |
0d456194 | 403 | |
9096f1fb JH |
404 | if (port->interrupt_in_urb) { |
405 | err = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL); | |
406 | if (err) { | |
8bb7ec65 | 407 | dev_err(&port->dev, "%s: submit int urb failed: %d\n", |
9096f1fb JH |
408 | __func__, err); |
409 | } | |
410 | } | |
411 | ||
0d456194 MG |
412 | /* Start reading from the IN endpoint */ |
413 | for (i = 0; i < N_IN_URB; i++) { | |
414 | urb = portdata->in_urbs[i]; | |
415 | if (!urb) | |
416 | continue; | |
417 | err = usb_submit_urb(urb, GFP_KERNEL); | |
418 | if (err) { | |
8bb7ec65 JH |
419 | dev_err(&port->dev, |
420 | "%s: submit read urb %d failed: %d\n", | |
421 | __func__, i, err); | |
0d456194 MG |
422 | } |
423 | } | |
424 | ||
0d456194 | 425 | spin_lock_irq(&intfdata->susp_lock); |
c1c01803 JH |
426 | if (++intfdata->open_ports == 1) |
427 | serial->interface->needs_remote_wakeup = 1; | |
0d456194 | 428 | spin_unlock_irq(&intfdata->susp_lock); |
9a91aedc | 429 | /* this balances a get in the generic USB serial code */ |
0d456194 MG |
430 | usb_autopm_put_interface(serial->interface); |
431 | ||
432 | return 0; | |
433 | } | |
434 | EXPORT_SYMBOL(usb_wwan_open); | |
435 | ||
79eed03e JH |
436 | static void unbusy_queued_urb(struct urb *urb, |
437 | struct usb_wwan_port_private *portdata) | |
438 | { | |
439 | int i; | |
440 | ||
441 | for (i = 0; i < N_OUT_URB; i++) { | |
442 | if (urb == portdata->out_urbs[i]) { | |
443 | clear_bit(i, &portdata->out_busy); | |
444 | break; | |
445 | } | |
446 | } | |
447 | } | |
448 | ||
0d456194 MG |
449 | void usb_wwan_close(struct usb_serial_port *port) |
450 | { | |
451 | int i; | |
452 | struct usb_serial *serial = port->serial; | |
453 | struct usb_wwan_port_private *portdata; | |
37357ca5 | 454 | struct usb_wwan_intf_private *intfdata = usb_get_serial_data(serial); |
79eed03e | 455 | struct urb *urb; |
0d456194 | 456 | |
0d456194 MG |
457 | portdata = usb_get_serial_port_data(port); |
458 | ||
b0a9aa6d JH |
459 | /* |
460 | * Need to take susp_lock to make sure port is not already being | |
d41861ca | 461 | * resumed, but no need to hold it due to initialized |
b0a9aa6d | 462 | */ |
e6d144bc | 463 | spin_lock_irq(&intfdata->susp_lock); |
c1c01803 JH |
464 | if (--intfdata->open_ports == 0) |
465 | serial->interface->needs_remote_wakeup = 0; | |
e6d144bc JH |
466 | spin_unlock_irq(&intfdata->susp_lock); |
467 | ||
79eed03e JH |
468 | for (;;) { |
469 | urb = usb_get_from_anchor(&portdata->delayed); | |
470 | if (!urb) | |
471 | break; | |
472 | unbusy_queued_urb(urb, portdata); | |
473 | usb_autopm_put_interface_async(serial->interface); | |
474 | } | |
475 | ||
e6d144bc JH |
476 | for (i = 0; i < N_IN_URB; i++) |
477 | usb_kill_urb(portdata->in_urbs[i]); | |
478 | for (i = 0; i < N_OUT_URB; i++) | |
479 | usb_kill_urb(portdata->out_urbs[i]); | |
9096f1fb | 480 | usb_kill_urb(port->interrupt_in_urb); |
e6d144bc | 481 | |
e6d144bc | 482 | usb_autopm_get_interface_no_resume(serial->interface); |
0d456194 MG |
483 | } |
484 | EXPORT_SYMBOL(usb_wwan_close); | |
485 | ||
b8f0e820 JH |
486 | static struct urb *usb_wwan_setup_urb(struct usb_serial_port *port, |
487 | int endpoint, | |
0d456194 MG |
488 | int dir, void *ctx, char *buf, int len, |
489 | void (*callback) (struct urb *)) | |
490 | { | |
b8f0e820 | 491 | struct usb_serial *serial = port->serial; |
0d456194 MG |
492 | struct urb *urb; |
493 | ||
0d456194 | 494 | urb = usb_alloc_urb(0, GFP_KERNEL); /* No ISO */ |
10c642d0 | 495 | if (!urb) |
0d456194 | 496 | return NULL; |
0d456194 | 497 | |
0d456194 MG |
498 | usb_fill_bulk_urb(urb, serial->dev, |
499 | usb_sndbulkpipe(serial->dev, endpoint) | dir, | |
500 | buf, len, callback, ctx); | |
501 | ||
502 | return urb; | |
503 | } | |
504 | ||
b8f0e820 | 505 | int usb_wwan_port_probe(struct usb_serial_port *port) |
0d456194 | 506 | { |
0d456194 | 507 | struct usb_wwan_port_private *portdata; |
b8f0e820 JH |
508 | struct urb *urb; |
509 | u8 *buffer; | |
b8f0e820 | 510 | int i; |
0d456194 | 511 | |
bd73bd88 JH |
512 | if (!port->bulk_in_size || !port->bulk_out_size) |
513 | return -ENODEV; | |
514 | ||
b8f0e820 JH |
515 | portdata = kzalloc(sizeof(*portdata), GFP_KERNEL); |
516 | if (!portdata) | |
517 | return -ENOMEM; | |
0d456194 | 518 | |
b8f0e820 | 519 | init_usb_anchor(&portdata->delayed); |
0d456194 | 520 | |
b8f0e820 JH |
521 | for (i = 0; i < N_IN_URB; i++) { |
522 | buffer = (u8 *)__get_free_page(GFP_KERNEL); | |
523 | if (!buffer) | |
524 | goto bail_out_error; | |
525 | portdata->in_buffer[i] = buffer; | |
526 | ||
527 | urb = usb_wwan_setup_urb(port, port->bulk_in_endpointAddress, | |
528 | USB_DIR_IN, port, | |
529 | buffer, IN_BUFLEN, | |
530 | usb_wwan_indat_callback); | |
531 | portdata->in_urbs[i] = urb; | |
532 | } | |
0d456194 | 533 | |
b8f0e820 | 534 | for (i = 0; i < N_OUT_URB; i++) { |
b8f0e820 JH |
535 | buffer = kmalloc(OUT_BUFLEN, GFP_KERNEL); |
536 | if (!buffer) | |
537 | goto bail_out_error2; | |
538 | portdata->out_buffer[i] = buffer; | |
0d456194 | 539 | |
b8f0e820 JH |
540 | urb = usb_wwan_setup_urb(port, port->bulk_out_endpointAddress, |
541 | USB_DIR_OUT, port, | |
542 | buffer, OUT_BUFLEN, | |
543 | usb_wwan_outdat_callback); | |
544 | portdata->out_urbs[i] = urb; | |
545 | } | |
0d456194 | 546 | |
b8f0e820 | 547 | usb_set_serial_port_data(port, portdata); |
0d456194 | 548 | |
0d456194 MG |
549 | return 0; |
550 | ||
551 | bail_out_error2: | |
b8f0e820 JH |
552 | for (i = 0; i < N_OUT_URB; i++) { |
553 | usb_free_urb(portdata->out_urbs[i]); | |
554 | kfree(portdata->out_buffer[i]); | |
555 | } | |
0d456194 | 556 | bail_out_error: |
b8f0e820 JH |
557 | for (i = 0; i < N_IN_URB; i++) { |
558 | usb_free_urb(portdata->in_urbs[i]); | |
559 | free_page((unsigned long)portdata->in_buffer[i]); | |
560 | } | |
0d456194 | 561 | kfree(portdata); |
b8f0e820 JH |
562 | |
563 | return -ENOMEM; | |
0d456194 | 564 | } |
b8f0e820 | 565 | EXPORT_SYMBOL_GPL(usb_wwan_port_probe); |
0d456194 | 566 | |
a1028f0a | 567 | int usb_wwan_port_remove(struct usb_serial_port *port) |
0d456194 | 568 | { |
a1028f0a | 569 | int i; |
0d456194 MG |
570 | struct usb_wwan_port_private *portdata; |
571 | ||
a1028f0a BM |
572 | portdata = usb_get_serial_port_data(port); |
573 | usb_set_serial_port_data(port, NULL); | |
574 | ||
a1028f0a | 575 | for (i = 0; i < N_IN_URB; i++) { |
a1028f0a BM |
576 | usb_free_urb(portdata->in_urbs[i]); |
577 | free_page((unsigned long)portdata->in_buffer[i]); | |
578 | } | |
579 | for (i = 0; i < N_OUT_URB; i++) { | |
a1028f0a BM |
580 | usb_free_urb(portdata->out_urbs[i]); |
581 | kfree(portdata->out_buffer[i]); | |
0d456194 | 582 | } |
0d456194 | 583 | |
a1028f0a | 584 | kfree(portdata); |
2b4aceab | 585 | |
a1028f0a | 586 | return 0; |
0d456194 | 587 | } |
a1028f0a | 588 | EXPORT_SYMBOL(usb_wwan_port_remove); |
0d456194 | 589 | |
a1028f0a | 590 | #ifdef CONFIG_PM |
ae75c940 | 591 | static void stop_urbs(struct usb_serial *serial) |
0d456194 MG |
592 | { |
593 | int i, j; | |
594 | struct usb_serial_port *port; | |
595 | struct usb_wwan_port_private *portdata; | |
596 | ||
0d456194 MG |
597 | for (i = 0; i < serial->num_ports; ++i) { |
598 | port = serial->port[i]; | |
599 | portdata = usb_get_serial_port_data(port); | |
032129cb BM |
600 | if (!portdata) |
601 | continue; | |
a1028f0a BM |
602 | for (j = 0; j < N_IN_URB; j++) |
603 | usb_kill_urb(portdata->in_urbs[j]); | |
604 | for (j = 0; j < N_OUT_URB; j++) | |
605 | usb_kill_urb(portdata->out_urbs[j]); | |
ae75c940 | 606 | usb_kill_urb(port->interrupt_in_urb); |
0d456194 MG |
607 | } |
608 | } | |
0d456194 | 609 | |
0d456194 MG |
610 | int usb_wwan_suspend(struct usb_serial *serial, pm_message_t message) |
611 | { | |
37357ca5 | 612 | struct usb_wwan_intf_private *intfdata = usb_get_serial_data(serial); |
0d456194 | 613 | |
170fad9e | 614 | spin_lock_irq(&intfdata->susp_lock); |
5b1b0b81 | 615 | if (PMSG_IS_AUTO(message)) { |
170fad9e JH |
616 | if (intfdata->in_flight) { |
617 | spin_unlock_irq(&intfdata->susp_lock); | |
0d456194 | 618 | return -EBUSY; |
170fad9e | 619 | } |
0d456194 | 620 | } |
0d456194 MG |
621 | intfdata->suspended = 1; |
622 | spin_unlock_irq(&intfdata->susp_lock); | |
170fad9e | 623 | |
ae75c940 | 624 | stop_urbs(serial); |
0d456194 MG |
625 | |
626 | return 0; | |
627 | } | |
628 | EXPORT_SYMBOL(usb_wwan_suspend); | |
629 | ||
3362c91c JH |
630 | /* Caller must hold susp_lock. */ |
631 | static int usb_wwan_submit_delayed_urbs(struct usb_serial_port *port) | |
0d456194 | 632 | { |
7436f412 | 633 | struct usb_serial *serial = port->serial; |
37357ca5 | 634 | struct usb_wwan_intf_private *data = usb_get_serial_data(serial); |
0d456194 MG |
635 | struct usb_wwan_port_private *portdata; |
636 | struct urb *urb; | |
7436f412 JH |
637 | int err_count = 0; |
638 | int err; | |
0d456194 MG |
639 | |
640 | portdata = usb_get_serial_port_data(port); | |
37357ca5 | 641 | |
3362c91c JH |
642 | for (;;) { |
643 | urb = usb_get_from_anchor(&portdata->delayed); | |
644 | if (!urb) | |
645 | break; | |
646 | ||
0d456194 | 647 | err = usb_submit_urb(urb, GFP_ATOMIC); |
7436f412 | 648 | if (err) { |
3362c91c | 649 | dev_err(&port->dev, "%s: submit urb failed: %d\n", |
7436f412 JH |
650 | __func__, err); |
651 | err_count++; | |
652 | unbusy_queued_urb(urb, portdata); | |
653 | usb_autopm_put_interface_async(serial->interface); | |
654 | continue; | |
16871dca | 655 | } |
7436f412 | 656 | data->in_flight++; |
0d456194 | 657 | } |
fb7ad4f9 | 658 | |
7436f412 JH |
659 | if (err_count) |
660 | return -EIO; | |
661 | ||
662 | return 0; | |
0d456194 MG |
663 | } |
664 | ||
665 | int usb_wwan_resume(struct usb_serial *serial) | |
666 | { | |
667 | int i, j; | |
668 | struct usb_serial_port *port; | |
37357ca5 | 669 | struct usb_wwan_intf_private *intfdata = usb_get_serial_data(serial); |
0d456194 MG |
670 | struct usb_wwan_port_private *portdata; |
671 | struct urb *urb; | |
fb7ad4f9 JH |
672 | int err; |
673 | int err_count = 0; | |
0d456194 | 674 | |
d9e93c08 | 675 | spin_lock_irq(&intfdata->susp_lock); |
0d456194 | 676 | for (i = 0; i < serial->num_ports; i++) { |
0d456194 | 677 | port = serial->port[i]; |
0d456194 | 678 | |
d41861ca | 679 | if (!tty_port_initialized(&port->port)) |
0d456194 | 680 | continue; |
0d456194 | 681 | |
b0a9aa6d JH |
682 | portdata = usb_get_serial_port_data(port); |
683 | ||
9096f1fb JH |
684 | if (port->interrupt_in_urb) { |
685 | err = usb_submit_urb(port->interrupt_in_urb, | |
686 | GFP_ATOMIC); | |
687 | if (err) { | |
688 | dev_err(&port->dev, | |
689 | "%s: submit int urb failed: %d\n", | |
690 | __func__, err); | |
fb7ad4f9 | 691 | err_count++; |
9096f1fb JH |
692 | } |
693 | } | |
694 | ||
3362c91c | 695 | err = usb_wwan_submit_delayed_urbs(port); |
fb7ad4f9 JH |
696 | if (err) |
697 | err_count++; | |
698 | ||
0d456194 MG |
699 | for (j = 0; j < N_IN_URB; j++) { |
700 | urb = portdata->in_urbs[j]; | |
701 | err = usb_submit_urb(urb, GFP_ATOMIC); | |
702 | if (err < 0) { | |
b0f9d003 JH |
703 | dev_err(&port->dev, |
704 | "%s: submit read urb %d failed: %d\n", | |
705 | __func__, i, err); | |
fb7ad4f9 | 706 | err_count++; |
0d456194 MG |
707 | } |
708 | } | |
0d456194 | 709 | } |
0d456194 MG |
710 | intfdata->suspended = 0; |
711 | spin_unlock_irq(&intfdata->susp_lock); | |
fb7ad4f9 JH |
712 | |
713 | if (err_count) | |
714 | return -EIO; | |
715 | ||
716 | return 0; | |
0d456194 MG |
717 | } |
718 | EXPORT_SYMBOL(usb_wwan_resume); | |
719 | #endif | |
720 | ||
721 | MODULE_AUTHOR(DRIVER_AUTHOR); | |
722 | MODULE_DESCRIPTION(DRIVER_DESC); | |
627cfa89 | 723 | MODULE_LICENSE("GPL v2"); |