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 * 3. All advertising materials mentioning features or use of this software 49 * must display the following acknowledgement: 50 * This product includes software developed by the NetBSD 51 * Foundation, Inc. and its contributors. 52 * 4. Neither the name of The NetBSD Foundation nor the names of its 53 * contributors may be used to endorse or promote products derived 54 * from this software without specific prior written permission. 55 * 56 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 57 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 58 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 59 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 60 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 61 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 62 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 63 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 64 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 65 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 66 * POSSIBILITY OF SUCH DAMAGE. 67 */ 68 69 /* 70 * Comm Class spec: http://www.usb.org/developers/devclass_docs/usbccs10.pdf 71 * http://www.usb.org/developers/devclass_docs/usbcdc11.pdf 72 * http://www.usb.org/developers/devclass_docs/cdc_wmc10.zip 73 */ 74 75 /* 76 * TODO: 77 * - Add error recovery in various places; the big problem is what 78 * to do in a callback if there is an error. 79 * - Implement a Call Device for modems without multiplexed commands. 80 * 81 */ 82 83 #include <sys/stdint.h> 84 #include <sys/stddef.h> 85 #include <sys/param.h> 86 #include <sys/queue.h> 87 #include <sys/types.h> 88 #include <sys/systm.h> 89 #include <sys/kernel.h> 90 #include <sys/bus.h> 91 #include <sys/linker_set.h> 92 #include <sys/module.h> 93 #include <sys/lock.h> 94 #include <sys/mutex.h> 95 #include <sys/condvar.h> 96 #include <sys/sysctl.h> 97 #include <sys/sx.h> 98 #include <sys/unistd.h> 99 #include <sys/callout.h> 100 #include <sys/malloc.h> 101 #include <sys/priv.h> 102 103 #include <dev/usb/usb.h> 104 #include <dev/usb/usbdi.h> 105 #include <dev/usb/usbdi_util.h> 106 #include <dev/usb/usbhid.h> 107 #include <dev/usb/usb_cdc.h> 108 #include "usbdevs.h" 109 110 #include <dev/usb/usb_ioctl.h> 111 112 #define USB_DEBUG_VAR umodem_debug 113 #include <dev/usb/usb_debug.h> 114 #include <dev/usb/usb_process.h> 115 #include <dev/usb/quirk/usb_quirk.h> 116 117 #include <dev/usb/serial/usb_serial.h> 118 119 #ifdef USB_DEBUG 120 static int umodem_debug = 0; 121 122 SYSCTL_NODE(_hw_usb, OID_AUTO, umodem, CTLFLAG_RW, 0, "USB umodem"); 123 SYSCTL_INT(_hw_usb_umodem, OID_AUTO, debug, CTLFLAG_RW, 124 &umodem_debug, 0, "Debug level"); 125 #endif 126 127 static const struct usb_device_id umodem_devs[] = { 128 /* Generic Modem class match */ 129 {USB_IFACE_CLASS(UICLASS_CDC), 130 USB_IFACE_SUBCLASS(UISUBCLASS_ABSTRACT_CONTROL_MODEL), 131 USB_IFACE_PROTOCOL(UIPROTO_CDC_AT)}, 132 /* Kyocera AH-K3001V */ 133 {USB_VPI(USB_VENDOR_KYOCERA, USB_PRODUCT_KYOCERA_AHK3001V, 1)}, 134 {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC5720, 1)}, 135 {USB_VPI(USB_VENDOR_CURITEL, USB_PRODUCT_CURITEL_PC5740, 1)}, 136 }; 137 138 /* 139 * As speeds for umodem deivces increase, these numbers will need to 140 * be increased. They should be good for G3 speeds and below. 141 * 142 * TODO: The TTY buffers should be increased! 143 */ 144 #define UMODEM_BUF_SIZE 1024 145 146 enum { 147 UMODEM_BULK_WR, 148 UMODEM_BULK_RD, 149 UMODEM_INTR_RD, 150 UMODEM_N_TRANSFER, 151 }; 152 153 #define UMODEM_MODVER 1 /* module version */ 154 155 struct umodem_softc { 156 struct ucom_super_softc sc_super_ucom; 157 struct ucom_softc sc_ucom; 158 159 struct usb_xfer *sc_xfer[UMODEM_N_TRANSFER]; 160 struct usb_device *sc_udev; 161 struct mtx sc_mtx; 162 163 uint16_t sc_line; 164 165 uint8_t sc_lsr; /* local status register */ 166 uint8_t sc_msr; /* modem status register */ 167 uint8_t sc_ctrl_iface_no; 168 uint8_t sc_data_iface_no; 169 uint8_t sc_iface_index[2]; 170 uint8_t sc_cm_over_data; 171 uint8_t sc_cm_cap; /* CM capabilities */ 172 uint8_t sc_acm_cap; /* ACM capabilities */ 173 }; 174 175 static device_probe_t umodem_probe; 176 static device_attach_t umodem_attach; 177 static device_detach_t umodem_detach; 178 179 static usb_callback_t umodem_intr_callback; 180 static usb_callback_t umodem_write_callback; 181 static usb_callback_t umodem_read_callback; 182 183 static void umodem_start_read(struct ucom_softc *); 184 static void umodem_stop_read(struct ucom_softc *); 185 static void umodem_start_write(struct ucom_softc *); 186 static void umodem_stop_write(struct ucom_softc *); 187 static void umodem_get_caps(struct usb_attach_arg *, uint8_t *, uint8_t *); 188 static void umodem_cfg_get_status(struct ucom_softc *, uint8_t *, 189 uint8_t *); 190 static int umodem_pre_param(struct ucom_softc *, struct termios *); 191 static void umodem_cfg_param(struct ucom_softc *, struct termios *); 192 static int umodem_ioctl(struct ucom_softc *, uint32_t, caddr_t, int, 193 struct thread *); 194 static void umodem_cfg_set_dtr(struct ucom_softc *, uint8_t); 195 static void umodem_cfg_set_rts(struct ucom_softc *, uint8_t); 196 static void umodem_cfg_set_break(struct ucom_softc *, uint8_t); 197 static void *umodem_get_desc(struct usb_attach_arg *, uint8_t, uint8_t); 198 static usb_error_t umodem_set_comm_feature(struct usb_device *, uint8_t, 199 uint16_t, uint16_t); 200 static void umodem_poll(struct ucom_softc *ucom); 201 202 static const struct usb_config umodem_config[UMODEM_N_TRANSFER] = { 203 204 [UMODEM_BULK_WR] = { 205 .type = UE_BULK, 206 .endpoint = UE_ADDR_ANY, 207 .direction = UE_DIR_OUT, 208 .if_index = 0, 209 .bufsize = UMODEM_BUF_SIZE, 210 .flags = {.pipe_bof = 1,.force_short_xfer = 1,}, 211 .callback = &umodem_write_callback, 212 }, 213 214 [UMODEM_BULK_RD] = { 215 .type = UE_BULK, 216 .endpoint = UE_ADDR_ANY, 217 .direction = UE_DIR_IN, 218 .if_index = 0, 219 .bufsize = UMODEM_BUF_SIZE, 220 .flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, 221 .callback = &umodem_read_callback, 222 }, 223 224 [UMODEM_INTR_RD] = { 225 .type = UE_INTERRUPT, 226 .endpoint = UE_ADDR_ANY, 227 .direction = UE_DIR_IN, 228 .if_index = 1, 229 .flags = {.pipe_bof = 1,.short_xfer_ok = 1,.no_pipe_ok = 1,}, 230 .bufsize = 0, /* use wMaxPacketSize */ 231 .callback = &umodem_intr_callback, 232 }, 233 }; 234 235 static const struct ucom_callback umodem_callback = { 236 .ucom_cfg_get_status = &umodem_cfg_get_status, 237 .ucom_cfg_set_dtr = &umodem_cfg_set_dtr, 238 .ucom_cfg_set_rts = &umodem_cfg_set_rts, 239 .ucom_cfg_set_break = &umodem_cfg_set_break, 240 .ucom_cfg_param = &umodem_cfg_param, 241 .ucom_pre_param = &umodem_pre_param, 242 .ucom_ioctl = &umodem_ioctl, 243 .ucom_start_read = &umodem_start_read, 244 .ucom_stop_read = &umodem_stop_read, 245 .ucom_start_write = &umodem_start_write, 246 .ucom_stop_write = &umodem_stop_write, 247 .ucom_poll = &umodem_poll, 248 }; 249 250 static device_method_t umodem_methods[] = { 251 DEVMETHOD(device_probe, umodem_probe), 252 DEVMETHOD(device_attach, umodem_attach), 253 DEVMETHOD(device_detach, umodem_detach), 254 {0, 0} 255 }; 256 257 static devclass_t umodem_devclass; 258 259 static driver_t umodem_driver = { 260 .name = "umodem", 261 .methods = umodem_methods, 262 .size = sizeof(struct umodem_softc), 263 }; 264 265 DRIVER_MODULE(umodem, uhub, umodem_driver, umodem_devclass, NULL, 0); 266 MODULE_DEPEND(umodem, ucom, 1, 1, 1); 267 MODULE_DEPEND(umodem, usb, 1, 1, 1); 268 MODULE_VERSION(umodem, UMODEM_MODVER); 269 270 static int 271 umodem_probe(device_t dev) 272 { 273 struct usb_attach_arg *uaa = device_get_ivars(dev); 274 int error; 275 276 DPRINTFN(11, "\n"); 277 278 if (uaa->usb_mode != USB_MODE_HOST) { 279 return (ENXIO); 280 } 281 error = usbd_lookup_id_by_uaa(umodem_devs, sizeof(umodem_devs), uaa); 282 return (error); 283 } 284 285 static int 286 umodem_attach(device_t dev) 287 { 288 struct usb_attach_arg *uaa = device_get_ivars(dev); 289 struct umodem_softc *sc = device_get_softc(dev); 290 struct usb_cdc_cm_descriptor *cmd; 291 struct usb_cdc_union_descriptor *cud; 292 uint8_t i; 293 int error; 294 295 device_set_usb_desc(dev); 296 mtx_init(&sc->sc_mtx, "umodem", NULL, MTX_DEF); 297 298 sc->sc_ctrl_iface_no = uaa->info.bIfaceNum; 299 sc->sc_iface_index[1] = uaa->info.bIfaceIndex; 300 sc->sc_udev = uaa->device; 301 302 umodem_get_caps(uaa, &sc->sc_cm_cap, &sc->sc_acm_cap); 303 304 /* get the data interface number */ 305 306 cmd = umodem_get_desc(uaa, UDESC_CS_INTERFACE, UDESCSUB_CDC_CM); 307 308 if ((cmd == NULL) || (cmd->bLength < sizeof(*cmd))) { 309 310 cud = usbd_find_descriptor(uaa->device, NULL, 311 uaa->info.bIfaceIndex, UDESC_CS_INTERFACE, 312 0 - 1, UDESCSUB_CDC_UNION, 0 - 1); 313 314 if ((cud == NULL) || (cud->bLength < sizeof(*cud))) { 315 device_printf(dev, "Missing descriptor. " 316 "Assuming data interface is next.\n"); 317 if (sc->sc_ctrl_iface_no == 0xFF) 318 goto detach; 319 else 320 sc->sc_data_iface_no = 321 sc->sc_ctrl_iface_no + 1; 322 } else { 323 sc->sc_data_iface_no = cud->bSlaveInterface[0]; 324 } 325 } else { 326 sc->sc_data_iface_no = cmd->bDataInterface; 327 } 328 329 device_printf(dev, "data interface %d, has %sCM over " 330 "data, has %sbreak\n", 331 sc->sc_data_iface_no, 332 sc->sc_cm_cap & USB_CDC_CM_OVER_DATA ? "" : "no ", 333 sc->sc_acm_cap & USB_CDC_ACM_HAS_BREAK ? "" : "no "); 334 335 /* get the data interface too */ 336 337 for (i = 0;; i++) { 338 struct usb_interface *iface; 339 struct usb_interface_descriptor *id; 340 341 iface = usbd_get_iface(uaa->device, i); 342 343 if (iface) { 344 345 id = usbd_get_interface_descriptor(iface); 346 347 if (id && (id->bInterfaceNumber == sc->sc_data_iface_no)) { 348 sc->sc_iface_index[0] = i; 349 usbd_set_parent_iface(uaa->device, i, uaa->info.bIfaceIndex); 350 break; 351 } 352 } else { 353 device_printf(dev, "no data interface\n"); 354 goto detach; 355 } 356 } 357 358 if (usb_test_quirk(uaa, UQ_ASSUME_CM_OVER_DATA)) { 359 sc->sc_cm_over_data = 1; 360 } else { 361 if (sc->sc_cm_cap & USB_CDC_CM_OVER_DATA) { 362 if (sc->sc_acm_cap & USB_CDC_ACM_HAS_FEATURE) { 363 364 error = umodem_set_comm_feature 365 (uaa->device, sc->sc_ctrl_iface_no, 366 UCDC_ABSTRACT_STATE, UCDC_DATA_MULTIPLEXED); 367 368 /* ignore any errors */ 369 } 370 sc->sc_cm_over_data = 1; 371 } 372 } 373 error = usbd_transfer_setup(uaa->device, 374 sc->sc_iface_index, sc->sc_xfer, 375 umodem_config, UMODEM_N_TRANSFER, 376 sc, &sc->sc_mtx); 377 if (error) { 378 goto detach; 379 } 380 381 /* clear stall at first run */ 382 mtx_lock(&sc->sc_mtx); 383 usbd_xfer_set_stall(sc->sc_xfer[UMODEM_BULK_WR]); 384 usbd_xfer_set_stall(sc->sc_xfer[UMODEM_BULK_RD]); 385 mtx_unlock(&sc->sc_mtx); 386 387 error = ucom_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc, 388 &umodem_callback, &sc->sc_mtx); 389 if (error) { 390 goto detach; 391 } 392 return (0); 393 394 detach: 395 umodem_detach(dev); 396 return (ENXIO); 397 } 398 399 static void 400 umodem_start_read(struct ucom_softc *ucom) 401 { 402 struct umodem_softc *sc = ucom->sc_parent; 403 404 /* start interrupt endpoint, if any */ 405 usbd_transfer_start(sc->sc_xfer[UMODEM_INTR_RD]); 406 407 /* start read endpoint */ 408 usbd_transfer_start(sc->sc_xfer[UMODEM_BULK_RD]); 409 } 410 411 static void 412 umodem_stop_read(struct ucom_softc *ucom) 413 { 414 struct umodem_softc *sc = ucom->sc_parent; 415 416 /* stop interrupt endpoint, if any */ 417 usbd_transfer_stop(sc->sc_xfer[UMODEM_INTR_RD]); 418 419 /* stop read endpoint */ 420 usbd_transfer_stop(sc->sc_xfer[UMODEM_BULK_RD]); 421 } 422 423 static void 424 umodem_start_write(struct ucom_softc *ucom) 425 { 426 struct umodem_softc *sc = ucom->sc_parent; 427 428 usbd_transfer_start(sc->sc_xfer[UMODEM_BULK_WR]); 429 } 430 431 static void 432 umodem_stop_write(struct ucom_softc *ucom) 433 { 434 struct umodem_softc *sc = ucom->sc_parent; 435 436 usbd_transfer_stop(sc->sc_xfer[UMODEM_BULK_WR]); 437 } 438 439 static void 440 umodem_get_caps(struct usb_attach_arg *uaa, uint8_t *cm, uint8_t *acm) 441 { 442 struct usb_cdc_cm_descriptor *cmd; 443 struct usb_cdc_acm_descriptor *cad; 444 445 cmd = umodem_get_desc(uaa, UDESC_CS_INTERFACE, UDESCSUB_CDC_CM); 446 if ((cmd == NULL) || (cmd->bLength < sizeof(*cmd))) { 447 DPRINTF("no CM desc (faking one)\n"); 448 *cm = USB_CDC_CM_DOES_CM | USB_CDC_CM_OVER_DATA; 449 } else 450 *cm = cmd->bmCapabilities; 451 452 cad = umodem_get_desc(uaa, UDESC_CS_INTERFACE, UDESCSUB_CDC_ACM); 453 if ((cad == NULL) || (cad->bLength < sizeof(*cad))) { 454 DPRINTF("no ACM desc\n"); 455 *acm = 0; 456 } else 457 *acm = cad->bmCapabilities; 458 } 459 460 static void 461 umodem_cfg_get_status(struct ucom_softc *ucom, uint8_t *lsr, uint8_t *msr) 462 { 463 struct umodem_softc *sc = ucom->sc_parent; 464 465 DPRINTF("\n"); 466 467 *lsr = sc->sc_lsr; 468 *msr = sc->sc_msr; 469 } 470 471 static int 472 umodem_pre_param(struct ucom_softc *ucom, struct termios *t) 473 { 474 return (0); /* we accept anything */ 475 } 476 477 static void 478 umodem_cfg_param(struct ucom_softc *ucom, struct termios *t) 479 { 480 struct umodem_softc *sc = ucom->sc_parent; 481 struct usb_cdc_line_state ls; 482 struct usb_device_request req; 483 484 DPRINTF("sc=%p\n", sc); 485 486 bzero(&ls, sizeof(ls)); 487 488 USETDW(ls.dwDTERate, t->c_ospeed); 489 490 ls.bCharFormat = (t->c_cflag & CSTOPB) ? 491 UCDC_STOP_BIT_2 : UCDC_STOP_BIT_1; 492 493 ls.bParityType = (t->c_cflag & PARENB) ? 494 ((t->c_cflag & PARODD) ? 495 UCDC_PARITY_ODD : UCDC_PARITY_EVEN) : UCDC_PARITY_NONE; 496 497 switch (t->c_cflag & CSIZE) { 498 case CS5: 499 ls.bDataBits = 5; 500 break; 501 case CS6: 502 ls.bDataBits = 6; 503 break; 504 case CS7: 505 ls.bDataBits = 7; 506 break; 507 case CS8: 508 ls.bDataBits = 8; 509 break; 510 } 511 512 DPRINTF("rate=%d fmt=%d parity=%d bits=%d\n", 513 UGETDW(ls.dwDTERate), ls.bCharFormat, 514 ls.bParityType, ls.bDataBits); 515 516 req.bmRequestType = UT_WRITE_CLASS_INTERFACE; 517 req.bRequest = UCDC_SET_LINE_CODING; 518 USETW(req.wValue, 0); 519 req.wIndex[0] = sc->sc_ctrl_iface_no; 520 req.wIndex[1] = 0; 521 USETW(req.wLength, sizeof(ls)); 522 523 ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 524 &req, &ls, 0, 1000); 525 } 526 527 static int 528 umodem_ioctl(struct ucom_softc *ucom, uint32_t cmd, caddr_t data, 529 int flag, struct thread *td) 530 { 531 struct umodem_softc *sc = ucom->sc_parent; 532 int error = 0; 533 534 DPRINTF("cmd=0x%08x\n", cmd); 535 536 switch (cmd) { 537 case USB_GET_CM_OVER_DATA: 538 *(int *)data = sc->sc_cm_over_data; 539 break; 540 541 case USB_SET_CM_OVER_DATA: 542 if (*(int *)data != sc->sc_cm_over_data) { 543 /* XXX change it */ 544 } 545 break; 546 547 default: 548 DPRINTF("unknown\n"); 549 error = ENOIOCTL; 550 break; 551 } 552 553 return (error); 554 } 555 556 static void 557 umodem_cfg_set_dtr(struct ucom_softc *ucom, uint8_t onoff) 558 { 559 struct umodem_softc *sc = ucom->sc_parent; 560 struct usb_device_request req; 561 562 DPRINTF("onoff=%d\n", onoff); 563 564 if (onoff) 565 sc->sc_line |= UCDC_LINE_DTR; 566 else 567 sc->sc_line &= ~UCDC_LINE_DTR; 568 569 req.bmRequestType = UT_WRITE_CLASS_INTERFACE; 570 req.bRequest = UCDC_SET_CONTROL_LINE_STATE; 571 USETW(req.wValue, sc->sc_line); 572 req.wIndex[0] = sc->sc_ctrl_iface_no; 573 req.wIndex[1] = 0; 574 USETW(req.wLength, 0); 575 576 ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 577 &req, NULL, 0, 1000); 578 } 579 580 static void 581 umodem_cfg_set_rts(struct ucom_softc *ucom, uint8_t onoff) 582 { 583 struct umodem_softc *sc = ucom->sc_parent; 584 struct usb_device_request req; 585 586 DPRINTF("onoff=%d\n", onoff); 587 588 if (onoff) 589 sc->sc_line |= UCDC_LINE_RTS; 590 else 591 sc->sc_line &= ~UCDC_LINE_RTS; 592 593 req.bmRequestType = UT_WRITE_CLASS_INTERFACE; 594 req.bRequest = UCDC_SET_CONTROL_LINE_STATE; 595 USETW(req.wValue, sc->sc_line); 596 req.wIndex[0] = sc->sc_ctrl_iface_no; 597 req.wIndex[1] = 0; 598 USETW(req.wLength, 0); 599 600 ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 601 &req, NULL, 0, 1000); 602 } 603 604 static void 605 umodem_cfg_set_break(struct ucom_softc *ucom, uint8_t onoff) 606 { 607 struct umodem_softc *sc = ucom->sc_parent; 608 struct usb_device_request req; 609 uint16_t temp; 610 611 DPRINTF("onoff=%d\n", onoff); 612 613 if (sc->sc_acm_cap & USB_CDC_ACM_HAS_BREAK) { 614 615 temp = onoff ? UCDC_BREAK_ON : UCDC_BREAK_OFF; 616 617 req.bmRequestType = UT_WRITE_CLASS_INTERFACE; 618 req.bRequest = UCDC_SEND_BREAK; 619 USETW(req.wValue, temp); 620 req.wIndex[0] = sc->sc_ctrl_iface_no; 621 req.wIndex[1] = 0; 622 USETW(req.wLength, 0); 623 624 ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 625 &req, NULL, 0, 1000); 626 } 627 } 628 629 static void 630 umodem_intr_callback(struct usb_xfer *xfer, usb_error_t error) 631 { 632 struct usb_cdc_notification pkt; 633 struct umodem_softc *sc = usbd_xfer_softc(xfer); 634 struct usb_page_cache *pc; 635 uint16_t wLen; 636 int actlen; 637 638 usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); 639 640 switch (USB_GET_STATE(xfer)) { 641 case USB_ST_TRANSFERRED: 642 643 if (actlen < 8) { 644 DPRINTF("received short packet, " 645 "%d bytes\n", actlen); 646 goto tr_setup; 647 } 648 if (actlen > sizeof(pkt)) { 649 DPRINTF("truncating message\n"); 650 actlen = sizeof(pkt); 651 } 652 pc = usbd_xfer_get_frame(xfer, 0); 653 usbd_copy_out(pc, 0, &pkt, actlen); 654 655 actlen -= 8; 656 657 wLen = UGETW(pkt.wLength); 658 if (actlen > wLen) { 659 actlen = wLen; 660 } 661 if (pkt.bmRequestType != UCDC_NOTIFICATION) { 662 DPRINTF("unknown message type, " 663 "0x%02x, on notify pipe!\n", 664 pkt.bmRequestType); 665 goto tr_setup; 666 } 667 switch (pkt.bNotification) { 668 case UCDC_N_SERIAL_STATE: 669 /* 670 * Set the serial state in ucom driver based on 671 * the bits from the notify message 672 */ 673 if (actlen < 2) { 674 DPRINTF("invalid notification " 675 "length, %d bytes!\n", actlen); 676 break; 677 } 678 DPRINTF("notify bytes = %02x%02x\n", 679 pkt.data[0], 680 pkt.data[1]); 681 682 /* Currently, lsr is always zero. */ 683 sc->sc_lsr = 0; 684 sc->sc_msr = 0; 685 686 if (pkt.data[0] & UCDC_N_SERIAL_RI) { 687 sc->sc_msr |= SER_RI; 688 } 689 if (pkt.data[0] & UCDC_N_SERIAL_DSR) { 690 sc->sc_msr |= SER_DSR; 691 } 692 if (pkt.data[0] & UCDC_N_SERIAL_DCD) { 693 sc->sc_msr |= SER_DCD; 694 } 695 ucom_status_change(&sc->sc_ucom); 696 break; 697 698 default: 699 DPRINTF("unknown notify message: 0x%02x\n", 700 pkt.bNotification); 701 break; 702 } 703 704 case USB_ST_SETUP: 705 tr_setup: 706 usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); 707 usbd_transfer_submit(xfer); 708 return; 709 710 default: /* Error */ 711 if (error != USB_ERR_CANCELLED) { 712 /* try to clear stall first */ 713 usbd_xfer_set_stall(xfer); 714 goto tr_setup; 715 } 716 return; 717 718 } 719 } 720 721 static void 722 umodem_write_callback(struct usb_xfer *xfer, usb_error_t error) 723 { 724 struct umodem_softc *sc = usbd_xfer_softc(xfer); 725 struct usb_page_cache *pc; 726 uint32_t actlen; 727 728 switch (USB_GET_STATE(xfer)) { 729 case USB_ST_SETUP: 730 case USB_ST_TRANSFERRED: 731 tr_setup: 732 pc = usbd_xfer_get_frame(xfer, 0); 733 if (ucom_get_data(&sc->sc_ucom, pc, 0, 734 UMODEM_BUF_SIZE, &actlen)) { 735 736 usbd_xfer_set_frame_len(xfer, 0, actlen); 737 usbd_transfer_submit(xfer); 738 } 739 return; 740 741 default: /* Error */ 742 if (error != USB_ERR_CANCELLED) { 743 /* try to clear stall first */ 744 usbd_xfer_set_stall(xfer); 745 goto tr_setup; 746 } 747 return; 748 } 749 } 750 751 static void 752 umodem_read_callback(struct usb_xfer *xfer, usb_error_t error) 753 { 754 struct umodem_softc *sc = usbd_xfer_softc(xfer); 755 struct usb_page_cache *pc; 756 int actlen; 757 758 usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); 759 760 switch (USB_GET_STATE(xfer)) { 761 case USB_ST_TRANSFERRED: 762 763 DPRINTF("actlen=%d\n", actlen); 764 765 pc = usbd_xfer_get_frame(xfer, 0); 766 ucom_put_data(&sc->sc_ucom, pc, 0, actlen); 767 768 case USB_ST_SETUP: 769 tr_setup: 770 usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); 771 usbd_transfer_submit(xfer); 772 return; 773 774 default: /* Error */ 775 if (error != USB_ERR_CANCELLED) { 776 /* try to clear stall first */ 777 usbd_xfer_set_stall(xfer); 778 goto tr_setup; 779 } 780 return; 781 } 782 } 783 784 static void * 785 umodem_get_desc(struct usb_attach_arg *uaa, uint8_t type, uint8_t subtype) 786 { 787 return (usbd_find_descriptor(uaa->device, NULL, uaa->info.bIfaceIndex, 788 type, 0 - 1, subtype, 0 - 1)); 789 } 790 791 static usb_error_t 792 umodem_set_comm_feature(struct usb_device *udev, uint8_t iface_no, 793 uint16_t feature, uint16_t state) 794 { 795 struct usb_device_request req; 796 struct usb_cdc_abstract_state ast; 797 798 DPRINTF("feature=%d state=%d\n", 799 feature, state); 800 801 req.bmRequestType = UT_WRITE_CLASS_INTERFACE; 802 req.bRequest = UCDC_SET_COMM_FEATURE; 803 USETW(req.wValue, feature); 804 req.wIndex[0] = iface_no; 805 req.wIndex[1] = 0; 806 USETW(req.wLength, UCDC_ABSTRACT_STATE_LENGTH); 807 USETW(ast.wState, state); 808 809 return (usbd_do_request(udev, NULL, &req, &ast)); 810 } 811 812 static int 813 umodem_detach(device_t dev) 814 { 815 struct umodem_softc *sc = device_get_softc(dev); 816 817 DPRINTF("sc=%p\n", sc); 818 819 ucom_detach(&sc->sc_super_ucom, &sc->sc_ucom, 1); 820 usbd_transfer_unsetup(sc->sc_xfer, UMODEM_N_TRANSFER); 821 mtx_destroy(&sc->sc_mtx); 822 823 return (0); 824 } 825 826 static void 827 umodem_poll(struct ucom_softc *ucom) 828 { 829 struct umodem_softc *sc = ucom->sc_parent; 830 usbd_transfer_poll(sc->sc_xfer, UMODEM_N_TRANSFER); 831 } 832