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