Deleted Added
sdiff udiff text old ( 185950 ) new ( 187176 )
full compact
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 $");
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}