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