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