blob: 912472f26e4f6c5a9ad30357f7a47d9a3085a1d9 [file] [log] [blame]
Greg Kroah-Hartman5fd54ac2017-11-03 11:28:30 +01001// SPDX-License-Identifier: GPL-2.0
Matthew Garrett0d456192010-04-01 12:31:07 -04002/*
3 USB Driver layer for GSM modems
4
5 Copyright (C) 2005 Matthias Urlichs <smurf@smurf.noris.de>
6
Matthew Garrett0d456192010-04-01 12:31:07 -04007 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
Matthew Garrett0d456192010-04-01 12:31:07 -040019#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>
Peter Huewe66921ed2010-12-09 23:27:35 +010030#include <linux/uaccess.h>
Matthew Garrett0d456192010-04-01 12:31:07 -040031#include <linux/usb.h>
32#include <linux/usb/serial.h>
Dan Williams02303f72010-11-19 16:04:00 -060033#include <linux/serial.h>
Matthew Garrett0d456192010-04-01 12:31:07 -040034#include "usb-wwan.h"
35
David Ward669e7292015-09-16 12:27:57 -040036/*
37 * Generate DTR/RTS signals on the port using the SET_CONTROL_LINE_STATE request
38 * in CDC ACM.
39 */
40static 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
Matthew Garrett0d456192010-04-01 12:31:07 -040070void usb_wwan_dtr_rts(struct usb_serial_port *port, int on)
71{
Matthew Garrett0d456192010-04-01 12:31:07 -040072 struct usb_wwan_port_private *portdata;
Matthew Garrett0d456192010-04-01 12:31:07 -040073 struct usb_wwan_intf_private *intfdata;
74
Johan Hovold37357ca2014-05-26 19:23:26 +020075 intfdata = usb_get_serial_data(port->serial);
Matthew Garrett0d456192010-04-01 12:31:07 -040076
David Ward669e7292015-09-16 12:27:57 -040077 if (!intfdata->use_send_setup)
Matthew Garrett0d456192010-04-01 12:31:07 -040078 return;
79
80 portdata = usb_get_serial_port_data(port);
Johan Hovoldb2ca6992013-02-13 17:53:28 +010081 /* FIXME: locking */
Matthew Garrett0d456192010-04-01 12:31:07 -040082 portdata->rts_state = on;
83 portdata->dtr_state = on;
Johan Hovoldb2ca6992013-02-13 17:53:28 +010084
David Ward669e7292015-09-16 12:27:57 -040085 usb_wwan_send_setup(port);
Matthew Garrett0d456192010-04-01 12:31:07 -040086}
87EXPORT_SYMBOL(usb_wwan_dtr_rts);
88
Alan Cox60b33c12011-02-14 16:26:14 +000089int usb_wwan_tiocmget(struct tty_struct *tty)
Matthew Garrett0d456192010-04-01 12:31:07 -040090{
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}
106EXPORT_SYMBOL(usb_wwan_tiocmget);
107
Alan Cox20b9d172011-02-14 16:26:50 +0000108int usb_wwan_tiocmset(struct tty_struct *tty,
Matthew Garrett0d456192010-04-01 12:31:07 -0400109 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);
Johan Hovold37357ca2014-05-26 19:23:26 +0200116 intfdata = usb_get_serial_data(port->serial);
Matthew Garrett0d456192010-04-01 12:31:07 -0400117
David Ward669e7292015-09-16 12:27:57 -0400118 if (!intfdata->use_send_setup)
Matthew Garrett0d456192010-04-01 12:31:07 -0400119 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;
David Ward669e7292015-09-16 12:27:57 -0400131 return usb_wwan_send_setup(port);
Matthew Garrett0d456192010-04-01 12:31:07 -0400132}
133EXPORT_SYMBOL(usb_wwan_tiocmset);
134
Dan Williams02303f72010-11-19 16:04:00 -0600135static int get_serial_info(struct usb_serial_port *port,
136 struct serial_struct __user *retinfo)
137{
138 struct serial_struct tmp;
139
Dan Williams02303f72010-11-19 16:04:00 -0600140 memset(&tmp, 0, sizeof(tmp));
Greg Kroah-Hartmane5b1e202013-06-07 11:04:28 -0700141 tmp.line = port->minor;
Greg Kroah-Hartman11438322013-06-06 10:32:00 -0700142 tmp.port = port->port_number;
Dan Williams02303f72010-11-19 16:04:00 -0600143 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
154static 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
Alan Cox00a0d0d2011-02-14 16:27:06 +0000185int usb_wwan_ioctl(struct tty_struct *tty,
Dan Williams02303f72010-11-19 16:04:00 -0600186 unsigned int cmd, unsigned long arg)
187{
188 struct usb_serial_port *port = tty->driver_data;
189
Greg Kroah-Hartmana80be972012-09-13 17:41:50 -0700190 dev_dbg(&port->dev, "%s cmd 0x%04x\n", __func__, cmd);
Dan Williams02303f72010-11-19 16:04:00 -0600191
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
Greg Kroah-Hartmana80be972012-09-13 17:41:50 -0700203 dev_dbg(&port->dev, "%s arg not supported\n", __func__);
Dan Williams02303f72010-11-19 16:04:00 -0600204
205 return -ENOIOCTLCMD;
206}
207EXPORT_SYMBOL(usb_wwan_ioctl);
208
Matthew Garrett0d456192010-04-01 12:31:07 -0400209int 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);
Johan Hovold37357ca2014-05-26 19:23:26 +0200221 intfdata = usb_get_serial_data(port->serial);
Matthew Garrett0d456192010-04-01 12:31:07 -0400222
Greg Kroah-Hartmana80be972012-09-13 17:41:50 -0700223 dev_dbg(&port->dev, "%s: write (%d chars)\n", __func__, count);
Matthew Garrett0d456192010-04-01 12:31:07 -0400224
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 }
Greg Kroah-Hartmana80be972012-09-13 17:41:50 -0700240 dev_dbg(&port->dev, "%s: endpoint %d buf %d\n", __func__,
241 usb_pipeendpoint(this_urb->pipe), i);
Matthew Garrett0d456192010-04-01 12:31:07 -0400242
243 err = usb_autopm_get_interface_async(port->serial->interface);
xiao jindb090472014-05-26 19:23:13 +0200244 if (err < 0) {
245 clear_bit(i, &portdata->out_busy);
Matthew Garrett0d456192010-04-01 12:31:07 -0400246 break;
xiao jindb090472014-05-26 19:23:13 +0200247 }
Matthew Garrett0d456192010-04-01 12:31:07 -0400248
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) {
Johan Hovold8bb7ec62014-05-26 19:23:31 +0200262 dev_err(&port->dev,
263 "%s: submit urb %d failed: %d\n",
264 __func__, i, err);
Matthew Garrett0d456192010-04-01 12:31:07 -0400265 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);
Oliver Neukum3d06bf12011-02-10 15:33:17 +0100270 usb_autopm_put_interface_async(port->serial->interface);
Oliver Neukum433508a2011-02-10 15:33:23 +0100271 break;
Matthew Garrett0d456192010-04-01 12:31:07 -0400272 }
273 }
274
275 portdata->tx_start_time[i] = jiffies;
276 buf += todo;
277 left -= todo;
278 }
279
280 count -= left;
Greg Kroah-Hartmana80be972012-09-13 17:41:50 -0700281 dev_dbg(&port->dev, "%s: wrote (did %d)\n", __func__, count);
Matthew Garrett0d456192010-04-01 12:31:07 -0400282 return count;
283}
284EXPORT_SYMBOL(usb_wwan_write);
285
286static void usb_wwan_indat_callback(struct urb *urb)
287{
288 int err;
289 int endpoint;
290 struct usb_serial_port *port;
Greg Kroah-Hartmana80be972012-09-13 17:41:50 -0700291 struct device *dev;
Matthew Garrett0d456192010-04-01 12:31:07 -0400292 unsigned char *data = urb->transfer_buffer;
293 int status = urb->status;
294
Matthew Garrett0d456192010-04-01 12:31:07 -0400295 endpoint = usb_pipeendpoint(urb->pipe);
296 port = urb->context;
Greg Kroah-Hartmana80be972012-09-13 17:41:50 -0700297 dev = &port->dev;
Matthew Garrett0d456192010-04-01 12:31:07 -0400298
299 if (status) {
Greg Kroah-Hartmana80be972012-09-13 17:41:50 -0700300 dev_dbg(dev, "%s: nonzero status: %d on endpoint %02x.\n",
301 __func__, status, endpoint);
Matthew Garrett0d456192010-04-01 12:31:07 -0400302 } else {
Jiri Slaby2e124b42013-01-03 15:53:06 +0100303 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__);
Matt Burtch6c1ee662013-08-12 10:11:39 -0700309 }
310 /* Resubmit urb so we continue receiving */
311 err = usb_submit_urb(urb, GFP_ATOMIC);
312 if (err) {
Johan Hovoldd2958d12015-08-17 17:35:24 +0200313 if (err != -EPERM && err != -ENODEV) {
Matt Burtch6c1ee662013-08-12 10:11:39 -0700314 dev_err(dev, "%s: resubmit read urb failed. (%d)\n",
315 __func__, err);
316 /* busy also in error unless we are killed */
Dan Carpenterec428992012-04-20 09:33:31 +0300317 usb_mark_last_busy(port->serial->dev);
Matthew Garrett0d456192010-04-01 12:31:07 -0400318 }
Matt Burtch6c1ee662013-08-12 10:11:39 -0700319 } else {
320 usb_mark_last_busy(port->serial->dev);
Matthew Garrett0d456192010-04-01 12:31:07 -0400321 }
Matthew Garrett0d456192010-04-01 12:31:07 -0400322}
323
324static 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;
John Ognessa323f942018-06-24 00:32:15 +0200329 unsigned long flags;
Matthew Garrett0d456192010-04-01 12:31:07 -0400330 int i;
331
Matthew Garrett0d456192010-04-01 12:31:07 -0400332 port = urb->context;
Johan Hovold37357ca2014-05-26 19:23:26 +0200333 intfdata = usb_get_serial_data(port->serial);
Matthew Garrett0d456192010-04-01 12:31:07 -0400334
335 usb_serial_port_softint(port);
336 usb_autopm_put_interface_async(port->serial->interface);
337 portdata = usb_get_serial_port_data(port);
John Ognessa323f942018-06-24 00:32:15 +0200338 spin_lock_irqsave(&intfdata->susp_lock, flags);
Matthew Garrett0d456192010-04-01 12:31:07 -0400339 intfdata->in_flight--;
John Ognessa323f942018-06-24 00:32:15 +0200340 spin_unlock_irqrestore(&intfdata->susp_lock, flags);
Matthew Garrett0d456192010-04-01 12:31:07 -0400341
342 for (i = 0; i < N_OUT_URB; ++i) {
343 if (portdata->out_urbs[i] == urb) {
Peter Zijlstra4e857c52014-03-17 18:06:10 +0100344 smp_mb__before_atomic();
Matthew Garrett0d456192010-04-01 12:31:07 -0400345 clear_bit(i, &portdata->out_busy);
346 break;
347 }
348 }
349}
350
351int usb_wwan_write_room(struct tty_struct *tty)
352{
353 struct usb_serial_port *port = tty->driver_data;
354 struct usb_wwan_port_private *portdata;
355 int i;
356 int data_len = 0;
357 struct urb *this_urb;
358
359 portdata = usb_get_serial_port_data(port);
360
361 for (i = 0; i < N_OUT_URB; i++) {
362 this_urb = portdata->out_urbs[i];
363 if (this_urb && !test_bit(i, &portdata->out_busy))
364 data_len += OUT_BUFLEN;
365 }
366
Greg Kroah-Hartmana80be972012-09-13 17:41:50 -0700367 dev_dbg(&port->dev, "%s: %d\n", __func__, data_len);
Matthew Garrett0d456192010-04-01 12:31:07 -0400368 return data_len;
369}
370EXPORT_SYMBOL(usb_wwan_write_room);
371
372int usb_wwan_chars_in_buffer(struct tty_struct *tty)
373{
374 struct usb_serial_port *port = tty->driver_data;
375 struct usb_wwan_port_private *portdata;
376 int i;
377 int data_len = 0;
378 struct urb *this_urb;
379
380 portdata = usb_get_serial_port_data(port);
381
382 for (i = 0; i < N_OUT_URB; i++) {
383 this_urb = portdata->out_urbs[i];
384 /* FIXME: This locking is insufficient as this_urb may
385 go unused during the test */
386 if (this_urb && test_bit(i, &portdata->out_busy))
387 data_len += this_urb->transfer_buffer_length;
388 }
Greg Kroah-Hartmana80be972012-09-13 17:41:50 -0700389 dev_dbg(&port->dev, "%s: %d\n", __func__, data_len);
Matthew Garrett0d456192010-04-01 12:31:07 -0400390 return data_len;
391}
392EXPORT_SYMBOL(usb_wwan_chars_in_buffer);
393
394int usb_wwan_open(struct tty_struct *tty, struct usb_serial_port *port)
395{
396 struct usb_wwan_port_private *portdata;
397 struct usb_wwan_intf_private *intfdata;
398 struct usb_serial *serial = port->serial;
399 int i, err;
400 struct urb *urb;
401
402 portdata = usb_get_serial_port_data(port);
Johan Hovold37357ca2014-05-26 19:23:26 +0200403 intfdata = usb_get_serial_data(serial);
Matthew Garrett0d456192010-04-01 12:31:07 -0400404
Johan Hovold9096f1f2014-05-26 19:23:17 +0200405 if (port->interrupt_in_urb) {
406 err = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
407 if (err) {
Johan Hovold8bb7ec62014-05-26 19:23:31 +0200408 dev_err(&port->dev, "%s: submit int urb failed: %d\n",
Johan Hovold9096f1f2014-05-26 19:23:17 +0200409 __func__, err);
410 }
411 }
412
Matthew Garrett0d456192010-04-01 12:31:07 -0400413 /* Start reading from the IN endpoint */
414 for (i = 0; i < N_IN_URB; i++) {
415 urb = portdata->in_urbs[i];
416 if (!urb)
417 continue;
418 err = usb_submit_urb(urb, GFP_KERNEL);
419 if (err) {
Johan Hovold8bb7ec62014-05-26 19:23:31 +0200420 dev_err(&port->dev,
421 "%s: submit read urb %d failed: %d\n",
422 __func__, i, err);
Matthew Garrett0d456192010-04-01 12:31:07 -0400423 }
424 }
425
Matthew Garrett0d456192010-04-01 12:31:07 -0400426 spin_lock_irq(&intfdata->susp_lock);
Johan Hovoldc1c01802014-05-26 19:23:20 +0200427 if (++intfdata->open_ports == 1)
428 serial->interface->needs_remote_wakeup = 1;
Matthew Garrett0d456192010-04-01 12:31:07 -0400429 spin_unlock_irq(&intfdata->susp_lock);
Oliver Neukum9a91aed2011-02-10 15:33:37 +0100430 /* this balances a get in the generic USB serial code */
Matthew Garrett0d456192010-04-01 12:31:07 -0400431 usb_autopm_put_interface(serial->interface);
432
433 return 0;
434}
435EXPORT_SYMBOL(usb_wwan_open);
436
Johan Hovold79eed032014-05-26 19:23:16 +0200437static void unbusy_queued_urb(struct urb *urb,
438 struct usb_wwan_port_private *portdata)
439{
440 int i;
441
442 for (i = 0; i < N_OUT_URB; i++) {
443 if (urb == portdata->out_urbs[i]) {
444 clear_bit(i, &portdata->out_busy);
445 break;
446 }
447 }
448}
449
Matthew Garrett0d456192010-04-01 12:31:07 -0400450void usb_wwan_close(struct usb_serial_port *port)
451{
452 int i;
453 struct usb_serial *serial = port->serial;
454 struct usb_wwan_port_private *portdata;
Johan Hovold37357ca2014-05-26 19:23:26 +0200455 struct usb_wwan_intf_private *intfdata = usb_get_serial_data(serial);
Johan Hovold79eed032014-05-26 19:23:16 +0200456 struct urb *urb;
Matthew Garrett0d456192010-04-01 12:31:07 -0400457
Matthew Garrett0d456192010-04-01 12:31:07 -0400458 portdata = usb_get_serial_port_data(port);
459
Johan Hovoldb0a9aa62014-05-26 19:23:32 +0200460 /*
461 * Need to take susp_lock to make sure port is not already being
Peter Hurleyd41861c2016-04-09 17:53:25 -0700462 * resumed, but no need to hold it due to initialized
Johan Hovoldb0a9aa62014-05-26 19:23:32 +0200463 */
Johan Hovolde6d144b2013-03-21 12:36:37 +0100464 spin_lock_irq(&intfdata->susp_lock);
Johan Hovoldc1c01802014-05-26 19:23:20 +0200465 if (--intfdata->open_ports == 0)
466 serial->interface->needs_remote_wakeup = 0;
Johan Hovolde6d144b2013-03-21 12:36:37 +0100467 spin_unlock_irq(&intfdata->susp_lock);
Matthew Garrett0d456192010-04-01 12:31:07 -0400468
Johan Hovold79eed032014-05-26 19:23:16 +0200469 for (;;) {
470 urb = usb_get_from_anchor(&portdata->delayed);
471 if (!urb)
472 break;
473 unbusy_queued_urb(urb, portdata);
474 usb_autopm_put_interface_async(serial->interface);
475 }
476
Johan Hovolde6d144b2013-03-21 12:36:37 +0100477 for (i = 0; i < N_IN_URB; i++)
478 usb_kill_urb(portdata->in_urbs[i]);
479 for (i = 0; i < N_OUT_URB; i++)
480 usb_kill_urb(portdata->out_urbs[i]);
Johan Hovold9096f1f2014-05-26 19:23:17 +0200481 usb_kill_urb(port->interrupt_in_urb);
Johan Hovolde6d144b2013-03-21 12:36:37 +0100482
Johan Hovolde6d144b2013-03-21 12:36:37 +0100483 usb_autopm_get_interface_no_resume(serial->interface);
Matthew Garrett0d456192010-04-01 12:31:07 -0400484}
485EXPORT_SYMBOL(usb_wwan_close);
486
Johan Hovoldb8f0e822012-10-25 10:29:16 +0200487static struct urb *usb_wwan_setup_urb(struct usb_serial_port *port,
488 int endpoint,
Matthew Garrett0d456192010-04-01 12:31:07 -0400489 int dir, void *ctx, char *buf, int len,
490 void (*callback) (struct urb *))
491{
Johan Hovoldb8f0e822012-10-25 10:29:16 +0200492 struct usb_serial *serial = port->serial;
Matthew Garrett0d456192010-04-01 12:31:07 -0400493 struct urb *urb;
494
Matthew Garrett0d456192010-04-01 12:31:07 -0400495 urb = usb_alloc_urb(0, GFP_KERNEL); /* No ISO */
Johan Hovold10c642d2013-12-29 19:22:56 +0100496 if (!urb)
Matthew Garrett0d456192010-04-01 12:31:07 -0400497 return NULL;
Matthew Garrett0d456192010-04-01 12:31:07 -0400498
Matthew Garrett0d456192010-04-01 12:31:07 -0400499 usb_fill_bulk_urb(urb, serial->dev,
500 usb_sndbulkpipe(serial->dev, endpoint) | dir,
501 buf, len, callback, ctx);
502
503 return urb;
504}
505
Johan Hovoldb8f0e822012-10-25 10:29:16 +0200506int usb_wwan_port_probe(struct usb_serial_port *port)
Matthew Garrett0d456192010-04-01 12:31:07 -0400507{
Matthew Garrett0d456192010-04-01 12:31:07 -0400508 struct usb_wwan_port_private *portdata;
Johan Hovoldb8f0e822012-10-25 10:29:16 +0200509 struct urb *urb;
Matthew Garrett0d456192010-04-01 12:31:07 -0400510 u8 *buffer;
Johan Hovoldb8f0e822012-10-25 10:29:16 +0200511 int i;
Matthew Garrett0d456192010-04-01 12:31:07 -0400512
Johan Hovoldbd73bd82014-04-03 13:06:46 +0200513 if (!port->bulk_in_size || !port->bulk_out_size)
514 return -ENODEV;
515
Johan Hovoldb8f0e822012-10-25 10:29:16 +0200516 portdata = kzalloc(sizeof(*portdata), GFP_KERNEL);
517 if (!portdata)
518 return -ENOMEM;
Matthew Garrett0d456192010-04-01 12:31:07 -0400519
Johan Hovoldb8f0e822012-10-25 10:29:16 +0200520 init_usb_anchor(&portdata->delayed);
Matthew Garrett0d456192010-04-01 12:31:07 -0400521
Johan Hovoldb8f0e822012-10-25 10:29:16 +0200522 for (i = 0; i < N_IN_URB; i++) {
523 buffer = (u8 *)__get_free_page(GFP_KERNEL);
524 if (!buffer)
525 goto bail_out_error;
526 portdata->in_buffer[i] = buffer;
Matthew Garrett0d456192010-04-01 12:31:07 -0400527
Johan Hovoldb8f0e822012-10-25 10:29:16 +0200528 urb = usb_wwan_setup_urb(port, port->bulk_in_endpointAddress,
529 USB_DIR_IN, port,
530 buffer, IN_BUFLEN,
531 usb_wwan_indat_callback);
532 portdata->in_urbs[i] = urb;
533 }
Matthew Garrett0d456192010-04-01 12:31:07 -0400534
Johan Hovoldb8f0e822012-10-25 10:29:16 +0200535 for (i = 0; i < N_OUT_URB; i++) {
Johan Hovoldb8f0e822012-10-25 10:29:16 +0200536 buffer = kmalloc(OUT_BUFLEN, GFP_KERNEL);
537 if (!buffer)
538 goto bail_out_error2;
539 portdata->out_buffer[i] = buffer;
540
541 urb = usb_wwan_setup_urb(port, port->bulk_out_endpointAddress,
542 USB_DIR_OUT, port,
543 buffer, OUT_BUFLEN,
544 usb_wwan_outdat_callback);
545 portdata->out_urbs[i] = urb;
546 }
547
548 usb_set_serial_port_data(port, portdata);
549
Matthew Garrett0d456192010-04-01 12:31:07 -0400550 return 0;
551
552bail_out_error2:
Johan Hovoldb8f0e822012-10-25 10:29:16 +0200553 for (i = 0; i < N_OUT_URB; i++) {
554 usb_free_urb(portdata->out_urbs[i]);
555 kfree(portdata->out_buffer[i]);
556 }
Matthew Garrett0d456192010-04-01 12:31:07 -0400557bail_out_error:
Johan Hovoldb8f0e822012-10-25 10:29:16 +0200558 for (i = 0; i < N_IN_URB; i++) {
559 usb_free_urb(portdata->in_urbs[i]);
560 free_page((unsigned long)portdata->in_buffer[i]);
561 }
Matthew Garrett0d456192010-04-01 12:31:07 -0400562 kfree(portdata);
Johan Hovoldb8f0e822012-10-25 10:29:16 +0200563
564 return -ENOMEM;
Matthew Garrett0d456192010-04-01 12:31:07 -0400565}
Johan Hovoldb8f0e822012-10-25 10:29:16 +0200566EXPORT_SYMBOL_GPL(usb_wwan_port_probe);
Matthew Garrett0d456192010-04-01 12:31:07 -0400567
Bjørn Morka1028f02012-07-27 01:11:41 +0200568int usb_wwan_port_remove(struct usb_serial_port *port)
569{
570 int i;
571 struct usb_wwan_port_private *portdata;
572
573 portdata = usb_get_serial_port_data(port);
574 usb_set_serial_port_data(port, NULL);
575
Bjørn Morka1028f02012-07-27 01:11:41 +0200576 for (i = 0; i < N_IN_URB; i++) {
Bjørn Morka1028f02012-07-27 01:11:41 +0200577 usb_free_urb(portdata->in_urbs[i]);
578 free_page((unsigned long)portdata->in_buffer[i]);
579 }
580 for (i = 0; i < N_OUT_URB; i++) {
Bjørn Morka1028f02012-07-27 01:11:41 +0200581 usb_free_urb(portdata->out_urbs[i]);
582 kfree(portdata->out_buffer[i]);
583 }
584
Bjørn Morka1028f02012-07-27 01:11:41 +0200585 kfree(portdata);
Johan Hovold2b4acea2014-05-26 19:23:23 +0200586
Bjørn Morka1028f02012-07-27 01:11:41 +0200587 return 0;
588}
589EXPORT_SYMBOL(usb_wwan_port_remove);
590
591#ifdef CONFIG_PM
Johan Hovoldae75c942014-05-26 19:23:24 +0200592static void stop_urbs(struct usb_serial *serial)
Matthew Garrett0d456192010-04-01 12:31:07 -0400593{
594 int i, j;
595 struct usb_serial_port *port;
596 struct usb_wwan_port_private *portdata;
597
Matthew Garrett0d456192010-04-01 12:31:07 -0400598 for (i = 0; i < serial->num_ports; ++i) {
599 port = serial->port[i];
600 portdata = usb_get_serial_port_data(port);
Bjørn Mork032129c2012-07-27 01:11:43 +0200601 if (!portdata)
602 continue;
Matthew Garrett0d456192010-04-01 12:31:07 -0400603 for (j = 0; j < N_IN_URB; j++)
604 usb_kill_urb(portdata->in_urbs[j]);
605 for (j = 0; j < N_OUT_URB; j++)
606 usb_kill_urb(portdata->out_urbs[j]);
Johan Hovoldae75c942014-05-26 19:23:24 +0200607 usb_kill_urb(port->interrupt_in_urb);
Matthew Garrett0d456192010-04-01 12:31:07 -0400608 }
609}
610
Matthew Garrett0d456192010-04-01 12:31:07 -0400611int usb_wwan_suspend(struct usb_serial *serial, pm_message_t message)
612{
Johan Hovold37357ca2014-05-26 19:23:26 +0200613 struct usb_wwan_intf_private *intfdata = usb_get_serial_data(serial);
Matthew Garrett0d456192010-04-01 12:31:07 -0400614
615 spin_lock_irq(&intfdata->susp_lock);
Johan Hovold170fad92014-05-26 19:23:15 +0200616 if (PMSG_IS_AUTO(message)) {
617 if (intfdata->in_flight) {
618 spin_unlock_irq(&intfdata->susp_lock);
619 return -EBUSY;
620 }
621 }
Matthew Garrett0d456192010-04-01 12:31:07 -0400622 intfdata->suspended = 1;
623 spin_unlock_irq(&intfdata->susp_lock);
Johan Hovold170fad92014-05-26 19:23:15 +0200624
Johan Hovoldae75c942014-05-26 19:23:24 +0200625 stop_urbs(serial);
Matthew Garrett0d456192010-04-01 12:31:07 -0400626
627 return 0;
628}
629EXPORT_SYMBOL(usb_wwan_suspend);
630
Johan Hovold3362c912014-05-26 19:23:27 +0200631/* Caller must hold susp_lock. */
632static int usb_wwan_submit_delayed_urbs(struct usb_serial_port *port)
Matthew Garrett0d456192010-04-01 12:31:07 -0400633{
Johan Hovold7436f412014-05-26 19:23:19 +0200634 struct usb_serial *serial = port->serial;
Johan Hovold37357ca2014-05-26 19:23:26 +0200635 struct usb_wwan_intf_private *data = usb_get_serial_data(serial);
Matthew Garrett0d456192010-04-01 12:31:07 -0400636 struct usb_wwan_port_private *portdata;
637 struct urb *urb;
Johan Hovold7436f412014-05-26 19:23:19 +0200638 int err_count = 0;
639 int err;
Matthew Garrett0d456192010-04-01 12:31:07 -0400640
641 portdata = usb_get_serial_port_data(port);
Johan Hovold37357ca2014-05-26 19:23:26 +0200642
Johan Hovold3362c912014-05-26 19:23:27 +0200643 for (;;) {
644 urb = usb_get_from_anchor(&portdata->delayed);
645 if (!urb)
646 break;
647
Matthew Garrett0d456192010-04-01 12:31:07 -0400648 err = usb_submit_urb(urb, GFP_ATOMIC);
Johan Hovold7436f412014-05-26 19:23:19 +0200649 if (err) {
Johan Hovold3362c912014-05-26 19:23:27 +0200650 dev_err(&port->dev, "%s: submit urb failed: %d\n",
Johan Hovold7436f412014-05-26 19:23:19 +0200651 __func__, err);
652 err_count++;
653 unbusy_queued_urb(urb, portdata);
654 usb_autopm_put_interface_async(serial->interface);
655 continue;
Oliver Neukum16871dc2011-02-10 15:33:29 +0100656 }
Johan Hovold7436f412014-05-26 19:23:19 +0200657 data->in_flight++;
Matthew Garrett0d456192010-04-01 12:31:07 -0400658 }
Johan Hovoldfb7ad4f2014-05-26 19:23:18 +0200659
Johan Hovold7436f412014-05-26 19:23:19 +0200660 if (err_count)
661 return -EIO;
662
663 return 0;
Matthew Garrett0d456192010-04-01 12:31:07 -0400664}
665
666int usb_wwan_resume(struct usb_serial *serial)
667{
668 int i, j;
669 struct usb_serial_port *port;
Johan Hovold37357ca2014-05-26 19:23:26 +0200670 struct usb_wwan_intf_private *intfdata = usb_get_serial_data(serial);
Matthew Garrett0d456192010-04-01 12:31:07 -0400671 struct usb_wwan_port_private *portdata;
672 struct urb *urb;
Johan Hovoldfb7ad4f2014-05-26 19:23:18 +0200673 int err;
674 int err_count = 0;
Matthew Garrett0d456192010-04-01 12:31:07 -0400675
xiao jind9e93c02014-05-26 19:23:14 +0200676 spin_lock_irq(&intfdata->susp_lock);
Matthew Garrett0d456192010-04-01 12:31:07 -0400677 for (i = 0; i < serial->num_ports; i++) {
Matthew Garrett0d456192010-04-01 12:31:07 -0400678 port = serial->port[i];
Matthew Garrett0d456192010-04-01 12:31:07 -0400679
Peter Hurleyd41861c2016-04-09 17:53:25 -0700680 if (!tty_port_initialized(&port->port))
Matthew Garrett0d456192010-04-01 12:31:07 -0400681 continue;
Matthew Garrett0d456192010-04-01 12:31:07 -0400682
Johan Hovoldb0a9aa62014-05-26 19:23:32 +0200683 portdata = usb_get_serial_port_data(port);
684
Johan Hovold9096f1f2014-05-26 19:23:17 +0200685 if (port->interrupt_in_urb) {
686 err = usb_submit_urb(port->interrupt_in_urb,
687 GFP_ATOMIC);
688 if (err) {
689 dev_err(&port->dev,
690 "%s: submit int urb failed: %d\n",
691 __func__, err);
Johan Hovoldfb7ad4f2014-05-26 19:23:18 +0200692 err_count++;
Johan Hovold9096f1f2014-05-26 19:23:17 +0200693 }
694 }
695
Johan Hovold3362c912014-05-26 19:23:27 +0200696 err = usb_wwan_submit_delayed_urbs(port);
Johan Hovoldfb7ad4f2014-05-26 19:23:18 +0200697 if (err)
698 err_count++;
699
Matthew Garrett0d456192010-04-01 12:31:07 -0400700 for (j = 0; j < N_IN_URB; j++) {
701 urb = portdata->in_urbs[j];
702 err = usb_submit_urb(urb, GFP_ATOMIC);
703 if (err < 0) {
Johan Hovoldb0f9d002014-05-26 19:23:25 +0200704 dev_err(&port->dev,
705 "%s: submit read urb %d failed: %d\n",
706 __func__, i, err);
Johan Hovoldfb7ad4f2014-05-26 19:23:18 +0200707 err_count++;
Matthew Garrett0d456192010-04-01 12:31:07 -0400708 }
709 }
Matthew Garrett0d456192010-04-01 12:31:07 -0400710 }
Matthew Garrett0d456192010-04-01 12:31:07 -0400711 intfdata->suspended = 0;
712 spin_unlock_irq(&intfdata->susp_lock);
Johan Hovoldfb7ad4f2014-05-26 19:23:18 +0200713
714 if (err_count)
715 return -EIO;
716
717 return 0;
Matthew Garrett0d456192010-04-01 12:31:07 -0400718}
719EXPORT_SYMBOL(usb_wwan_resume);
720#endif
721
722MODULE_AUTHOR(DRIVER_AUTHOR);
723MODULE_DESCRIPTION(DRIVER_DESC);
Johan Hovold627cfa82017-11-03 18:12:08 +0100724MODULE_LICENSE("GPL v2");