u3g.c revision 1.3.2.3 1 1.3.2.2 haad /*
2 1.3.2.2 haad * Copyright (c) 2008 AnyWi Technologies
3 1.3.2.2 haad * Author: Andrea Guzzo <aguzzo (at) anywi.com>
4 1.3.2.2 haad * * based on uark.c 1.1 2006/08/14 08:30:22 jsg *
5 1.3.2.2 haad * * parts from ubsa.c 183348 2008-09-25 12:00:56Z phk *
6 1.3.2.2 haad *
7 1.3.2.2 haad * Permission to use, copy, modify, and distribute this software for any
8 1.3.2.2 haad * purpose with or without fee is hereby granted, provided that the above
9 1.3.2.2 haad * copyright notice and this permission notice appear in all copies.
10 1.3.2.2 haad *
11 1.3.2.2 haad * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 1.3.2.2 haad * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 1.3.2.2 haad * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 1.3.2.2 haad * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 1.3.2.2 haad * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 1.3.2.2 haad * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 1.3.2.2 haad * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 1.3.2.2 haad *
19 1.3.2.2 haad * $FreeBSD$
20 1.3.2.2 haad */
21 1.3.2.2 haad
22 1.3.2.2 haad #include <sys/param.h>
23 1.3.2.2 haad #include <sys/systm.h>
24 1.3.2.2 haad #include <sys/kernel.h>
25 1.3.2.2 haad #include <sys/malloc.h>
26 1.3.2.2 haad #include <sys/module.h>
27 1.3.2.2 haad #include <sys/bus.h>
28 1.3.2.2 haad #include <sys/ioccom.h>
29 1.3.2.2 haad #include <sys/fcntl.h>
30 1.3.2.2 haad #include <sys/conf.h>
31 1.3.2.2 haad #include <sys/tty.h>
32 1.3.2.2 haad #include <sys/file.h>
33 1.3.2.2 haad #include <sys/selinfo.h>
34 1.3.2.2 haad
35 1.3.2.2 haad #include <dev/usb/usb.h>
36 1.3.2.2 haad #include <dev/usb/usbdi.h>
37 1.3.2.2 haad #include <dev/usb/usbdivar.h>
38 1.3.2.2 haad #include <dev/usb/usbdi_util.h>
39 1.3.2.2 haad
40 1.3.2.2 haad #include <dev/usb/ucomvar.h>
41 1.3.2.2 haad
42 1.3.2.2 haad #include "usbdevs.h"
43 1.3.2.2 haad
44 1.3.2.2 haad #define U3GBUFSZ 1024
45 1.3.2.2 haad #define U3G_MAXPORTS 4
46 1.3.2.2 haad
47 1.3.2.2 haad struct u3g_softc {
48 1.3.2.2 haad device_t sc_ucom[U3G_MAXPORTS];;
49 1.3.2.2 haad device_t sc_dev;
50 1.3.2.2 haad usbd_device_handle sc_udev;
51 1.3.2.2 haad u_char sc_msr;
52 1.3.2.2 haad u_char sc_lsr;
53 1.3.2.2 haad u_char numports;
54 1.3.2.2 haad
55 1.3.2.2 haad usbd_interface_handle sc_intr_iface; /* interrupt interface */
56 1.3.2.2 haad #ifdef U3G_DEBUG
57 1.3.2.2 haad int sc_intr_number; /* interrupt number */
58 1.3.2.2 haad usbd_pipe_handle sc_intr_pipe; /* interrupt pipe */
59 1.3.2.2 haad u_char *sc_intr_buf; /* interrupt buffer */
60 1.3.2.2 haad #endif
61 1.3.2.2 haad int sc_isize;
62 1.3.2.2 haad bool sc_pseudodev;
63 1.3.2.2 haad };
64 1.3.2.2 haad
65 1.3.2.2 haad struct ucom_methods u3g_methods = {
66 1.3.2.2 haad NULL,
67 1.3.2.2 haad NULL,
68 1.3.2.2 haad NULL,
69 1.3.2.2 haad NULL,
70 1.3.2.2 haad NULL,
71 1.3.2.2 haad NULL,
72 1.3.2.2 haad NULL,
73 1.3.2.2 haad NULL,
74 1.3.2.2 haad };
75 1.3.2.2 haad
76 1.3.2.2 haad static const struct usb_devno u3g_devs[] = {
77 1.3.2.2 haad /* OEM: Option N.V. */
78 1.3.2.2 haad { USB_VENDOR_OPTIONNV, USB_PRODUCT_OPTIONNV_QUADPLUSUMTS },
79 1.3.2.2 haad { USB_VENDOR_OPTIONNV, USB_PRODUCT_OPTIONNV_HSDPA },
80 1.3.2.2 haad { USB_VENDOR_OPTIONNV, USB_PRODUCT_OPTIONNV_GTMAXHSUPA },
81 1.3.2.2 haad /* OEM: Qualcomm, Inc. */
82 1.3.2.2 haad { USB_VENDOR_QUALCOMMINC, USB_PRODUCT_QUALCOMMINC_CDMA_MSM },
83 1.3.2.2 haad /* OEM: Huawei */
84 1.3.2.2 haad { USB_VENDOR_HUAWEI, USB_PRODUCT_HUAWEI_MOBILE },
85 1.3.2.2 haad { USB_VENDOR_HUAWEI, USB_PRODUCT_HUAWEI_E220 },
86 1.3.2.2 haad /* OEM: Novatel */
87 1.3.2.2 haad { USB_VENDOR_NOVATEL2, USB_PRODUCT_NOVATEL2_MERLINV620 },
88 1.3.2.2 haad { USB_VENDOR_NOVATEL2, USB_PRODUCT_NOVATEL2_ES620 },
89 1.3.2.2 haad { USB_VENDOR_NOVATEL2, USB_PRODUCT_NOVATEL2_MC950D },
90 1.3.2.2 haad { USB_VENDOR_NOVATEL2, USB_PRODUCT_NOVATEL2_U720 },
91 1.3.2.2 haad { USB_VENDOR_NOVATEL2, USB_PRODUCT_NOVATEL2_U727 },
92 1.3.2.2 haad { USB_VENDOR_NOVATEL2, USB_PRODUCT_NOVATEL2_MERLINU740 },
93 1.3.2.2 haad { USB_VENDOR_NOVATEL2, USB_PRODUCT_NOVATEL2_U740_2 },
94 1.3.2.2 haad { USB_VENDOR_NOVATEL2, USB_PRODUCT_NOVATEL2_U870 },
95 1.3.2.2 haad { USB_VENDOR_NOVATEL2, USB_PRODUCT_NOVATEL2_MERLINV620 },
96 1.3.2.2 haad { USB_VENDOR_NOVATEL2, USB_PRODUCT_NOVATEL2_S720 },
97 1.3.2.2 haad { USB_VENDOR_NOVATEL2, USB_PRODUCT_NOVATEL2_V740 },
98 1.3.2.2 haad { USB_VENDOR_NOVATEL2, USB_PRODUCT_NOVATEL2_X950D },
99 1.3.2.2 haad { USB_VENDOR_NOVATEL2, USB_PRODUCT_NOVATEL2_XU870 },
100 1.3.2.2 haad { USB_VENDOR_DELL, USB_PRODUCT_DELL_W5500 },
101 1.3.2.2 haad #if 0
102 1.3.2.2 haad { USB_VENDOR_NOVATEL2, USB_PRODUCT_NOVATEL2_MC950D_DRIVER },
103 1.3.2.2 haad #endif
104 1.3.2.2 haad /* OEM: Merlin */
105 1.3.2.2 haad { USB_VENDOR_MERLIN, USB_PRODUCT_MERLIN_V620 },
106 1.3.2.2 haad
107 1.3.2.2 haad /* OEM: Sierra Wireless: */
108 1.3.2.2 haad { USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AIRCARD580 },
109 1.3.2.2 haad { USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AIRCARD595 },
110 1.3.2.2 haad { USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC595U },
111 1.3.2.2 haad { USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC597E },
112 1.3.2.2 haad { USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_C597 },
113 1.3.2.2 haad { USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC880 },
114 1.3.2.2 haad { USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC880E },
115 1.3.2.2 haad { USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC880U },
116 1.3.2.2 haad { USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC881 },
117 1.3.2.2 haad { USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC881E },
118 1.3.2.2 haad { USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC881U },
119 1.3.2.2 haad { USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_EM5625 },
120 1.3.2.2 haad { USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC5720 },
121 1.3.2.2 haad { USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC5720_2 },
122 1.3.2.2 haad { USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC5725 },
123 1.3.2.2 haad { USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MINI5725 },
124 1.3.2.2 haad { USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AIRCARD875 },
125 1.3.2.2 haad { USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8755 },
126 1.3.2.2 haad { USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8755_2 },
127 1.3.2.2 haad { USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8755_3 },
128 1.3.2.2 haad { USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8765 },
129 1.3.2.2 haad { USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC875U },
130 1.3.2.2 haad { USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8775_2 },
131 1.3.2.2 haad { USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8780 },
132 1.3.2.2 haad { USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8781 },
133 1.3.2.2 haad };
134 1.3.2.2 haad
135 1.3.2.2 haad #ifdef U3G_DEBUG
136 1.3.2.2 haad static void
137 1.3.2.2 haad u3g_intr(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status)
138 1.3.2.2 haad {
139 1.3.2.2 haad struct u3g_softc *sc = (struct u3g_softc *)priv;
140 1.3.2.2 haad aprint_normal_dev(sc->sc_dev, "INTERRUPT CALLBACK\n");
141 1.3.2.2 haad }
142 1.3.2.2 haad #endif
143 1.3.2.2 haad
144 1.3.2.2 haad static int
145 1.3.2.2 haad u3g_novatel_reinit(struct usb_attach_arg *uaa)
146 1.3.2.2 haad {
147 1.3.2.2 haad unsigned char cmd[31];
148 1.3.2.2 haad usbd_interface_handle iface;
149 1.3.2.2 haad usb_interface_descriptor_t *id;
150 1.3.2.2 haad usb_endpoint_descriptor_t *ed;
151 1.3.2.2 haad usbd_pipe_handle pipe;
152 1.3.2.2 haad usbd_xfer_handle xfer;
153 1.3.2.2 haad int err, i;
154 1.3.2.2 haad
155 1.3.2.2 haad memset(cmd, 0, sizeof(cmd));
156 1.3.2.2 haad /* Byte 0..3: Command Block Wrapper (CBW) signature */
157 1.3.2.2 haad cmd[0] = 0x55;
158 1.3.2.2 haad cmd[1] = 0x53;
159 1.3.2.2 haad cmd[2] = 0x42;
160 1.3.2.2 haad cmd[3] = 0x43;
161 1.3.2.2 haad /* 4..7: CBW Tag, has to unique, but only a single transfer used. */
162 1.3.2.2 haad cmd[4] = 0x01;
163 1.3.2.2 haad /* 8..11: CBW Transfer Length, no data here */
164 1.3.2.2 haad /* 12: CBW Flag: output, so 0 */
165 1.3.2.2 haad /* 13: CBW Lun: 0 */
166 1.3.2.2 haad /* 14: CBW Length */
167 1.3.2.2 haad cmd[14] = 0x06;
168 1.3.2.2 haad /* Rest is the SCSI payload */
169 1.3.2.2 haad /* 0: SCSI START/STOP opcode */
170 1.3.2.2 haad cmd[15] = 0x1b;
171 1.3.2.2 haad /* 1..3 unused */
172 1.3.2.2 haad /* 4 Load/Eject command */
173 1.3.2.2 haad cmd[19] = 0x02;
174 1.3.2.2 haad /* 5: unused */
175 1.3.2.2 haad
176 1.3.2.2 haad
177 1.3.2.2 haad /* Move the device into the configured state. */
178 1.3.2.2 haad err = usbd_set_config_index(uaa->device, 0, 0);
179 1.3.2.2 haad if (err) {
180 1.3.2.2 haad aprint_error("u3g: failed to set configuration index\n");
181 1.3.2.2 haad return UMATCH_NONE;
182 1.3.2.2 haad }
183 1.3.2.2 haad
184 1.3.2.2 haad err = usbd_device2interface_handle(uaa->device, 0, &iface);
185 1.3.2.2 haad if (err != 0) {
186 1.3.2.2 haad aprint_error("u3g: failed to get interface\n");
187 1.3.2.2 haad return UMATCH_NONE;
188 1.3.2.2 haad }
189 1.3.2.2 haad
190 1.3.2.2 haad id = usbd_get_interface_descriptor(iface);
191 1.3.2.2 haad ed = NULL;
192 1.3.2.2 haad for (i = 0 ; i < id->bNumEndpoints ; i++) {
193 1.3.2.2 haad ed = usbd_interface2endpoint_descriptor(iface, i);
194 1.3.2.2 haad if (ed == NULL)
195 1.3.2.2 haad continue;
196 1.3.2.2 haad if (UE_GET_DIR(ed->bEndpointAddress) != UE_DIR_OUT)
197 1.3.2.2 haad continue;
198 1.3.2.2 haad if ((ed->bmAttributes & UE_XFERTYPE) == UE_BULK)
199 1.3.2.2 haad break;
200 1.3.2.2 haad }
201 1.3.2.2 haad
202 1.3.2.2 haad if (i == id->bNumEndpoints)
203 1.3.2.2 haad return UMATCH_NONE;
204 1.3.2.2 haad
205 1.3.2.2 haad err = usbd_open_pipe(iface, ed->bEndpointAddress, USBD_EXCLUSIVE_USE,
206 1.3.2.2 haad &pipe);
207 1.3.2.2 haad if (err != 0) {
208 1.3.2.2 haad aprint_error("u3g: failed to open bulk transfer pipe %d\n",
209 1.3.2.2 haad ed->bEndpointAddress);
210 1.3.2.2 haad return UMATCH_NONE;
211 1.3.2.2 haad }
212 1.3.2.2 haad
213 1.3.2.2 haad xfer = usbd_alloc_xfer(uaa->device);
214 1.3.2.2 haad if (xfer != NULL) {
215 1.3.2.2 haad usbd_setup_xfer(xfer, pipe, NULL, cmd, sizeof(cmd),
216 1.3.2.2 haad USBD_SYNCHRONOUS, USBD_DEFAULT_TIMEOUT, NULL);
217 1.3.2.2 haad
218 1.3.2.2 haad err = usbd_transfer(xfer);
219 1.3.2.2 haad if (err)
220 1.3.2.2 haad aprint_error("u3g: transfer failed\n");
221 1.3.2.2 haad usbd_free_xfer(xfer);
222 1.3.2.2 haad } else {
223 1.3.2.2 haad aprint_error("u3g: failed to allocate xfer\n");
224 1.3.2.2 haad err = USBD_NOMEM;
225 1.3.2.2 haad }
226 1.3.2.2 haad
227 1.3.2.2 haad usbd_abort_pipe(pipe);
228 1.3.2.2 haad usbd_close_pipe(pipe);
229 1.3.2.2 haad
230 1.3.2.2 haad return (err == USBD_NORMAL_COMPLETION ? UMATCH_HIGHEST : UMATCH_NONE);
231 1.3.2.2 haad }
232 1.3.2.2 haad
233 1.3.2.2 haad static int
234 1.3.2.2 haad u3g_huawei_reinit(usbd_device_handle dev)
235 1.3.2.2 haad {
236 1.3.2.2 haad /* The Huawei device presents itself as a umass device with Windows
237 1.3.2.2 haad * drivers on it. After installation of the driver, it reinits into a
238 1.3.2.2 haad * 3G serial device.
239 1.3.2.2 haad */
240 1.3.2.2 haad usb_device_request_t req;
241 1.3.2.2 haad usb_config_descriptor_t *cdesc;
242 1.3.2.2 haad
243 1.3.2.2 haad /* Get the config descriptor */
244 1.3.2.2 haad cdesc = usbd_get_config_descriptor(dev);
245 1.3.2.2 haad if (cdesc == NULL)
246 1.3.2.2 haad return (UMATCH_NONE);
247 1.3.2.2 haad
248 1.3.2.2 haad /* One iface means umass mode, more than 1 (4 usually) means 3G mode */
249 1.3.2.2 haad if (cdesc->bNumInterface > 1)
250 1.3.2.2 haad return (UMATCH_VENDOR_PRODUCT);
251 1.3.2.2 haad
252 1.3.2.2 haad req.bmRequestType = UT_WRITE_DEVICE;
253 1.3.2.2 haad req.bRequest = UR_SET_FEATURE;
254 1.3.2.2 haad USETW(req.wValue, UF_DEVICE_REMOTE_WAKEUP);
255 1.3.2.2 haad USETW(req.wIndex, UHF_PORT_SUSPEND);
256 1.3.2.2 haad USETW(req.wLength, 0);
257 1.3.2.2 haad
258 1.3.2.2 haad (void) usbd_do_request(dev, &req, 0);
259 1.3.2.2 haad
260 1.3.2.2 haad
261 1.3.2.2 haad return (UMATCH_HIGHEST); /* Match to prevent umass from attaching */
262 1.3.2.2 haad }
263 1.3.2.2 haad
264 1.3.2.2 haad static int
265 1.3.2.3 haad u3g_sierra_reinit(usbd_device_handle dev)
266 1.3.2.3 haad {
267 1.3.2.3 haad /* Some Sierra devices presents themselves as a umass device with
268 1.3.2.3 haad * Windows drivers on it. After installation of the driver, it
269 1.3.2.3 haad * reinits into a * 3G serial device.
270 1.3.2.3 haad */
271 1.3.2.3 haad usb_device_request_t req;
272 1.3.2.3 haad usb_config_descriptor_t *cdesc;
273 1.3.2.3 haad
274 1.3.2.3 haad /* Get the config descriptor */
275 1.3.2.3 haad cdesc = usbd_get_config_descriptor(dev);
276 1.3.2.3 haad if (cdesc == NULL)
277 1.3.2.3 haad return (UMATCH_NONE);
278 1.3.2.3 haad
279 1.3.2.3 haad req.bmRequestType = UT_VENDOR;
280 1.3.2.3 haad req.bRequest = UR_SET_INTERFACE;
281 1.3.2.3 haad USETW(req.wValue, UF_DEVICE_REMOTE_WAKEUP);
282 1.3.2.3 haad USETW(req.wIndex, UHF_PORT_CONNECTION);
283 1.3.2.3 haad USETW(req.wLength, 0);
284 1.3.2.3 haad
285 1.3.2.3 haad (void) usbd_do_request(dev, &req, 0);
286 1.3.2.3 haad
287 1.3.2.3 haad return (UMATCH_HIGHEST); /* Match to prevent umass from attaching */
288 1.3.2.3 haad }
289 1.3.2.3 haad
290 1.3.2.3 haad static int
291 1.3.2.2 haad u3g_match(device_t parent, cfdata_t match, void *aux)
292 1.3.2.2 haad {
293 1.3.2.2 haad struct usb_attach_arg *uaa = aux;
294 1.3.2.2 haad
295 1.3.2.2 haad if (uaa->vendor == USB_VENDOR_HUAWEI)
296 1.3.2.2 haad return u3g_huawei_reinit(uaa->device);
297 1.3.2.2 haad
298 1.3.2.2 haad if (uaa->vendor == USB_VENDOR_NOVATEL2 &&
299 1.3.2.2 haad uaa->product == USB_PRODUCT_NOVATEL2_MC950D_DRIVER)
300 1.3.2.2 haad return u3g_novatel_reinit(uaa);
301 1.3.2.2 haad
302 1.3.2.3 haad if (uaa->vendor == USB_VENDOR_SIERRA &&
303 1.3.2.3 haad uaa->product == USB_PRODUCT_SIERRA_INSTALLER)
304 1.3.2.3 haad return u3g_sierra_reinit(uaa->device);
305 1.3.2.3 haad
306 1.3.2.2 haad if (usb_lookup(u3g_devs, uaa->vendor, uaa->product))
307 1.3.2.2 haad return UMATCH_VENDOR_PRODUCT;
308 1.3.2.2 haad
309 1.3.2.2 haad return UMATCH_NONE;
310 1.3.2.2 haad }
311 1.3.2.2 haad
312 1.3.2.2 haad static void
313 1.3.2.2 haad u3g_attach(device_t parent, device_t self, void *aux)
314 1.3.2.2 haad {
315 1.3.2.2 haad struct u3g_softc *sc = device_private(self);
316 1.3.2.2 haad struct usb_attach_arg *uaa = aux;
317 1.3.2.2 haad usbd_device_handle dev = uaa->device;
318 1.3.2.2 haad usbd_interface_handle iface;
319 1.3.2.2 haad usb_interface_descriptor_t *id;
320 1.3.2.2 haad usb_endpoint_descriptor_t *ed;
321 1.3.2.2 haad usbd_status error;
322 1.3.2.2 haad int i, n;
323 1.3.2.2 haad usb_config_descriptor_t *cdesc;
324 1.3.2.2 haad
325 1.3.2.2 haad aprint_naive("\n");
326 1.3.2.2 haad aprint_normal("\n");
327 1.3.2.2 haad
328 1.3.2.2 haad if (uaa->vendor == USB_VENDOR_NOVATEL2 &&
329 1.3.2.2 haad uaa->product == USB_PRODUCT_NOVATEL2_MC950D_DRIVER) {
330 1.3.2.2 haad /* About to disappear... */
331 1.3.2.2 haad sc->sc_pseudodev = true;
332 1.3.2.2 haad return;
333 1.3.2.2 haad }
334 1.3.2.2 haad
335 1.3.2.2 haad sc->sc_dev = self;
336 1.3.2.2 haad #ifdef U3G_DEBUG
337 1.3.2.2 haad sc->sc_intr_number = -1;
338 1.3.2.2 haad sc->sc_intr_pipe = NULL;
339 1.3.2.2 haad #endif
340 1.3.2.2 haad /* Move the device into the configured state. */
341 1.3.2.2 haad error = usbd_set_config_index(dev, 0, 1);
342 1.3.2.2 haad if (error) {
343 1.3.2.2 haad aprint_error_dev(self, "failed to set configuration: %s\n",
344 1.3.2.2 haad usbd_errstr(error));
345 1.3.2.2 haad return;
346 1.3.2.2 haad }
347 1.3.2.2 haad
348 1.3.2.2 haad /* get the config descriptor */
349 1.3.2.2 haad cdesc = usbd_get_config_descriptor(dev);
350 1.3.2.2 haad
351 1.3.2.2 haad if (cdesc == NULL) {
352 1.3.2.2 haad aprint_error_dev(self, "failed to get configuration descriptor\n");
353 1.3.2.2 haad return;
354 1.3.2.2 haad }
355 1.3.2.2 haad
356 1.3.2.2 haad if (uaa->vendor == USB_VENDOR_HUAWEI && cdesc->bNumInterface > 1) {
357 1.3.2.2 haad /* About to disappear... */
358 1.3.2.2 haad sc->sc_pseudodev = true;
359 1.3.2.2 haad return;
360 1.3.2.2 haad }
361 1.3.2.2 haad
362 1.3.2.3 haad if (uaa->vendor == USB_VENDOR_SIERRA &&
363 1.3.2.3 haad uaa->product == USB_PRODUCT_SIERRA_INSTALLER) {
364 1.3.2.3 haad /* About to disappear... */
365 1.3.2.3 haad sc->sc_pseudodev = true;
366 1.3.2.3 haad return;
367 1.3.2.3 haad }
368 1.3.2.3 haad
369 1.3.2.2 haad sc->sc_udev = dev;
370 1.3.2.2 haad sc->numports = (cdesc->bNumInterface <= U3G_MAXPORTS)?cdesc->bNumInterface:U3G_MAXPORTS;
371 1.3.2.2 haad for ( i = 0; i < sc->numports; i++ ) {
372 1.3.2.2 haad struct ucom_attach_args uca;
373 1.3.2.2 haad
374 1.3.2.2 haad error = usbd_device2interface_handle(dev, i, &iface);
375 1.3.2.2 haad if (error) {
376 1.3.2.2 haad aprint_error_dev(self,
377 1.3.2.2 haad "failed to get interface, err=%s\n",
378 1.3.2.2 haad usbd_errstr(error));
379 1.3.2.2 haad return;
380 1.3.2.2 haad }
381 1.3.2.2 haad id = usbd_get_interface_descriptor(iface);
382 1.3.2.2 haad
383 1.3.2.2 haad uca.info = "Generic 3G Serial Device";
384 1.3.2.2 haad uca.ibufsize = U3GBUFSZ;
385 1.3.2.2 haad uca.obufsize = U3GBUFSZ;
386 1.3.2.2 haad uca.ibufsizepad = U3GBUFSZ;
387 1.3.2.2 haad uca.portno = i;
388 1.3.2.2 haad uca.opkthdrlen = 0;
389 1.3.2.2 haad uca.device = dev;
390 1.3.2.2 haad uca.iface = iface;
391 1.3.2.2 haad uca.methods = &u3g_methods;
392 1.3.2.2 haad uca.arg = sc;
393 1.3.2.2 haad
394 1.3.2.2 haad uca.bulkin = uca.bulkout = -1;
395 1.3.2.2 haad for (n = 0; n < id->bNumEndpoints; n++) {
396 1.3.2.2 haad ed = usbd_interface2endpoint_descriptor(iface, n);
397 1.3.2.2 haad if (ed == NULL) {
398 1.3.2.2 haad aprint_error_dev(self,
399 1.3.2.2 haad "could not read endpoint descriptor\n");
400 1.3.2.2 haad return;
401 1.3.2.2 haad }
402 1.3.2.2 haad if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
403 1.3.2.2 haad UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK)
404 1.3.2.2 haad uca.bulkin = ed->bEndpointAddress;
405 1.3.2.2 haad else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT &&
406 1.3.2.2 haad UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK)
407 1.3.2.2 haad uca.bulkout = ed->bEndpointAddress;
408 1.3.2.2 haad }
409 1.3.2.2 haad if (uca.bulkin == -1 || uca.bulkout == -1) {
410 1.3.2.2 haad aprint_error_dev(self, "missing endpoint\n");
411 1.3.2.2 haad return;
412 1.3.2.2 haad }
413 1.3.2.2 haad
414 1.3.2.2 haad sc->sc_ucom[i] = config_found_sm_loc(self, "ucombus", NULL, &uca,
415 1.3.2.2 haad ucomprint, ucomsubmatch);
416 1.3.2.2 haad }
417 1.3.2.2 haad
418 1.3.2.2 haad #ifdef U3G_DEBUG
419 1.3.2.2 haad if (sc->sc_intr_number != -1 && sc->sc_intr_pipe == NULL) {
420 1.3.2.2 haad sc->sc_intr_buf = malloc(sc->sc_isize, M_USBDEV, M_WAITOK);
421 1.3.2.2 haad error = usbd_open_pipe_intr(sc->sc_intr_iface,
422 1.3.2.2 haad sc->sc_intr_number,
423 1.3.2.2 haad USBD_SHORT_XFER_OK,
424 1.3.2.2 haad &sc->sc_intr_pipe,
425 1.3.2.2 haad sc,
426 1.3.2.2 haad sc->sc_intr_buf,
427 1.3.2.2 haad sc->sc_isize,
428 1.3.2.2 haad u3g_intr,
429 1.3.2.2 haad 100);
430 1.3.2.2 haad if (error) {
431 1.3.2.2 haad aprint_error_dev(self,
432 1.3.2.2 haad "cannot open interrupt pipe (addr %d)\n",
433 1.3.2.2 haad sc->sc_intr_number);
434 1.3.2.2 haad return;
435 1.3.2.2 haad }
436 1.3.2.2 haad }
437 1.3.2.2 haad #endif
438 1.3.2.2 haad
439 1.3.2.2 haad
440 1.3.2.2 haad if (!pmf_device_register(self, NULL, NULL))
441 1.3.2.2 haad aprint_error_dev(self, "couldn't establish power handler\n");
442 1.3.2.2 haad }
443 1.3.2.2 haad
444 1.3.2.2 haad static int
445 1.3.2.2 haad u3g_detach(device_t self, int flags)
446 1.3.2.2 haad {
447 1.3.2.2 haad struct u3g_softc *sc = device_private(self);
448 1.3.2.2 haad int rv = 0;
449 1.3.2.2 haad int i;
450 1.3.2.2 haad
451 1.3.2.2 haad if (sc->sc_pseudodev)
452 1.3.2.2 haad return 0;
453 1.3.2.2 haad
454 1.3.2.2 haad pmf_device_deregister(self);
455 1.3.2.2 haad
456 1.3.2.2 haad for (i = 0; i < sc->numports; i++) {
457 1.3.2.2 haad if(sc->sc_ucom[i]) {
458 1.3.2.2 haad rv = config_detach(sc->sc_ucom[i], flags);
459 1.3.2.2 haad if(rv != 0) {
460 1.3.2.2 haad aprint_verbose_dev(self, "Can't deallocat port %d", i);
461 1.3.2.2 haad return rv;
462 1.3.2.2 haad }
463 1.3.2.2 haad }
464 1.3.2.2 haad }
465 1.3.2.2 haad
466 1.3.2.2 haad #ifdef U3G_DEBUG
467 1.3.2.2 haad if (sc->sc_intr_pipe != NULL) {
468 1.3.2.2 haad int err = usbd_abort_pipe(sc->sc_intr_pipe);
469 1.3.2.2 haad if (err)
470 1.3.2.2 haad aprint_error_dev(self,
471 1.3.2.2 haad "abort interrupt pipe failed: %s\n",
472 1.3.2.2 haad usbd_errstr(err));
473 1.3.2.2 haad err = usbd_close_pipe(sc->sc_intr_pipe);
474 1.3.2.2 haad if (err)
475 1.3.2.2 haad aprint_error_dev(self,
476 1.3.2.2 haad "close interrupt pipe failed: %s\n",
477 1.3.2.2 haad usbd_errstr(err));
478 1.3.2.2 haad free(sc->sc_intr_buf, M_USBDEV);
479 1.3.2.2 haad sc->sc_intr_pipe = NULL;
480 1.3.2.2 haad }
481 1.3.2.2 haad #endif
482 1.3.2.2 haad
483 1.3.2.2 haad return 0;
484 1.3.2.2 haad }
485 1.3.2.2 haad
486 1.3.2.2 haad static void
487 1.3.2.2 haad u3g_childdet(device_t self, device_t child)
488 1.3.2.2 haad {
489 1.3.2.2 haad struct u3g_softc *sc = device_private(self);
490 1.3.2.2 haad int i;
491 1.3.2.2 haad
492 1.3.2.2 haad for (i = 0; i < sc->numports; i++) {
493 1.3.2.2 haad if (sc->sc_ucom[i] == child)
494 1.3.2.2 haad sc->sc_ucom[i] = NULL;
495 1.3.2.2 haad }
496 1.3.2.2 haad }
497 1.3.2.2 haad
498 1.3.2.2 haad static int
499 1.3.2.2 haad u3g_activate(device_t self, enum devact act)
500 1.3.2.2 haad {
501 1.3.2.2 haad struct u3g_softc *sc = device_private(self);
502 1.3.2.2 haad int i, rv = 0;
503 1.3.2.2 haad
504 1.3.2.2 haad switch (act) {
505 1.3.2.2 haad case DVACT_ACTIVATE:
506 1.3.2.2 haad return (EOPNOTSUPP);
507 1.3.2.2 haad break;
508 1.3.2.2 haad
509 1.3.2.2 haad case DVACT_DEACTIVATE:
510 1.3.2.2 haad for (i = 0; i < sc->numports; i++) {
511 1.3.2.2 haad if (sc->sc_ucom[i] && config_deactivate(sc->sc_ucom[i]))
512 1.3.2.2 haad rv = -1;
513 1.3.2.2 haad }
514 1.3.2.2 haad break;
515 1.3.2.2 haad }
516 1.3.2.2 haad return (rv);
517 1.3.2.2 haad }
518 1.3.2.2 haad
519 1.3.2.2 haad CFATTACH_DECL2_NEW(u3g, sizeof(struct u3g_softc), u3g_match,
520 1.3.2.2 haad u3g_attach, u3g_detach, u3g_activate, NULL, u3g_childdet);
521