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