1 /* $NetBSD: umodem.c,v 1.45 2002/09/23 05:51:23 simonb Exp $ */
2
3 #include <sys/cdefs.h>
4 /*-
5 * SPDX-License-Identifier: BSD-2-Clause
6 *
7 * Copyright (c) 2003 M. Warner Losh <imp@FreeBSD.org>
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * SUCH DAMAGE.
29 */
30
31 /*-
32 * Copyright (c) 1998 The NetBSD Foundation, Inc.
33 * All rights reserved.
34 *
35 * This code is derived from software contributed to The NetBSD Foundation
36 * by Lennart Augustsson (lennart@augustsson.net) at
37 * Carlstedt Research & Technology.
38 *
39 * Redistribution and use in source and binary forms, with or without
40 * modification, are permitted provided that the following conditions
41 * are met:
42 * 1. Redistributions of source code must retain the above copyright
43 * notice, this list of conditions and the following disclaimer.
44 * 2. Redistributions in binary form must reproduce the above copyright
45 * notice, this list of conditions and the following disclaimer in the
46 * documentation and/or other materials provided with the distribution.
47 *
48 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
49 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
50 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
51 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
52 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
53 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
54 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
55 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
56 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
57 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
58 * POSSIBILITY OF SUCH DAMAGE.
59 */
60
61 /*
62 * Comm Class spec: http://www.usb.org/developers/devclass_docs/usbccs10.pdf
63 * http://www.usb.org/developers/devclass_docs/usbcdc11.pdf
64 * http://www.usb.org/developers/devclass_docs/cdc_wmc10.zip
65 */
66
67 /*
68 * TODO:
69 * - Add error recovery in various places; the big problem is what
70 * to do in a callback if there is an error.
71 * - Implement a Call Device for modems without multiplexed commands.
72 *
73 */
74
75 #include <sys/stdint.h>
76 #include <sys/stddef.h>
77 #include <sys/param.h>
78 #include <sys/queue.h>
79 #include <sys/types.h>
80 #include <sys/systm.h>
81 #include <sys/kernel.h>
82 #include <sys/bus.h>
83 #include <sys/module.h>
84 #include <sys/lock.h>
85 #include <sys/mutex.h>
86 #include <sys/condvar.h>
87 #include <sys/sysctl.h>
88 #include <sys/sx.h>
89 #include <sys/unistd.h>
90 #include <sys/callout.h>
91 #include <sys/malloc.h>
92 #include <sys/priv.h>
93
94 #include <dev/usb/usb.h>
95 #include <dev/usb/usbdi.h>
96 #include <dev/usb/usbdi_util.h>
97 #include <dev/usb/usbhid.h>
98 #include <dev/usb/usb_cdc.h>
99 #include "usbdevs.h"
100 #include "usb_if.h"
101
102 #include <dev/usb/usb_ioctl.h>
103
104 #define USB_DEBUG_VAR umodem_debug
105 #include <dev/usb/usb_debug.h>
106 #include <dev/usb/usb_process.h>
107 #include <dev/usb/quirk/usb_quirk.h>
108
109 #include <dev/usb/serial/usb_serial.h>
110
111 #ifdef USB_DEBUG
112 static int umodem_debug = 0;
113
114 static SYSCTL_NODE(_hw_usb, OID_AUTO, umodem, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
115 "USB umodem");
116 SYSCTL_INT(_hw_usb_umodem, OID_AUTO, debug, CTLFLAG_RWTUN,
117 &umodem_debug, 0, "Debug level");
118 #endif
119
120 static const STRUCT_USB_DUAL_ID umodem_dual_devs[] = {
121 /* Generic Modem class match */
122 {USB_IFACE_CLASS(UICLASS_CDC),
123 USB_IFACE_SUBCLASS(UISUBCLASS_ABSTRACT_CONTROL_MODEL),
124 USB_IFACE_PROTOCOL(UIPROTO_CDC_AT)},
125 {USB_IFACE_CLASS(UICLASS_CDC),
126 USB_IFACE_SUBCLASS(UISUBCLASS_ABSTRACT_CONTROL_MODEL),
127 USB_IFACE_PROTOCOL(UIPROTO_CDC_NONE)},
128 };
129
130 static const STRUCT_USB_HOST_ID umodem_host_devs[] = {
131 /* Huawei Modem class match */
132 {USB_VENDOR(USB_VENDOR_HUAWEI), USB_IFACE_CLASS(UICLASS_VENDOR),
133 USB_IFACE_SUBCLASS(0x02), USB_IFACE_PROTOCOL(0x01)},
134 {USB_VENDOR(USB_VENDOR_HUAWEI), USB_IFACE_CLASS(UICLASS_VENDOR),
135 USB_IFACE_SUBCLASS(0x02), USB_IFACE_PROTOCOL(0x02)},
136 {USB_VENDOR(USB_VENDOR_HUAWEI), USB_IFACE_CLASS(UICLASS_VENDOR),
137 USB_IFACE_SUBCLASS(0x02), USB_IFACE_PROTOCOL(0x10)},
138 {USB_VENDOR(USB_VENDOR_HUAWEI), USB_IFACE_CLASS(UICLASS_VENDOR),
139 USB_IFACE_SUBCLASS(0x02), USB_IFACE_PROTOCOL(0x12)},
140 {USB_VENDOR(USB_VENDOR_HUAWEI), USB_IFACE_CLASS(UICLASS_VENDOR),
141 USB_IFACE_SUBCLASS(0x02), USB_IFACE_PROTOCOL(0x61)},
142 {USB_VENDOR(USB_VENDOR_HUAWEI), USB_IFACE_CLASS(UICLASS_VENDOR),
143 USB_IFACE_SUBCLASS(0x02), USB_IFACE_PROTOCOL(0x62)},
144 {USB_VENDOR(USB_VENDOR_HUAWEI),USB_IFACE_CLASS(UICLASS_CDC),
145 USB_IFACE_SUBCLASS(UISUBCLASS_ABSTRACT_CONTROL_MODEL),
146 USB_IFACE_PROTOCOL(0xFF)},
147 {USB_VENDOR(USB_VENDOR_HUAWEI), USB_IFACE_CLASS(0xFF),
148 USB_IFACE_SUBCLASS(0xF), USB_IFACE_PROTOCOL(0xFF)},
149 /* Kyocera AH-K3001V */
150 {USB_VPI(USB_VENDOR_KYOCERA, USB_PRODUCT_KYOCERA_AHK3001V, 1)},
151 {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC5720, 1)},
152 {USB_VPI(USB_VENDOR_CURITEL, USB_PRODUCT_CURITEL_PC5740, 1)},
153 /* Winbond */
154 {USB_VENDOR(USB_VENDOR_WINBOND), USB_PRODUCT(USB_PRODUCT_WINBOND_CDC)},
155 };
156
157 /*
158 * As speeds for umodem devices increase, these numbers will need to
159 * be increased. They should be good for G3 speeds and below.
160 *
161 * TODO: The TTY buffers should be increased!
162 */
163 #define UMODEM_BUF_SIZE 1024
164
165 enum {
166 UMODEM_BULK_WR,
167 UMODEM_BULK_RD,
168 UMODEM_INTR_WR,
169 UMODEM_INTR_RD,
170 UMODEM_N_TRANSFER,
171 };
172
173 #define UMODEM_MODVER 1 /* module version */
174
175 struct umodem_softc {
176 struct ucom_super_softc sc_super_ucom;
177 struct ucom_softc sc_ucom;
178
179 struct usb_xfer *sc_xfer[UMODEM_N_TRANSFER];
180 struct usb_device *sc_udev;
181 struct mtx sc_mtx;
182
183 uint16_t sc_line;
184
185 uint8_t sc_lsr; /* local status register */
186 uint8_t sc_msr; /* modem status register */
187 uint8_t sc_ctrl_iface_no;
188 uint8_t sc_data_iface_no;
189 uint8_t sc_iface_index[2];
190 uint8_t sc_cm_over_data;
191 uint8_t sc_cm_cap; /* CM capabilities */
192 uint8_t sc_acm_cap; /* ACM capabilities */
193 uint8_t sc_line_coding[32]; /* used in USB device mode */
194 uint8_t sc_abstract_state[32]; /* used in USB device mode */
195 };
196
197 static device_probe_t umodem_probe;
198 static device_attach_t umodem_attach;
199 static device_detach_t umodem_detach;
200 static usb_handle_request_t umodem_handle_request;
201
202 static void umodem_free_softc(struct umodem_softc *);
203
204 static usb_callback_t umodem_intr_read_callback;
205 static usb_callback_t umodem_intr_write_callback;
206 static usb_callback_t umodem_write_callback;
207 static usb_callback_t umodem_read_callback;
208
209 static void umodem_free(struct ucom_softc *);
210 static void umodem_start_read(struct ucom_softc *);
211 static void umodem_stop_read(struct ucom_softc *);
212 static void umodem_start_write(struct ucom_softc *);
213 static void umodem_stop_write(struct ucom_softc *);
214 static void umodem_get_caps(struct usb_attach_arg *, uint8_t *, uint8_t *);
215 static void umodem_cfg_get_status(struct ucom_softc *, uint8_t *,
216 uint8_t *);
217 static int umodem_pre_param(struct ucom_softc *, struct termios *);
218 static void umodem_cfg_param(struct ucom_softc *, struct termios *);
219 static void umodem_cfg_open(struct ucom_softc *);
220 static int umodem_ioctl(struct ucom_softc *, uint32_t, caddr_t, int,
221 struct thread *);
222 static void umodem_cfg_set_dtr(struct ucom_softc *, uint8_t);
223 static void umodem_cfg_set_rts(struct ucom_softc *, uint8_t);
224 static void umodem_cfg_set_break(struct ucom_softc *, uint8_t);
225 static void *umodem_get_desc(struct usb_attach_arg *, uint8_t, uint8_t);
226 static usb_error_t umodem_set_comm_feature(struct usb_device *, uint8_t,
227 uint16_t, uint16_t);
228 static void umodem_poll(struct ucom_softc *ucom);
229 static void umodem_find_data_iface(struct usb_attach_arg *uaa,
230 uint8_t, uint8_t *, uint8_t *);
231
232 static const struct usb_config umodem_config[UMODEM_N_TRANSFER] = {
233 [UMODEM_BULK_WR] = {
234 .type = UE_BULK,
235 .endpoint = UE_ADDR_ANY,
236 .direction = UE_DIR_TX,
237 .if_index = 0,
238 .bufsize = UMODEM_BUF_SIZE,
239 .flags = {.pipe_bof = 1,.force_short_xfer = 1,},
240 .callback = &umodem_write_callback,
241 .usb_mode = USB_MODE_DUAL,
242 },
243
244 [UMODEM_BULK_RD] = {
245 .type = UE_BULK,
246 .endpoint = UE_ADDR_ANY,
247 .direction = UE_DIR_RX,
248 .if_index = 0,
249 .bufsize = UMODEM_BUF_SIZE,
250 .flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
251 .callback = &umodem_read_callback,
252 .usb_mode = USB_MODE_DUAL,
253 },
254
255 [UMODEM_INTR_WR] = {
256 .type = UE_INTERRUPT,
257 .endpoint = UE_ADDR_ANY,
258 .direction = UE_DIR_TX,
259 .if_index = 1,
260 .flags = {.pipe_bof = 1,.short_xfer_ok = 1,.no_pipe_ok = 1,},
261 .bufsize = 0, /* use wMaxPacketSize */
262 .callback = &umodem_intr_write_callback,
263 .usb_mode = USB_MODE_DEVICE,
264 },
265
266 [UMODEM_INTR_RD] = {
267 .type = UE_INTERRUPT,
268 .endpoint = UE_ADDR_ANY,
269 .direction = UE_DIR_RX,
270 .if_index = 1,
271 .flags = {.pipe_bof = 1,.short_xfer_ok = 1,.no_pipe_ok = 1,},
272 .bufsize = 0, /* use wMaxPacketSize */
273 .callback = &umodem_intr_read_callback,
274 .usb_mode = USB_MODE_HOST,
275 },
276 };
277
278 static const struct ucom_callback umodem_callback = {
279 .ucom_cfg_get_status = &umodem_cfg_get_status,
280 .ucom_cfg_set_dtr = &umodem_cfg_set_dtr,
281 .ucom_cfg_set_rts = &umodem_cfg_set_rts,
282 .ucom_cfg_set_break = &umodem_cfg_set_break,
283 .ucom_cfg_param = &umodem_cfg_param,
284 .ucom_pre_param = &umodem_pre_param,
285 .ucom_cfg_open = &umodem_cfg_open,
286 .ucom_ioctl = &umodem_ioctl,
287 .ucom_start_read = &umodem_start_read,
288 .ucom_stop_read = &umodem_stop_read,
289 .ucom_start_write = &umodem_start_write,
290 .ucom_stop_write = &umodem_stop_write,
291 .ucom_poll = &umodem_poll,
292 .ucom_free = &umodem_free,
293 };
294
295 static device_method_t umodem_methods[] = {
296 /* USB interface */
297 DEVMETHOD(usb_handle_request, umodem_handle_request),
298
299 /* Device interface */
300 DEVMETHOD(device_probe, umodem_probe),
301 DEVMETHOD(device_attach, umodem_attach),
302 DEVMETHOD(device_detach, umodem_detach),
303 DEVMETHOD_END
304 };
305
306 static driver_t umodem_driver = {
307 .name = "umodem",
308 .methods = umodem_methods,
309 .size = sizeof(struct umodem_softc),
310 };
311
312 DRIVER_MODULE(umodem, uhub, umodem_driver, NULL, NULL);
313 MODULE_DEPEND(umodem, ucom, 1, 1, 1);
314 MODULE_DEPEND(umodem, usb, 1, 1, 1);
315 MODULE_VERSION(umodem, UMODEM_MODVER);
316 USB_PNP_DUAL_INFO(umodem_dual_devs);
317 USB_PNP_HOST_INFO(umodem_host_devs);
318
319 static int
umodem_probe(device_t dev)320 umodem_probe(device_t dev)
321 {
322 struct usb_attach_arg *uaa = device_get_ivars(dev);
323 int error;
324
325 DPRINTFN(11, "\n");
326
327 error = usbd_lookup_id_by_uaa(umodem_host_devs,
328 sizeof(umodem_host_devs), uaa);
329 if (error) {
330 error = usbd_lookup_id_by_uaa(umodem_dual_devs,
331 sizeof(umodem_dual_devs), uaa);
332 if (error)
333 return (error);
334 }
335 return (BUS_PROBE_GENERIC);
336 }
337
338 static int
umodem_attach(device_t dev)339 umodem_attach(device_t dev)
340 {
341 struct usb_attach_arg *uaa = device_get_ivars(dev);
342 struct umodem_softc *sc = device_get_softc(dev);
343 struct usb_cdc_cm_descriptor *cmd;
344 struct usb_cdc_union_descriptor *cud;
345 uint8_t i;
346 int error;
347
348 device_set_usb_desc(dev);
349 mtx_init(&sc->sc_mtx, "umodem", NULL, MTX_DEF);
350 ucom_ref(&sc->sc_super_ucom);
351
352 sc->sc_ctrl_iface_no = uaa->info.bIfaceNum;
353 sc->sc_iface_index[1] = uaa->info.bIfaceIndex;
354 sc->sc_udev = uaa->device;
355
356 umodem_get_caps(uaa, &sc->sc_cm_cap, &sc->sc_acm_cap);
357
358 /* get the data interface number */
359
360 cmd = NULL;
361 if (!usb_test_quirk(uaa, UQ_IGNORE_CDC_CM))
362 cmd = umodem_get_desc(uaa, UDESC_CS_INTERFACE, UDESCSUB_CDC_CM);
363
364 if ((cmd == NULL) || (cmd->bLength < sizeof(*cmd))) {
365 cud = usbd_find_descriptor(uaa->device, NULL,
366 uaa->info.bIfaceIndex, UDESC_CS_INTERFACE,
367 0xFF, UDESCSUB_CDC_UNION, 0xFF);
368
369 if ((cud == NULL) || (cud->bLength < sizeof(*cud))) {
370 DPRINTF("Missing descriptor. "
371 "Assuming data interface is next.\n");
372 if (sc->sc_ctrl_iface_no == 0xFF) {
373 goto detach;
374 } else {
375 uint8_t class_match = 0;
376
377 /* set default interface number */
378 sc->sc_data_iface_no = 0xFF;
379
380 /* try to find the data interface backwards */
381 umodem_find_data_iface(uaa,
382 uaa->info.bIfaceIndex - 1,
383 &sc->sc_data_iface_no, &class_match);
384
385 /* try to find the data interface forwards */
386 umodem_find_data_iface(uaa,
387 uaa->info.bIfaceIndex + 1,
388 &sc->sc_data_iface_no, &class_match);
389
390 /* check if nothing was found */
391 if (sc->sc_data_iface_no == 0xFF)
392 goto detach;
393 }
394 } else {
395 sc->sc_data_iface_no = cud->bSlaveInterface[0];
396 }
397 } else {
398 sc->sc_data_iface_no = cmd->bDataInterface;
399 }
400
401 device_printf(dev, "data interface %d, has %sCM over "
402 "data, has %sbreak\n",
403 sc->sc_data_iface_no,
404 sc->sc_cm_cap & USB_CDC_CM_OVER_DATA ? "" : "no ",
405 sc->sc_acm_cap & USB_CDC_ACM_HAS_BREAK ? "" : "no ");
406
407 /* get the data interface too */
408
409 for (i = 0;; i++) {
410 struct usb_interface *iface;
411 struct usb_interface_descriptor *id;
412
413 iface = usbd_get_iface(uaa->device, i);
414
415 if (iface) {
416 id = usbd_get_interface_descriptor(iface);
417
418 if (id && (id->bInterfaceNumber == sc->sc_data_iface_no)) {
419 sc->sc_iface_index[0] = i;
420 usbd_set_parent_iface(uaa->device, i, uaa->info.bIfaceIndex);
421 break;
422 }
423 } else {
424 device_printf(dev, "no data interface\n");
425 goto detach;
426 }
427 }
428
429 if (usb_test_quirk(uaa, UQ_ASSUME_CM_OVER_DATA)) {
430 sc->sc_cm_over_data = 1;
431 } else {
432 if (sc->sc_cm_cap & USB_CDC_CM_OVER_DATA) {
433 if (sc->sc_acm_cap & USB_CDC_ACM_HAS_FEATURE) {
434 error = umodem_set_comm_feature
435 (uaa->device, sc->sc_ctrl_iface_no,
436 UCDC_ABSTRACT_STATE, UCDC_DATA_MULTIPLEXED);
437
438 /* ignore any errors */
439 }
440 sc->sc_cm_over_data = 1;
441 }
442 }
443 error = usbd_transfer_setup(uaa->device,
444 sc->sc_iface_index, sc->sc_xfer,
445 umodem_config, UMODEM_N_TRANSFER,
446 sc, &sc->sc_mtx);
447 if (error) {
448 device_printf(dev, "Can't setup transfer\n");
449 goto detach;
450 }
451
452 ucom_set_usb_mode(&sc->sc_super_ucom, uaa->usb_mode);
453
454 error = ucom_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc,
455 &umodem_callback, &sc->sc_mtx);
456 if (error) {
457 device_printf(dev, "Can't attach com\n");
458 goto detach;
459 }
460 ucom_set_pnpinfo_usb(&sc->sc_super_ucom, dev);
461
462 return (0);
463
464 detach:
465 umodem_detach(dev);
466 return (ENXIO);
467 }
468
469 static void
umodem_find_data_iface(struct usb_attach_arg * uaa,uint8_t iface_index,uint8_t * p_data_no,uint8_t * p_match_class)470 umodem_find_data_iface(struct usb_attach_arg *uaa,
471 uint8_t iface_index, uint8_t *p_data_no, uint8_t *p_match_class)
472 {
473 struct usb_interface_descriptor *id;
474 struct usb_interface *iface;
475
476 iface = usbd_get_iface(uaa->device, iface_index);
477
478 /* check for end of interfaces */
479 if (iface == NULL)
480 return;
481
482 id = usbd_get_interface_descriptor(iface);
483
484 /* check for non-matching interface class */
485 if (id->bInterfaceClass != UICLASS_CDC_DATA ||
486 id->bInterfaceSubClass != UISUBCLASS_DATA) {
487 /* if we got a class match then return */
488 if (*p_match_class)
489 return;
490 } else {
491 *p_match_class = 1;
492 }
493
494 DPRINTFN(11, "Match at index %u\n", iface_index);
495
496 *p_data_no = id->bInterfaceNumber;
497 }
498
499 static void
umodem_start_read(struct ucom_softc * ucom)500 umodem_start_read(struct ucom_softc *ucom)
501 {
502 struct umodem_softc *sc = ucom->sc_parent;
503
504 /* start interrupt endpoint, if any */
505 usbd_transfer_start(sc->sc_xfer[UMODEM_INTR_RD]);
506
507 /* start read endpoint */
508 usbd_transfer_start(sc->sc_xfer[UMODEM_BULK_RD]);
509 }
510
511 static void
umodem_stop_read(struct ucom_softc * ucom)512 umodem_stop_read(struct ucom_softc *ucom)
513 {
514 struct umodem_softc *sc = ucom->sc_parent;
515
516 /* stop interrupt endpoint, if any */
517 usbd_transfer_stop(sc->sc_xfer[UMODEM_INTR_RD]);
518
519 /* stop read endpoint */
520 usbd_transfer_stop(sc->sc_xfer[UMODEM_BULK_RD]);
521 }
522
523 static void
umodem_start_write(struct ucom_softc * ucom)524 umodem_start_write(struct ucom_softc *ucom)
525 {
526 struct umodem_softc *sc = ucom->sc_parent;
527
528 usbd_transfer_start(sc->sc_xfer[UMODEM_INTR_WR]);
529 usbd_transfer_start(sc->sc_xfer[UMODEM_BULK_WR]);
530 }
531
532 static void
umodem_stop_write(struct ucom_softc * ucom)533 umodem_stop_write(struct ucom_softc *ucom)
534 {
535 struct umodem_softc *sc = ucom->sc_parent;
536
537 usbd_transfer_stop(sc->sc_xfer[UMODEM_INTR_WR]);
538 usbd_transfer_stop(sc->sc_xfer[UMODEM_BULK_WR]);
539 }
540
541 static void
umodem_get_caps(struct usb_attach_arg * uaa,uint8_t * cm,uint8_t * acm)542 umodem_get_caps(struct usb_attach_arg *uaa, uint8_t *cm, uint8_t *acm)
543 {
544 struct usb_cdc_cm_descriptor *cmd;
545 struct usb_cdc_acm_descriptor *cad;
546
547 cmd = umodem_get_desc(uaa, UDESC_CS_INTERFACE, UDESCSUB_CDC_CM);
548 if ((cmd == NULL) || (cmd->bLength < sizeof(*cmd))) {
549 DPRINTF("no CM desc (faking one)\n");
550 *cm = USB_CDC_CM_DOES_CM | USB_CDC_CM_OVER_DATA;
551 } else
552 *cm = cmd->bmCapabilities;
553
554 cad = umodem_get_desc(uaa, UDESC_CS_INTERFACE, UDESCSUB_CDC_ACM);
555 if ((cad == NULL) || (cad->bLength < sizeof(*cad))) {
556 DPRINTF("no ACM desc\n");
557 *acm = 0;
558 } else
559 *acm = cad->bmCapabilities;
560 }
561
562 static void
umodem_cfg_get_status(struct ucom_softc * ucom,uint8_t * lsr,uint8_t * msr)563 umodem_cfg_get_status(struct ucom_softc *ucom, uint8_t *lsr, uint8_t *msr)
564 {
565 struct umodem_softc *sc = ucom->sc_parent;
566
567 DPRINTF("\n");
568
569 /* XXX Note: sc_lsr is always zero */
570 *lsr = sc->sc_lsr;
571 *msr = sc->sc_msr;
572 }
573
574 static int
umodem_pre_param(struct ucom_softc * ucom,struct termios * t)575 umodem_pre_param(struct ucom_softc *ucom, struct termios *t)
576 {
577 return (0); /* we accept anything */
578 }
579
580 static void
umodem_cfg_param(struct ucom_softc * ucom,struct termios * t)581 umodem_cfg_param(struct ucom_softc *ucom, struct termios *t)
582 {
583 struct umodem_softc *sc = ucom->sc_parent;
584 struct usb_cdc_line_state ls;
585 struct usb_device_request req;
586
587 DPRINTF("sc=%p\n", sc);
588
589 memset(&ls, 0, sizeof(ls));
590
591 USETDW(ls.dwDTERate, t->c_ospeed);
592
593 ls.bCharFormat = (t->c_cflag & CSTOPB) ?
594 UCDC_STOP_BIT_2 : UCDC_STOP_BIT_1;
595
596 ls.bParityType = (t->c_cflag & PARENB) ?
597 ((t->c_cflag & PARODD) ?
598 UCDC_PARITY_ODD : UCDC_PARITY_EVEN) : UCDC_PARITY_NONE;
599
600 switch (t->c_cflag & CSIZE) {
601 case CS5:
602 ls.bDataBits = 5;
603 break;
604 case CS6:
605 ls.bDataBits = 6;
606 break;
607 case CS7:
608 ls.bDataBits = 7;
609 break;
610 case CS8:
611 ls.bDataBits = 8;
612 break;
613 }
614
615 DPRINTF("rate=%d fmt=%d parity=%d bits=%d\n",
616 UGETDW(ls.dwDTERate), ls.bCharFormat,
617 ls.bParityType, ls.bDataBits);
618
619 req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
620 req.bRequest = UCDC_SET_LINE_CODING;
621 USETW(req.wValue, 0);
622 req.wIndex[0] = sc->sc_ctrl_iface_no;
623 req.wIndex[1] = 0;
624 USETW(req.wLength, sizeof(ls));
625
626 ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
627 &req, &ls, 0, 1000);
628 }
629
630 static void
umodem_cfg_open(struct ucom_softc * ucom)631 umodem_cfg_open(struct ucom_softc *ucom)
632 {
633 struct umodem_softc *sc = ucom->sc_parent;
634
635 /* clear stall, if in USB host mode */
636 if ((sc->sc_super_ucom.sc_flag & UCOM_FLAG_DEVICE_MODE) == 0) {
637 usbd_xfer_set_stall(sc->sc_xfer[UMODEM_BULK_WR]);
638 usbd_xfer_set_stall(sc->sc_xfer[UMODEM_BULK_RD]);
639 }
640 }
641
642 static int
umodem_ioctl(struct ucom_softc * ucom,uint32_t cmd,caddr_t data,int flag,struct thread * td)643 umodem_ioctl(struct ucom_softc *ucom, uint32_t cmd, caddr_t data,
644 int flag, struct thread *td)
645 {
646 struct umodem_softc *sc = ucom->sc_parent;
647 int error = 0;
648
649 DPRINTF("cmd=0x%08x\n", cmd);
650
651 switch (cmd) {
652 case USB_GET_CM_OVER_DATA:
653 *(int *)data = sc->sc_cm_over_data;
654 break;
655
656 case USB_SET_CM_OVER_DATA:
657 if (*(int *)data != sc->sc_cm_over_data) {
658 /* XXX change it */
659 }
660 break;
661
662 default:
663 DPRINTF("unknown\n");
664 error = ENOIOCTL;
665 break;
666 }
667
668 return (error);
669 }
670
671 static void
umodem_cfg_set_dtr(struct ucom_softc * ucom,uint8_t onoff)672 umodem_cfg_set_dtr(struct ucom_softc *ucom, uint8_t onoff)
673 {
674 struct umodem_softc *sc = ucom->sc_parent;
675 struct usb_device_request req;
676
677 DPRINTF("onoff=%d\n", onoff);
678
679 if (onoff)
680 sc->sc_line |= UCDC_LINE_DTR;
681 else
682 sc->sc_line &= ~UCDC_LINE_DTR;
683
684 req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
685 req.bRequest = UCDC_SET_CONTROL_LINE_STATE;
686 USETW(req.wValue, sc->sc_line);
687 req.wIndex[0] = sc->sc_ctrl_iface_no;
688 req.wIndex[1] = 0;
689 USETW(req.wLength, 0);
690
691 ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
692 &req, NULL, 0, 1000);
693 }
694
695 static void
umodem_cfg_set_rts(struct ucom_softc * ucom,uint8_t onoff)696 umodem_cfg_set_rts(struct ucom_softc *ucom, uint8_t onoff)
697 {
698 struct umodem_softc *sc = ucom->sc_parent;
699 struct usb_device_request req;
700
701 DPRINTF("onoff=%d\n", onoff);
702
703 if (onoff)
704 sc->sc_line |= UCDC_LINE_RTS;
705 else
706 sc->sc_line &= ~UCDC_LINE_RTS;
707
708 req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
709 req.bRequest = UCDC_SET_CONTROL_LINE_STATE;
710 USETW(req.wValue, sc->sc_line);
711 req.wIndex[0] = sc->sc_ctrl_iface_no;
712 req.wIndex[1] = 0;
713 USETW(req.wLength, 0);
714
715 ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
716 &req, NULL, 0, 1000);
717 }
718
719 static void
umodem_cfg_set_break(struct ucom_softc * ucom,uint8_t onoff)720 umodem_cfg_set_break(struct ucom_softc *ucom, uint8_t onoff)
721 {
722 struct umodem_softc *sc = ucom->sc_parent;
723 struct usb_device_request req;
724 uint16_t temp;
725
726 DPRINTF("onoff=%d\n", onoff);
727
728 if (sc->sc_acm_cap & USB_CDC_ACM_HAS_BREAK) {
729 temp = onoff ? UCDC_BREAK_ON : UCDC_BREAK_OFF;
730
731 req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
732 req.bRequest = UCDC_SEND_BREAK;
733 USETW(req.wValue, temp);
734 req.wIndex[0] = sc->sc_ctrl_iface_no;
735 req.wIndex[1] = 0;
736 USETW(req.wLength, 0);
737
738 ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
739 &req, NULL, 0, 1000);
740 }
741 }
742
743 static void
umodem_intr_write_callback(struct usb_xfer * xfer,usb_error_t error)744 umodem_intr_write_callback(struct usb_xfer *xfer, usb_error_t error)
745 {
746 int actlen;
747
748 usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
749
750 switch (USB_GET_STATE(xfer)) {
751 case USB_ST_TRANSFERRED:
752
753 DPRINTF("Transferred %d bytes\n", actlen);
754
755 /* FALLTHROUGH */
756 case USB_ST_SETUP:
757 tr_setup:
758 break;
759
760 default: /* Error */
761 if (error != USB_ERR_CANCELLED) {
762 /* start clear stall */
763 usbd_xfer_set_stall(xfer);
764 goto tr_setup;
765 }
766 break;
767 }
768 }
769
770 static void
umodem_intr_read_callback(struct usb_xfer * xfer,usb_error_t error)771 umodem_intr_read_callback(struct usb_xfer *xfer, usb_error_t error)
772 {
773 struct usb_cdc_notification pkt;
774 struct umodem_softc *sc = usbd_xfer_softc(xfer);
775 struct usb_page_cache *pc;
776 uint16_t wLen;
777 int actlen;
778
779 usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
780
781 switch (USB_GET_STATE(xfer)) {
782 case USB_ST_TRANSFERRED:
783
784 if (actlen < 8) {
785 DPRINTF("received short packet, "
786 "%d bytes\n", actlen);
787 goto tr_setup;
788 }
789 if (actlen > (int)sizeof(pkt)) {
790 DPRINTF("truncating message\n");
791 actlen = sizeof(pkt);
792 }
793 pc = usbd_xfer_get_frame(xfer, 0);
794 usbd_copy_out(pc, 0, &pkt, actlen);
795
796 actlen -= 8;
797
798 wLen = UGETW(pkt.wLength);
799 if (actlen > wLen) {
800 actlen = wLen;
801 }
802 if (pkt.bmRequestType != UCDC_NOTIFICATION) {
803 DPRINTF("unknown message type, "
804 "0x%02x, on notify pipe!\n",
805 pkt.bmRequestType);
806 goto tr_setup;
807 }
808 switch (pkt.bNotification) {
809 case UCDC_N_SERIAL_STATE:
810 /*
811 * Set the serial state in ucom driver based on
812 * the bits from the notify message
813 */
814 if (actlen < 2) {
815 DPRINTF("invalid notification "
816 "length, %d bytes!\n", actlen);
817 break;
818 }
819 DPRINTF("notify bytes = %02x%02x\n",
820 pkt.data[0],
821 pkt.data[1]);
822
823 /* Currently, lsr is always zero. */
824 sc->sc_lsr = 0;
825 sc->sc_msr = 0;
826
827 if (pkt.data[0] & UCDC_N_SERIAL_RI) {
828 sc->sc_msr |= SER_RI;
829 }
830 if (pkt.data[0] & UCDC_N_SERIAL_DSR) {
831 sc->sc_msr |= SER_DSR;
832 }
833 if (pkt.data[0] & UCDC_N_SERIAL_DCD) {
834 sc->sc_msr |= SER_DCD;
835 }
836 ucom_status_change(&sc->sc_ucom);
837 break;
838
839 default:
840 DPRINTF("unknown notify message: 0x%02x\n",
841 pkt.bNotification);
842 break;
843 }
844
845 case USB_ST_SETUP:
846 tr_setup:
847 usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
848 usbd_transfer_submit(xfer);
849 return;
850
851 default: /* Error */
852 if (error != USB_ERR_CANCELLED) {
853 /* try to clear stall first */
854 usbd_xfer_set_stall(xfer);
855 goto tr_setup;
856 }
857 return;
858 }
859 }
860
861 static void
umodem_write_callback(struct usb_xfer * xfer,usb_error_t error)862 umodem_write_callback(struct usb_xfer *xfer, usb_error_t error)
863 {
864 struct umodem_softc *sc = usbd_xfer_softc(xfer);
865 struct usb_page_cache *pc;
866 uint32_t actlen;
867
868 switch (USB_GET_STATE(xfer)) {
869 case USB_ST_SETUP:
870 case USB_ST_TRANSFERRED:
871 tr_setup:
872 pc = usbd_xfer_get_frame(xfer, 0);
873 if (ucom_get_data(&sc->sc_ucom, pc, 0,
874 UMODEM_BUF_SIZE, &actlen)) {
875 usbd_xfer_set_frame_len(xfer, 0, actlen);
876 usbd_transfer_submit(xfer);
877 }
878 return;
879
880 default: /* Error */
881 if (error != USB_ERR_CANCELLED) {
882 /* try to clear stall first */
883 usbd_xfer_set_stall(xfer);
884 goto tr_setup;
885 }
886 return;
887 }
888 }
889
890 static void
umodem_read_callback(struct usb_xfer * xfer,usb_error_t error)891 umodem_read_callback(struct usb_xfer *xfer, usb_error_t error)
892 {
893 struct umodem_softc *sc = usbd_xfer_softc(xfer);
894 struct usb_page_cache *pc;
895 int actlen;
896
897 usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
898
899 switch (USB_GET_STATE(xfer)) {
900 case USB_ST_TRANSFERRED:
901
902 DPRINTF("actlen=%d\n", actlen);
903
904 pc = usbd_xfer_get_frame(xfer, 0);
905 ucom_put_data(&sc->sc_ucom, pc, 0, actlen);
906
907 case USB_ST_SETUP:
908 tr_setup:
909 usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
910 usbd_transfer_submit(xfer);
911 return;
912
913 default: /* Error */
914 if (error != USB_ERR_CANCELLED) {
915 /* try to clear stall first */
916 usbd_xfer_set_stall(xfer);
917 goto tr_setup;
918 }
919 return;
920 }
921 }
922
923 static void *
umodem_get_desc(struct usb_attach_arg * uaa,uint8_t type,uint8_t subtype)924 umodem_get_desc(struct usb_attach_arg *uaa, uint8_t type, uint8_t subtype)
925 {
926 return (usbd_find_descriptor(uaa->device, NULL, uaa->info.bIfaceIndex,
927 type, 0xFF, subtype, 0xFF));
928 }
929
930 static usb_error_t
umodem_set_comm_feature(struct usb_device * udev,uint8_t iface_no,uint16_t feature,uint16_t state)931 umodem_set_comm_feature(struct usb_device *udev, uint8_t iface_no,
932 uint16_t feature, uint16_t state)
933 {
934 struct usb_device_request req;
935 struct usb_cdc_abstract_state ast;
936
937 DPRINTF("feature=%d state=%d\n",
938 feature, state);
939
940 req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
941 req.bRequest = UCDC_SET_COMM_FEATURE;
942 USETW(req.wValue, feature);
943 req.wIndex[0] = iface_no;
944 req.wIndex[1] = 0;
945 USETW(req.wLength, UCDC_ABSTRACT_STATE_LENGTH);
946 USETW(ast.wState, state);
947
948 return (usbd_do_request(udev, NULL, &req, &ast));
949 }
950
951 static int
umodem_detach(device_t dev)952 umodem_detach(device_t dev)
953 {
954 struct umodem_softc *sc = device_get_softc(dev);
955
956 DPRINTF("sc=%p\n", sc);
957
958 ucom_detach(&sc->sc_super_ucom, &sc->sc_ucom);
959 usbd_transfer_unsetup(sc->sc_xfer, UMODEM_N_TRANSFER);
960
961 device_claim_softc(dev);
962
963 umodem_free_softc(sc);
964
965 return (0);
966 }
967
968 UCOM_UNLOAD_DRAIN(umodem);
969
970 static void
umodem_free_softc(struct umodem_softc * sc)971 umodem_free_softc(struct umodem_softc *sc)
972 {
973 if (ucom_unref(&sc->sc_super_ucom)) {
974 mtx_destroy(&sc->sc_mtx);
975 device_free_softc(sc);
976 }
977 }
978
979 static void
umodem_free(struct ucom_softc * ucom)980 umodem_free(struct ucom_softc *ucom)
981 {
982 umodem_free_softc(ucom->sc_parent);
983 }
984
985 static void
umodem_poll(struct ucom_softc * ucom)986 umodem_poll(struct ucom_softc *ucom)
987 {
988 struct umodem_softc *sc = ucom->sc_parent;
989 usbd_transfer_poll(sc->sc_xfer, UMODEM_N_TRANSFER);
990 }
991
992 static int
umodem_handle_request(device_t dev,const void * preq,void ** pptr,uint16_t * plen,uint16_t offset,uint8_t * pstate)993 umodem_handle_request(device_t dev,
994 const void *preq, void **pptr, uint16_t *plen,
995 uint16_t offset, uint8_t *pstate)
996 {
997 struct umodem_softc *sc = device_get_softc(dev);
998 const struct usb_device_request *req = preq;
999 uint8_t is_complete = *pstate;
1000
1001 DPRINTF("sc=%p\n", sc);
1002
1003 if (!is_complete) {
1004 if ((req->bmRequestType == UT_WRITE_CLASS_INTERFACE) &&
1005 (req->bRequest == UCDC_SET_LINE_CODING) &&
1006 (req->wIndex[0] == sc->sc_ctrl_iface_no) &&
1007 (req->wIndex[1] == 0x00) &&
1008 (req->wValue[0] == 0x00) &&
1009 (req->wValue[1] == 0x00)) {
1010 if (offset == 0) {
1011 *plen = sizeof(sc->sc_line_coding);
1012 *pptr = &sc->sc_line_coding;
1013 } else {
1014 *plen = 0;
1015 }
1016 return (0);
1017 } else if ((req->bmRequestType == UT_WRITE_CLASS_INTERFACE) &&
1018 (req->wIndex[0] == sc->sc_ctrl_iface_no) &&
1019 (req->wIndex[1] == 0x00) &&
1020 (req->bRequest == UCDC_SET_COMM_FEATURE)) {
1021 if (offset == 0) {
1022 *plen = sizeof(sc->sc_abstract_state);
1023 *pptr = &sc->sc_abstract_state;
1024 } else {
1025 *plen = 0;
1026 }
1027 return (0);
1028 } else if ((req->bmRequestType == UT_WRITE_CLASS_INTERFACE) &&
1029 (req->wIndex[0] == sc->sc_ctrl_iface_no) &&
1030 (req->wIndex[1] == 0x00) &&
1031 (req->bRequest == UCDC_SET_CONTROL_LINE_STATE)) {
1032 *plen = 0;
1033 return (0);
1034 } else if ((req->bmRequestType == UT_WRITE_CLASS_INTERFACE) &&
1035 (req->wIndex[0] == sc->sc_ctrl_iface_no) &&
1036 (req->wIndex[1] == 0x00) &&
1037 (req->bRequest == UCDC_SEND_BREAK)) {
1038 *plen = 0;
1039 return (0);
1040 }
1041 }
1042 return (ENXIO); /* use builtin handler */
1043 }
1044