aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDan Williams <dcbw@redhat.com>2010-11-19 17:04:00 -0500
committerGreg Kroah-Hartman <gregkh@suse.de>2010-11-30 19:44:57 -0500
commit02303f73373aa1da19dbec510ec5a4e2576f9610 (patch)
tree965bb7f33b4c96fd543803d41248c962cb79958e
parent73f35c60d5c4a98061fc0f94505bf26fd4bb1a1c (diff)
usb-wwan: implement TIOCGSERIAL and TIOCSSERIAL to avoid blocking close(2)
Some devices (ex ZTE 2726) simply don't respond at all when data is sent to some of their USB interfaces. The data gets stuck in the TTYs queue and sits there until close(2), which them blocks because closing_wait defaults to 30 seconds (even though the fd is O_NONBLOCK). This is rarely desired. Implement the standard mechanism to adjust closing_wait and let applications handle it how they want to. Signed-off-by: Dan Williams <dcbw@redhat.com>
-rw-r--r--drivers/usb/serial/option.c1
-rw-r--r--drivers/usb/serial/usb-wwan.h2
-rw-r--r--drivers/usb/serial/usb_wwan.c78
3 files changed, 81 insertions, 0 deletions
diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c
index 2297fb1bcf65..2a56cc35ad31 100644
--- a/drivers/usb/serial/option.c
+++ b/drivers/usb/serial/option.c
@@ -989,6 +989,7 @@ static struct usb_serial_driver option_1port_device = {
989 .set_termios = usb_wwan_set_termios, 989 .set_termios = usb_wwan_set_termios,
990 .tiocmget = usb_wwan_tiocmget, 990 .tiocmget = usb_wwan_tiocmget,
991 .tiocmset = usb_wwan_tiocmset, 991 .tiocmset = usb_wwan_tiocmset,
992 .ioctl = usb_wwan_ioctl,
992 .attach = usb_wwan_startup, 993 .attach = usb_wwan_startup,
993 .disconnect = usb_wwan_disconnect, 994 .disconnect = usb_wwan_disconnect,
994 .release = usb_wwan_release, 995 .release = usb_wwan_release,
diff --git a/drivers/usb/serial/usb-wwan.h b/drivers/usb/serial/usb-wwan.h
index 2be298a1305b..3ab77c5d9819 100644
--- a/drivers/usb/serial/usb-wwan.h
+++ b/drivers/usb/serial/usb-wwan.h
@@ -18,6 +18,8 @@ extern void usb_wwan_set_termios(struct tty_struct *tty,
18extern int usb_wwan_tiocmget(struct tty_struct *tty, struct file *file); 18extern int usb_wwan_tiocmget(struct tty_struct *tty, struct file *file);
19extern int usb_wwan_tiocmset(struct tty_struct *tty, struct file *file, 19extern int usb_wwan_tiocmset(struct tty_struct *tty, struct file *file,
20 unsigned int set, unsigned int clear); 20 unsigned int set, unsigned int clear);
21extern int usb_wwan_ioctl(struct tty_struct *tty, struct file *file,
22 unsigned int cmd, unsigned long arg);
21extern int usb_wwan_send_setup(struct usb_serial_port *port); 23extern int usb_wwan_send_setup(struct usb_serial_port *port);
22extern int usb_wwan_write(struct tty_struct *tty, struct usb_serial_port *port, 24extern int usb_wwan_write(struct tty_struct *tty, struct usb_serial_port *port,
23 const unsigned char *buf, int count); 25 const unsigned char *buf, int count);
diff --git a/drivers/usb/serial/usb_wwan.c b/drivers/usb/serial/usb_wwan.c
index fbc946797801..660b7caef784 100644
--- a/drivers/usb/serial/usb_wwan.c
+++ b/drivers/usb/serial/usb_wwan.c
@@ -33,6 +33,7 @@
33#include <linux/bitops.h> 33#include <linux/bitops.h>
34#include <linux/usb.h> 34#include <linux/usb.h>
35#include <linux/usb/serial.h> 35#include <linux/usb/serial.h>
36#include <linux/serial.h>
36#include "usb-wwan.h" 37#include "usb-wwan.h"
37 38
38static int debug; 39static int debug;
@@ -123,6 +124,83 @@ int usb_wwan_tiocmset(struct tty_struct *tty, struct file *file,
123} 124}
124EXPORT_SYMBOL(usb_wwan_tiocmset); 125EXPORT_SYMBOL(usb_wwan_tiocmset);
125 126
127static int get_serial_info(struct usb_serial_port *port,
128 struct serial_struct __user *retinfo)
129{
130 struct serial_struct tmp;
131
132 if (!retinfo)
133 return -EFAULT;
134
135 memset(&tmp, 0, sizeof(tmp));
136 tmp.line = port->serial->minor;
137 tmp.port = port->number;
138 tmp.baud_base = tty_get_baud_rate(port->port.tty);
139 tmp.close_delay = port->port.close_delay / 10;
140 tmp.closing_wait = port->port.closing_wait == ASYNC_CLOSING_WAIT_NONE ?
141 ASYNC_CLOSING_WAIT_NONE :
142 port->port.closing_wait / 10;
143
144 if (copy_to_user(retinfo, &tmp, sizeof(*retinfo)))
145 return -EFAULT;
146 return 0;
147}
148
149static int set_serial_info(struct usb_serial_port *port,
150 struct serial_struct __user *newinfo)
151{
152 struct serial_struct new_serial;
153 unsigned int closing_wait, close_delay;
154 int retval = 0;
155
156 if (copy_from_user(&new_serial, newinfo, sizeof(new_serial)))
157 return -EFAULT;
158
159 close_delay = new_serial.close_delay * 10;
160 closing_wait = new_serial.closing_wait == ASYNC_CLOSING_WAIT_NONE ?
161 ASYNC_CLOSING_WAIT_NONE : new_serial.closing_wait * 10;
162
163 mutex_lock(&port->port.mutex);
164
165 if (!capable(CAP_SYS_ADMIN)) {
166 if ((close_delay != port->port.close_delay) ||
167 (closing_wait != port->port.closing_wait))
168 retval = -EPERM;
169 else
170 retval = -EOPNOTSUPP;
171 } else {
172 port->port.close_delay = close_delay;
173 port->port.closing_wait = closing_wait;
174 }
175
176 mutex_unlock(&port->port.mutex);
177 return retval;
178}
179
180int usb_wwan_ioctl(struct tty_struct *tty, struct file *file,
181 unsigned int cmd, unsigned long arg)
182{
183 struct usb_serial_port *port = tty->driver_data;
184
185 dbg("%s cmd 0x%04x", __func__, cmd);
186
187 switch (cmd) {
188 case TIOCGSERIAL:
189 return get_serial_info(port,
190 (struct serial_struct __user *) arg);
191 case TIOCSSERIAL:
192 return set_serial_info(port,
193 (struct serial_struct __user *) arg);
194 default:
195 break;
196 }
197
198 dbg("%s arg not supported", __func__);
199
200 return -ENOIOCTLCMD;
201}
202EXPORT_SYMBOL(usb_wwan_ioctl);
203
126/* Write */ 204/* Write */
127int usb_wwan_write(struct tty_struct *tty, struct usb_serial_port *port, 205int usb_wwan_write(struct tty_struct *tty, struct usb_serial_port *port,
128 const unsigned char *buf, int count) 206 const unsigned char *buf, int count)