diff options
-rw-r--r-- | drivers/usb/serial/Kconfig | 9 | ||||
-rw-r--r-- | drivers/usb/serial/Makefile | 1 | ||||
-rw-r--r-- | drivers/usb/serial/qcserial.c | 145 |
3 files changed, 155 insertions, 0 deletions
diff --git a/drivers/usb/serial/Kconfig b/drivers/usb/serial/Kconfig index dbc0781a4163..4afe73e8ec4a 100644 --- a/drivers/usb/serial/Kconfig +++ b/drivers/usb/serial/Kconfig | |||
@@ -472,6 +472,15 @@ config USB_SERIAL_OTI6858 | |||
472 | To compile this driver as a module, choose M here: the | 472 | To compile this driver as a module, choose M here: the |
473 | module will be called oti6858. | 473 | module will be called oti6858. |
474 | 474 | ||
475 | config USB_SERIAL_QUALCOMM | ||
476 | tristate "USB Qualcomm Serial modem" | ||
477 | help | ||
478 | Say Y here if you have a Qualcomm USB modem device. These are | ||
479 | usually wireless cellular modems. | ||
480 | |||
481 | To compile this driver as a module, choose M here: the | ||
482 | module will be called qcserial. | ||
483 | |||
475 | config USB_SERIAL_SPCP8X5 | 484 | config USB_SERIAL_SPCP8X5 |
476 | tristate "USB SPCP8x5 USB To Serial Driver" | 485 | tristate "USB SPCP8x5 USB To Serial Driver" |
477 | help | 486 | help |
diff --git a/drivers/usb/serial/Makefile b/drivers/usb/serial/Makefile index 222939739ffe..94043babe1d3 100644 --- a/drivers/usb/serial/Makefile +++ b/drivers/usb/serial/Makefile | |||
@@ -45,6 +45,7 @@ obj-$(CONFIG_USB_SERIAL_OPTICON) += opticon.o | |||
45 | obj-$(CONFIG_USB_SERIAL_OPTION) += option.o | 45 | obj-$(CONFIG_USB_SERIAL_OPTION) += option.o |
46 | obj-$(CONFIG_USB_SERIAL_OTI6858) += oti6858.o | 46 | obj-$(CONFIG_USB_SERIAL_OTI6858) += oti6858.o |
47 | obj-$(CONFIG_USB_SERIAL_PL2303) += pl2303.o | 47 | obj-$(CONFIG_USB_SERIAL_PL2303) += pl2303.o |
48 | obj-$(CONFIG_USB_SERIAL_QUALCOMM) += qcserial.o | ||
48 | obj-$(CONFIG_USB_SERIAL_SAFE) += safe_serial.o | 49 | obj-$(CONFIG_USB_SERIAL_SAFE) += safe_serial.o |
49 | obj-$(CONFIG_USB_SERIAL_SIEMENS_MPI) += siemens_mpi.o | 50 | obj-$(CONFIG_USB_SERIAL_SIEMENS_MPI) += siemens_mpi.o |
50 | obj-$(CONFIG_USB_SERIAL_SIERRAWIRELESS) += sierra.o | 51 | obj-$(CONFIG_USB_SERIAL_SIERRAWIRELESS) += sierra.o |
diff --git a/drivers/usb/serial/qcserial.c b/drivers/usb/serial/qcserial.c new file mode 100644 index 000000000000..6c6add50feaa --- /dev/null +++ b/drivers/usb/serial/qcserial.c | |||
@@ -0,0 +1,145 @@ | |||
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 | { } /* Terminating entry */ | ||
28 | }; | ||
29 | MODULE_DEVICE_TABLE(usb, id_table); | ||
30 | |||
31 | static struct usb_driver qcdriver = { | ||
32 | .name = "qcserial", | ||
33 | .probe = usb_serial_probe, | ||
34 | .disconnect = usb_serial_disconnect, | ||
35 | .id_table = id_table, | ||
36 | .suspend = usb_serial_suspend, | ||
37 | .resume = usb_serial_resume, | ||
38 | .supports_autosuspend = true, | ||
39 | }; | ||
40 | |||
41 | static int qcprobe(struct usb_serial *serial, const struct usb_device_id *id) | ||
42 | { | ||
43 | int retval = -ENODEV; | ||
44 | __u8 nintf; | ||
45 | __u8 ifnum; | ||
46 | |||
47 | dbg("%s", __func__); | ||
48 | |||
49 | nintf = serial->dev->actconfig->desc.bNumInterfaces; | ||
50 | dbg("Num Interfaces = %d", nintf); | ||
51 | ifnum = serial->interface->cur_altsetting->desc.bInterfaceNumber; | ||
52 | dbg("This Interface = %d", ifnum); | ||
53 | |||
54 | switch (nintf) { | ||
55 | case 1: | ||
56 | /* QDL mode */ | ||
57 | if (serial->interface->num_altsetting == 2) { | ||
58 | struct usb_host_interface *intf; | ||
59 | |||
60 | intf = &serial->interface->altsetting[1]; | ||
61 | if (intf->desc.bNumEndpoints == 2) { | ||
62 | if (usb_endpoint_is_bulk_in(&intf->endpoint[0].desc) && | ||
63 | usb_endpoint_is_bulk_out(&intf->endpoint[1].desc)) { | ||
64 | dbg("QDL port found"); | ||
65 | retval = usb_set_interface(serial->dev, ifnum, 1); | ||
66 | if (retval < 0) { | ||
67 | dev_err(&serial->dev->dev, | ||
68 | "Could not set interface, error %d\n", | ||
69 | retval); | ||
70 | retval = -ENODEV; | ||
71 | } | ||
72 | return retval; | ||
73 | } | ||
74 | } | ||
75 | } | ||
76 | break; | ||
77 | |||
78 | case 4: | ||
79 | /* Composite mode */ | ||
80 | if (ifnum == 2) { | ||
81 | dbg("Modem port found"); | ||
82 | retval = usb_set_interface(serial->dev, ifnum, 0); | ||
83 | if (retval < 0) { | ||
84 | dev_err(&serial->dev->dev, | ||
85 | "Could not set interface, error %d\n", | ||
86 | retval); | ||
87 | retval = -ENODEV; | ||
88 | } | ||
89 | return retval; | ||
90 | } | ||
91 | break; | ||
92 | |||
93 | default: | ||
94 | dev_err(&serial->dev->dev, | ||
95 | "unknown number of interfaces: %d\n", nintf); | ||
96 | return -ENODEV; | ||
97 | } | ||
98 | |||
99 | return retval; | ||
100 | } | ||
101 | |||
102 | static struct usb_serial_driver qcdevice = { | ||
103 | .driver = { | ||
104 | .owner = THIS_MODULE, | ||
105 | .name = "qcserial", | ||
106 | }, | ||
107 | .description = "Qualcomm USB modem", | ||
108 | .id_table = id_table, | ||
109 | .usb_driver = &qcdriver, | ||
110 | .num_ports = 1, | ||
111 | .probe = qcprobe, | ||
112 | }; | ||
113 | |||
114 | static int __init qcinit(void) | ||
115 | { | ||
116 | int retval; | ||
117 | |||
118 | retval = usb_serial_register(&qcdevice); | ||
119 | if (retval) | ||
120 | return retval; | ||
121 | |||
122 | retval = usb_register(&qcdriver); | ||
123 | if (retval) { | ||
124 | usb_serial_deregister(&qcdevice); | ||
125 | return retval; | ||
126 | } | ||
127 | |||
128 | return 0; | ||
129 | } | ||
130 | |||
131 | static void __exit qcexit(void) | ||
132 | { | ||
133 | usb_deregister(&qcdriver); | ||
134 | usb_serial_deregister(&qcdevice); | ||
135 | } | ||
136 | |||
137 | module_init(qcinit); | ||
138 | module_exit(qcexit); | ||
139 | |||
140 | MODULE_AUTHOR(DRIVER_AUTHOR); | ||
141 | MODULE_DESCRIPTION(DRIVER_DESC); | ||
142 | MODULE_LICENSE("GPL v2"); | ||
143 | |||
144 | module_param(debug, bool, S_IRUGO | S_IWUSR); | ||
145 | MODULE_PARM_DESC(debug, "Debug enabled or not"); | ||