Deleted Added
full compact
ugensa.c (185950) ugensa.c (187176)
1/* $FreeBSD: head/sys/dev/usb2/serial/ugensa2.c 185950 2008-12-11 23:17:48Z thompsa $ */
1/* $FreeBSD: head/sys/dev/usb2/serial/ugensa2.c 187176 2009-01-13 19:03:47Z thompsa $ */
2/* $NetBSD: ugensa.c,v 1.9.2.1 2007/03/24 14:55:50 yamt Exp $ */
3
4/*
5 * Copyright (c) 2004, 2005 The NetBSD Foundation, Inc.
6 * All rights reserved.
7 *
8 * This code is derived from software contributed to The NetBSD Foundation
9 * by Roland C. Dowdeswell <elric@netbsd.org>.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. All advertising materials mentioning features or use of this software
20 * must display the following acknowledgement:
21 * This product includes software developed by the NetBSD
22 * Foundation, Inc. and its contributors.
23 * 4. Neither the name of The NetBSD Foundation nor the names of its
24 * contributors may be used to endorse or promote products derived
25 * from this software without specific prior written permission.
26 *
27 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
28 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
29 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
30 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
31 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
32 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
33 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
34 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
35 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
36 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
37 * POSSIBILITY OF SUCH DAMAGE.
38 */
39
40/*
41 * NOTE: all function names beginning like "ugensa_cfg_" can only
42 * be called from within the config thread function !
43 */
44
45#include <dev/usb2/include/usb2_devid.h>
46#include <dev/usb2/include/usb2_standard.h>
47#include <dev/usb2/include/usb2_mfunc.h>
48#include <dev/usb2/include/usb2_error.h>
49#include <dev/usb2/include/usb2_cdc.h>
50#include <dev/usb2/include/usb2_defs.h>
51
52#define USB_DEBUG_VAR usb2_debug
53
54#include <dev/usb2/core/usb2_core.h>
55#include <dev/usb2/core/usb2_debug.h>
56#include <dev/usb2/core/usb2_process.h>
2/* $NetBSD: ugensa.c,v 1.9.2.1 2007/03/24 14:55:50 yamt Exp $ */
3
4/*
5 * Copyright (c) 2004, 2005 The NetBSD Foundation, Inc.
6 * All rights reserved.
7 *
8 * This code is derived from software contributed to The NetBSD Foundation
9 * by Roland C. Dowdeswell <elric@netbsd.org>.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. All advertising materials mentioning features or use of this software
20 * must display the following acknowledgement:
21 * This product includes software developed by the NetBSD
22 * Foundation, Inc. and its contributors.
23 * 4. Neither the name of The NetBSD Foundation nor the names of its
24 * contributors may be used to endorse or promote products derived
25 * from this software without specific prior written permission.
26 *
27 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
28 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
29 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
30 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
31 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
32 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
33 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
34 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
35 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
36 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
37 * POSSIBILITY OF SUCH DAMAGE.
38 */
39
40/*
41 * NOTE: all function names beginning like "ugensa_cfg_" can only
42 * be called from within the config thread function !
43 */
44
45#include <dev/usb2/include/usb2_devid.h>
46#include <dev/usb2/include/usb2_standard.h>
47#include <dev/usb2/include/usb2_mfunc.h>
48#include <dev/usb2/include/usb2_error.h>
49#include <dev/usb2/include/usb2_cdc.h>
50#include <dev/usb2/include/usb2_defs.h>
51
52#define USB_DEBUG_VAR usb2_debug
53
54#include <dev/usb2/core/usb2_core.h>
55#include <dev/usb2/core/usb2_debug.h>
56#include <dev/usb2/core/usb2_process.h>
57#include <dev/usb2/core/usb2_config_td.h>
58#include <dev/usb2/core/usb2_request.h>
59#include <dev/usb2/core/usb2_lookup.h>
60#include <dev/usb2/core/usb2_util.h>
61#include <dev/usb2/core/usb2_device.h>
62
63#include <dev/usb2/serial/usb2_serial.h>
64
65#define UGENSA_BUF_SIZE 2048 /* bytes */
66#define UGENSA_N_TRANSFER 4 /* units */
67#define UGENSA_CONFIG_INDEX 0
68#define UGENSA_IFACE_INDEX 0
69#define UGENSA_IFACE_MAX 8 /* exclusivly */
70
71struct ugensa_sub_softc {
72 struct usb2_com_softc *sc_usb2_com_ptr;
73 struct usb2_xfer *sc_xfer[UGENSA_N_TRANSFER];
74
75 uint8_t sc_flags;
76#define UGENSA_FLAG_BULK_READ_STALL 0x01
77#define UGENSA_FLAG_BULK_WRITE_STALL 0x02
78};
79
80struct ugensa_softc {
81 struct usb2_com_super_softc sc_super_ucom;
82 struct usb2_com_softc sc_ucom[UGENSA_IFACE_MAX];
83 struct ugensa_sub_softc sc_sub[UGENSA_IFACE_MAX];
84
85 struct mtx sc_mtx;
86 uint8_t sc_niface;
87};
88
89/* prototypes */
90
91static device_probe_t ugensa_probe;
92static device_attach_t ugensa_attach;
93static device_detach_t ugensa_detach;
94
95static usb2_callback_t ugensa_bulk_write_callback;
96static usb2_callback_t ugensa_bulk_write_clear_stall_callback;
97static usb2_callback_t ugensa_bulk_read_callback;
98static usb2_callback_t ugensa_bulk_read_clear_stall_callback;
99
100static void ugensa_start_read(struct usb2_com_softc *);
101static void ugensa_stop_read(struct usb2_com_softc *);
102static void ugensa_start_write(struct usb2_com_softc *);
103static void ugensa_stop_write(struct usb2_com_softc *);
104
105static const struct usb2_config
106 ugensa_xfer_config[UGENSA_N_TRANSFER] = {
107
108 [0] = {
109 .type = UE_BULK,
110 .endpoint = UE_ADDR_ANY,
111 .direction = UE_DIR_OUT,
112 .mh.bufsize = UGENSA_BUF_SIZE,
113 .mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,},
114 .mh.callback = &ugensa_bulk_write_callback,
115 },
116
117 [1] = {
118 .type = UE_BULK,
119 .endpoint = UE_ADDR_ANY,
120 .direction = UE_DIR_IN,
121 .mh.bufsize = UGENSA_BUF_SIZE,
122 .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
123 .mh.callback = &ugensa_bulk_read_callback,
124 },
125
126 [2] = {
127 .type = UE_CONTROL,
128 .endpoint = 0x00, /* Control pipe */
129 .direction = UE_DIR_ANY,
130 .mh.bufsize = sizeof(struct usb2_device_request),
131 .mh.flags = {},
132 .mh.callback = &ugensa_bulk_write_clear_stall_callback,
133 .mh.timeout = 1000, /* 1 second */
134 .mh.interval = 50, /* 50ms */
135 },
136
137 [3] = {
138 .type = UE_CONTROL,
139 .endpoint = 0x00, /* Control pipe */
140 .direction = UE_DIR_ANY,
141 .mh.bufsize = sizeof(struct usb2_device_request),
142 .mh.flags = {},
143 .mh.callback = &ugensa_bulk_read_clear_stall_callback,
144 .mh.timeout = 1000, /* 1 second */
145 .mh.interval = 50, /* 50ms */
146 },
147};
148
149static const struct usb2_com_callback ugensa_callback = {
150 .usb2_com_start_read = &ugensa_start_read,
151 .usb2_com_stop_read = &ugensa_stop_read,
152 .usb2_com_start_write = &ugensa_start_write,
153 .usb2_com_stop_write = &ugensa_stop_write,
154};
155
156static device_method_t ugensa_methods[] = {
157 /* Device methods */
158 DEVMETHOD(device_probe, ugensa_probe),
159 DEVMETHOD(device_attach, ugensa_attach),
160 DEVMETHOD(device_detach, ugensa_detach),
161 {0, 0}
162};
163
164static devclass_t ugensa_devclass;
165
166static driver_t ugensa_driver = {
167 .name = "ugensa",
168 .methods = ugensa_methods,
169 .size = sizeof(struct ugensa_softc),
170};
171
172DRIVER_MODULE(ugensa, ushub, ugensa_driver, ugensa_devclass, NULL, 0);
173MODULE_DEPEND(ugensa, usb2_serial, 1, 1, 1);
174MODULE_DEPEND(ugensa, usb2_core, 1, 1, 1);
175
176static const struct usb2_device_id ugensa_devs[] = {
177 {USB_VPI(USB_VENDOR_AIRPRIME, USB_PRODUCT_AIRPRIME_PC5220, 0)},
178 {USB_VPI(USB_VENDOR_CMOTECH, USB_PRODUCT_CMOTECH_CDMA_MODEM1, 0)},
179 {USB_VPI(USB_VENDOR_KYOCERA2, USB_PRODUCT_KYOCERA2_CDMA_MSM_K, 0)},
180 {USB_VPI(USB_VENDOR_HP, USB_PRODUCT_HP_49GPLUS, 0)},
181/* {USB_VPI(USB_VENDOR_HUAWEI, USB_PRODUCT_HUAWEI_E270, 0)}, */
182 {USB_VPI(USB_VENDOR_HUAWEI, USB_PRODUCT_HUAWEI_MOBILE, 0)},
183 {USB_VPI(USB_VENDOR_MERLIN, USB_PRODUCT_MERLIN_V620, 0)},
184 {USB_VPI(USB_VENDOR_NOVATEL2, USB_PRODUCT_NOVATEL2_FLEXPACKGPS, 0)},
185};
186
187static int
188ugensa_probe(device_t dev)
189{
190 struct usb2_attach_arg *uaa = device_get_ivars(dev);
191
192 if (uaa->usb2_mode != USB_MODE_HOST) {
193 return (ENXIO);
194 }
195 if (uaa->info.bConfigIndex != UGENSA_CONFIG_INDEX) {
196 return (ENXIO);
197 }
198 if (uaa->info.bIfaceIndex != 0) {
199 return (ENXIO);
200 }
201 return (usb2_lookup_id_by_uaa(ugensa_devs, sizeof(ugensa_devs), uaa));
202}
203
204static int
205ugensa_attach(device_t dev)
206{
207 struct usb2_attach_arg *uaa = device_get_ivars(dev);
208 struct ugensa_softc *sc = device_get_softc(dev);
209 struct ugensa_sub_softc *ssc;
210 struct usb2_interface *iface;
211 int32_t error;
212 uint8_t iface_index;
213 int x, cnt;
214
215 if (sc == NULL)
216 return (ENOMEM);
217
218 device_set_usb2_desc(dev);
219 mtx_init(&sc->sc_mtx, "ugensa", NULL, MTX_DEF);
220
221 /* Figure out how many interfaces this device has got */
222 for (cnt = 0; cnt < UGENSA_IFACE_MAX; cnt++) {
223 if ((usb2_get_pipe(uaa->device, cnt, ugensa_xfer_config + 0) == NULL) ||
224 (usb2_get_pipe(uaa->device, cnt, ugensa_xfer_config + 1) == NULL)) {
225 /* we have reached the end */
226 break;
227 }
228 }
229
230 if (cnt == 0) {
231 device_printf(dev, "No interfaces!\n");
232 goto detach;
233 }
234 for (x = 0; x < cnt; x++) {
235 iface = usb2_get_iface(uaa->device, x);
236 if (iface->idesc->bInterfaceClass != UICLASS_VENDOR)
237 /* Not a serial port, most likely a SD reader */
238 continue;
239
240 ssc = sc->sc_sub + sc->sc_niface;
241 ssc->sc_usb2_com_ptr = sc->sc_ucom + sc->sc_niface;
242
243 iface_index = (UGENSA_IFACE_INDEX + x);
244 error = usb2_transfer_setup(uaa->device,
245 &iface_index, ssc->sc_xfer, ugensa_xfer_config,
246 UGENSA_N_TRANSFER, ssc, &sc->sc_mtx);
247
248 if (error) {
249 device_printf(dev, "allocating USB "
250 "transfers failed!\n");
251 goto detach;
252 }
253 /* clear stall at first run */
254 ssc->sc_flags |= (UGENSA_FLAG_BULK_WRITE_STALL |
255 UGENSA_FLAG_BULK_READ_STALL);
256
257 /* initialize port number */
258 ssc->sc_usb2_com_ptr->sc_portno = sc->sc_niface;
259 sc->sc_niface++;
260 if (x != uaa->info.bIfaceIndex)
261 usb2_set_parent_iface(uaa->device, x,
262 uaa->info.bIfaceIndex);
263 }
264 device_printf(dev, "Found %d interfaces.\n", sc->sc_niface);
265
266 error = usb2_com_attach(&sc->sc_super_ucom, sc->sc_ucom, sc->sc_niface, sc,
267 &ugensa_callback, &sc->sc_mtx);
268 if (error) {
269 DPRINTF("attach failed\n");
270 goto detach;
271 }
272 return (0); /* success */
273
274detach:
275 ugensa_detach(dev);
276 return (ENXIO); /* failure */
277}
278
279static int
280ugensa_detach(device_t dev)
281{
282 struct ugensa_softc *sc = device_get_softc(dev);
283 uint8_t x;
284
285 usb2_com_detach(&sc->sc_super_ucom, sc->sc_ucom, sc->sc_niface);
286
287 for (x = 0; x < sc->sc_niface; x++) {
288 usb2_transfer_unsetup(sc->sc_sub[x].sc_xfer, UGENSA_N_TRANSFER);
289 }
290 mtx_destroy(&sc->sc_mtx);
291
292 return (0);
293}
294
295static void
296ugensa_bulk_write_callback(struct usb2_xfer *xfer)
297{
298 struct ugensa_sub_softc *ssc = xfer->priv_sc;
299 uint32_t actlen;
300
301 switch (USB_GET_STATE(xfer)) {
302 case USB_ST_SETUP:
303 case USB_ST_TRANSFERRED:
304 if (ssc->sc_flags & UGENSA_FLAG_BULK_WRITE_STALL) {
305 usb2_transfer_start(ssc->sc_xfer[2]);
306 return;
307 }
308 if (usb2_com_get_data(ssc->sc_usb2_com_ptr, xfer->frbuffers, 0,
309 UGENSA_BUF_SIZE, &actlen)) {
310 xfer->frlengths[0] = actlen;
311 usb2_start_hardware(xfer);
312 }
313 return;
314
315 default: /* Error */
316 if (xfer->error != USB_ERR_CANCELLED) {
317 ssc->sc_flags |= UGENSA_FLAG_BULK_WRITE_STALL;
318 usb2_transfer_start(ssc->sc_xfer[2]);
319 }
320 return;
321
322 }
323}
324
325static void
326ugensa_bulk_write_clear_stall_callback(struct usb2_xfer *xfer)
327{
328 struct ugensa_sub_softc *ssc = xfer->priv_sc;
329 struct usb2_xfer *xfer_other = ssc->sc_xfer[0];
330
331 if (usb2_clear_stall_callback(xfer, xfer_other)) {
332 DPRINTF("stall cleared\n");
333 ssc->sc_flags &= ~UGENSA_FLAG_BULK_WRITE_STALL;
334 usb2_transfer_start(xfer_other);
335 }
336}
337
338static void
339ugensa_bulk_read_callback(struct usb2_xfer *xfer)
340{
341 struct ugensa_sub_softc *ssc = xfer->priv_sc;
342
343 switch (USB_GET_STATE(xfer)) {
344 case USB_ST_TRANSFERRED:
345 usb2_com_put_data(ssc->sc_usb2_com_ptr, xfer->frbuffers, 0,
346 xfer->actlen);
347
348 case USB_ST_SETUP:
349 if (ssc->sc_flags & UGENSA_FLAG_BULK_READ_STALL) {
350 usb2_transfer_start(ssc->sc_xfer[3]);
351 } else {
352 xfer->frlengths[0] = xfer->max_data_length;
353 usb2_start_hardware(xfer);
354 }
355 return;
356
357 default: /* Error */
358 if (xfer->error != USB_ERR_CANCELLED) {
359 ssc->sc_flags |= UGENSA_FLAG_BULK_READ_STALL;
360 usb2_transfer_start(ssc->sc_xfer[3]);
361 }
362 return;
363
364 }
365}
366
367static void
368ugensa_bulk_read_clear_stall_callback(struct usb2_xfer *xfer)
369{
370 struct ugensa_sub_softc *ssc = xfer->priv_sc;
371 struct usb2_xfer *xfer_other = ssc->sc_xfer[1];
372
373 if (usb2_clear_stall_callback(xfer, xfer_other)) {
374 DPRINTF("stall cleared\n");
375 ssc->sc_flags &= ~UGENSA_FLAG_BULK_READ_STALL;
376 usb2_transfer_start(xfer_other);
377 }
378}
379
380static void
381ugensa_start_read(struct usb2_com_softc *ucom)
382{
383 struct ugensa_softc *sc = ucom->sc_parent;
384 struct ugensa_sub_softc *ssc = sc->sc_sub + ucom->sc_portno;
385
386 usb2_transfer_start(ssc->sc_xfer[1]);
387}
388
389static void
390ugensa_stop_read(struct usb2_com_softc *ucom)
391{
392 struct ugensa_softc *sc = ucom->sc_parent;
393 struct ugensa_sub_softc *ssc = sc->sc_sub + ucom->sc_portno;
394
395 usb2_transfer_stop(ssc->sc_xfer[3]);
396 usb2_transfer_stop(ssc->sc_xfer[1]);
397}
398
399static void
400ugensa_start_write(struct usb2_com_softc *ucom)
401{
402 struct ugensa_softc *sc = ucom->sc_parent;
403 struct ugensa_sub_softc *ssc = sc->sc_sub + ucom->sc_portno;
404
405 usb2_transfer_start(ssc->sc_xfer[0]);
406}
407
408static void
409ugensa_stop_write(struct usb2_com_softc *ucom)
410{
411 struct ugensa_softc *sc = ucom->sc_parent;
412 struct ugensa_sub_softc *ssc = sc->sc_sub + ucom->sc_portno;
413
414 usb2_transfer_stop(ssc->sc_xfer[2]);
415 usb2_transfer_stop(ssc->sc_xfer[0]);
416}
57#include <dev/usb2/core/usb2_request.h>
58#include <dev/usb2/core/usb2_lookup.h>
59#include <dev/usb2/core/usb2_util.h>
60#include <dev/usb2/core/usb2_device.h>
61
62#include <dev/usb2/serial/usb2_serial.h>
63
64#define UGENSA_BUF_SIZE 2048 /* bytes */
65#define UGENSA_N_TRANSFER 4 /* units */
66#define UGENSA_CONFIG_INDEX 0
67#define UGENSA_IFACE_INDEX 0
68#define UGENSA_IFACE_MAX 8 /* exclusivly */
69
70struct ugensa_sub_softc {
71 struct usb2_com_softc *sc_usb2_com_ptr;
72 struct usb2_xfer *sc_xfer[UGENSA_N_TRANSFER];
73
74 uint8_t sc_flags;
75#define UGENSA_FLAG_BULK_READ_STALL 0x01
76#define UGENSA_FLAG_BULK_WRITE_STALL 0x02
77};
78
79struct ugensa_softc {
80 struct usb2_com_super_softc sc_super_ucom;
81 struct usb2_com_softc sc_ucom[UGENSA_IFACE_MAX];
82 struct ugensa_sub_softc sc_sub[UGENSA_IFACE_MAX];
83
84 struct mtx sc_mtx;
85 uint8_t sc_niface;
86};
87
88/* prototypes */
89
90static device_probe_t ugensa_probe;
91static device_attach_t ugensa_attach;
92static device_detach_t ugensa_detach;
93
94static usb2_callback_t ugensa_bulk_write_callback;
95static usb2_callback_t ugensa_bulk_write_clear_stall_callback;
96static usb2_callback_t ugensa_bulk_read_callback;
97static usb2_callback_t ugensa_bulk_read_clear_stall_callback;
98
99static void ugensa_start_read(struct usb2_com_softc *);
100static void ugensa_stop_read(struct usb2_com_softc *);
101static void ugensa_start_write(struct usb2_com_softc *);
102static void ugensa_stop_write(struct usb2_com_softc *);
103
104static const struct usb2_config
105 ugensa_xfer_config[UGENSA_N_TRANSFER] = {
106
107 [0] = {
108 .type = UE_BULK,
109 .endpoint = UE_ADDR_ANY,
110 .direction = UE_DIR_OUT,
111 .mh.bufsize = UGENSA_BUF_SIZE,
112 .mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,},
113 .mh.callback = &ugensa_bulk_write_callback,
114 },
115
116 [1] = {
117 .type = UE_BULK,
118 .endpoint = UE_ADDR_ANY,
119 .direction = UE_DIR_IN,
120 .mh.bufsize = UGENSA_BUF_SIZE,
121 .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
122 .mh.callback = &ugensa_bulk_read_callback,
123 },
124
125 [2] = {
126 .type = UE_CONTROL,
127 .endpoint = 0x00, /* Control pipe */
128 .direction = UE_DIR_ANY,
129 .mh.bufsize = sizeof(struct usb2_device_request),
130 .mh.flags = {},
131 .mh.callback = &ugensa_bulk_write_clear_stall_callback,
132 .mh.timeout = 1000, /* 1 second */
133 .mh.interval = 50, /* 50ms */
134 },
135
136 [3] = {
137 .type = UE_CONTROL,
138 .endpoint = 0x00, /* Control pipe */
139 .direction = UE_DIR_ANY,
140 .mh.bufsize = sizeof(struct usb2_device_request),
141 .mh.flags = {},
142 .mh.callback = &ugensa_bulk_read_clear_stall_callback,
143 .mh.timeout = 1000, /* 1 second */
144 .mh.interval = 50, /* 50ms */
145 },
146};
147
148static const struct usb2_com_callback ugensa_callback = {
149 .usb2_com_start_read = &ugensa_start_read,
150 .usb2_com_stop_read = &ugensa_stop_read,
151 .usb2_com_start_write = &ugensa_start_write,
152 .usb2_com_stop_write = &ugensa_stop_write,
153};
154
155static device_method_t ugensa_methods[] = {
156 /* Device methods */
157 DEVMETHOD(device_probe, ugensa_probe),
158 DEVMETHOD(device_attach, ugensa_attach),
159 DEVMETHOD(device_detach, ugensa_detach),
160 {0, 0}
161};
162
163static devclass_t ugensa_devclass;
164
165static driver_t ugensa_driver = {
166 .name = "ugensa",
167 .methods = ugensa_methods,
168 .size = sizeof(struct ugensa_softc),
169};
170
171DRIVER_MODULE(ugensa, ushub, ugensa_driver, ugensa_devclass, NULL, 0);
172MODULE_DEPEND(ugensa, usb2_serial, 1, 1, 1);
173MODULE_DEPEND(ugensa, usb2_core, 1, 1, 1);
174
175static const struct usb2_device_id ugensa_devs[] = {
176 {USB_VPI(USB_VENDOR_AIRPRIME, USB_PRODUCT_AIRPRIME_PC5220, 0)},
177 {USB_VPI(USB_VENDOR_CMOTECH, USB_PRODUCT_CMOTECH_CDMA_MODEM1, 0)},
178 {USB_VPI(USB_VENDOR_KYOCERA2, USB_PRODUCT_KYOCERA2_CDMA_MSM_K, 0)},
179 {USB_VPI(USB_VENDOR_HP, USB_PRODUCT_HP_49GPLUS, 0)},
180/* {USB_VPI(USB_VENDOR_HUAWEI, USB_PRODUCT_HUAWEI_E270, 0)}, */
181 {USB_VPI(USB_VENDOR_HUAWEI, USB_PRODUCT_HUAWEI_MOBILE, 0)},
182 {USB_VPI(USB_VENDOR_MERLIN, USB_PRODUCT_MERLIN_V620, 0)},
183 {USB_VPI(USB_VENDOR_NOVATEL2, USB_PRODUCT_NOVATEL2_FLEXPACKGPS, 0)},
184};
185
186static int
187ugensa_probe(device_t dev)
188{
189 struct usb2_attach_arg *uaa = device_get_ivars(dev);
190
191 if (uaa->usb2_mode != USB_MODE_HOST) {
192 return (ENXIO);
193 }
194 if (uaa->info.bConfigIndex != UGENSA_CONFIG_INDEX) {
195 return (ENXIO);
196 }
197 if (uaa->info.bIfaceIndex != 0) {
198 return (ENXIO);
199 }
200 return (usb2_lookup_id_by_uaa(ugensa_devs, sizeof(ugensa_devs), uaa));
201}
202
203static int
204ugensa_attach(device_t dev)
205{
206 struct usb2_attach_arg *uaa = device_get_ivars(dev);
207 struct ugensa_softc *sc = device_get_softc(dev);
208 struct ugensa_sub_softc *ssc;
209 struct usb2_interface *iface;
210 int32_t error;
211 uint8_t iface_index;
212 int x, cnt;
213
214 if (sc == NULL)
215 return (ENOMEM);
216
217 device_set_usb2_desc(dev);
218 mtx_init(&sc->sc_mtx, "ugensa", NULL, MTX_DEF);
219
220 /* Figure out how many interfaces this device has got */
221 for (cnt = 0; cnt < UGENSA_IFACE_MAX; cnt++) {
222 if ((usb2_get_pipe(uaa->device, cnt, ugensa_xfer_config + 0) == NULL) ||
223 (usb2_get_pipe(uaa->device, cnt, ugensa_xfer_config + 1) == NULL)) {
224 /* we have reached the end */
225 break;
226 }
227 }
228
229 if (cnt == 0) {
230 device_printf(dev, "No interfaces!\n");
231 goto detach;
232 }
233 for (x = 0; x < cnt; x++) {
234 iface = usb2_get_iface(uaa->device, x);
235 if (iface->idesc->bInterfaceClass != UICLASS_VENDOR)
236 /* Not a serial port, most likely a SD reader */
237 continue;
238
239 ssc = sc->sc_sub + sc->sc_niface;
240 ssc->sc_usb2_com_ptr = sc->sc_ucom + sc->sc_niface;
241
242 iface_index = (UGENSA_IFACE_INDEX + x);
243 error = usb2_transfer_setup(uaa->device,
244 &iface_index, ssc->sc_xfer, ugensa_xfer_config,
245 UGENSA_N_TRANSFER, ssc, &sc->sc_mtx);
246
247 if (error) {
248 device_printf(dev, "allocating USB "
249 "transfers failed!\n");
250 goto detach;
251 }
252 /* clear stall at first run */
253 ssc->sc_flags |= (UGENSA_FLAG_BULK_WRITE_STALL |
254 UGENSA_FLAG_BULK_READ_STALL);
255
256 /* initialize port number */
257 ssc->sc_usb2_com_ptr->sc_portno = sc->sc_niface;
258 sc->sc_niface++;
259 if (x != uaa->info.bIfaceIndex)
260 usb2_set_parent_iface(uaa->device, x,
261 uaa->info.bIfaceIndex);
262 }
263 device_printf(dev, "Found %d interfaces.\n", sc->sc_niface);
264
265 error = usb2_com_attach(&sc->sc_super_ucom, sc->sc_ucom, sc->sc_niface, sc,
266 &ugensa_callback, &sc->sc_mtx);
267 if (error) {
268 DPRINTF("attach failed\n");
269 goto detach;
270 }
271 return (0); /* success */
272
273detach:
274 ugensa_detach(dev);
275 return (ENXIO); /* failure */
276}
277
278static int
279ugensa_detach(device_t dev)
280{
281 struct ugensa_softc *sc = device_get_softc(dev);
282 uint8_t x;
283
284 usb2_com_detach(&sc->sc_super_ucom, sc->sc_ucom, sc->sc_niface);
285
286 for (x = 0; x < sc->sc_niface; x++) {
287 usb2_transfer_unsetup(sc->sc_sub[x].sc_xfer, UGENSA_N_TRANSFER);
288 }
289 mtx_destroy(&sc->sc_mtx);
290
291 return (0);
292}
293
294static void
295ugensa_bulk_write_callback(struct usb2_xfer *xfer)
296{
297 struct ugensa_sub_softc *ssc = xfer->priv_sc;
298 uint32_t actlen;
299
300 switch (USB_GET_STATE(xfer)) {
301 case USB_ST_SETUP:
302 case USB_ST_TRANSFERRED:
303 if (ssc->sc_flags & UGENSA_FLAG_BULK_WRITE_STALL) {
304 usb2_transfer_start(ssc->sc_xfer[2]);
305 return;
306 }
307 if (usb2_com_get_data(ssc->sc_usb2_com_ptr, xfer->frbuffers, 0,
308 UGENSA_BUF_SIZE, &actlen)) {
309 xfer->frlengths[0] = actlen;
310 usb2_start_hardware(xfer);
311 }
312 return;
313
314 default: /* Error */
315 if (xfer->error != USB_ERR_CANCELLED) {
316 ssc->sc_flags |= UGENSA_FLAG_BULK_WRITE_STALL;
317 usb2_transfer_start(ssc->sc_xfer[2]);
318 }
319 return;
320
321 }
322}
323
324static void
325ugensa_bulk_write_clear_stall_callback(struct usb2_xfer *xfer)
326{
327 struct ugensa_sub_softc *ssc = xfer->priv_sc;
328 struct usb2_xfer *xfer_other = ssc->sc_xfer[0];
329
330 if (usb2_clear_stall_callback(xfer, xfer_other)) {
331 DPRINTF("stall cleared\n");
332 ssc->sc_flags &= ~UGENSA_FLAG_BULK_WRITE_STALL;
333 usb2_transfer_start(xfer_other);
334 }
335}
336
337static void
338ugensa_bulk_read_callback(struct usb2_xfer *xfer)
339{
340 struct ugensa_sub_softc *ssc = xfer->priv_sc;
341
342 switch (USB_GET_STATE(xfer)) {
343 case USB_ST_TRANSFERRED:
344 usb2_com_put_data(ssc->sc_usb2_com_ptr, xfer->frbuffers, 0,
345 xfer->actlen);
346
347 case USB_ST_SETUP:
348 if (ssc->sc_flags & UGENSA_FLAG_BULK_READ_STALL) {
349 usb2_transfer_start(ssc->sc_xfer[3]);
350 } else {
351 xfer->frlengths[0] = xfer->max_data_length;
352 usb2_start_hardware(xfer);
353 }
354 return;
355
356 default: /* Error */
357 if (xfer->error != USB_ERR_CANCELLED) {
358 ssc->sc_flags |= UGENSA_FLAG_BULK_READ_STALL;
359 usb2_transfer_start(ssc->sc_xfer[3]);
360 }
361 return;
362
363 }
364}
365
366static void
367ugensa_bulk_read_clear_stall_callback(struct usb2_xfer *xfer)
368{
369 struct ugensa_sub_softc *ssc = xfer->priv_sc;
370 struct usb2_xfer *xfer_other = ssc->sc_xfer[1];
371
372 if (usb2_clear_stall_callback(xfer, xfer_other)) {
373 DPRINTF("stall cleared\n");
374 ssc->sc_flags &= ~UGENSA_FLAG_BULK_READ_STALL;
375 usb2_transfer_start(xfer_other);
376 }
377}
378
379static void
380ugensa_start_read(struct usb2_com_softc *ucom)
381{
382 struct ugensa_softc *sc = ucom->sc_parent;
383 struct ugensa_sub_softc *ssc = sc->sc_sub + ucom->sc_portno;
384
385 usb2_transfer_start(ssc->sc_xfer[1]);
386}
387
388static void
389ugensa_stop_read(struct usb2_com_softc *ucom)
390{
391 struct ugensa_softc *sc = ucom->sc_parent;
392 struct ugensa_sub_softc *ssc = sc->sc_sub + ucom->sc_portno;
393
394 usb2_transfer_stop(ssc->sc_xfer[3]);
395 usb2_transfer_stop(ssc->sc_xfer[1]);
396}
397
398static void
399ugensa_start_write(struct usb2_com_softc *ucom)
400{
401 struct ugensa_softc *sc = ucom->sc_parent;
402 struct ugensa_sub_softc *ssc = sc->sc_sub + ucom->sc_portno;
403
404 usb2_transfer_start(ssc->sc_xfer[0]);
405}
406
407static void
408ugensa_stop_write(struct usb2_com_softc *ucom)
409{
410 struct ugensa_softc *sc = ucom->sc_parent;
411 struct ugensa_sub_softc *ssc = sc->sc_sub + ucom->sc_portno;
412
413 usb2_transfer_stop(ssc->sc_xfer[2]);
414 usb2_transfer_stop(ssc->sc_xfer[0]);
415}