blob: 2219eab83e0cf3a7d1a0e505cf457e05241238bf [file] [log] [blame]
Pavankumar Kondetie2ba8112013-03-01 09:34:02 +05301/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
Hemant Kumar0cac8ed2012-01-31 14:39:23 -08002 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 */
12
Steve Mucklef132c6c2012-06-06 18:30:57 -070013#include <linux/module.h>
Hemant Kumar0cac8ed2012-01-31 14:39:23 -080014#include <linux/slab.h>
15#include <linux/tty.h>
16#include <linux/serial.h>
17#include <linux/tty_flip.h>
18#include <linux/uaccess.h>
19#include <linux/usb.h>
20#include <linux/usb/ch9.h>
21#include <linux/usb/cdc.h>
22#include <linux/usb/serial.h>
23#include <asm/unaligned.h>
24
25
26/* output control lines*/
27#define CSVT_CTRL_DTR 0x01
28#define CSVT_CTRL_RTS 0x02
29
30/* input control lines*/
Pavankumar Kondetie2ba8112013-03-01 09:34:02 +053031#define CSVT_CTRL_CD 0x01
Hemant Kumar0cac8ed2012-01-31 14:39:23 -080032#define CSVT_CTRL_DSR 0x02
Pavankumar Kondetie2ba8112013-03-01 09:34:02 +053033#define CSVT_CTRL_BRK 0x04
34#define CSVT_CTRL_RI 0x08
35
36#define CSVT_CTRL_FRAMING 0x10
37#define CSVT_CTRL_PARITY 0x20
38#define CSVT_CTRL_OVERRUN 0x40
Hemant Kumar0cac8ed2012-01-31 14:39:23 -080039
40static int debug;
41module_param(debug, int, S_IRUGO | S_IWUSR);
42
43struct csvt_ctrl_dev {
44 struct mutex dev_lock;
45
46 /* input control lines (DSR, CTS, CD, RI) */
47 unsigned int cbits_tolocal;
48
49 /* output control lines (DTR, RTS) */
50 unsigned int cbits_tomdm;
51};
52
53static const struct usb_device_id id_table[] = {
Hemant Kumar31aed6a2012-03-05 14:56:26 -080054 { USB_DEVICE_AND_INTERFACE_INFO(0x05c6 , 0x904c, 0xff, 0xfe, 0xff)},
Hemant Kumar0cac8ed2012-01-31 14:39:23 -080055 {}, /* terminating entry */
56};
57MODULE_DEVICE_TABLE(usb, id_table);
58
59static struct usb_driver csvt_driver = {
60 .name = "qc_csvt",
61 .probe = usb_serial_probe,
62 .disconnect = usb_serial_disconnect,
63 .id_table = id_table,
64 .suspend = usb_serial_suspend,
65 .resume = usb_serial_resume,
66 .supports_autosuspend = true,
67};
68
69#define CSVT_IFC_NUM 4
70
71static int csvt_probe(struct usb_serial *serial, const struct usb_device_id *id)
72{
73 struct usb_host_interface *intf =
74 serial->interface->cur_altsetting;
75
76 pr_debug("%s:\n", __func__);
77
78 if (intf->desc.bInterfaceNumber != CSVT_IFC_NUM)
79 return -ENODEV;
80
81 usb_enable_autosuspend(serial->dev);
82
83 return 0;
84}
85
86static int csvt_ctrl_write_cmd(struct csvt_ctrl_dev *dev,
87 struct usb_serial_port *port)
88{
89 struct usb_device *udev = port->serial->dev;
90 struct usb_interface *iface = port->serial->interface;
91 unsigned int iface_num;
92 int retval = 0;
93
94 retval = usb_autopm_get_interface(iface);
95 if (retval < 0) {
96 dev_err(&port->dev, "%s: Unable to resume interface: %d\n",
97 __func__, retval);
98 return retval;
99 }
100
101 dev_dbg(&port->dev, "%s: cbits to mdm 0x%x\n", __func__,
102 dev->cbits_tomdm);
103
104 iface_num = iface->cur_altsetting->desc.bInterfaceNumber;
105
106 retval = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
107 USB_CDC_REQ_SET_CONTROL_LINE_STATE,
108 (USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE),
109 dev->cbits_tomdm,
110 iface_num,
111 NULL, 0, USB_CTRL_SET_TIMEOUT);
112 usb_autopm_put_interface(iface);
113
114 return retval;
115}
116
117static void csvt_ctrl_dtr_rts(struct usb_serial_port *port, int on)
118{
119 struct csvt_ctrl_dev *dev = usb_get_serial_port_data(port);
120
121 if (!dev)
122 return;
123
124 dev_dbg(&port->dev, "%s", __func__);
125
126 mutex_lock(&dev->dev_lock);
127 if (on) {
128 dev->cbits_tomdm |= CSVT_CTRL_DTR;
129 dev->cbits_tomdm |= CSVT_CTRL_RTS;
130 } else {
131 dev->cbits_tomdm &= ~CSVT_CTRL_DTR;
132 dev->cbits_tomdm &= ~CSVT_CTRL_RTS;
133 }
134 mutex_unlock(&dev->dev_lock);
135
136 csvt_ctrl_write_cmd(dev, port);
137}
138
139static int get_serial_info(struct usb_serial_port *port,
140 struct serial_struct __user *retinfo)
141{
142 struct serial_struct tmp;
143
144 if (!retinfo)
145 return -EFAULT;
146
147 memset(&tmp, 0, sizeof(tmp));
148 tmp.line = port->serial->minor;
149 tmp.port = port->number;
150 tmp.baud_base = tty_get_baud_rate(port->port.tty);
151 tmp.close_delay = port->port.close_delay / 10;
152 tmp.closing_wait =
153 port->port.closing_wait == ASYNC_CLOSING_WAIT_NONE ?
154 ASYNC_CLOSING_WAIT_NONE :
155 port->port.closing_wait / 10;
156
157 if (copy_to_user(retinfo, &tmp, sizeof(*retinfo)))
158 return -EFAULT;
159 return 0;
160}
161
162static int set_serial_info(struct usb_serial_port *port,
163 struct serial_struct __user *newinfo)
164{
165 struct serial_struct new_serial;
166 unsigned int closing_wait;
167 unsigned int close_delay;
168 int retval = 0;
169
170 if (copy_from_user(&new_serial, newinfo, sizeof(new_serial)))
171 return -EFAULT;
172
173 close_delay = new_serial.close_delay * 10;
174 closing_wait = new_serial.closing_wait == ASYNC_CLOSING_WAIT_NONE ?
175 ASYNC_CLOSING_WAIT_NONE : new_serial.closing_wait * 10;
176
177 mutex_lock(&port->port.mutex);
178
179 if (!capable(CAP_SYS_ADMIN)) {
180 if ((close_delay != port->port.close_delay) ||
181 (closing_wait != port->port.closing_wait))
182 retval = -EPERM;
183 else
184 retval = -EOPNOTSUPP;
185 } else {
186 port->port.close_delay = close_delay;
187 port->port.closing_wait = closing_wait;
188 }
189
190 mutex_unlock(&port->port.mutex);
191 return retval;
192}
193
194static int csvt_ctrl_ioctl(struct tty_struct *tty, unsigned int cmd,
195 unsigned long arg)
196{
197 struct usb_serial_port *port = tty->driver_data;
198
199 dev_dbg(&port->dev, "%s cmd 0x%04x", __func__, cmd);
200
201 switch (cmd) {
202 case TIOCGSERIAL:
203 return get_serial_info(port,
204 (struct serial_struct __user *) arg);
205 case TIOCSSERIAL:
206 return set_serial_info(port,
207 (struct serial_struct __user *) arg);
208 default:
209 break;
210 }
211
212 dev_err(&port->dev, "%s arg not supported", __func__);
213
214 return -ENOIOCTLCMD;
215}
216
217static int csvt_ctrl_tiocmget(struct tty_struct *tty)
218{
219 struct usb_serial_port *port = tty->driver_data;
220 struct csvt_ctrl_dev *dev = usb_get_serial_port_data(port);
221 unsigned int control_state = 0;
222
223 if (!dev)
224 return -ENODEV;
225
226 mutex_lock(&dev->dev_lock);
227 control_state = (dev->cbits_tomdm & CSVT_CTRL_DTR ? TIOCM_DTR : 0) |
228 (dev->cbits_tomdm & CSVT_CTRL_RTS ? TIOCM_RTS : 0) |
229 (dev->cbits_tolocal & CSVT_CTRL_DSR ? TIOCM_DSR : 0) |
230 (dev->cbits_tolocal & CSVT_CTRL_RI ? TIOCM_RI : 0) |
231 (dev->cbits_tolocal & CSVT_CTRL_CD ? TIOCM_CD : 0) |
Pavankumar Kondetie2ba8112013-03-01 09:34:02 +0530232 TIOCM_CTS; /* USB CDC spec did not define CTS control signal */
Hemant Kumar0cac8ed2012-01-31 14:39:23 -0800233 mutex_unlock(&dev->dev_lock);
234
235 dev_dbg(&port->dev, "%s -- %x", __func__, control_state);
236
237 return control_state;
238}
239
240static int csvt_ctrl_tiocmset(struct tty_struct *tty,
241 unsigned int set, unsigned int clear)
242{
243 struct usb_serial_port *port = tty->driver_data;
244 struct csvt_ctrl_dev *dev = usb_get_serial_port_data(port);
245
246 if (!dev)
247 return -ENODEV;
248
249 dev_dbg(&port->dev, "%s\n", __func__);
250
251 mutex_lock(&dev->dev_lock);
252 if (set & CSVT_CTRL_DTR)
253 dev->cbits_tomdm |= TIOCM_DTR;
254 if (set & CSVT_CTRL_RTS)
255 dev->cbits_tomdm |= TIOCM_RTS;
256
257 if (clear & CSVT_CTRL_DTR)
258 dev->cbits_tomdm &= ~TIOCM_DTR;
259 if (clear & CSVT_CTRL_RTS)
260 dev->cbits_tomdm &= ~TIOCM_RTS;
261 mutex_unlock(&dev->dev_lock);
262
263 return csvt_ctrl_write_cmd(dev, port);
264}
265
266static void csvt_ctrl_set_termios(struct tty_struct *tty,
267 struct usb_serial_port *port,
268 struct ktermios *old_termios)
269{
270 struct csvt_ctrl_dev *dev = usb_get_serial_port_data(port);
271
272 if (!dev)
273 return;
274
275 dev_dbg(&port->dev, "%s", __func__);
276
277 /* Doesn't support option setting */
278 tty_termios_copy_hw(tty->termios, old_termios);
279
280 csvt_ctrl_write_cmd(dev, port);
281}
282
283static void csvt_ctrl_int_cb(struct urb *urb)
284{
285 int status;
286 struct usb_cdc_notification *ctrl;
287 struct usb_serial_port *port = urb->context;
288 struct csvt_ctrl_dev *dev;
289 unsigned int ctrl_bits;
290 unsigned char *data;
291
292 switch (urb->status) {
293 case 0:
294 /*success*/
295 break;
296 case -ESHUTDOWN:
297 case -ENOENT:
298 case -ECONNRESET:
299 case -EPROTO:
300 /* unplug */
301 return;
302 case -EPIPE:
303 dev_err(&port->dev, "%s: stall on int endpoint\n", __func__);
304 /* TBD : halt to be cleared in work */
305 case -EOVERFLOW:
306 default:
307 pr_debug_ratelimited("%s: non zero urb status = %d\n",
308 __func__, urb->status);
309 goto resubmit_int_urb;
310 }
311
312 dev = usb_get_serial_port_data(port);
313 if (!dev)
314 return;
315
316 ctrl = urb->transfer_buffer;
317 data = (unsigned char *)(ctrl + 1);
318
319 usb_serial_debug_data(debug, &port->dev, __func__,
320 urb->actual_length, data);
321
322 switch (ctrl->bNotificationType) {
323 case USB_CDC_NOTIFY_NETWORK_CONNECTION:
324 dev_dbg(&port->dev, "%s network\n", ctrl->wValue ?
325 "connected to" : "disconnected from");
326 break;
327 case USB_CDC_NOTIFY_SERIAL_STATE:
328 ctrl_bits = get_unaligned_le16(data);
329 dev_dbg(&port->dev, "serial state: %d\n", ctrl_bits);
330 dev->cbits_tolocal = ctrl_bits;
331 break;
332 default:
333 dev_err(&port->dev, "%s: unknown notification %d received:"
334 "index %d len %d data0 %d data1 %d",
335 __func__, ctrl->bNotificationType, ctrl->wIndex,
336 ctrl->wLength, data[0], data[1]);
337 }
338
339resubmit_int_urb:
340 status = usb_submit_urb(urb, GFP_ATOMIC);
341 if (status)
342 dev_err(&port->dev, "%s: Error re-submitting Int URB %d\n",
343 __func__, status);
344
345}
346
347static int csvt_ctrl_open(struct tty_struct *tty,
348 struct usb_serial_port *port)
349{
350 int retval;
351
352 dev_dbg(&port->dev, "%s port %d", __func__, port->number);
353
354 retval = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
355 if (retval) {
356 dev_err(&port->dev, "usb_submit_urb(read int) failed\n");
357 return retval;
358 }
359
360 retval = usb_serial_generic_open(tty, port);
361 if (retval)
362 usb_kill_urb(port->interrupt_in_urb);
363
364 return retval;
365}
366
367static void csvt_ctrl_close(struct usb_serial_port *port)
368{
369 dev_dbg(&port->dev, "%s port %d", __func__, port->number);
370
371 usb_serial_generic_close(port);
372 usb_kill_urb(port->interrupt_in_urb);
373}
374
375static int csvt_ctrl_attach(struct usb_serial *serial)
376{
377 struct csvt_ctrl_dev *dev;
378
379 dev = kzalloc(sizeof(*dev), GFP_KERNEL);
380 if (!dev)
381 return -ENOMEM;
382
383 mutex_init(&dev->dev_lock);
384 usb_set_serial_port_data(serial->port[0], dev);
385
386 return 0;
387}
388
389static void csvt_ctrl_release(struct usb_serial *serial)
390{
391 struct usb_serial_port *port = serial->port[0];
392 struct csvt_ctrl_dev *dev = usb_get_serial_port_data(port);
393
394 dev_dbg(&port->dev, "%s", __func__);
395
396 kfree(dev);
397 usb_set_serial_port_data(port, NULL);
398}
399
400static struct usb_serial_driver csvt_device = {
401 .driver = {
402 .owner = THIS_MODULE,
403 .name = "qc_csvt",
404 },
405 .description = "qc_csvt",
406 .id_table = id_table,
Hemant Kumar0cac8ed2012-01-31 14:39:23 -0800407 .num_ports = 1,
408 .open = csvt_ctrl_open,
409 .close = csvt_ctrl_close,
410 .probe = csvt_probe,
411 .dtr_rts = csvt_ctrl_dtr_rts,
412 .tiocmget = csvt_ctrl_tiocmget,
413 .tiocmset = csvt_ctrl_tiocmset,
414 .ioctl = csvt_ctrl_ioctl,
415 .set_termios = csvt_ctrl_set_termios,
416 .read_int_callback = csvt_ctrl_int_cb,
417 .attach = csvt_ctrl_attach,
418 .release = csvt_ctrl_release,
419};
420
Steve Mucklef132c6c2012-06-06 18:30:57 -0700421static struct usb_serial_driver * const serial_drivers[] = {
422 &csvt_device,
423 NULL,
424};
425
Hemant Kumar0cac8ed2012-01-31 14:39:23 -0800426static int __init csvt_init(void)
427{
428 int retval;
429
Steve Mucklef132c6c2012-06-06 18:30:57 -0700430 retval = usb_serial_register_drivers(&csvt_driver, serial_drivers);
Hemant Kumar0cac8ed2012-01-31 14:39:23 -0800431 if (retval) {
432 err("%s: usb serial register failed\n", __func__);
433 return retval;
434 }
435
Hemant Kumar0cac8ed2012-01-31 14:39:23 -0800436 return 0;
437}
438
439static void __exit csvt_exit(void)
440{
Steve Mucklef132c6c2012-06-06 18:30:57 -0700441 usb_serial_deregister_drivers(&csvt_driver, serial_drivers);
Hemant Kumar0cac8ed2012-01-31 14:39:23 -0800442}
443
444module_init(csvt_init);
445module_exit(csvt_exit);
446
447MODULE_LICENSE("GPL v2");