xref: /openbmc/linux/drivers/usb/serial/usb_wwan.c (revision 0d4561947b8ddd5d944bdbbdc1ea1d6fd9a06041)
1*0d456194SMatthew Garrett /*
2*0d456194SMatthew Garrett   USB Driver layer for GSM modems
3*0d456194SMatthew Garrett 
4*0d456194SMatthew Garrett   Copyright (C) 2005  Matthias Urlichs <smurf@smurf.noris.de>
5*0d456194SMatthew Garrett 
6*0d456194SMatthew Garrett   This driver is free software; you can redistribute it and/or modify
7*0d456194SMatthew Garrett   it under the terms of Version 2 of the GNU General Public License as
8*0d456194SMatthew Garrett   published by the Free Software Foundation.
9*0d456194SMatthew Garrett 
10*0d456194SMatthew Garrett   Portions copied from the Keyspan driver by Hugh Blemings <hugh@blemings.org>
11*0d456194SMatthew Garrett 
12*0d456194SMatthew Garrett   History: see the git log.
13*0d456194SMatthew Garrett 
14*0d456194SMatthew Garrett   Work sponsored by: Sigos GmbH, Germany <info@sigos.de>
15*0d456194SMatthew Garrett 
16*0d456194SMatthew Garrett   This driver exists because the "normal" serial driver doesn't work too well
17*0d456194SMatthew Garrett   with GSM modems. Issues:
18*0d456194SMatthew Garrett   - data loss -- one single Receive URB is not nearly enough
19*0d456194SMatthew Garrett   - controlling the baud rate doesn't make sense
20*0d456194SMatthew Garrett */
21*0d456194SMatthew Garrett 
22*0d456194SMatthew Garrett #define DRIVER_VERSION "v0.7.2"
23*0d456194SMatthew Garrett #define DRIVER_AUTHOR "Matthias Urlichs <smurf@smurf.noris.de>"
24*0d456194SMatthew Garrett #define DRIVER_DESC "USB Driver for GSM modems"
25*0d456194SMatthew Garrett 
26*0d456194SMatthew Garrett #include <linux/kernel.h>
27*0d456194SMatthew Garrett #include <linux/jiffies.h>
28*0d456194SMatthew Garrett #include <linux/errno.h>
29*0d456194SMatthew Garrett #include <linux/slab.h>
30*0d456194SMatthew Garrett #include <linux/tty.h>
31*0d456194SMatthew Garrett #include <linux/tty_flip.h>
32*0d456194SMatthew Garrett #include <linux/module.h>
33*0d456194SMatthew Garrett #include <linux/bitops.h>
34*0d456194SMatthew Garrett #include <linux/usb.h>
35*0d456194SMatthew Garrett #include <linux/usb/serial.h>
36*0d456194SMatthew Garrett #include "usb-wwan.h"
37*0d456194SMatthew Garrett 
38*0d456194SMatthew Garrett static int debug;
39*0d456194SMatthew Garrett 
40*0d456194SMatthew Garrett void usb_wwan_dtr_rts(struct usb_serial_port *port, int on)
41*0d456194SMatthew Garrett {
42*0d456194SMatthew Garrett 	struct usb_serial *serial = port->serial;
43*0d456194SMatthew Garrett 	struct usb_wwan_port_private *portdata;
44*0d456194SMatthew Garrett 
45*0d456194SMatthew Garrett 	struct usb_wwan_intf_private *intfdata;
46*0d456194SMatthew Garrett 
47*0d456194SMatthew Garrett 	dbg("%s", __func__);
48*0d456194SMatthew Garrett 
49*0d456194SMatthew Garrett 	intfdata = port->serial->private;
50*0d456194SMatthew Garrett 
51*0d456194SMatthew Garrett 	if (!intfdata->send_setup)
52*0d456194SMatthew Garrett 		return;
53*0d456194SMatthew Garrett 
54*0d456194SMatthew Garrett 	portdata = usb_get_serial_port_data(port);
55*0d456194SMatthew Garrett 	mutex_lock(&serial->disc_mutex);
56*0d456194SMatthew Garrett 	portdata->rts_state = on;
57*0d456194SMatthew Garrett 	portdata->dtr_state = on;
58*0d456194SMatthew Garrett 	if (serial->dev)
59*0d456194SMatthew Garrett 		intfdata->send_setup(port);
60*0d456194SMatthew Garrett 	mutex_unlock(&serial->disc_mutex);
61*0d456194SMatthew Garrett }
62*0d456194SMatthew Garrett EXPORT_SYMBOL(usb_wwan_dtr_rts);
63*0d456194SMatthew Garrett 
64*0d456194SMatthew Garrett void usb_wwan_set_termios(struct tty_struct *tty,
65*0d456194SMatthew Garrett 			  struct usb_serial_port *port,
66*0d456194SMatthew Garrett 			  struct ktermios *old_termios)
67*0d456194SMatthew Garrett {
68*0d456194SMatthew Garrett 	struct usb_wwan_intf_private *intfdata = port->serial->private;
69*0d456194SMatthew Garrett 
70*0d456194SMatthew Garrett 	dbg("%s", __func__);
71*0d456194SMatthew Garrett 
72*0d456194SMatthew Garrett 	/* Doesn't support option setting */
73*0d456194SMatthew Garrett 	tty_termios_copy_hw(tty->termios, old_termios);
74*0d456194SMatthew Garrett 
75*0d456194SMatthew Garrett 	if (intfdata->send_setup)
76*0d456194SMatthew Garrett 		intfdata->send_setup(port);
77*0d456194SMatthew Garrett }
78*0d456194SMatthew Garrett EXPORT_SYMBOL(usb_wwan_set_termios);
79*0d456194SMatthew Garrett 
80*0d456194SMatthew Garrett int usb_wwan_tiocmget(struct tty_struct *tty, struct file *file)
81*0d456194SMatthew Garrett {
82*0d456194SMatthew Garrett 	struct usb_serial_port *port = tty->driver_data;
83*0d456194SMatthew Garrett 	unsigned int value;
84*0d456194SMatthew Garrett 	struct usb_wwan_port_private *portdata;
85*0d456194SMatthew Garrett 
86*0d456194SMatthew Garrett 	portdata = usb_get_serial_port_data(port);
87*0d456194SMatthew Garrett 
88*0d456194SMatthew Garrett 	value = ((portdata->rts_state) ? TIOCM_RTS : 0) |
89*0d456194SMatthew Garrett 	    ((portdata->dtr_state) ? TIOCM_DTR : 0) |
90*0d456194SMatthew Garrett 	    ((portdata->cts_state) ? TIOCM_CTS : 0) |
91*0d456194SMatthew Garrett 	    ((portdata->dsr_state) ? TIOCM_DSR : 0) |
92*0d456194SMatthew Garrett 	    ((portdata->dcd_state) ? TIOCM_CAR : 0) |
93*0d456194SMatthew Garrett 	    ((portdata->ri_state) ? TIOCM_RNG : 0);
94*0d456194SMatthew Garrett 
95*0d456194SMatthew Garrett 	return value;
96*0d456194SMatthew Garrett }
97*0d456194SMatthew Garrett EXPORT_SYMBOL(usb_wwan_tiocmget);
98*0d456194SMatthew Garrett 
99*0d456194SMatthew Garrett int usb_wwan_tiocmset(struct tty_struct *tty, struct file *file,
100*0d456194SMatthew Garrett 		      unsigned int set, unsigned int clear)
101*0d456194SMatthew Garrett {
102*0d456194SMatthew Garrett 	struct usb_serial_port *port = tty->driver_data;
103*0d456194SMatthew Garrett 	struct usb_wwan_port_private *portdata;
104*0d456194SMatthew Garrett 	struct usb_wwan_intf_private *intfdata;
105*0d456194SMatthew Garrett 
106*0d456194SMatthew Garrett 	portdata = usb_get_serial_port_data(port);
107*0d456194SMatthew Garrett 	intfdata = port->serial->private;
108*0d456194SMatthew Garrett 
109*0d456194SMatthew Garrett 	if (!intfdata->send_setup)
110*0d456194SMatthew Garrett 		return -EINVAL;
111*0d456194SMatthew Garrett 
112*0d456194SMatthew Garrett 	/* FIXME: what locks portdata fields ? */
113*0d456194SMatthew Garrett 	if (set & TIOCM_RTS)
114*0d456194SMatthew Garrett 		portdata->rts_state = 1;
115*0d456194SMatthew Garrett 	if (set & TIOCM_DTR)
116*0d456194SMatthew Garrett 		portdata->dtr_state = 1;
117*0d456194SMatthew Garrett 
118*0d456194SMatthew Garrett 	if (clear & TIOCM_RTS)
119*0d456194SMatthew Garrett 		portdata->rts_state = 0;
120*0d456194SMatthew Garrett 	if (clear & TIOCM_DTR)
121*0d456194SMatthew Garrett 		portdata->dtr_state = 0;
122*0d456194SMatthew Garrett 	return intfdata->send_setup(port);
123*0d456194SMatthew Garrett }
124*0d456194SMatthew Garrett EXPORT_SYMBOL(usb_wwan_tiocmset);
125*0d456194SMatthew Garrett 
126*0d456194SMatthew Garrett /* Write */
127*0d456194SMatthew Garrett int usb_wwan_write(struct tty_struct *tty, struct usb_serial_port *port,
128*0d456194SMatthew Garrett 		   const unsigned char *buf, int count)
129*0d456194SMatthew Garrett {
130*0d456194SMatthew Garrett 	struct usb_wwan_port_private *portdata;
131*0d456194SMatthew Garrett 	struct usb_wwan_intf_private *intfdata;
132*0d456194SMatthew Garrett 	int i;
133*0d456194SMatthew Garrett 	int left, todo;
134*0d456194SMatthew Garrett 	struct urb *this_urb = NULL;	/* spurious */
135*0d456194SMatthew Garrett 	int err;
136*0d456194SMatthew Garrett 	unsigned long flags;
137*0d456194SMatthew Garrett 
138*0d456194SMatthew Garrett 	portdata = usb_get_serial_port_data(port);
139*0d456194SMatthew Garrett 	intfdata = port->serial->private;
140*0d456194SMatthew Garrett 
141*0d456194SMatthew Garrett 	dbg("%s: write (%d chars)", __func__, count);
142*0d456194SMatthew Garrett 
143*0d456194SMatthew Garrett 	i = 0;
144*0d456194SMatthew Garrett 	left = count;
145*0d456194SMatthew Garrett 	for (i = 0; left > 0 && i < N_OUT_URB; i++) {
146*0d456194SMatthew Garrett 		todo = left;
147*0d456194SMatthew Garrett 		if (todo > OUT_BUFLEN)
148*0d456194SMatthew Garrett 			todo = OUT_BUFLEN;
149*0d456194SMatthew Garrett 
150*0d456194SMatthew Garrett 		this_urb = portdata->out_urbs[i];
151*0d456194SMatthew Garrett 		if (test_and_set_bit(i, &portdata->out_busy)) {
152*0d456194SMatthew Garrett 			if (time_before(jiffies,
153*0d456194SMatthew Garrett 					portdata->tx_start_time[i] + 10 * HZ))
154*0d456194SMatthew Garrett 				continue;
155*0d456194SMatthew Garrett 			usb_unlink_urb(this_urb);
156*0d456194SMatthew Garrett 			continue;
157*0d456194SMatthew Garrett 		}
158*0d456194SMatthew Garrett 		dbg("%s: endpoint %d buf %d", __func__,
159*0d456194SMatthew Garrett 		    usb_pipeendpoint(this_urb->pipe), i);
160*0d456194SMatthew Garrett 
161*0d456194SMatthew Garrett 		err = usb_autopm_get_interface_async(port->serial->interface);
162*0d456194SMatthew Garrett 		if (err < 0)
163*0d456194SMatthew Garrett 			break;
164*0d456194SMatthew Garrett 
165*0d456194SMatthew Garrett 		/* send the data */
166*0d456194SMatthew Garrett 		memcpy(this_urb->transfer_buffer, buf, todo);
167*0d456194SMatthew Garrett 		this_urb->transfer_buffer_length = todo;
168*0d456194SMatthew Garrett 
169*0d456194SMatthew Garrett 		spin_lock_irqsave(&intfdata->susp_lock, flags);
170*0d456194SMatthew Garrett 		if (intfdata->suspended) {
171*0d456194SMatthew Garrett 			usb_anchor_urb(this_urb, &portdata->delayed);
172*0d456194SMatthew Garrett 			spin_unlock_irqrestore(&intfdata->susp_lock, flags);
173*0d456194SMatthew Garrett 		} else {
174*0d456194SMatthew Garrett 			intfdata->in_flight++;
175*0d456194SMatthew Garrett 			spin_unlock_irqrestore(&intfdata->susp_lock, flags);
176*0d456194SMatthew Garrett 			err = usb_submit_urb(this_urb, GFP_ATOMIC);
177*0d456194SMatthew Garrett 			if (err) {
178*0d456194SMatthew Garrett 				dbg("usb_submit_urb %p (write bulk) failed "
179*0d456194SMatthew Garrett 				    "(%d)", this_urb, err);
180*0d456194SMatthew Garrett 				clear_bit(i, &portdata->out_busy);
181*0d456194SMatthew Garrett 				spin_lock_irqsave(&intfdata->susp_lock, flags);
182*0d456194SMatthew Garrett 				intfdata->in_flight--;
183*0d456194SMatthew Garrett 				spin_unlock_irqrestore(&intfdata->susp_lock,
184*0d456194SMatthew Garrett 						       flags);
185*0d456194SMatthew Garrett 				continue;
186*0d456194SMatthew Garrett 			}
187*0d456194SMatthew Garrett 		}
188*0d456194SMatthew Garrett 
189*0d456194SMatthew Garrett 		portdata->tx_start_time[i] = jiffies;
190*0d456194SMatthew Garrett 		buf += todo;
191*0d456194SMatthew Garrett 		left -= todo;
192*0d456194SMatthew Garrett 	}
193*0d456194SMatthew Garrett 
194*0d456194SMatthew Garrett 	count -= left;
195*0d456194SMatthew Garrett 	dbg("%s: wrote (did %d)", __func__, count);
196*0d456194SMatthew Garrett 	return count;
197*0d456194SMatthew Garrett }
198*0d456194SMatthew Garrett EXPORT_SYMBOL(usb_wwan_write);
199*0d456194SMatthew Garrett 
200*0d456194SMatthew Garrett static void usb_wwan_indat_callback(struct urb *urb)
201*0d456194SMatthew Garrett {
202*0d456194SMatthew Garrett 	int err;
203*0d456194SMatthew Garrett 	int endpoint;
204*0d456194SMatthew Garrett 	struct usb_serial_port *port;
205*0d456194SMatthew Garrett 	struct tty_struct *tty;
206*0d456194SMatthew Garrett 	unsigned char *data = urb->transfer_buffer;
207*0d456194SMatthew Garrett 	int status = urb->status;
208*0d456194SMatthew Garrett 
209*0d456194SMatthew Garrett 	dbg("%s: %p", __func__, urb);
210*0d456194SMatthew Garrett 
211*0d456194SMatthew Garrett 	endpoint = usb_pipeendpoint(urb->pipe);
212*0d456194SMatthew Garrett 	port = urb->context;
213*0d456194SMatthew Garrett 
214*0d456194SMatthew Garrett 	if (status) {
215*0d456194SMatthew Garrett 		dbg("%s: nonzero status: %d on endpoint %02x.",
216*0d456194SMatthew Garrett 		    __func__, status, endpoint);
217*0d456194SMatthew Garrett 	} else {
218*0d456194SMatthew Garrett 		tty = tty_port_tty_get(&port->port);
219*0d456194SMatthew Garrett 		if (urb->actual_length) {
220*0d456194SMatthew Garrett 			tty_insert_flip_string(tty, data, urb->actual_length);
221*0d456194SMatthew Garrett 			tty_flip_buffer_push(tty);
222*0d456194SMatthew Garrett 		} else
223*0d456194SMatthew Garrett 			dbg("%s: empty read urb received", __func__);
224*0d456194SMatthew Garrett 		tty_kref_put(tty);
225*0d456194SMatthew Garrett 
226*0d456194SMatthew Garrett 		/* Resubmit urb so we continue receiving */
227*0d456194SMatthew Garrett 		if (status != -ESHUTDOWN) {
228*0d456194SMatthew Garrett 			err = usb_submit_urb(urb, GFP_ATOMIC);
229*0d456194SMatthew Garrett 			if (err && err != -EPERM)
230*0d456194SMatthew Garrett 				printk(KERN_ERR "%s: resubmit read urb failed. "
231*0d456194SMatthew Garrett 				       "(%d)", __func__, err);
232*0d456194SMatthew Garrett 			else
233*0d456194SMatthew Garrett 				usb_mark_last_busy(port->serial->dev);
234*0d456194SMatthew Garrett 		}
235*0d456194SMatthew Garrett 
236*0d456194SMatthew Garrett 	}
237*0d456194SMatthew Garrett 	return;
238*0d456194SMatthew Garrett }
239*0d456194SMatthew Garrett 
240*0d456194SMatthew Garrett static void usb_wwan_outdat_callback(struct urb *urb)
241*0d456194SMatthew Garrett {
242*0d456194SMatthew Garrett 	struct usb_serial_port *port;
243*0d456194SMatthew Garrett 	struct usb_wwan_port_private *portdata;
244*0d456194SMatthew Garrett 	struct usb_wwan_intf_private *intfdata;
245*0d456194SMatthew Garrett 	int i;
246*0d456194SMatthew Garrett 
247*0d456194SMatthew Garrett 	dbg("%s", __func__);
248*0d456194SMatthew Garrett 
249*0d456194SMatthew Garrett 	port = urb->context;
250*0d456194SMatthew Garrett 	intfdata = port->serial->private;
251*0d456194SMatthew Garrett 
252*0d456194SMatthew Garrett 	usb_serial_port_softint(port);
253*0d456194SMatthew Garrett 	usb_autopm_put_interface_async(port->serial->interface);
254*0d456194SMatthew Garrett 	portdata = usb_get_serial_port_data(port);
255*0d456194SMatthew Garrett 	spin_lock(&intfdata->susp_lock);
256*0d456194SMatthew Garrett 	intfdata->in_flight--;
257*0d456194SMatthew Garrett 	spin_unlock(&intfdata->susp_lock);
258*0d456194SMatthew Garrett 
259*0d456194SMatthew Garrett 	for (i = 0; i < N_OUT_URB; ++i) {
260*0d456194SMatthew Garrett 		if (portdata->out_urbs[i] == urb) {
261*0d456194SMatthew Garrett 			smp_mb__before_clear_bit();
262*0d456194SMatthew Garrett 			clear_bit(i, &portdata->out_busy);
263*0d456194SMatthew Garrett 			break;
264*0d456194SMatthew Garrett 		}
265*0d456194SMatthew Garrett 	}
266*0d456194SMatthew Garrett }
267*0d456194SMatthew Garrett 
268*0d456194SMatthew Garrett int usb_wwan_write_room(struct tty_struct *tty)
269*0d456194SMatthew Garrett {
270*0d456194SMatthew Garrett 	struct usb_serial_port *port = tty->driver_data;
271*0d456194SMatthew Garrett 	struct usb_wwan_port_private *portdata;
272*0d456194SMatthew Garrett 	int i;
273*0d456194SMatthew Garrett 	int data_len = 0;
274*0d456194SMatthew Garrett 	struct urb *this_urb;
275*0d456194SMatthew Garrett 
276*0d456194SMatthew Garrett 	portdata = usb_get_serial_port_data(port);
277*0d456194SMatthew Garrett 
278*0d456194SMatthew Garrett 	for (i = 0; i < N_OUT_URB; i++) {
279*0d456194SMatthew Garrett 		this_urb = portdata->out_urbs[i];
280*0d456194SMatthew Garrett 		if (this_urb && !test_bit(i, &portdata->out_busy))
281*0d456194SMatthew Garrett 			data_len += OUT_BUFLEN;
282*0d456194SMatthew Garrett 	}
283*0d456194SMatthew Garrett 
284*0d456194SMatthew Garrett 	dbg("%s: %d", __func__, data_len);
285*0d456194SMatthew Garrett 	return data_len;
286*0d456194SMatthew Garrett }
287*0d456194SMatthew Garrett EXPORT_SYMBOL(usb_wwan_write_room);
288*0d456194SMatthew Garrett 
289*0d456194SMatthew Garrett int usb_wwan_chars_in_buffer(struct tty_struct *tty)
290*0d456194SMatthew Garrett {
291*0d456194SMatthew Garrett 	struct usb_serial_port *port = tty->driver_data;
292*0d456194SMatthew Garrett 	struct usb_wwan_port_private *portdata;
293*0d456194SMatthew Garrett 	int i;
294*0d456194SMatthew Garrett 	int data_len = 0;
295*0d456194SMatthew Garrett 	struct urb *this_urb;
296*0d456194SMatthew Garrett 
297*0d456194SMatthew Garrett 	portdata = usb_get_serial_port_data(port);
298*0d456194SMatthew Garrett 
299*0d456194SMatthew Garrett 	for (i = 0; i < N_OUT_URB; i++) {
300*0d456194SMatthew Garrett 		this_urb = portdata->out_urbs[i];
301*0d456194SMatthew Garrett 		/* FIXME: This locking is insufficient as this_urb may
302*0d456194SMatthew Garrett 		   go unused during the test */
303*0d456194SMatthew Garrett 		if (this_urb && test_bit(i, &portdata->out_busy))
304*0d456194SMatthew Garrett 			data_len += this_urb->transfer_buffer_length;
305*0d456194SMatthew Garrett 	}
306*0d456194SMatthew Garrett 	dbg("%s: %d", __func__, data_len);
307*0d456194SMatthew Garrett 	return data_len;
308*0d456194SMatthew Garrett }
309*0d456194SMatthew Garrett EXPORT_SYMBOL(usb_wwan_chars_in_buffer);
310*0d456194SMatthew Garrett 
311*0d456194SMatthew Garrett int usb_wwan_open(struct tty_struct *tty, struct usb_serial_port *port)
312*0d456194SMatthew Garrett {
313*0d456194SMatthew Garrett 	struct usb_wwan_port_private *portdata;
314*0d456194SMatthew Garrett 	struct usb_wwan_intf_private *intfdata;
315*0d456194SMatthew Garrett 	struct usb_serial *serial = port->serial;
316*0d456194SMatthew Garrett 	int i, err;
317*0d456194SMatthew Garrett 	struct urb *urb;
318*0d456194SMatthew Garrett 
319*0d456194SMatthew Garrett 	portdata = usb_get_serial_port_data(port);
320*0d456194SMatthew Garrett 	intfdata = serial->private;
321*0d456194SMatthew Garrett 
322*0d456194SMatthew Garrett 	dbg("%s", __func__);
323*0d456194SMatthew Garrett 
324*0d456194SMatthew Garrett 	/* Start reading from the IN endpoint */
325*0d456194SMatthew Garrett 	for (i = 0; i < N_IN_URB; i++) {
326*0d456194SMatthew Garrett 		urb = portdata->in_urbs[i];
327*0d456194SMatthew Garrett 		if (!urb)
328*0d456194SMatthew Garrett 			continue;
329*0d456194SMatthew Garrett 		err = usb_submit_urb(urb, GFP_KERNEL);
330*0d456194SMatthew Garrett 		if (err) {
331*0d456194SMatthew Garrett 			dbg("%s: submit urb %d failed (%d) %d",
332*0d456194SMatthew Garrett 			    __func__, i, err, urb->transfer_buffer_length);
333*0d456194SMatthew Garrett 		}
334*0d456194SMatthew Garrett 	}
335*0d456194SMatthew Garrett 
336*0d456194SMatthew Garrett 	if (intfdata->send_setup)
337*0d456194SMatthew Garrett 		intfdata->send_setup(port);
338*0d456194SMatthew Garrett 
339*0d456194SMatthew Garrett 	serial->interface->needs_remote_wakeup = 1;
340*0d456194SMatthew Garrett 	spin_lock_irq(&intfdata->susp_lock);
341*0d456194SMatthew Garrett 	portdata->opened = 1;
342*0d456194SMatthew Garrett 	spin_unlock_irq(&intfdata->susp_lock);
343*0d456194SMatthew Garrett 	usb_autopm_put_interface(serial->interface);
344*0d456194SMatthew Garrett 
345*0d456194SMatthew Garrett 	return 0;
346*0d456194SMatthew Garrett }
347*0d456194SMatthew Garrett EXPORT_SYMBOL(usb_wwan_open);
348*0d456194SMatthew Garrett 
349*0d456194SMatthew Garrett void usb_wwan_close(struct usb_serial_port *port)
350*0d456194SMatthew Garrett {
351*0d456194SMatthew Garrett 	int i;
352*0d456194SMatthew Garrett 	struct usb_serial *serial = port->serial;
353*0d456194SMatthew Garrett 	struct usb_wwan_port_private *portdata;
354*0d456194SMatthew Garrett 	struct usb_wwan_intf_private *intfdata = port->serial->private;
355*0d456194SMatthew Garrett 
356*0d456194SMatthew Garrett 	dbg("%s", __func__);
357*0d456194SMatthew Garrett 	portdata = usb_get_serial_port_data(port);
358*0d456194SMatthew Garrett 
359*0d456194SMatthew Garrett 	if (serial->dev) {
360*0d456194SMatthew Garrett 		/* Stop reading/writing urbs */
361*0d456194SMatthew Garrett 		spin_lock_irq(&intfdata->susp_lock);
362*0d456194SMatthew Garrett 		portdata->opened = 0;
363*0d456194SMatthew Garrett 		spin_unlock_irq(&intfdata->susp_lock);
364*0d456194SMatthew Garrett 
365*0d456194SMatthew Garrett 		for (i = 0; i < N_IN_URB; i++)
366*0d456194SMatthew Garrett 			usb_kill_urb(portdata->in_urbs[i]);
367*0d456194SMatthew Garrett 		for (i = 0; i < N_OUT_URB; i++)
368*0d456194SMatthew Garrett 			usb_kill_urb(portdata->out_urbs[i]);
369*0d456194SMatthew Garrett 		usb_autopm_get_interface(serial->interface);
370*0d456194SMatthew Garrett 		serial->interface->needs_remote_wakeup = 0;
371*0d456194SMatthew Garrett 	}
372*0d456194SMatthew Garrett }
373*0d456194SMatthew Garrett EXPORT_SYMBOL(usb_wwan_close);
374*0d456194SMatthew Garrett 
375*0d456194SMatthew Garrett /* Helper functions used by usb_wwan_setup_urbs */
376*0d456194SMatthew Garrett static struct urb *usb_wwan_setup_urb(struct usb_serial *serial, int endpoint,
377*0d456194SMatthew Garrett 				      int dir, void *ctx, char *buf, int len,
378*0d456194SMatthew Garrett 				      void (*callback) (struct urb *))
379*0d456194SMatthew Garrett {
380*0d456194SMatthew Garrett 	struct urb *urb;
381*0d456194SMatthew Garrett 
382*0d456194SMatthew Garrett 	if (endpoint == -1)
383*0d456194SMatthew Garrett 		return NULL;	/* endpoint not needed */
384*0d456194SMatthew Garrett 
385*0d456194SMatthew Garrett 	urb = usb_alloc_urb(0, GFP_KERNEL);	/* No ISO */
386*0d456194SMatthew Garrett 	if (urb == NULL) {
387*0d456194SMatthew Garrett 		dbg("%s: alloc for endpoint %d failed.", __func__, endpoint);
388*0d456194SMatthew Garrett 		return NULL;
389*0d456194SMatthew Garrett 	}
390*0d456194SMatthew Garrett 
391*0d456194SMatthew Garrett 	/* Fill URB using supplied data. */
392*0d456194SMatthew Garrett 	usb_fill_bulk_urb(urb, serial->dev,
393*0d456194SMatthew Garrett 			  usb_sndbulkpipe(serial->dev, endpoint) | dir,
394*0d456194SMatthew Garrett 			  buf, len, callback, ctx);
395*0d456194SMatthew Garrett 
396*0d456194SMatthew Garrett 	return urb;
397*0d456194SMatthew Garrett }
398*0d456194SMatthew Garrett 
399*0d456194SMatthew Garrett /* Setup urbs */
400*0d456194SMatthew Garrett static void usb_wwan_setup_urbs(struct usb_serial *serial)
401*0d456194SMatthew Garrett {
402*0d456194SMatthew Garrett 	int i, j;
403*0d456194SMatthew Garrett 	struct usb_serial_port *port;
404*0d456194SMatthew Garrett 	struct usb_wwan_port_private *portdata;
405*0d456194SMatthew Garrett 
406*0d456194SMatthew Garrett 	dbg("%s", __func__);
407*0d456194SMatthew Garrett 
408*0d456194SMatthew Garrett 	for (i = 0; i < serial->num_ports; i++) {
409*0d456194SMatthew Garrett 		port = serial->port[i];
410*0d456194SMatthew Garrett 		portdata = usb_get_serial_port_data(port);
411*0d456194SMatthew Garrett 
412*0d456194SMatthew Garrett 		/* Do indat endpoints first */
413*0d456194SMatthew Garrett 		for (j = 0; j < N_IN_URB; ++j) {
414*0d456194SMatthew Garrett 			portdata->in_urbs[j] = usb_wwan_setup_urb(serial,
415*0d456194SMatthew Garrett 								  port->
416*0d456194SMatthew Garrett 								  bulk_in_endpointAddress,
417*0d456194SMatthew Garrett 								  USB_DIR_IN,
418*0d456194SMatthew Garrett 								  port,
419*0d456194SMatthew Garrett 								  portdata->
420*0d456194SMatthew Garrett 								  in_buffer[j],
421*0d456194SMatthew Garrett 								  IN_BUFLEN,
422*0d456194SMatthew Garrett 								  usb_wwan_indat_callback);
423*0d456194SMatthew Garrett 		}
424*0d456194SMatthew Garrett 
425*0d456194SMatthew Garrett 		/* outdat endpoints */
426*0d456194SMatthew Garrett 		for (j = 0; j < N_OUT_URB; ++j) {
427*0d456194SMatthew Garrett 			portdata->out_urbs[j] = usb_wwan_setup_urb(serial,
428*0d456194SMatthew Garrett 								   port->
429*0d456194SMatthew Garrett 								   bulk_out_endpointAddress,
430*0d456194SMatthew Garrett 								   USB_DIR_OUT,
431*0d456194SMatthew Garrett 								   port,
432*0d456194SMatthew Garrett 								   portdata->
433*0d456194SMatthew Garrett 								   out_buffer
434*0d456194SMatthew Garrett 								   [j],
435*0d456194SMatthew Garrett 								   OUT_BUFLEN,
436*0d456194SMatthew Garrett 								   usb_wwan_outdat_callback);
437*0d456194SMatthew Garrett 		}
438*0d456194SMatthew Garrett 	}
439*0d456194SMatthew Garrett }
440*0d456194SMatthew Garrett 
441*0d456194SMatthew Garrett int usb_wwan_startup(struct usb_serial *serial)
442*0d456194SMatthew Garrett {
443*0d456194SMatthew Garrett 	int i, j, err;
444*0d456194SMatthew Garrett 	struct usb_serial_port *port;
445*0d456194SMatthew Garrett 	struct usb_wwan_port_private *portdata;
446*0d456194SMatthew Garrett 	u8 *buffer;
447*0d456194SMatthew Garrett 
448*0d456194SMatthew Garrett 	dbg("%s", __func__);
449*0d456194SMatthew Garrett 
450*0d456194SMatthew Garrett 	/* Now setup per port private data */
451*0d456194SMatthew Garrett 	for (i = 0; i < serial->num_ports; i++) {
452*0d456194SMatthew Garrett 		port = serial->port[i];
453*0d456194SMatthew Garrett 		portdata = kzalloc(sizeof(*portdata), GFP_KERNEL);
454*0d456194SMatthew Garrett 		if (!portdata) {
455*0d456194SMatthew Garrett 			dbg("%s: kmalloc for usb_wwan_port_private (%d) failed!.",
456*0d456194SMatthew Garrett 			    __func__, i);
457*0d456194SMatthew Garrett 			return 1;
458*0d456194SMatthew Garrett 		}
459*0d456194SMatthew Garrett 		init_usb_anchor(&portdata->delayed);
460*0d456194SMatthew Garrett 
461*0d456194SMatthew Garrett 		for (j = 0; j < N_IN_URB; j++) {
462*0d456194SMatthew Garrett 			buffer = (u8 *) __get_free_page(GFP_KERNEL);
463*0d456194SMatthew Garrett 			if (!buffer)
464*0d456194SMatthew Garrett 				goto bail_out_error;
465*0d456194SMatthew Garrett 			portdata->in_buffer[j] = buffer;
466*0d456194SMatthew Garrett 		}
467*0d456194SMatthew Garrett 
468*0d456194SMatthew Garrett 		for (j = 0; j < N_OUT_URB; j++) {
469*0d456194SMatthew Garrett 			buffer = kmalloc(OUT_BUFLEN, GFP_KERNEL);
470*0d456194SMatthew Garrett 			if (!buffer)
471*0d456194SMatthew Garrett 				goto bail_out_error2;
472*0d456194SMatthew Garrett 			portdata->out_buffer[j] = buffer;
473*0d456194SMatthew Garrett 		}
474*0d456194SMatthew Garrett 
475*0d456194SMatthew Garrett 		usb_set_serial_port_data(port, portdata);
476*0d456194SMatthew Garrett 
477*0d456194SMatthew Garrett 		if (!port->interrupt_in_urb)
478*0d456194SMatthew Garrett 			continue;
479*0d456194SMatthew Garrett 		err = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
480*0d456194SMatthew Garrett 		if (err)
481*0d456194SMatthew Garrett 			dbg("%s: submit irq_in urb failed %d", __func__, err);
482*0d456194SMatthew Garrett 	}
483*0d456194SMatthew Garrett 	usb_wwan_setup_urbs(serial);
484*0d456194SMatthew Garrett 	return 0;
485*0d456194SMatthew Garrett 
486*0d456194SMatthew Garrett bail_out_error2:
487*0d456194SMatthew Garrett 	for (j = 0; j < N_OUT_URB; j++)
488*0d456194SMatthew Garrett 		kfree(portdata->out_buffer[j]);
489*0d456194SMatthew Garrett bail_out_error:
490*0d456194SMatthew Garrett 	for (j = 0; j < N_IN_URB; j++)
491*0d456194SMatthew Garrett 		if (portdata->in_buffer[j])
492*0d456194SMatthew Garrett 			free_page((unsigned long)portdata->in_buffer[j]);
493*0d456194SMatthew Garrett 	kfree(portdata);
494*0d456194SMatthew Garrett 	return 1;
495*0d456194SMatthew Garrett }
496*0d456194SMatthew Garrett EXPORT_SYMBOL(usb_wwan_startup);
497*0d456194SMatthew Garrett 
498*0d456194SMatthew Garrett static void stop_read_write_urbs(struct usb_serial *serial)
499*0d456194SMatthew Garrett {
500*0d456194SMatthew Garrett 	int i, j;
501*0d456194SMatthew Garrett 	struct usb_serial_port *port;
502*0d456194SMatthew Garrett 	struct usb_wwan_port_private *portdata;
503*0d456194SMatthew Garrett 
504*0d456194SMatthew Garrett 	/* Stop reading/writing urbs */
505*0d456194SMatthew Garrett 	for (i = 0; i < serial->num_ports; ++i) {
506*0d456194SMatthew Garrett 		port = serial->port[i];
507*0d456194SMatthew Garrett 		portdata = usb_get_serial_port_data(port);
508*0d456194SMatthew Garrett 		for (j = 0; j < N_IN_URB; j++)
509*0d456194SMatthew Garrett 			usb_kill_urb(portdata->in_urbs[j]);
510*0d456194SMatthew Garrett 		for (j = 0; j < N_OUT_URB; j++)
511*0d456194SMatthew Garrett 			usb_kill_urb(portdata->out_urbs[j]);
512*0d456194SMatthew Garrett 	}
513*0d456194SMatthew Garrett }
514*0d456194SMatthew Garrett 
515*0d456194SMatthew Garrett void usb_wwan_disconnect(struct usb_serial *serial)
516*0d456194SMatthew Garrett {
517*0d456194SMatthew Garrett 	dbg("%s", __func__);
518*0d456194SMatthew Garrett 
519*0d456194SMatthew Garrett 	stop_read_write_urbs(serial);
520*0d456194SMatthew Garrett }
521*0d456194SMatthew Garrett EXPORT_SYMBOL(usb_wwan_disconnect);
522*0d456194SMatthew Garrett 
523*0d456194SMatthew Garrett void usb_wwan_release(struct usb_serial *serial)
524*0d456194SMatthew Garrett {
525*0d456194SMatthew Garrett 	int i, j;
526*0d456194SMatthew Garrett 	struct usb_serial_port *port;
527*0d456194SMatthew Garrett 	struct usb_wwan_port_private *portdata;
528*0d456194SMatthew Garrett 
529*0d456194SMatthew Garrett 	dbg("%s", __func__);
530*0d456194SMatthew Garrett 
531*0d456194SMatthew Garrett 	/* Now free them */
532*0d456194SMatthew Garrett 	for (i = 0; i < serial->num_ports; ++i) {
533*0d456194SMatthew Garrett 		port = serial->port[i];
534*0d456194SMatthew Garrett 		portdata = usb_get_serial_port_data(port);
535*0d456194SMatthew Garrett 
536*0d456194SMatthew Garrett 		for (j = 0; j < N_IN_URB; j++) {
537*0d456194SMatthew Garrett 			usb_free_urb(portdata->in_urbs[j]);
538*0d456194SMatthew Garrett 			free_page((unsigned long)
539*0d456194SMatthew Garrett 				  portdata->in_buffer[j]);
540*0d456194SMatthew Garrett 			portdata->in_urbs[j] = NULL;
541*0d456194SMatthew Garrett 		}
542*0d456194SMatthew Garrett 		for (j = 0; j < N_OUT_URB; j++) {
543*0d456194SMatthew Garrett 			usb_free_urb(portdata->out_urbs[j]);
544*0d456194SMatthew Garrett 			kfree(portdata->out_buffer[j]);
545*0d456194SMatthew Garrett 			portdata->out_urbs[j] = NULL;
546*0d456194SMatthew Garrett 		}
547*0d456194SMatthew Garrett 	}
548*0d456194SMatthew Garrett 
549*0d456194SMatthew Garrett 	/* Now free per port private data */
550*0d456194SMatthew Garrett 	for (i = 0; i < serial->num_ports; i++) {
551*0d456194SMatthew Garrett 		port = serial->port[i];
552*0d456194SMatthew Garrett 		kfree(usb_get_serial_port_data(port));
553*0d456194SMatthew Garrett 	}
554*0d456194SMatthew Garrett }
555*0d456194SMatthew Garrett EXPORT_SYMBOL(usb_wwan_release);
556*0d456194SMatthew Garrett 
557*0d456194SMatthew Garrett #ifdef CONFIG_PM
558*0d456194SMatthew Garrett int usb_wwan_suspend(struct usb_serial *serial, pm_message_t message)
559*0d456194SMatthew Garrett {
560*0d456194SMatthew Garrett 	struct usb_wwan_intf_private *intfdata = serial->private;
561*0d456194SMatthew Garrett 	int b;
562*0d456194SMatthew Garrett 
563*0d456194SMatthew Garrett 	dbg("%s entered", __func__);
564*0d456194SMatthew Garrett 
565*0d456194SMatthew Garrett 	if (message.event & PM_EVENT_AUTO) {
566*0d456194SMatthew Garrett 		spin_lock_irq(&intfdata->susp_lock);
567*0d456194SMatthew Garrett 		b = intfdata->in_flight;
568*0d456194SMatthew Garrett 		spin_unlock_irq(&intfdata->susp_lock);
569*0d456194SMatthew Garrett 
570*0d456194SMatthew Garrett 		if (b)
571*0d456194SMatthew Garrett 			return -EBUSY;
572*0d456194SMatthew Garrett 	}
573*0d456194SMatthew Garrett 
574*0d456194SMatthew Garrett 	spin_lock_irq(&intfdata->susp_lock);
575*0d456194SMatthew Garrett 	intfdata->suspended = 1;
576*0d456194SMatthew Garrett 	spin_unlock_irq(&intfdata->susp_lock);
577*0d456194SMatthew Garrett 	stop_read_write_urbs(serial);
578*0d456194SMatthew Garrett 
579*0d456194SMatthew Garrett 	return 0;
580*0d456194SMatthew Garrett }
581*0d456194SMatthew Garrett EXPORT_SYMBOL(usb_wwan_suspend);
582*0d456194SMatthew Garrett 
583*0d456194SMatthew Garrett static void play_delayed(struct usb_serial_port *port)
584*0d456194SMatthew Garrett {
585*0d456194SMatthew Garrett 	struct usb_wwan_intf_private *data;
586*0d456194SMatthew Garrett 	struct usb_wwan_port_private *portdata;
587*0d456194SMatthew Garrett 	struct urb *urb;
588*0d456194SMatthew Garrett 	int err;
589*0d456194SMatthew Garrett 
590*0d456194SMatthew Garrett 	portdata = usb_get_serial_port_data(port);
591*0d456194SMatthew Garrett 	data = port->serial->private;
592*0d456194SMatthew Garrett 	while ((urb = usb_get_from_anchor(&portdata->delayed))) {
593*0d456194SMatthew Garrett 		err = usb_submit_urb(urb, GFP_ATOMIC);
594*0d456194SMatthew Garrett 		if (!err)
595*0d456194SMatthew Garrett 			data->in_flight++;
596*0d456194SMatthew Garrett 	}
597*0d456194SMatthew Garrett }
598*0d456194SMatthew Garrett 
599*0d456194SMatthew Garrett int usb_wwan_resume(struct usb_serial *serial)
600*0d456194SMatthew Garrett {
601*0d456194SMatthew Garrett 	int i, j;
602*0d456194SMatthew Garrett 	struct usb_serial_port *port;
603*0d456194SMatthew Garrett 	struct usb_wwan_intf_private *intfdata = serial->private;
604*0d456194SMatthew Garrett 	struct usb_wwan_port_private *portdata;
605*0d456194SMatthew Garrett 	struct urb *urb;
606*0d456194SMatthew Garrett 	int err = 0;
607*0d456194SMatthew Garrett 
608*0d456194SMatthew Garrett 	dbg("%s entered", __func__);
609*0d456194SMatthew Garrett 	/* get the interrupt URBs resubmitted unconditionally */
610*0d456194SMatthew Garrett 	for (i = 0; i < serial->num_ports; i++) {
611*0d456194SMatthew Garrett 		port = serial->port[i];
612*0d456194SMatthew Garrett 		if (!port->interrupt_in_urb) {
613*0d456194SMatthew Garrett 			dbg("%s: No interrupt URB for port %d", __func__, i);
614*0d456194SMatthew Garrett 			continue;
615*0d456194SMatthew Garrett 		}
616*0d456194SMatthew Garrett 		err = usb_submit_urb(port->interrupt_in_urb, GFP_NOIO);
617*0d456194SMatthew Garrett 		dbg("Submitted interrupt URB for port %d (result %d)", i, err);
618*0d456194SMatthew Garrett 		if (err < 0) {
619*0d456194SMatthew Garrett 			err("%s: Error %d for interrupt URB of port%d",
620*0d456194SMatthew Garrett 			    __func__, err, i);
621*0d456194SMatthew Garrett 			goto err_out;
622*0d456194SMatthew Garrett 		}
623*0d456194SMatthew Garrett 	}
624*0d456194SMatthew Garrett 
625*0d456194SMatthew Garrett 	for (i = 0; i < serial->num_ports; i++) {
626*0d456194SMatthew Garrett 		/* walk all ports */
627*0d456194SMatthew Garrett 		port = serial->port[i];
628*0d456194SMatthew Garrett 		portdata = usb_get_serial_port_data(port);
629*0d456194SMatthew Garrett 
630*0d456194SMatthew Garrett 		/* skip closed ports */
631*0d456194SMatthew Garrett 		spin_lock_irq(&intfdata->susp_lock);
632*0d456194SMatthew Garrett 		if (!portdata->opened) {
633*0d456194SMatthew Garrett 			spin_unlock_irq(&intfdata->susp_lock);
634*0d456194SMatthew Garrett 			continue;
635*0d456194SMatthew Garrett 		}
636*0d456194SMatthew Garrett 
637*0d456194SMatthew Garrett 		for (j = 0; j < N_IN_URB; j++) {
638*0d456194SMatthew Garrett 			urb = portdata->in_urbs[j];
639*0d456194SMatthew Garrett 			err = usb_submit_urb(urb, GFP_ATOMIC);
640*0d456194SMatthew Garrett 			if (err < 0) {
641*0d456194SMatthew Garrett 				err("%s: Error %d for bulk URB %d",
642*0d456194SMatthew Garrett 				    __func__, err, i);
643*0d456194SMatthew Garrett 				spin_unlock_irq(&intfdata->susp_lock);
644*0d456194SMatthew Garrett 				goto err_out;
645*0d456194SMatthew Garrett 			}
646*0d456194SMatthew Garrett 		}
647*0d456194SMatthew Garrett 		play_delayed(port);
648*0d456194SMatthew Garrett 		spin_unlock_irq(&intfdata->susp_lock);
649*0d456194SMatthew Garrett 	}
650*0d456194SMatthew Garrett 	spin_lock_irq(&intfdata->susp_lock);
651*0d456194SMatthew Garrett 	intfdata->suspended = 0;
652*0d456194SMatthew Garrett 	spin_unlock_irq(&intfdata->susp_lock);
653*0d456194SMatthew Garrett err_out:
654*0d456194SMatthew Garrett 	return err;
655*0d456194SMatthew Garrett }
656*0d456194SMatthew Garrett EXPORT_SYMBOL(usb_wwan_resume);
657*0d456194SMatthew Garrett #endif
658*0d456194SMatthew Garrett 
659*0d456194SMatthew Garrett MODULE_AUTHOR(DRIVER_AUTHOR);
660*0d456194SMatthew Garrett MODULE_DESCRIPTION(DRIVER_DESC);
661*0d456194SMatthew Garrett MODULE_VERSION(DRIVER_VERSION);
662*0d456194SMatthew Garrett MODULE_LICENSE("GPL");
663*0d456194SMatthew Garrett 
664*0d456194SMatthew Garrett module_param(debug, bool, S_IRUGO | S_IWUSR);
665*0d456194SMatthew Garrett MODULE_PARM_DESC(debug, "Debug messages");
666