diff options
Diffstat (limited to 'drivers/usb/serial/qcserial.c')
-rw-r--r-- | drivers/usb/serial/qcserial.c | 147 |
1 files changed, 147 insertions, 0 deletions
diff --git a/drivers/usb/serial/qcserial.c b/drivers/usb/serial/qcserial.c new file mode 100644 index 000000000000..e6d6b0c17fd9 --- /dev/null +++ b/drivers/usb/serial/qcserial.c | |||
@@ -0,0 +1,147 @@ | |||
1 | /* | ||
2 | * Qualcomm Serial USB driver | ||
3 | * | ||
4 | * Copyright (c) 2008 QUALCOMM Incorporated. | ||
5 | * Copyright (c) 2009 Greg Kroah-Hartman <gregkh@suse.de> | ||
6 | * Copyright (c) 2009 Novell Inc. | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or | ||
9 | * modify it under the terms of the GNU General Public License version | ||
10 | * 2 as published by the Free Software Foundation. | ||
11 | * | ||
12 | */ | ||
13 | |||
14 | #include <linux/tty.h> | ||
15 | #include <linux/tty_flip.h> | ||
16 | #include <linux/usb.h> | ||
17 | #include <linux/usb/serial.h> | ||
18 | |||
19 | #define DRIVER_AUTHOR "Qualcomm Inc" | ||
20 | #define DRIVER_DESC "Qualcomm USB Serial driver" | ||
21 | |||
22 | static int debug; | ||
23 | |||
24 | static struct usb_device_id id_table[] = { | ||
25 | {USB_DEVICE(0x05c6, 0x9211)}, /* Acer Gobi QDL device */ | ||
26 | {USB_DEVICE(0x05c6, 0x9212)}, /* Acer Gobi Modem Device */ | ||
27 | {USB_DEVICE(0x03f0, 0x1f1d)}, /* HP un2400 Gobi Modem Device */ | ||
28 | {USB_DEVICE(0x03f0, 0x201d)}, /* HP un2400 Gobi QDL Device */ | ||
29 | { } /* Terminating entry */ | ||
30 | }; | ||
31 | MODULE_DEVICE_TABLE(usb, id_table); | ||
32 | |||
33 | static struct usb_driver qcdriver = { | ||
34 | .name = "qcserial", | ||
35 | .probe = usb_serial_probe, | ||
36 | .disconnect = usb_serial_disconnect, | ||
37 | .id_table = id_table, | ||
38 | .suspend = usb_serial_suspend, | ||
39 | .resume = usb_serial_resume, | ||
40 | .supports_autosuspend = true, | ||
41 | }; | ||
42 | |||
43 | static int qcprobe(struct usb_serial *serial, const struct usb_device_id *id) | ||
44 | { | ||
45 | int retval = -ENODEV; | ||
46 | __u8 nintf; | ||
47 | __u8 ifnum; | ||
48 | |||
49 | dbg("%s", __func__); | ||
50 | |||
51 | nintf = serial->dev->actconfig->desc.bNumInterfaces; | ||
52 | dbg("Num Interfaces = %d", nintf); | ||
53 | ifnum = serial->interface->cur_altsetting->desc.bInterfaceNumber; | ||
54 | dbg("This Interface = %d", ifnum); | ||
55 | |||
56 | switch (nintf) { | ||
57 | case 1: | ||
58 | /* QDL mode */ | ||
59 | if (serial->interface->num_altsetting == 2) { | ||
60 | struct usb_host_interface *intf; | ||
61 | |||
62 | intf = &serial->interface->altsetting[1]; | ||
63 | if (intf->desc.bNumEndpoints == 2) { | ||
64 | if (usb_endpoint_is_bulk_in(&intf->endpoint[0].desc) && | ||
65 | usb_endpoint_is_bulk_out(&intf->endpoint[1].desc)) { | ||
66 | dbg("QDL port found"); | ||
67 | retval = usb_set_interface(serial->dev, ifnum, 1); | ||
68 | if (retval < 0) { | ||
69 | dev_err(&serial->dev->dev, | ||
70 | "Could not set interface, error %d\n", | ||
71 | retval); | ||
72 | retval = -ENODEV; | ||
73 | } | ||
74 | return retval; | ||
75 | } | ||
76 | } | ||
77 | } | ||
78 | break; | ||
79 | |||
80 | case 4: | ||
81 | /* Composite mode */ | ||
82 | if (ifnum == 2) { | ||
83 | dbg("Modem port found"); | ||
84 | retval = usb_set_interface(serial->dev, ifnum, 0); | ||
85 | if (retval < 0) { | ||
86 | dev_err(&serial->dev->dev, | ||
87 | "Could not set interface, error %d\n", | ||
88 | retval); | ||
89 | retval = -ENODEV; | ||
90 | } | ||
91 | return retval; | ||
92 | } | ||
93 | break; | ||
94 | |||
95 | default: | ||
96 | dev_err(&serial->dev->dev, | ||
97 | "unknown number of interfaces: %d\n", nintf); | ||
98 | return -ENODEV; | ||
99 | } | ||
100 | |||
101 | return retval; | ||
102 | } | ||
103 | |||
104 | static struct usb_serial_driver qcdevice = { | ||
105 | .driver = { | ||
106 | .owner = THIS_MODULE, | ||
107 | .name = "qcserial", | ||
108 | }, | ||
109 | .description = "Qualcomm USB modem", | ||
110 | .id_table = id_table, | ||
111 | .usb_driver = &qcdriver, | ||
112 | .num_ports = 1, | ||
113 | .probe = qcprobe, | ||
114 | }; | ||
115 | |||
116 | static int __init qcinit(void) | ||
117 | { | ||
118 | int retval; | ||
119 | |||
120 | retval = usb_serial_register(&qcdevice); | ||
121 | if (retval) | ||
122 | return retval; | ||
123 | |||
124 | retval = usb_register(&qcdriver); | ||
125 | if (retval) { | ||
126 | usb_serial_deregister(&qcdevice); | ||
127 | return retval; | ||
128 | } | ||
129 | |||
130 | return 0; | ||
131 | } | ||
132 | |||
133 | static void __exit qcexit(void) | ||
134 | { | ||
135 | usb_deregister(&qcdriver); | ||
136 | usb_serial_deregister(&qcdevice); | ||
137 | } | ||
138 | |||
139 | module_init(qcinit); | ||
140 | module_exit(qcexit); | ||
141 | |||
142 | MODULE_AUTHOR(DRIVER_AUTHOR); | ||
143 | MODULE_DESCRIPTION(DRIVER_DESC); | ||
144 | MODULE_LICENSE("GPL v2"); | ||
145 | |||
146 | module_param(debug, bool, S_IRUGO | S_IWUSR); | ||
147 | MODULE_PARM_DESC(debug, "Debug enabled or not"); | ||