diff options
author | Glenn Elliott <gelliott@cs.unc.edu> | 2012-03-04 19:47:13 -0500 |
---|---|---|
committer | Glenn Elliott <gelliott@cs.unc.edu> | 2012-03-04 19:47:13 -0500 |
commit | c71c03bda1e86c9d5198c5d83f712e695c4f2a1e (patch) | |
tree | ecb166cb3e2b7e2adb3b5e292245fefd23381ac8 /drivers/usb/serial/usb_wwan.c | |
parent | ea53c912f8a86a8567697115b6a0d8152beee5c8 (diff) | |
parent | 6a00f206debf8a5c8899055726ad127dbeeed098 (diff) |
Merge branch 'mpi-master' into wip-k-fmlpwip-k-fmlp
Conflicts:
litmus/sched_cedf.c
Diffstat (limited to 'drivers/usb/serial/usb_wwan.c')
-rw-r--r-- | drivers/usb/serial/usb_wwan.c | 141 |
1 files changed, 125 insertions, 16 deletions
diff --git a/drivers/usb/serial/usb_wwan.c b/drivers/usb/serial/usb_wwan.c index 0c70b4a621bb..e4fad5e643d7 100644 --- a/drivers/usb/serial/usb_wwan.c +++ b/drivers/usb/serial/usb_wwan.c | |||
@@ -31,8 +31,10 @@ | |||
31 | #include <linux/tty_flip.h> | 31 | #include <linux/tty_flip.h> |
32 | #include <linux/module.h> | 32 | #include <linux/module.h> |
33 | #include <linux/bitops.h> | 33 | #include <linux/bitops.h> |
34 | #include <linux/uaccess.h> | ||
34 | #include <linux/usb.h> | 35 | #include <linux/usb.h> |
35 | #include <linux/usb/serial.h> | 36 | #include <linux/usb/serial.h> |
37 | #include <linux/serial.h> | ||
36 | #include "usb-wwan.h" | 38 | #include "usb-wwan.h" |
37 | 39 | ||
38 | static int debug; | 40 | static int debug; |
@@ -77,7 +79,7 @@ void usb_wwan_set_termios(struct tty_struct *tty, | |||
77 | } | 79 | } |
78 | EXPORT_SYMBOL(usb_wwan_set_termios); | 80 | EXPORT_SYMBOL(usb_wwan_set_termios); |
79 | 81 | ||
80 | int usb_wwan_tiocmget(struct tty_struct *tty, struct file *file) | 82 | int usb_wwan_tiocmget(struct tty_struct *tty) |
81 | { | 83 | { |
82 | struct usb_serial_port *port = tty->driver_data; | 84 | struct usb_serial_port *port = tty->driver_data; |
83 | unsigned int value; | 85 | unsigned int value; |
@@ -96,7 +98,7 @@ int usb_wwan_tiocmget(struct tty_struct *tty, struct file *file) | |||
96 | } | 98 | } |
97 | EXPORT_SYMBOL(usb_wwan_tiocmget); | 99 | EXPORT_SYMBOL(usb_wwan_tiocmget); |
98 | 100 | ||
99 | int usb_wwan_tiocmset(struct tty_struct *tty, struct file *file, | 101 | int usb_wwan_tiocmset(struct tty_struct *tty, |
100 | unsigned int set, unsigned int clear) | 102 | unsigned int set, unsigned int clear) |
101 | { | 103 | { |
102 | struct usb_serial_port *port = tty->driver_data; | 104 | struct usb_serial_port *port = tty->driver_data; |
@@ -123,6 +125,83 @@ int usb_wwan_tiocmset(struct tty_struct *tty, struct file *file, | |||
123 | } | 125 | } |
124 | EXPORT_SYMBOL(usb_wwan_tiocmset); | 126 | EXPORT_SYMBOL(usb_wwan_tiocmset); |
125 | 127 | ||
128 | static int get_serial_info(struct usb_serial_port *port, | ||
129 | struct serial_struct __user *retinfo) | ||
130 | { | ||
131 | struct serial_struct tmp; | ||
132 | |||
133 | if (!retinfo) | ||
134 | return -EFAULT; | ||
135 | |||
136 | memset(&tmp, 0, sizeof(tmp)); | ||
137 | tmp.line = port->serial->minor; | ||
138 | tmp.port = port->number; | ||
139 | tmp.baud_base = tty_get_baud_rate(port->port.tty); | ||
140 | tmp.close_delay = port->port.close_delay / 10; | ||
141 | tmp.closing_wait = port->port.closing_wait == ASYNC_CLOSING_WAIT_NONE ? | ||
142 | ASYNC_CLOSING_WAIT_NONE : | ||
143 | port->port.closing_wait / 10; | ||
144 | |||
145 | if (copy_to_user(retinfo, &tmp, sizeof(*retinfo))) | ||
146 | return -EFAULT; | ||
147 | return 0; | ||
148 | } | ||
149 | |||
150 | static int set_serial_info(struct usb_serial_port *port, | ||
151 | struct serial_struct __user *newinfo) | ||
152 | { | ||
153 | struct serial_struct new_serial; | ||
154 | unsigned int closing_wait, close_delay; | ||
155 | int retval = 0; | ||
156 | |||
157 | if (copy_from_user(&new_serial, newinfo, sizeof(new_serial))) | ||
158 | return -EFAULT; | ||
159 | |||
160 | close_delay = new_serial.close_delay * 10; | ||
161 | closing_wait = new_serial.closing_wait == ASYNC_CLOSING_WAIT_NONE ? | ||
162 | ASYNC_CLOSING_WAIT_NONE : new_serial.closing_wait * 10; | ||
163 | |||
164 | mutex_lock(&port->port.mutex); | ||
165 | |||
166 | if (!capable(CAP_SYS_ADMIN)) { | ||
167 | if ((close_delay != port->port.close_delay) || | ||
168 | (closing_wait != port->port.closing_wait)) | ||
169 | retval = -EPERM; | ||
170 | else | ||
171 | retval = -EOPNOTSUPP; | ||
172 | } else { | ||
173 | port->port.close_delay = close_delay; | ||
174 | port->port.closing_wait = closing_wait; | ||
175 | } | ||
176 | |||
177 | mutex_unlock(&port->port.mutex); | ||
178 | return retval; | ||
179 | } | ||
180 | |||
181 | int usb_wwan_ioctl(struct tty_struct *tty, | ||
182 | unsigned int cmd, unsigned long arg) | ||
183 | { | ||
184 | struct usb_serial_port *port = tty->driver_data; | ||
185 | |||
186 | dbg("%s cmd 0x%04x", __func__, cmd); | ||
187 | |||
188 | switch (cmd) { | ||
189 | case TIOCGSERIAL: | ||
190 | return get_serial_info(port, | ||
191 | (struct serial_struct __user *) arg); | ||
192 | case TIOCSSERIAL: | ||
193 | return set_serial_info(port, | ||
194 | (struct serial_struct __user *) arg); | ||
195 | default: | ||
196 | break; | ||
197 | } | ||
198 | |||
199 | dbg("%s arg not supported", __func__); | ||
200 | |||
201 | return -ENOIOCTLCMD; | ||
202 | } | ||
203 | EXPORT_SYMBOL(usb_wwan_ioctl); | ||
204 | |||
126 | /* Write */ | 205 | /* Write */ |
127 | int usb_wwan_write(struct tty_struct *tty, struct usb_serial_port *port, | 206 | int usb_wwan_write(struct tty_struct *tty, struct usb_serial_port *port, |
128 | const unsigned char *buf, int count) | 207 | const unsigned char *buf, int count) |
@@ -182,7 +261,8 @@ int usb_wwan_write(struct tty_struct *tty, struct usb_serial_port *port, | |||
182 | intfdata->in_flight--; | 261 | intfdata->in_flight--; |
183 | spin_unlock_irqrestore(&intfdata->susp_lock, | 262 | spin_unlock_irqrestore(&intfdata->susp_lock, |
184 | flags); | 263 | flags); |
185 | continue; | 264 | usb_autopm_put_interface_async(port->serial->interface); |
265 | break; | ||
186 | } | 266 | } |
187 | } | 267 | } |
188 | 268 | ||
@@ -216,25 +296,32 @@ static void usb_wwan_indat_callback(struct urb *urb) | |||
216 | __func__, status, endpoint); | 296 | __func__, status, endpoint); |
217 | } else { | 297 | } else { |
218 | tty = tty_port_tty_get(&port->port); | 298 | tty = tty_port_tty_get(&port->port); |
219 | if (urb->actual_length) { | 299 | if (tty) { |
220 | tty_insert_flip_string(tty, data, urb->actual_length); | 300 | if (urb->actual_length) { |
221 | tty_flip_buffer_push(tty); | 301 | tty_insert_flip_string(tty, data, |
222 | } else | 302 | urb->actual_length); |
223 | dbg("%s: empty read urb received", __func__); | 303 | tty_flip_buffer_push(tty); |
224 | tty_kref_put(tty); | 304 | } else |
305 | dbg("%s: empty read urb received", __func__); | ||
306 | tty_kref_put(tty); | ||
307 | } | ||
225 | 308 | ||
226 | /* Resubmit urb so we continue receiving */ | 309 | /* Resubmit urb so we continue receiving */ |
227 | if (status != -ESHUTDOWN) { | 310 | if (status != -ESHUTDOWN) { |
228 | err = usb_submit_urb(urb, GFP_ATOMIC); | 311 | err = usb_submit_urb(urb, GFP_ATOMIC); |
229 | if (err && err != -EPERM) | 312 | if (err) { |
230 | printk(KERN_ERR "%s: resubmit read urb failed. " | 313 | if (err != -EPERM) { |
231 | "(%d)", __func__, err); | 314 | printk(KERN_ERR "%s: resubmit read urb failed. " |
232 | else | 315 | "(%d)", __func__, err); |
316 | /* busy also in error unless we are killed */ | ||
317 | usb_mark_last_busy(port->serial->dev); | ||
318 | } | ||
319 | } else { | ||
233 | usb_mark_last_busy(port->serial->dev); | 320 | usb_mark_last_busy(port->serial->dev); |
321 | } | ||
234 | } | 322 | } |
235 | 323 | ||
236 | } | 324 | } |
237 | return; | ||
238 | } | 325 | } |
239 | 326 | ||
240 | static void usb_wwan_outdat_callback(struct urb *urb) | 327 | static void usb_wwan_outdat_callback(struct urb *urb) |
@@ -340,6 +427,7 @@ int usb_wwan_open(struct tty_struct *tty, struct usb_serial_port *port) | |||
340 | spin_lock_irq(&intfdata->susp_lock); | 427 | spin_lock_irq(&intfdata->susp_lock); |
341 | portdata->opened = 1; | 428 | portdata->opened = 1; |
342 | spin_unlock_irq(&intfdata->susp_lock); | 429 | spin_unlock_irq(&intfdata->susp_lock); |
430 | /* this balances a get in the generic USB serial code */ | ||
343 | usb_autopm_put_interface(serial->interface); | 431 | usb_autopm_put_interface(serial->interface); |
344 | 432 | ||
345 | return 0; | 433 | return 0; |
@@ -366,7 +454,8 @@ void usb_wwan_close(struct usb_serial_port *port) | |||
366 | usb_kill_urb(portdata->in_urbs[i]); | 454 | usb_kill_urb(portdata->in_urbs[i]); |
367 | for (i = 0; i < N_OUT_URB; i++) | 455 | for (i = 0; i < N_OUT_URB; i++) |
368 | usb_kill_urb(portdata->out_urbs[i]); | 456 | usb_kill_urb(portdata->out_urbs[i]); |
369 | usb_autopm_get_interface(serial->interface); | 457 | /* balancing - important as an error cannot be handled*/ |
458 | usb_autopm_get_interface_no_resume(serial->interface); | ||
370 | serial->interface->needs_remote_wakeup = 0; | 459 | serial->interface->needs_remote_wakeup = 0; |
371 | } | 460 | } |
372 | } | 461 | } |
@@ -580,6 +669,18 @@ int usb_wwan_suspend(struct usb_serial *serial, pm_message_t message) | |||
580 | } | 669 | } |
581 | EXPORT_SYMBOL(usb_wwan_suspend); | 670 | EXPORT_SYMBOL(usb_wwan_suspend); |
582 | 671 | ||
672 | static void unbusy_queued_urb(struct urb *urb, struct usb_wwan_port_private *portdata) | ||
673 | { | ||
674 | int i; | ||
675 | |||
676 | for (i = 0; i < N_OUT_URB; i++) { | ||
677 | if (urb == portdata->out_urbs[i]) { | ||
678 | clear_bit(i, &portdata->out_busy); | ||
679 | break; | ||
680 | } | ||
681 | } | ||
682 | } | ||
683 | |||
583 | static void play_delayed(struct usb_serial_port *port) | 684 | static void play_delayed(struct usb_serial_port *port) |
584 | { | 685 | { |
585 | struct usb_wwan_intf_private *data; | 686 | struct usb_wwan_intf_private *data; |
@@ -591,8 +692,16 @@ static void play_delayed(struct usb_serial_port *port) | |||
591 | data = port->serial->private; | 692 | data = port->serial->private; |
592 | while ((urb = usb_get_from_anchor(&portdata->delayed))) { | 693 | while ((urb = usb_get_from_anchor(&portdata->delayed))) { |
593 | err = usb_submit_urb(urb, GFP_ATOMIC); | 694 | err = usb_submit_urb(urb, GFP_ATOMIC); |
594 | if (!err) | 695 | if (!err) { |
595 | data->in_flight++; | 696 | data->in_flight++; |
697 | } else { | ||
698 | /* we have to throw away the rest */ | ||
699 | do { | ||
700 | unbusy_queued_urb(urb, portdata); | ||
701 | usb_autopm_put_interface_no_suspend(port->serial->interface); | ||
702 | } while ((urb = usb_get_from_anchor(&portdata->delayed))); | ||
703 | break; | ||
704 | } | ||
596 | } | 705 | } |
597 | } | 706 | } |
598 | 707 | ||