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