Deleted Added
full compact
umodem.c (185950) umodem.c (187176)
1/* $NetBSD: umodem.c,v 1.45 2002/09/23 05:51:23 simonb Exp $ */
2
3#include <sys/cdefs.h>
1/* $NetBSD: umodem.c,v 1.45 2002/09/23 05:51:23 simonb Exp $ */
2
3#include <sys/cdefs.h>
4__FBSDID("$FreeBSD: head/sys/dev/usb2/serial/umodem2.c 185950 2008-12-11 23:17:48Z thompsa $");
4__FBSDID("$FreeBSD: head/sys/dev/usb2/serial/umodem2.c 187176 2009-01-13 19:03:47Z thompsa $");
5
6/*-
7 * Copyright (c) 2003, M. Warner Losh <imp@freebsd.org>.
8 * All rights reserved.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32/*-
33 * Copyright (c) 1998 The NetBSD Foundation, Inc.
34 * All rights reserved.
35 *
36 * This code is derived from software contributed to The NetBSD Foundation
37 * by Lennart Augustsson (lennart@augustsson.net) at
38 * Carlstedt Research & Technology.
39 *
40 * Redistribution and use in source and binary forms, with or without
41 * modification, are permitted provided that the following conditions
42 * are met:
43 * 1. Redistributions of source code must retain the above copyright
44 * notice, this list of conditions and the following disclaimer.
45 * 2. Redistributions in binary form must reproduce the above copyright
46 * notice, this list of conditions and the following disclaimer in the
47 * documentation and/or other materials provided with the distribution.
48 * 3. All advertising materials mentioning features or use of this software
49 * must display the following acknowledgement:
50 * This product includes software developed by the NetBSD
51 * Foundation, Inc. and its contributors.
52 * 4. Neither the name of The NetBSD Foundation nor the names of its
53 * contributors may be used to endorse or promote products derived
54 * from this software without specific prior written permission.
55 *
56 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
57 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
58 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
59 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
60 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
61 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
62 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
63 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
64 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
65 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
66 * POSSIBILITY OF SUCH DAMAGE.
67 */
68
69/*
70 * Comm Class spec: http://www.usb.org/developers/devclass_docs/usbccs10.pdf
71 * http://www.usb.org/developers/devclass_docs/usbcdc11.pdf
72 */
73
74/*
75 * TODO:
76 * - Add error recovery in various places; the big problem is what
77 * to do in a callback if there is an error.
78 * - Implement a Call Device for modems without multiplexed commands.
79 *
80 */
81
82#include <dev/usb2/include/usb2_devid.h>
83#include <dev/usb2/include/usb2_standard.h>
84#include <dev/usb2/include/usb2_mfunc.h>
85#include <dev/usb2/include/usb2_error.h>
86#include <dev/usb2/include/usb2_cdc.h>
87#include <dev/usb2/include/usb2_ioctl.h>
88#include <dev/usb2/include/usb2_defs.h>
89
90#define USB_DEBUG_VAR umodem_debug
91
92#include <dev/usb2/core/usb2_core.h>
93#include <dev/usb2/core/usb2_debug.h>
94#include <dev/usb2/core/usb2_process.h>
5
6/*-
7 * Copyright (c) 2003, M. Warner Losh <imp@freebsd.org>.
8 * All rights reserved.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32/*-
33 * Copyright (c) 1998 The NetBSD Foundation, Inc.
34 * All rights reserved.
35 *
36 * This code is derived from software contributed to The NetBSD Foundation
37 * by Lennart Augustsson (lennart@augustsson.net) at
38 * Carlstedt Research & Technology.
39 *
40 * Redistribution and use in source and binary forms, with or without
41 * modification, are permitted provided that the following conditions
42 * are met:
43 * 1. Redistributions of source code must retain the above copyright
44 * notice, this list of conditions and the following disclaimer.
45 * 2. Redistributions in binary form must reproduce the above copyright
46 * notice, this list of conditions and the following disclaimer in the
47 * documentation and/or other materials provided with the distribution.
48 * 3. All advertising materials mentioning features or use of this software
49 * must display the following acknowledgement:
50 * This product includes software developed by the NetBSD
51 * Foundation, Inc. and its contributors.
52 * 4. Neither the name of The NetBSD Foundation nor the names of its
53 * contributors may be used to endorse or promote products derived
54 * from this software without specific prior written permission.
55 *
56 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
57 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
58 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
59 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
60 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
61 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
62 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
63 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
64 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
65 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
66 * POSSIBILITY OF SUCH DAMAGE.
67 */
68
69/*
70 * Comm Class spec: http://www.usb.org/developers/devclass_docs/usbccs10.pdf
71 * http://www.usb.org/developers/devclass_docs/usbcdc11.pdf
72 */
73
74/*
75 * TODO:
76 * - Add error recovery in various places; the big problem is what
77 * to do in a callback if there is an error.
78 * - Implement a Call Device for modems without multiplexed commands.
79 *
80 */
81
82#include <dev/usb2/include/usb2_devid.h>
83#include <dev/usb2/include/usb2_standard.h>
84#include <dev/usb2/include/usb2_mfunc.h>
85#include <dev/usb2/include/usb2_error.h>
86#include <dev/usb2/include/usb2_cdc.h>
87#include <dev/usb2/include/usb2_ioctl.h>
88#include <dev/usb2/include/usb2_defs.h>
89
90#define USB_DEBUG_VAR umodem_debug
91
92#include <dev/usb2/core/usb2_core.h>
93#include <dev/usb2/core/usb2_debug.h>
94#include <dev/usb2/core/usb2_process.h>
95#include <dev/usb2/core/usb2_config_td.h>
96#include <dev/usb2/core/usb2_request.h>
97#include <dev/usb2/core/usb2_lookup.h>
98#include <dev/usb2/core/usb2_util.h>
99#include <dev/usb2/core/usb2_busdma.h>
100#include <dev/usb2/core/usb2_device.h>
101
102#include <dev/usb2/serial/usb2_serial.h>
103
104#if USB_DEBUG
105static int umodem_debug = 0;
106
107SYSCTL_NODE(_hw_usb2, OID_AUTO, umodem, CTLFLAG_RW, 0, "USB umodem");
108SYSCTL_INT(_hw_usb2_umodem, OID_AUTO, debug, CTLFLAG_RW,
109 &umodem_debug, 0, "Debug level");
110#endif
111
112static const struct usb2_device_id umodem_devs[] = {
113 /* Generic Modem class match */
114 {USB_IFACE_CLASS(UICLASS_CDC),
115 USB_IFACE_SUBCLASS(UISUBCLASS_ABSTRACT_CONTROL_MODEL),
116 USB_IFACE_PROTOCOL(UIPROTO_CDC_AT)},
117 /* Kyocera AH-K3001V */
118 {USB_VPI(USB_VENDOR_KYOCERA, USB_PRODUCT_KYOCERA_AHK3001V, 1)},
119 {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC5720, 1)},
120 {USB_VPI(USB_VENDOR_CURITEL, USB_PRODUCT_CURITEL_PC5740, 1)},
121};
122
123/*
124 * As speeds for umodem deivces increase, these numbers will need to
125 * be increased. They should be good for G3 speeds and below.
126 *
127 * TODO: The TTY buffers should be increased!
128 */
129#define UMODEM_BUF_SIZE 1024
130#define UMODEM_N_DATA_TRANSFER 4
131#define UMODEM_N_INTR_TRANSFER 2
132
133#define UMODEM_MODVER 1 /* module version */
134
135struct umodem_softc {
136 struct usb2_com_super_softc sc_super_ucom;
137 struct usb2_com_softc sc_ucom;
138
139 struct usb2_xfer *sc_xfer_data[UMODEM_N_DATA_TRANSFER];
140 struct usb2_xfer *sc_xfer_intr[UMODEM_N_INTR_TRANSFER];
141 struct usb2_device *sc_udev;
142
143 uint16_t sc_line;
144
145 uint8_t sc_lsr; /* local status register */
146 uint8_t sc_msr; /* modem status register */
147 uint8_t sc_ctrl_iface_no;
148 uint8_t sc_ctrl_iface_index;
149 uint8_t sc_data_iface_no;
150 uint8_t sc_data_iface_index;
151 uint8_t sc_cm_over_data;
152 uint8_t sc_cm_cap; /* CM capabilities */
153 uint8_t sc_acm_cap; /* ACM capabilities */
154 uint8_t sc_flag;
155#define UMODEM_FLAG_READ_STALL 0x01
156#define UMODEM_FLAG_WRITE_STALL 0x02
157#define UMODEM_FLAG_INTR_STALL 0x04
158};
159
160static device_probe_t umodem_probe;
161static device_attach_t umodem_attach;
162static device_detach_t umodem_detach;
163
164static usb2_callback_t umodem_intr_callback;
165static usb2_callback_t umodem_intr_clear_stall_callback;
166static usb2_callback_t umodem_write_callback;
167static usb2_callback_t umodem_read_callback;
168static usb2_callback_t umodem_write_clear_stall_callback;
169static usb2_callback_t umodem_read_clear_stall_callback;
170
171static void umodem_start_read(struct usb2_com_softc *);
172static void umodem_stop_read(struct usb2_com_softc *);
173static void umodem_start_write(struct usb2_com_softc *);
174static void umodem_stop_write(struct usb2_com_softc *);
175static void umodem_get_caps(struct usb2_attach_arg *, uint8_t *, uint8_t *);
176static void umodem_cfg_get_status(struct usb2_com_softc *, uint8_t *,
177 uint8_t *);
178static int umodem_pre_param(struct usb2_com_softc *, struct termios *);
179static void umodem_cfg_param(struct usb2_com_softc *, struct termios *);
180static int umodem_ioctl(struct usb2_com_softc *, uint32_t, caddr_t, int,
181 struct thread *);
182static void umodem_cfg_set_dtr(struct usb2_com_softc *, uint8_t);
183static void umodem_cfg_set_rts(struct usb2_com_softc *, uint8_t);
184static void umodem_cfg_set_break(struct usb2_com_softc *, uint8_t);
185static void *umodem_get_desc(struct usb2_attach_arg *, uint8_t, uint8_t);
186static usb2_error_t umodem_set_comm_feature(struct usb2_device *, uint8_t,
187 uint16_t, uint16_t);
188static void umodem_cfg_do_request(struct umodem_softc *,
189 struct usb2_device_request *, void *);
190
191static const struct usb2_config umodem_config_data[UMODEM_N_DATA_TRANSFER] = {
192
193 [0] = {
194 .type = UE_BULK,
195 .endpoint = UE_ADDR_ANY,
196 .direction = UE_DIR_OUT,
197 .mh.bufsize = UMODEM_BUF_SIZE,
198 .mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,},
199 .mh.callback = &umodem_write_callback,
200 },
201
202 [1] = {
203 .type = UE_BULK,
204 .endpoint = UE_ADDR_ANY,
205 .direction = UE_DIR_IN,
206 .mh.bufsize = UMODEM_BUF_SIZE,
207 .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
208 .mh.callback = &umodem_read_callback,
209 },
210
211 [2] = {
212 .type = UE_CONTROL,
213 .endpoint = 0x00, /* Control pipe */
214 .direction = UE_DIR_ANY,
215 .mh.bufsize = sizeof(struct usb2_device_request),
216 .mh.callback = &umodem_write_clear_stall_callback,
217 .mh.timeout = 1000, /* 1 second */
218 .mh.interval = 50, /* 50ms */
219 },
220
221 [3] = {
222 .type = UE_CONTROL,
223 .endpoint = 0x00, /* Control pipe */
224 .direction = UE_DIR_ANY,
225 .mh.bufsize = sizeof(struct usb2_device_request),
226 .mh.callback = &umodem_read_clear_stall_callback,
227 .mh.timeout = 1000, /* 1 second */
228 .mh.interval = 50, /* 50ms */
229 },
230};
231
232static const struct usb2_config umodem_config_intr[UMODEM_N_INTR_TRANSFER] = {
233 [0] = {
234 .type = UE_INTERRUPT,
235 .endpoint = UE_ADDR_ANY,
236 .direction = UE_DIR_IN,
237 .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
238 .mh.bufsize = 0, /* use wMaxPacketSize */
239 .mh.callback = &umodem_intr_callback,
240 },
241
242 [1] = {
243 .type = UE_CONTROL,
244 .endpoint = 0x00, /* Control pipe */
245 .direction = UE_DIR_ANY,
246 .mh.bufsize = sizeof(struct usb2_device_request),
247 .mh.callback = &umodem_intr_clear_stall_callback,
248 .mh.timeout = 1000, /* 1 second */
249 .mh.interval = 50, /* 50ms */
250 },
251};
252
253static const struct usb2_com_callback umodem_callback = {
254 .usb2_com_cfg_get_status = &umodem_cfg_get_status,
255 .usb2_com_cfg_set_dtr = &umodem_cfg_set_dtr,
256 .usb2_com_cfg_set_rts = &umodem_cfg_set_rts,
257 .usb2_com_cfg_set_break = &umodem_cfg_set_break,
258 .usb2_com_cfg_param = &umodem_cfg_param,
259 .usb2_com_pre_param = &umodem_pre_param,
260 .usb2_com_ioctl = &umodem_ioctl,
261 .usb2_com_start_read = &umodem_start_read,
262 .usb2_com_stop_read = &umodem_stop_read,
263 .usb2_com_start_write = &umodem_start_write,
264 .usb2_com_stop_write = &umodem_stop_write,
265};
266
267static device_method_t umodem_methods[] = {
268 DEVMETHOD(device_probe, umodem_probe),
269 DEVMETHOD(device_attach, umodem_attach),
270 DEVMETHOD(device_detach, umodem_detach),
271 {0, 0}
272};
273
274static devclass_t umodem_devclass;
275
276static driver_t umodem_driver = {
277 .name = "umodem",
278 .methods = umodem_methods,
279 .size = sizeof(struct umodem_softc),
280};
281
282DRIVER_MODULE(umodem, ushub, umodem_driver, umodem_devclass, NULL, 0);
283MODULE_DEPEND(umodem, usb2_serial, 1, 1, 1);
284MODULE_DEPEND(umodem, usb2_core, 1, 1, 1);
285MODULE_VERSION(umodem, UMODEM_MODVER);
286
287static int
288umodem_probe(device_t dev)
289{
290 struct usb2_attach_arg *uaa = device_get_ivars(dev);
291 uint8_t cm;
292 uint8_t acm;
293 int error;
294
295 DPRINTFN(11, "\n");
296
297 if (uaa->usb2_mode != USB_MODE_HOST) {
298 return (ENXIO);
299 }
300 error = usb2_lookup_id_by_uaa(umodem_devs, sizeof(umodem_devs), uaa);
301 if (error) {
302 return (error);
303 }
304 if (uaa->driver_info == NULL) {
305 /* some modems do not have any capabilities */
306 return (error);
307 }
308 umodem_get_caps(uaa, &cm, &acm);
309 if (!(cm & USB_CDC_CM_DOES_CM) ||
310 !(cm & USB_CDC_CM_OVER_DATA) ||
311 !(acm & USB_CDC_ACM_HAS_LINE)) {
312 error = ENXIO;
313 }
314 return (error);
315}
316
317static int
318umodem_attach(device_t dev)
319{
320 struct usb2_attach_arg *uaa = device_get_ivars(dev);
321 struct umodem_softc *sc = device_get_softc(dev);
322 struct usb2_cdc_cm_descriptor *cmd;
323 uint8_t i;
324 int error;
325
326 if (sc == NULL) {
327 return (ENOMEM);
328 }
329 device_set_usb2_desc(dev);
330
331 sc->sc_ctrl_iface_no = uaa->info.bIfaceNum;
332 sc->sc_ctrl_iface_index = uaa->info.bIfaceIndex;
333 sc->sc_udev = uaa->device;
334
335 umodem_get_caps(uaa, &sc->sc_cm_cap, &sc->sc_acm_cap);
336
337 /* get the data interface number */
338
339 cmd = umodem_get_desc(uaa, UDESC_CS_INTERFACE, UDESCSUB_CDC_CM);
340
341 if ((cmd == NULL) || (cmd->bLength < sizeof(*cmd))) {
342 device_printf(dev, "no CM descriptor!\n");
343 goto detach;
344 }
345 sc->sc_data_iface_no = cmd->bDataInterface;
346
347 device_printf(dev, "data interface %d, has %sCM over "
348 "data, has %sbreak\n",
349 sc->sc_data_iface_no,
350 sc->sc_cm_cap & USB_CDC_CM_OVER_DATA ? "" : "no ",
351 sc->sc_acm_cap & USB_CDC_ACM_HAS_BREAK ? "" : "no ");
352
353 /* get the data interface too */
354
355 for (i = 0;; i++) {
356 struct usb2_interface *iface;
357 struct usb2_interface_descriptor *id;
358
359 iface = usb2_get_iface(uaa->device, i);
360
361 if (iface) {
362
363 id = usb2_get_interface_descriptor(iface);
364
365 if (id && (id->bInterfaceNumber == sc->sc_data_iface_no)) {
366 sc->sc_data_iface_index = i;
367 usb2_set_parent_iface(uaa->device, i, uaa->info.bIfaceIndex);
368 break;
369 }
370 } else {
371 device_printf(dev, "no data interface!\n");
372 goto detach;
373 }
374 }
375
376 if (sc->sc_cm_cap & USB_CDC_CM_OVER_DATA) {
377 if (sc->sc_acm_cap & USB_CDC_ACM_HAS_FEATURE) {
378
379 error = umodem_set_comm_feature
380 (uaa->device, sc->sc_ctrl_iface_no,
381 UCDC_ABSTRACT_STATE, UCDC_DATA_MULTIPLEXED);
382
383 /* ignore any errors */
384 }
385 sc->sc_cm_over_data = 1;
386 }
387 error = usb2_transfer_setup(uaa->device,
388 &sc->sc_data_iface_index, sc->sc_xfer_data,
389 umodem_config_data, UMODEM_N_DATA_TRANSFER,
390 sc, &Giant);
391 if (error) {
392 goto detach;
393 }
394 error = usb2_transfer_setup(uaa->device,
395 &sc->sc_ctrl_iface_index, sc->sc_xfer_intr,
396 umodem_config_intr, UMODEM_N_INTR_TRANSFER,
397 sc, &Giant);
398
399 if (error == 0) {
400 device_printf(dev, "status change "
401 "notification available\n");
402 }
403 /* clear stall at first run */
404 sc->sc_flag |= (UMODEM_FLAG_READ_STALL |
405 UMODEM_FLAG_WRITE_STALL);
406
407 error = usb2_com_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc,
408 &umodem_callback, &Giant);
409 if (error) {
410 goto detach;
411 }
412 return (0);
413
414detach:
415 umodem_detach(dev);
416 return (ENXIO);
417}
418
419static void
420umodem_start_read(struct usb2_com_softc *ucom)
421{
422 struct umodem_softc *sc = ucom->sc_parent;
423
424 if (sc->sc_xfer_intr[0]) {
425 /* start interrupt endpoint */
426 usb2_transfer_start(sc->sc_xfer_intr[0]);
427 }
428 /* start read endpoint */
429 usb2_transfer_start(sc->sc_xfer_data[1]);
430}
431
432static void
433umodem_stop_read(struct usb2_com_softc *ucom)
434{
435 struct umodem_softc *sc = ucom->sc_parent;
436
437 if (sc->sc_xfer_intr[0]) {
438 /* stop interrupt endpoint */
439 usb2_transfer_stop(sc->sc_xfer_intr[0]);
440 }
441 /* stop read endpoint */
442 usb2_transfer_stop(sc->sc_xfer_data[3]);
443 usb2_transfer_stop(sc->sc_xfer_data[1]);
444}
445
446static void
447umodem_start_write(struct usb2_com_softc *ucom)
448{
449 struct umodem_softc *sc = ucom->sc_parent;
450
451 usb2_transfer_start(sc->sc_xfer_data[0]);
452}
453
454static void
455umodem_stop_write(struct usb2_com_softc *ucom)
456{
457 struct umodem_softc *sc = ucom->sc_parent;
458
459 usb2_transfer_stop(sc->sc_xfer_data[2]);
460 usb2_transfer_stop(sc->sc_xfer_data[0]);
461}
462
463static void
464umodem_get_caps(struct usb2_attach_arg *uaa, uint8_t *cm, uint8_t *acm)
465{
466 struct usb2_cdc_cm_descriptor *cmd;
467 struct usb2_cdc_acm_descriptor *cad;
468
469 *cm = *acm = 0;
470
471 cmd = umodem_get_desc(uaa, UDESC_CS_INTERFACE, UDESCSUB_CDC_CM);
472 if ((cmd == NULL) || (cmd->bLength < sizeof(*cmd))) {
473 DPRINTF("no CM desc\n");
474 return;
475 }
476 *cm = cmd->bmCapabilities;
477
478 cad = umodem_get_desc(uaa, UDESC_CS_INTERFACE, UDESCSUB_CDC_ACM);
479 if ((cad == NULL) || (cad->bLength < sizeof(*cad))) {
480 DPRINTF("no ACM desc\n");
481 return;
482 }
483 *acm = cad->bmCapabilities;
484}
485
486static void
487umodem_cfg_get_status(struct usb2_com_softc *ucom, uint8_t *lsr, uint8_t *msr)
488{
489 struct umodem_softc *sc = ucom->sc_parent;
490
491 DPRINTF("\n");
492
493 *lsr = sc->sc_lsr;
494 *msr = sc->sc_msr;
495}
496
497static int
498umodem_pre_param(struct usb2_com_softc *ucom, struct termios *t)
499{
500 return (0); /* we accept anything */
501}
502
503static void
504umodem_cfg_param(struct usb2_com_softc *ucom, struct termios *t)
505{
506 struct umodem_softc *sc = ucom->sc_parent;
507 struct usb2_cdc_line_state ls;
508 struct usb2_device_request req;
509
510 DPRINTF("sc=%p\n", sc);
511
512 bzero(&ls, sizeof(ls));
513
514 USETDW(ls.dwDTERate, t->c_ospeed);
515
516 ls.bCharFormat = (t->c_cflag & CSTOPB) ?
517 UCDC_STOP_BIT_2 : UCDC_STOP_BIT_1;
518
519 ls.bParityType = (t->c_cflag & PARENB) ?
520 ((t->c_cflag & PARODD) ?
521 UCDC_PARITY_ODD : UCDC_PARITY_EVEN) : UCDC_PARITY_NONE;
522
523 switch (t->c_cflag & CSIZE) {
524 case CS5:
525 ls.bDataBits = 5;
526 break;
527 case CS6:
528 ls.bDataBits = 6;
529 break;
530 case CS7:
531 ls.bDataBits = 7;
532 break;
533 case CS8:
534 ls.bDataBits = 8;
535 break;
536 }
537
538 DPRINTF("rate=%d fmt=%d parity=%d bits=%d\n",
539 UGETDW(ls.dwDTERate), ls.bCharFormat,
540 ls.bParityType, ls.bDataBits);
541
542 req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
543 req.bRequest = UCDC_SET_LINE_CODING;
544 USETW(req.wValue, 0);
545 req.wIndex[0] = sc->sc_ctrl_iface_no;
546 req.wIndex[1] = 0;
547 USETW(req.wLength, sizeof(ls));
548
549 umodem_cfg_do_request(sc, &req, &ls);
550}
551
552static int
553umodem_ioctl(struct usb2_com_softc *ucom, uint32_t cmd, caddr_t data,
554 int flag, struct thread *td)
555{
556 struct umodem_softc *sc = ucom->sc_parent;
557 int error = 0;
558
559 DPRINTF("cmd=0x%08x\n", cmd);
560
561 switch (cmd) {
562 case USB_GET_CM_OVER_DATA:
563 *(int *)data = sc->sc_cm_over_data;
564 break;
565
566 case USB_SET_CM_OVER_DATA:
567 if (*(int *)data != sc->sc_cm_over_data) {
568 /* XXX change it */
569 }
570 break;
571
572 default:
573 DPRINTF("unknown\n");
574 error = ENOIOCTL;
575 break;
576 }
577
578 return (error);
579}
580
581static void
582umodem_cfg_set_dtr(struct usb2_com_softc *ucom, uint8_t onoff)
583{
584 struct umodem_softc *sc = ucom->sc_parent;
585 struct usb2_device_request req;
586
587 DPRINTF("onoff=%d\n", onoff);
588
589 if (onoff)
590 sc->sc_line |= UCDC_LINE_DTR;
591 else
592 sc->sc_line &= ~UCDC_LINE_DTR;
593
594 req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
595 req.bRequest = UCDC_SET_CONTROL_LINE_STATE;
596 USETW(req.wValue, sc->sc_line);
597 req.wIndex[0] = sc->sc_ctrl_iface_no;
598 req.wIndex[1] = 0;
599 USETW(req.wLength, 0);
600
601 umodem_cfg_do_request(sc, &req, NULL);
602}
603
604static void
605umodem_cfg_set_rts(struct usb2_com_softc *ucom, uint8_t onoff)
606{
607 struct umodem_softc *sc = ucom->sc_parent;
608 struct usb2_device_request req;
609
610 DPRINTF("onoff=%d\n", onoff);
611
612 if (onoff)
613 sc->sc_line |= UCDC_LINE_RTS;
614 else
615 sc->sc_line &= ~UCDC_LINE_RTS;
616
617 req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
618 req.bRequest = UCDC_SET_CONTROL_LINE_STATE;
619 USETW(req.wValue, sc->sc_line);
620 req.wIndex[0] = sc->sc_ctrl_iface_no;
621 req.wIndex[1] = 0;
622 USETW(req.wLength, 0);
623
624 umodem_cfg_do_request(sc, &req, NULL);
625}
626
627static void
628umodem_cfg_set_break(struct usb2_com_softc *ucom, uint8_t onoff)
629{
630 struct umodem_softc *sc = ucom->sc_parent;
631 struct usb2_device_request req;
632 uint16_t temp;
633
634 DPRINTF("onoff=%d\n", onoff);
635
636 if (sc->sc_acm_cap & USB_CDC_ACM_HAS_BREAK) {
637
638 temp = onoff ? UCDC_BREAK_ON : UCDC_BREAK_OFF;
639
640 req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
641 req.bRequest = UCDC_SEND_BREAK;
642 USETW(req.wValue, temp);
643 req.wIndex[0] = sc->sc_ctrl_iface_no;
644 req.wIndex[1] = 0;
645 USETW(req.wLength, 0);
646
647 umodem_cfg_do_request(sc, &req, NULL);
648 }
649}
650
651static void
652umodem_intr_callback(struct usb2_xfer *xfer)
653{
654 struct usb2_cdc_notification pkt;
655 struct umodem_softc *sc = xfer->priv_sc;
656 uint16_t wLen;
657
658 switch (USB_GET_STATE(xfer)) {
659 case USB_ST_TRANSFERRED:
660
661 if (xfer->actlen < 8) {
662 DPRINTF("received short packet, "
663 "%d bytes\n", xfer->actlen);
664 goto tr_setup;
665 }
666 if (xfer->actlen > sizeof(pkt)) {
667 DPRINTF("truncating message\n");
668 xfer->actlen = sizeof(pkt);
669 }
670 usb2_copy_out(xfer->frbuffers, 0, &pkt, xfer->actlen);
671
672 xfer->actlen -= 8;
673
674 wLen = UGETW(pkt.wLength);
675 if (xfer->actlen > wLen) {
676 xfer->actlen = wLen;
677 }
678 if (pkt.bmRequestType != UCDC_NOTIFICATION) {
679 DPRINTF("unknown message type, "
680 "0x%02x, on notify pipe!\n",
681 pkt.bmRequestType);
682 goto tr_setup;
683 }
684 switch (pkt.bNotification) {
685 case UCDC_N_SERIAL_STATE:
686 /*
687 * Set the serial state in ucom driver based on
688 * the bits from the notify message
689 */
690 if (xfer->actlen < 2) {
691 DPRINTF("invalid notification "
692 "length, %d bytes!\n", xfer->actlen);
693 break;
694 }
695 DPRINTF("notify bytes = %02x%02x\n",
696 pkt.data[0],
697 pkt.data[1]);
698
699 /* Currently, lsr is always zero. */
700 sc->sc_lsr = 0;
701 sc->sc_msr = 0;
702
703 if (pkt.data[0] & UCDC_N_SERIAL_RI) {
704 sc->sc_msr |= SER_RI;
705 }
706 if (pkt.data[0] & UCDC_N_SERIAL_DSR) {
707 sc->sc_msr |= SER_DSR;
708 }
709 if (pkt.data[0] & UCDC_N_SERIAL_DCD) {
710 sc->sc_msr |= SER_DCD;
711 }
712 usb2_com_status_change(&sc->sc_ucom);
713 break;
714
715 default:
716 DPRINTF("unknown notify message: 0x%02x\n",
717 pkt.bNotification);
718 break;
719 }
720
721 case USB_ST_SETUP:
722tr_setup:
723 if (sc->sc_flag & UMODEM_FLAG_INTR_STALL) {
724 usb2_transfer_start(sc->sc_xfer_intr[1]);
725 } else {
726 xfer->frlengths[0] = xfer->max_data_length;
727 usb2_start_hardware(xfer);
728 }
729 return;
730
731 default: /* Error */
732 if (xfer->error != USB_ERR_CANCELLED) {
733 sc->sc_flag |= UMODEM_FLAG_INTR_STALL;
734 usb2_transfer_start(sc->sc_xfer_intr[1]);
735 }
736 return;
737
738 }
739}
740
741static void
742umodem_intr_clear_stall_callback(struct usb2_xfer *xfer)
743{
744 struct umodem_softc *sc = xfer->priv_sc;
745 struct usb2_xfer *xfer_other = sc->sc_xfer_intr[0];
746
747 if (usb2_clear_stall_callback(xfer, xfer_other)) {
748 DPRINTF("stall cleared\n");
749 sc->sc_flag &= ~UMODEM_FLAG_INTR_STALL;
750 usb2_transfer_start(xfer_other);
751 }
752}
753
754static void
755umodem_write_callback(struct usb2_xfer *xfer)
756{
757 struct umodem_softc *sc = xfer->priv_sc;
758 uint32_t actlen;
759
760 switch (USB_GET_STATE(xfer)) {
761 case USB_ST_SETUP:
762 case USB_ST_TRANSFERRED:
763 if (sc->sc_flag & UMODEM_FLAG_WRITE_STALL) {
764 usb2_transfer_start(sc->sc_xfer_data[2]);
765 return;
766 }
767 if (usb2_com_get_data(&sc->sc_ucom, xfer->frbuffers, 0,
768 UMODEM_BUF_SIZE, &actlen)) {
769
770 xfer->frlengths[0] = actlen;
771 usb2_start_hardware(xfer);
772 }
773 return;
774
775 default: /* Error */
776 if (xfer->error != USB_ERR_CANCELLED) {
777 sc->sc_flag |= UMODEM_FLAG_WRITE_STALL;
778 usb2_transfer_start(sc->sc_xfer_data[2]);
779 }
780 return;
781
782 }
783}
784
785static void
786umodem_write_clear_stall_callback(struct usb2_xfer *xfer)
787{
788 struct umodem_softc *sc = xfer->priv_sc;
789 struct usb2_xfer *xfer_other = sc->sc_xfer_data[0];
790
791 if (usb2_clear_stall_callback(xfer, xfer_other)) {
792 DPRINTF("stall cleared\n");
793 sc->sc_flag &= ~UMODEM_FLAG_WRITE_STALL;
794 usb2_transfer_start(xfer_other);
795 }
796}
797
798static void
799umodem_read_callback(struct usb2_xfer *xfer)
800{
801 struct umodem_softc *sc = xfer->priv_sc;
802
803 switch (USB_GET_STATE(xfer)) {
804 case USB_ST_TRANSFERRED:
805
806 DPRINTF("actlen=%d\n", xfer->actlen);
807
808 usb2_com_put_data(&sc->sc_ucom, xfer->frbuffers, 0,
809 xfer->actlen);
810
811 case USB_ST_SETUP:
812 if (sc->sc_flag & UMODEM_FLAG_READ_STALL) {
813 usb2_transfer_start(sc->sc_xfer_data[3]);
814 } else {
815 xfer->frlengths[0] = xfer->max_data_length;
816 usb2_start_hardware(xfer);
817 }
818 return;
819
820 default: /* Error */
821 if (xfer->error != USB_ERR_CANCELLED) {
822 sc->sc_flag |= UMODEM_FLAG_READ_STALL;
823 usb2_transfer_start(sc->sc_xfer_data[3]);
824 }
825 return;
826
827 }
828}
829
830static void
831umodem_read_clear_stall_callback(struct usb2_xfer *xfer)
832{
833 struct umodem_softc *sc = xfer->priv_sc;
834 struct usb2_xfer *xfer_other = sc->sc_xfer_data[1];
835
836 if (usb2_clear_stall_callback(xfer, xfer_other)) {
837 DPRINTF("stall cleared\n");
838 sc->sc_flag &= ~UMODEM_FLAG_READ_STALL;
839 usb2_transfer_start(xfer_other);
840 }
841}
842
843static void *
844umodem_get_desc(struct usb2_attach_arg *uaa, uint8_t type, uint8_t subtype)
845{
846 return (usb2_find_descriptor(uaa->device, NULL, uaa->info.bIfaceIndex,
847 type, 0 - 1, subtype, 0 - 1));
848}
849
850static usb2_error_t
851umodem_set_comm_feature(struct usb2_device *udev, uint8_t iface_no,
852 uint16_t feature, uint16_t state)
853{
854 struct usb2_device_request req;
855 struct usb2_cdc_abstract_state ast;
856
857 DPRINTF("feature=%d state=%d\n",
858 feature, state);
859
860 req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
861 req.bRequest = UCDC_SET_COMM_FEATURE;
862 USETW(req.wValue, feature);
863 req.wIndex[0] = iface_no;
864 req.wIndex[1] = 0;
865 USETW(req.wLength, UCDC_ABSTRACT_STATE_LENGTH);
866 USETW(ast.wState, state);
867
868 return (usb2_do_request(udev, &Giant, &req, &ast));
869}
870
871static int
872umodem_detach(device_t dev)
873{
874 struct umodem_softc *sc = device_get_softc(dev);
875
876 DPRINTF("sc=%p\n", sc);
877
878 usb2_com_detach(&sc->sc_super_ucom, &sc->sc_ucom, 1);
879
880 usb2_transfer_unsetup(sc->sc_xfer_intr, UMODEM_N_INTR_TRANSFER);
881
882 usb2_transfer_unsetup(sc->sc_xfer_data, UMODEM_N_DATA_TRANSFER);
883
884 return (0);
885}
886
887static void
888umodem_cfg_do_request(struct umodem_softc *sc, struct usb2_device_request *req,
889 void *data)
890{
891 uint16_t length;
892 usb2_error_t err;
893
894 if (usb2_com_cfg_is_gone(&sc->sc_ucom)) {
895 goto error;
896 }
897 err = usb2_do_request_flags(sc->sc_udev, &Giant, req,
898 data, 0, NULL, 1000);
899
900 if (err) {
901
902 DPRINTFN(0, "device request failed, err=%s "
903 "(ignored)\n", usb2_errstr(err));
904
905error:
906 length = UGETW(req->wLength);
907
908 if ((req->bmRequestType & UT_READ) && length) {
909 bzero(data, length);
910 }
911 }
912}
95#include <dev/usb2/core/usb2_request.h>
96#include <dev/usb2/core/usb2_lookup.h>
97#include <dev/usb2/core/usb2_util.h>
98#include <dev/usb2/core/usb2_busdma.h>
99#include <dev/usb2/core/usb2_device.h>
100
101#include <dev/usb2/serial/usb2_serial.h>
102
103#if USB_DEBUG
104static int umodem_debug = 0;
105
106SYSCTL_NODE(_hw_usb2, OID_AUTO, umodem, CTLFLAG_RW, 0, "USB umodem");
107SYSCTL_INT(_hw_usb2_umodem, OID_AUTO, debug, CTLFLAG_RW,
108 &umodem_debug, 0, "Debug level");
109#endif
110
111static const struct usb2_device_id umodem_devs[] = {
112 /* Generic Modem class match */
113 {USB_IFACE_CLASS(UICLASS_CDC),
114 USB_IFACE_SUBCLASS(UISUBCLASS_ABSTRACT_CONTROL_MODEL),
115 USB_IFACE_PROTOCOL(UIPROTO_CDC_AT)},
116 /* Kyocera AH-K3001V */
117 {USB_VPI(USB_VENDOR_KYOCERA, USB_PRODUCT_KYOCERA_AHK3001V, 1)},
118 {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC5720, 1)},
119 {USB_VPI(USB_VENDOR_CURITEL, USB_PRODUCT_CURITEL_PC5740, 1)},
120};
121
122/*
123 * As speeds for umodem deivces increase, these numbers will need to
124 * be increased. They should be good for G3 speeds and below.
125 *
126 * TODO: The TTY buffers should be increased!
127 */
128#define UMODEM_BUF_SIZE 1024
129#define UMODEM_N_DATA_TRANSFER 4
130#define UMODEM_N_INTR_TRANSFER 2
131
132#define UMODEM_MODVER 1 /* module version */
133
134struct umodem_softc {
135 struct usb2_com_super_softc sc_super_ucom;
136 struct usb2_com_softc sc_ucom;
137
138 struct usb2_xfer *sc_xfer_data[UMODEM_N_DATA_TRANSFER];
139 struct usb2_xfer *sc_xfer_intr[UMODEM_N_INTR_TRANSFER];
140 struct usb2_device *sc_udev;
141
142 uint16_t sc_line;
143
144 uint8_t sc_lsr; /* local status register */
145 uint8_t sc_msr; /* modem status register */
146 uint8_t sc_ctrl_iface_no;
147 uint8_t sc_ctrl_iface_index;
148 uint8_t sc_data_iface_no;
149 uint8_t sc_data_iface_index;
150 uint8_t sc_cm_over_data;
151 uint8_t sc_cm_cap; /* CM capabilities */
152 uint8_t sc_acm_cap; /* ACM capabilities */
153 uint8_t sc_flag;
154#define UMODEM_FLAG_READ_STALL 0x01
155#define UMODEM_FLAG_WRITE_STALL 0x02
156#define UMODEM_FLAG_INTR_STALL 0x04
157};
158
159static device_probe_t umodem_probe;
160static device_attach_t umodem_attach;
161static device_detach_t umodem_detach;
162
163static usb2_callback_t umodem_intr_callback;
164static usb2_callback_t umodem_intr_clear_stall_callback;
165static usb2_callback_t umodem_write_callback;
166static usb2_callback_t umodem_read_callback;
167static usb2_callback_t umodem_write_clear_stall_callback;
168static usb2_callback_t umodem_read_clear_stall_callback;
169
170static void umodem_start_read(struct usb2_com_softc *);
171static void umodem_stop_read(struct usb2_com_softc *);
172static void umodem_start_write(struct usb2_com_softc *);
173static void umodem_stop_write(struct usb2_com_softc *);
174static void umodem_get_caps(struct usb2_attach_arg *, uint8_t *, uint8_t *);
175static void umodem_cfg_get_status(struct usb2_com_softc *, uint8_t *,
176 uint8_t *);
177static int umodem_pre_param(struct usb2_com_softc *, struct termios *);
178static void umodem_cfg_param(struct usb2_com_softc *, struct termios *);
179static int umodem_ioctl(struct usb2_com_softc *, uint32_t, caddr_t, int,
180 struct thread *);
181static void umodem_cfg_set_dtr(struct usb2_com_softc *, uint8_t);
182static void umodem_cfg_set_rts(struct usb2_com_softc *, uint8_t);
183static void umodem_cfg_set_break(struct usb2_com_softc *, uint8_t);
184static void *umodem_get_desc(struct usb2_attach_arg *, uint8_t, uint8_t);
185static usb2_error_t umodem_set_comm_feature(struct usb2_device *, uint8_t,
186 uint16_t, uint16_t);
187static void umodem_cfg_do_request(struct umodem_softc *,
188 struct usb2_device_request *, void *);
189
190static const struct usb2_config umodem_config_data[UMODEM_N_DATA_TRANSFER] = {
191
192 [0] = {
193 .type = UE_BULK,
194 .endpoint = UE_ADDR_ANY,
195 .direction = UE_DIR_OUT,
196 .mh.bufsize = UMODEM_BUF_SIZE,
197 .mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,},
198 .mh.callback = &umodem_write_callback,
199 },
200
201 [1] = {
202 .type = UE_BULK,
203 .endpoint = UE_ADDR_ANY,
204 .direction = UE_DIR_IN,
205 .mh.bufsize = UMODEM_BUF_SIZE,
206 .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
207 .mh.callback = &umodem_read_callback,
208 },
209
210 [2] = {
211 .type = UE_CONTROL,
212 .endpoint = 0x00, /* Control pipe */
213 .direction = UE_DIR_ANY,
214 .mh.bufsize = sizeof(struct usb2_device_request),
215 .mh.callback = &umodem_write_clear_stall_callback,
216 .mh.timeout = 1000, /* 1 second */
217 .mh.interval = 50, /* 50ms */
218 },
219
220 [3] = {
221 .type = UE_CONTROL,
222 .endpoint = 0x00, /* Control pipe */
223 .direction = UE_DIR_ANY,
224 .mh.bufsize = sizeof(struct usb2_device_request),
225 .mh.callback = &umodem_read_clear_stall_callback,
226 .mh.timeout = 1000, /* 1 second */
227 .mh.interval = 50, /* 50ms */
228 },
229};
230
231static const struct usb2_config umodem_config_intr[UMODEM_N_INTR_TRANSFER] = {
232 [0] = {
233 .type = UE_INTERRUPT,
234 .endpoint = UE_ADDR_ANY,
235 .direction = UE_DIR_IN,
236 .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
237 .mh.bufsize = 0, /* use wMaxPacketSize */
238 .mh.callback = &umodem_intr_callback,
239 },
240
241 [1] = {
242 .type = UE_CONTROL,
243 .endpoint = 0x00, /* Control pipe */
244 .direction = UE_DIR_ANY,
245 .mh.bufsize = sizeof(struct usb2_device_request),
246 .mh.callback = &umodem_intr_clear_stall_callback,
247 .mh.timeout = 1000, /* 1 second */
248 .mh.interval = 50, /* 50ms */
249 },
250};
251
252static const struct usb2_com_callback umodem_callback = {
253 .usb2_com_cfg_get_status = &umodem_cfg_get_status,
254 .usb2_com_cfg_set_dtr = &umodem_cfg_set_dtr,
255 .usb2_com_cfg_set_rts = &umodem_cfg_set_rts,
256 .usb2_com_cfg_set_break = &umodem_cfg_set_break,
257 .usb2_com_cfg_param = &umodem_cfg_param,
258 .usb2_com_pre_param = &umodem_pre_param,
259 .usb2_com_ioctl = &umodem_ioctl,
260 .usb2_com_start_read = &umodem_start_read,
261 .usb2_com_stop_read = &umodem_stop_read,
262 .usb2_com_start_write = &umodem_start_write,
263 .usb2_com_stop_write = &umodem_stop_write,
264};
265
266static device_method_t umodem_methods[] = {
267 DEVMETHOD(device_probe, umodem_probe),
268 DEVMETHOD(device_attach, umodem_attach),
269 DEVMETHOD(device_detach, umodem_detach),
270 {0, 0}
271};
272
273static devclass_t umodem_devclass;
274
275static driver_t umodem_driver = {
276 .name = "umodem",
277 .methods = umodem_methods,
278 .size = sizeof(struct umodem_softc),
279};
280
281DRIVER_MODULE(umodem, ushub, umodem_driver, umodem_devclass, NULL, 0);
282MODULE_DEPEND(umodem, usb2_serial, 1, 1, 1);
283MODULE_DEPEND(umodem, usb2_core, 1, 1, 1);
284MODULE_VERSION(umodem, UMODEM_MODVER);
285
286static int
287umodem_probe(device_t dev)
288{
289 struct usb2_attach_arg *uaa = device_get_ivars(dev);
290 uint8_t cm;
291 uint8_t acm;
292 int error;
293
294 DPRINTFN(11, "\n");
295
296 if (uaa->usb2_mode != USB_MODE_HOST) {
297 return (ENXIO);
298 }
299 error = usb2_lookup_id_by_uaa(umodem_devs, sizeof(umodem_devs), uaa);
300 if (error) {
301 return (error);
302 }
303 if (uaa->driver_info == NULL) {
304 /* some modems do not have any capabilities */
305 return (error);
306 }
307 umodem_get_caps(uaa, &cm, &acm);
308 if (!(cm & USB_CDC_CM_DOES_CM) ||
309 !(cm & USB_CDC_CM_OVER_DATA) ||
310 !(acm & USB_CDC_ACM_HAS_LINE)) {
311 error = ENXIO;
312 }
313 return (error);
314}
315
316static int
317umodem_attach(device_t dev)
318{
319 struct usb2_attach_arg *uaa = device_get_ivars(dev);
320 struct umodem_softc *sc = device_get_softc(dev);
321 struct usb2_cdc_cm_descriptor *cmd;
322 uint8_t i;
323 int error;
324
325 if (sc == NULL) {
326 return (ENOMEM);
327 }
328 device_set_usb2_desc(dev);
329
330 sc->sc_ctrl_iface_no = uaa->info.bIfaceNum;
331 sc->sc_ctrl_iface_index = uaa->info.bIfaceIndex;
332 sc->sc_udev = uaa->device;
333
334 umodem_get_caps(uaa, &sc->sc_cm_cap, &sc->sc_acm_cap);
335
336 /* get the data interface number */
337
338 cmd = umodem_get_desc(uaa, UDESC_CS_INTERFACE, UDESCSUB_CDC_CM);
339
340 if ((cmd == NULL) || (cmd->bLength < sizeof(*cmd))) {
341 device_printf(dev, "no CM descriptor!\n");
342 goto detach;
343 }
344 sc->sc_data_iface_no = cmd->bDataInterface;
345
346 device_printf(dev, "data interface %d, has %sCM over "
347 "data, has %sbreak\n",
348 sc->sc_data_iface_no,
349 sc->sc_cm_cap & USB_CDC_CM_OVER_DATA ? "" : "no ",
350 sc->sc_acm_cap & USB_CDC_ACM_HAS_BREAK ? "" : "no ");
351
352 /* get the data interface too */
353
354 for (i = 0;; i++) {
355 struct usb2_interface *iface;
356 struct usb2_interface_descriptor *id;
357
358 iface = usb2_get_iface(uaa->device, i);
359
360 if (iface) {
361
362 id = usb2_get_interface_descriptor(iface);
363
364 if (id && (id->bInterfaceNumber == sc->sc_data_iface_no)) {
365 sc->sc_data_iface_index = i;
366 usb2_set_parent_iface(uaa->device, i, uaa->info.bIfaceIndex);
367 break;
368 }
369 } else {
370 device_printf(dev, "no data interface!\n");
371 goto detach;
372 }
373 }
374
375 if (sc->sc_cm_cap & USB_CDC_CM_OVER_DATA) {
376 if (sc->sc_acm_cap & USB_CDC_ACM_HAS_FEATURE) {
377
378 error = umodem_set_comm_feature
379 (uaa->device, sc->sc_ctrl_iface_no,
380 UCDC_ABSTRACT_STATE, UCDC_DATA_MULTIPLEXED);
381
382 /* ignore any errors */
383 }
384 sc->sc_cm_over_data = 1;
385 }
386 error = usb2_transfer_setup(uaa->device,
387 &sc->sc_data_iface_index, sc->sc_xfer_data,
388 umodem_config_data, UMODEM_N_DATA_TRANSFER,
389 sc, &Giant);
390 if (error) {
391 goto detach;
392 }
393 error = usb2_transfer_setup(uaa->device,
394 &sc->sc_ctrl_iface_index, sc->sc_xfer_intr,
395 umodem_config_intr, UMODEM_N_INTR_TRANSFER,
396 sc, &Giant);
397
398 if (error == 0) {
399 device_printf(dev, "status change "
400 "notification available\n");
401 }
402 /* clear stall at first run */
403 sc->sc_flag |= (UMODEM_FLAG_READ_STALL |
404 UMODEM_FLAG_WRITE_STALL);
405
406 error = usb2_com_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc,
407 &umodem_callback, &Giant);
408 if (error) {
409 goto detach;
410 }
411 return (0);
412
413detach:
414 umodem_detach(dev);
415 return (ENXIO);
416}
417
418static void
419umodem_start_read(struct usb2_com_softc *ucom)
420{
421 struct umodem_softc *sc = ucom->sc_parent;
422
423 if (sc->sc_xfer_intr[0]) {
424 /* start interrupt endpoint */
425 usb2_transfer_start(sc->sc_xfer_intr[0]);
426 }
427 /* start read endpoint */
428 usb2_transfer_start(sc->sc_xfer_data[1]);
429}
430
431static void
432umodem_stop_read(struct usb2_com_softc *ucom)
433{
434 struct umodem_softc *sc = ucom->sc_parent;
435
436 if (sc->sc_xfer_intr[0]) {
437 /* stop interrupt endpoint */
438 usb2_transfer_stop(sc->sc_xfer_intr[0]);
439 }
440 /* stop read endpoint */
441 usb2_transfer_stop(sc->sc_xfer_data[3]);
442 usb2_transfer_stop(sc->sc_xfer_data[1]);
443}
444
445static void
446umodem_start_write(struct usb2_com_softc *ucom)
447{
448 struct umodem_softc *sc = ucom->sc_parent;
449
450 usb2_transfer_start(sc->sc_xfer_data[0]);
451}
452
453static void
454umodem_stop_write(struct usb2_com_softc *ucom)
455{
456 struct umodem_softc *sc = ucom->sc_parent;
457
458 usb2_transfer_stop(sc->sc_xfer_data[2]);
459 usb2_transfer_stop(sc->sc_xfer_data[0]);
460}
461
462static void
463umodem_get_caps(struct usb2_attach_arg *uaa, uint8_t *cm, uint8_t *acm)
464{
465 struct usb2_cdc_cm_descriptor *cmd;
466 struct usb2_cdc_acm_descriptor *cad;
467
468 *cm = *acm = 0;
469
470 cmd = umodem_get_desc(uaa, UDESC_CS_INTERFACE, UDESCSUB_CDC_CM);
471 if ((cmd == NULL) || (cmd->bLength < sizeof(*cmd))) {
472 DPRINTF("no CM desc\n");
473 return;
474 }
475 *cm = cmd->bmCapabilities;
476
477 cad = umodem_get_desc(uaa, UDESC_CS_INTERFACE, UDESCSUB_CDC_ACM);
478 if ((cad == NULL) || (cad->bLength < sizeof(*cad))) {
479 DPRINTF("no ACM desc\n");
480 return;
481 }
482 *acm = cad->bmCapabilities;
483}
484
485static void
486umodem_cfg_get_status(struct usb2_com_softc *ucom, uint8_t *lsr, uint8_t *msr)
487{
488 struct umodem_softc *sc = ucom->sc_parent;
489
490 DPRINTF("\n");
491
492 *lsr = sc->sc_lsr;
493 *msr = sc->sc_msr;
494}
495
496static int
497umodem_pre_param(struct usb2_com_softc *ucom, struct termios *t)
498{
499 return (0); /* we accept anything */
500}
501
502static void
503umodem_cfg_param(struct usb2_com_softc *ucom, struct termios *t)
504{
505 struct umodem_softc *sc = ucom->sc_parent;
506 struct usb2_cdc_line_state ls;
507 struct usb2_device_request req;
508
509 DPRINTF("sc=%p\n", sc);
510
511 bzero(&ls, sizeof(ls));
512
513 USETDW(ls.dwDTERate, t->c_ospeed);
514
515 ls.bCharFormat = (t->c_cflag & CSTOPB) ?
516 UCDC_STOP_BIT_2 : UCDC_STOP_BIT_1;
517
518 ls.bParityType = (t->c_cflag & PARENB) ?
519 ((t->c_cflag & PARODD) ?
520 UCDC_PARITY_ODD : UCDC_PARITY_EVEN) : UCDC_PARITY_NONE;
521
522 switch (t->c_cflag & CSIZE) {
523 case CS5:
524 ls.bDataBits = 5;
525 break;
526 case CS6:
527 ls.bDataBits = 6;
528 break;
529 case CS7:
530 ls.bDataBits = 7;
531 break;
532 case CS8:
533 ls.bDataBits = 8;
534 break;
535 }
536
537 DPRINTF("rate=%d fmt=%d parity=%d bits=%d\n",
538 UGETDW(ls.dwDTERate), ls.bCharFormat,
539 ls.bParityType, ls.bDataBits);
540
541 req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
542 req.bRequest = UCDC_SET_LINE_CODING;
543 USETW(req.wValue, 0);
544 req.wIndex[0] = sc->sc_ctrl_iface_no;
545 req.wIndex[1] = 0;
546 USETW(req.wLength, sizeof(ls));
547
548 umodem_cfg_do_request(sc, &req, &ls);
549}
550
551static int
552umodem_ioctl(struct usb2_com_softc *ucom, uint32_t cmd, caddr_t data,
553 int flag, struct thread *td)
554{
555 struct umodem_softc *sc = ucom->sc_parent;
556 int error = 0;
557
558 DPRINTF("cmd=0x%08x\n", cmd);
559
560 switch (cmd) {
561 case USB_GET_CM_OVER_DATA:
562 *(int *)data = sc->sc_cm_over_data;
563 break;
564
565 case USB_SET_CM_OVER_DATA:
566 if (*(int *)data != sc->sc_cm_over_data) {
567 /* XXX change it */
568 }
569 break;
570
571 default:
572 DPRINTF("unknown\n");
573 error = ENOIOCTL;
574 break;
575 }
576
577 return (error);
578}
579
580static void
581umodem_cfg_set_dtr(struct usb2_com_softc *ucom, uint8_t onoff)
582{
583 struct umodem_softc *sc = ucom->sc_parent;
584 struct usb2_device_request req;
585
586 DPRINTF("onoff=%d\n", onoff);
587
588 if (onoff)
589 sc->sc_line |= UCDC_LINE_DTR;
590 else
591 sc->sc_line &= ~UCDC_LINE_DTR;
592
593 req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
594 req.bRequest = UCDC_SET_CONTROL_LINE_STATE;
595 USETW(req.wValue, sc->sc_line);
596 req.wIndex[0] = sc->sc_ctrl_iface_no;
597 req.wIndex[1] = 0;
598 USETW(req.wLength, 0);
599
600 umodem_cfg_do_request(sc, &req, NULL);
601}
602
603static void
604umodem_cfg_set_rts(struct usb2_com_softc *ucom, uint8_t onoff)
605{
606 struct umodem_softc *sc = ucom->sc_parent;
607 struct usb2_device_request req;
608
609 DPRINTF("onoff=%d\n", onoff);
610
611 if (onoff)
612 sc->sc_line |= UCDC_LINE_RTS;
613 else
614 sc->sc_line &= ~UCDC_LINE_RTS;
615
616 req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
617 req.bRequest = UCDC_SET_CONTROL_LINE_STATE;
618 USETW(req.wValue, sc->sc_line);
619 req.wIndex[0] = sc->sc_ctrl_iface_no;
620 req.wIndex[1] = 0;
621 USETW(req.wLength, 0);
622
623 umodem_cfg_do_request(sc, &req, NULL);
624}
625
626static void
627umodem_cfg_set_break(struct usb2_com_softc *ucom, uint8_t onoff)
628{
629 struct umodem_softc *sc = ucom->sc_parent;
630 struct usb2_device_request req;
631 uint16_t temp;
632
633 DPRINTF("onoff=%d\n", onoff);
634
635 if (sc->sc_acm_cap & USB_CDC_ACM_HAS_BREAK) {
636
637 temp = onoff ? UCDC_BREAK_ON : UCDC_BREAK_OFF;
638
639 req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
640 req.bRequest = UCDC_SEND_BREAK;
641 USETW(req.wValue, temp);
642 req.wIndex[0] = sc->sc_ctrl_iface_no;
643 req.wIndex[1] = 0;
644 USETW(req.wLength, 0);
645
646 umodem_cfg_do_request(sc, &req, NULL);
647 }
648}
649
650static void
651umodem_intr_callback(struct usb2_xfer *xfer)
652{
653 struct usb2_cdc_notification pkt;
654 struct umodem_softc *sc = xfer->priv_sc;
655 uint16_t wLen;
656
657 switch (USB_GET_STATE(xfer)) {
658 case USB_ST_TRANSFERRED:
659
660 if (xfer->actlen < 8) {
661 DPRINTF("received short packet, "
662 "%d bytes\n", xfer->actlen);
663 goto tr_setup;
664 }
665 if (xfer->actlen > sizeof(pkt)) {
666 DPRINTF("truncating message\n");
667 xfer->actlen = sizeof(pkt);
668 }
669 usb2_copy_out(xfer->frbuffers, 0, &pkt, xfer->actlen);
670
671 xfer->actlen -= 8;
672
673 wLen = UGETW(pkt.wLength);
674 if (xfer->actlen > wLen) {
675 xfer->actlen = wLen;
676 }
677 if (pkt.bmRequestType != UCDC_NOTIFICATION) {
678 DPRINTF("unknown message type, "
679 "0x%02x, on notify pipe!\n",
680 pkt.bmRequestType);
681 goto tr_setup;
682 }
683 switch (pkt.bNotification) {
684 case UCDC_N_SERIAL_STATE:
685 /*
686 * Set the serial state in ucom driver based on
687 * the bits from the notify message
688 */
689 if (xfer->actlen < 2) {
690 DPRINTF("invalid notification "
691 "length, %d bytes!\n", xfer->actlen);
692 break;
693 }
694 DPRINTF("notify bytes = %02x%02x\n",
695 pkt.data[0],
696 pkt.data[1]);
697
698 /* Currently, lsr is always zero. */
699 sc->sc_lsr = 0;
700 sc->sc_msr = 0;
701
702 if (pkt.data[0] & UCDC_N_SERIAL_RI) {
703 sc->sc_msr |= SER_RI;
704 }
705 if (pkt.data[0] & UCDC_N_SERIAL_DSR) {
706 sc->sc_msr |= SER_DSR;
707 }
708 if (pkt.data[0] & UCDC_N_SERIAL_DCD) {
709 sc->sc_msr |= SER_DCD;
710 }
711 usb2_com_status_change(&sc->sc_ucom);
712 break;
713
714 default:
715 DPRINTF("unknown notify message: 0x%02x\n",
716 pkt.bNotification);
717 break;
718 }
719
720 case USB_ST_SETUP:
721tr_setup:
722 if (sc->sc_flag & UMODEM_FLAG_INTR_STALL) {
723 usb2_transfer_start(sc->sc_xfer_intr[1]);
724 } else {
725 xfer->frlengths[0] = xfer->max_data_length;
726 usb2_start_hardware(xfer);
727 }
728 return;
729
730 default: /* Error */
731 if (xfer->error != USB_ERR_CANCELLED) {
732 sc->sc_flag |= UMODEM_FLAG_INTR_STALL;
733 usb2_transfer_start(sc->sc_xfer_intr[1]);
734 }
735 return;
736
737 }
738}
739
740static void
741umodem_intr_clear_stall_callback(struct usb2_xfer *xfer)
742{
743 struct umodem_softc *sc = xfer->priv_sc;
744 struct usb2_xfer *xfer_other = sc->sc_xfer_intr[0];
745
746 if (usb2_clear_stall_callback(xfer, xfer_other)) {
747 DPRINTF("stall cleared\n");
748 sc->sc_flag &= ~UMODEM_FLAG_INTR_STALL;
749 usb2_transfer_start(xfer_other);
750 }
751}
752
753static void
754umodem_write_callback(struct usb2_xfer *xfer)
755{
756 struct umodem_softc *sc = xfer->priv_sc;
757 uint32_t actlen;
758
759 switch (USB_GET_STATE(xfer)) {
760 case USB_ST_SETUP:
761 case USB_ST_TRANSFERRED:
762 if (sc->sc_flag & UMODEM_FLAG_WRITE_STALL) {
763 usb2_transfer_start(sc->sc_xfer_data[2]);
764 return;
765 }
766 if (usb2_com_get_data(&sc->sc_ucom, xfer->frbuffers, 0,
767 UMODEM_BUF_SIZE, &actlen)) {
768
769 xfer->frlengths[0] = actlen;
770 usb2_start_hardware(xfer);
771 }
772 return;
773
774 default: /* Error */
775 if (xfer->error != USB_ERR_CANCELLED) {
776 sc->sc_flag |= UMODEM_FLAG_WRITE_STALL;
777 usb2_transfer_start(sc->sc_xfer_data[2]);
778 }
779 return;
780
781 }
782}
783
784static void
785umodem_write_clear_stall_callback(struct usb2_xfer *xfer)
786{
787 struct umodem_softc *sc = xfer->priv_sc;
788 struct usb2_xfer *xfer_other = sc->sc_xfer_data[0];
789
790 if (usb2_clear_stall_callback(xfer, xfer_other)) {
791 DPRINTF("stall cleared\n");
792 sc->sc_flag &= ~UMODEM_FLAG_WRITE_STALL;
793 usb2_transfer_start(xfer_other);
794 }
795}
796
797static void
798umodem_read_callback(struct usb2_xfer *xfer)
799{
800 struct umodem_softc *sc = xfer->priv_sc;
801
802 switch (USB_GET_STATE(xfer)) {
803 case USB_ST_TRANSFERRED:
804
805 DPRINTF("actlen=%d\n", xfer->actlen);
806
807 usb2_com_put_data(&sc->sc_ucom, xfer->frbuffers, 0,
808 xfer->actlen);
809
810 case USB_ST_SETUP:
811 if (sc->sc_flag & UMODEM_FLAG_READ_STALL) {
812 usb2_transfer_start(sc->sc_xfer_data[3]);
813 } else {
814 xfer->frlengths[0] = xfer->max_data_length;
815 usb2_start_hardware(xfer);
816 }
817 return;
818
819 default: /* Error */
820 if (xfer->error != USB_ERR_CANCELLED) {
821 sc->sc_flag |= UMODEM_FLAG_READ_STALL;
822 usb2_transfer_start(sc->sc_xfer_data[3]);
823 }
824 return;
825
826 }
827}
828
829static void
830umodem_read_clear_stall_callback(struct usb2_xfer *xfer)
831{
832 struct umodem_softc *sc = xfer->priv_sc;
833 struct usb2_xfer *xfer_other = sc->sc_xfer_data[1];
834
835 if (usb2_clear_stall_callback(xfer, xfer_other)) {
836 DPRINTF("stall cleared\n");
837 sc->sc_flag &= ~UMODEM_FLAG_READ_STALL;
838 usb2_transfer_start(xfer_other);
839 }
840}
841
842static void *
843umodem_get_desc(struct usb2_attach_arg *uaa, uint8_t type, uint8_t subtype)
844{
845 return (usb2_find_descriptor(uaa->device, NULL, uaa->info.bIfaceIndex,
846 type, 0 - 1, subtype, 0 - 1));
847}
848
849static usb2_error_t
850umodem_set_comm_feature(struct usb2_device *udev, uint8_t iface_no,
851 uint16_t feature, uint16_t state)
852{
853 struct usb2_device_request req;
854 struct usb2_cdc_abstract_state ast;
855
856 DPRINTF("feature=%d state=%d\n",
857 feature, state);
858
859 req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
860 req.bRequest = UCDC_SET_COMM_FEATURE;
861 USETW(req.wValue, feature);
862 req.wIndex[0] = iface_no;
863 req.wIndex[1] = 0;
864 USETW(req.wLength, UCDC_ABSTRACT_STATE_LENGTH);
865 USETW(ast.wState, state);
866
867 return (usb2_do_request(udev, &Giant, &req, &ast));
868}
869
870static int
871umodem_detach(device_t dev)
872{
873 struct umodem_softc *sc = device_get_softc(dev);
874
875 DPRINTF("sc=%p\n", sc);
876
877 usb2_com_detach(&sc->sc_super_ucom, &sc->sc_ucom, 1);
878
879 usb2_transfer_unsetup(sc->sc_xfer_intr, UMODEM_N_INTR_TRANSFER);
880
881 usb2_transfer_unsetup(sc->sc_xfer_data, UMODEM_N_DATA_TRANSFER);
882
883 return (0);
884}
885
886static void
887umodem_cfg_do_request(struct umodem_softc *sc, struct usb2_device_request *req,
888 void *data)
889{
890 uint16_t length;
891 usb2_error_t err;
892
893 if (usb2_com_cfg_is_gone(&sc->sc_ucom)) {
894 goto error;
895 }
896 err = usb2_do_request_flags(sc->sc_udev, &Giant, req,
897 data, 0, NULL, 1000);
898
899 if (err) {
900
901 DPRINTFN(0, "device request failed, err=%s "
902 "(ignored)\n", usb2_errstr(err));
903
904error:
905 length = UGETW(req->wLength);
906
907 if ((req->bmRequestType & UT_READ) && length) {
908 bzero(data, length);
909 }
910 }
911}