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 102 #include <dev/usb/usb_ioctl.h> 103 104 #define USB_DEBUG_VAR umodem_debug 105 #include <dev/usb/usb_debug.h> 106 #include <dev/usb/usb_process.h> 107 #include <dev/usb/quirk/usb_quirk.h> 108 109 #include <dev/usb/serial/usb_serial.h> 110 111 #ifdef USB_DEBUG 112 static int umodem_debug = 0; 113 114 static SYSCTL_NODE(_hw_usb, OID_AUTO, umodem, CTLFLAG_RW, 0, "USB umodem"); 115 SYSCTL_INT(_hw_usb_umodem, OID_AUTO, debug, CTLFLAG_RW, 116 &umodem_debug, 0, "Debug level"); 117 #endif 118 119 static const STRUCT_USB_HOST_ID umodem_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 /* Huawei Modem class match */ 128 {USB_IFACE_CLASS(UICLASS_CDC), 129 USB_IFACE_SUBCLASS(UISUBCLASS_ABSTRACT_CONTROL_MODEL), 130 USB_IFACE_PROTOCOL(0xFF)}, 131 /* Kyocera AH-K3001V */ 132 {USB_VPI(USB_VENDOR_KYOCERA, USB_PRODUCT_KYOCERA_AHK3001V, 1)}, 133 {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC5720, 1)}, 134 {USB_VPI(USB_VENDOR_CURITEL, USB_PRODUCT_CURITEL_PC5740, 1)}, 135 }; 136 137 /* 138 * As speeds for umodem devices increase, these numbers will need to 139 * be increased. They should be good for G3 speeds and below. 140 * 141 * TODO: The TTY buffers should be increased! 142 */ 143 #define UMODEM_BUF_SIZE 1024 144 145 enum { 146 UMODEM_BULK_WR, 147 UMODEM_BULK_RD, 148 UMODEM_INTR_RD, 149 UMODEM_N_TRANSFER, 150 }; 151 152 #define UMODEM_MODVER 1 /* module version */ 153 154 struct umodem_softc { 155 struct ucom_super_softc sc_super_ucom; 156 struct ucom_softc sc_ucom; 157 158 struct usb_xfer *sc_xfer[UMODEM_N_TRANSFER]; 159 struct usb_device *sc_udev; 160 struct mtx sc_mtx; 161 162 uint16_t sc_line; 163 164 uint8_t sc_lsr; /* local status register */ 165 uint8_t sc_msr; /* modem status register */ 166 uint8_t sc_ctrl_iface_no; 167 uint8_t sc_data_iface_no; 168 uint8_t sc_iface_index[2]; 169 uint8_t sc_cm_over_data; 170 uint8_t sc_cm_cap; /* CM capabilities */ 171 uint8_t sc_acm_cap; /* ACM capabilities */ 172 }; 173 174 static device_probe_t umodem_probe; 175 static device_attach_t umodem_attach; 176 static device_detach_t umodem_detach; 177 static void umodem_free_softc(struct umodem_softc *); 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_free(struct ucom_softc *); 184 static void umodem_start_read(struct ucom_softc *); 185 static void umodem_stop_read(struct ucom_softc *); 186 static void umodem_start_write(struct ucom_softc *); 187 static void umodem_stop_write(struct ucom_softc *); 188 static void umodem_get_caps(struct usb_attach_arg *, uint8_t *, uint8_t *); 189 static void umodem_cfg_get_status(struct ucom_softc *, uint8_t *, 190 uint8_t *); 191 static int umodem_pre_param(struct ucom_softc *, struct termios *); 192 static void umodem_cfg_param(struct ucom_softc *, struct termios *); 193 static int umodem_ioctl(struct ucom_softc *, uint32_t, caddr_t, int, 194 struct thread *); 195 static void umodem_cfg_set_dtr(struct ucom_softc *, uint8_t); 196 static void umodem_cfg_set_rts(struct ucom_softc *, uint8_t); 197 static void umodem_cfg_set_break(struct ucom_softc *, uint8_t); 198 static void *umodem_get_desc(struct usb_attach_arg *, uint8_t, uint8_t); 199 static usb_error_t umodem_set_comm_feature(struct usb_device *, uint8_t, 200 uint16_t, uint16_t); 201 static void umodem_poll(struct ucom_softc *ucom); 202 static void umodem_find_data_iface(struct usb_attach_arg *uaa, 203 uint8_t, uint8_t *, uint8_t *); 204 205 static const struct usb_config umodem_config[UMODEM_N_TRANSFER] = { 206 207 [UMODEM_BULK_WR] = { 208 .type = UE_BULK, 209 .endpoint = UE_ADDR_ANY, 210 .direction = UE_DIR_OUT, 211 .if_index = 0, 212 .bufsize = UMODEM_BUF_SIZE, 213 .flags = {.pipe_bof = 1,.force_short_xfer = 1,}, 214 .callback = &umodem_write_callback, 215 }, 216 217 [UMODEM_BULK_RD] = { 218 .type = UE_BULK, 219 .endpoint = UE_ADDR_ANY, 220 .direction = UE_DIR_IN, 221 .if_index = 0, 222 .bufsize = UMODEM_BUF_SIZE, 223 .flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, 224 .callback = &umodem_read_callback, 225 }, 226 227 [UMODEM_INTR_RD] = { 228 .type = UE_INTERRUPT, 229 .endpoint = UE_ADDR_ANY, 230 .direction = UE_DIR_IN, 231 .if_index = 1, 232 .flags = {.pipe_bof = 1,.short_xfer_ok = 1,.no_pipe_ok = 1,}, 233 .bufsize = 0, /* use wMaxPacketSize */ 234 .callback = &umodem_intr_callback, 235 }, 236 }; 237 238 static const struct ucom_callback umodem_callback = { 239 .ucom_cfg_get_status = &umodem_cfg_get_status, 240 .ucom_cfg_set_dtr = &umodem_cfg_set_dtr, 241 .ucom_cfg_set_rts = &umodem_cfg_set_rts, 242 .ucom_cfg_set_break = &umodem_cfg_set_break, 243 .ucom_cfg_param = &umodem_cfg_param, 244 .ucom_pre_param = &umodem_pre_param, 245 .ucom_ioctl = &umodem_ioctl, 246 .ucom_start_read = &umodem_start_read, 247 .ucom_stop_read = &umodem_stop_read, 248 .ucom_start_write = &umodem_start_write, 249 .ucom_stop_write = &umodem_stop_write, 250 .ucom_poll = &umodem_poll, 251 .ucom_free = &umodem_free, 252 }; 253 254 static device_method_t umodem_methods[] = { 255 DEVMETHOD(device_probe, umodem_probe), 256 DEVMETHOD(device_attach, umodem_attach), 257 DEVMETHOD(device_detach, umodem_detach), 258 DEVMETHOD_END 259 }; 260 261 static devclass_t umodem_devclass; 262 263 static driver_t umodem_driver = { 264 .name = "umodem", 265 .methods = umodem_methods, 266 .size = sizeof(struct umodem_softc), 267 }; 268 269 DRIVER_MODULE(umodem, uhub, umodem_driver, umodem_devclass, NULL, 0); 270 MODULE_DEPEND(umodem, ucom, 1, 1, 1); 271 MODULE_DEPEND(umodem, usb, 1, 1, 1); 272 MODULE_VERSION(umodem, UMODEM_MODVER); 273 274 static int 275 umodem_probe(device_t dev) 276 { 277 struct usb_attach_arg *uaa = device_get_ivars(dev); 278 int error; 279 280 DPRINTFN(11, "\n"); 281 282 if (uaa->usb_mode != USB_MODE_HOST) 283 return (ENXIO); 284 285 error = usbd_lookup_id_by_uaa(umodem_devs, sizeof(umodem_devs), uaa); 286 if (error) 287 return (error); 288 289 return (BUS_PROBE_GENERIC); 290 } 291 292 static int 293 umodem_attach(device_t dev) 294 { 295 struct usb_attach_arg *uaa = device_get_ivars(dev); 296 struct umodem_softc *sc = device_get_softc(dev); 297 struct usb_cdc_cm_descriptor *cmd; 298 struct usb_cdc_union_descriptor *cud; 299 uint8_t i; 300 int error; 301 302 device_set_usb_desc(dev); 303 mtx_init(&sc->sc_mtx, "umodem", NULL, MTX_DEF); 304 ucom_ref(&sc->sc_super_ucom); 305 306 sc->sc_ctrl_iface_no = uaa->info.bIfaceNum; 307 sc->sc_iface_index[1] = uaa->info.bIfaceIndex; 308 sc->sc_udev = uaa->device; 309 310 umodem_get_caps(uaa, &sc->sc_cm_cap, &sc->sc_acm_cap); 311 312 /* get the data interface number */ 313 314 cmd = umodem_get_desc(uaa, UDESC_CS_INTERFACE, UDESCSUB_CDC_CM); 315 316 if ((cmd == NULL) || (cmd->bLength < sizeof(*cmd))) { 317 318 cud = usbd_find_descriptor(uaa->device, NULL, 319 uaa->info.bIfaceIndex, UDESC_CS_INTERFACE, 320 0xFF, UDESCSUB_CDC_UNION, 0xFF); 321 322 if ((cud == NULL) || (cud->bLength < sizeof(*cud))) { 323 DPRINTF("Missing descriptor. " 324 "Assuming data interface is next.\n"); 325 if (sc->sc_ctrl_iface_no == 0xFF) { 326 goto detach; 327 } else { 328 uint8_t class_match = 0; 329 330 /* set default interface number */ 331 sc->sc_data_iface_no = 0xFF; 332 333 /* try to find the data interface backwards */ 334 umodem_find_data_iface(uaa, 335 uaa->info.bIfaceIndex - 1, 336 &sc->sc_data_iface_no, &class_match); 337 338 /* try to find the data interface forwards */ 339 umodem_find_data_iface(uaa, 340 uaa->info.bIfaceIndex + 1, 341 &sc->sc_data_iface_no, &class_match); 342 343 /* check if nothing was found */ 344 if (sc->sc_data_iface_no == 0xFF) 345 goto detach; 346 } 347 } else { 348 sc->sc_data_iface_no = cud->bSlaveInterface[0]; 349 } 350 } else { 351 sc->sc_data_iface_no = cmd->bDataInterface; 352 } 353 354 device_printf(dev, "data interface %d, has %sCM over " 355 "data, has %sbreak\n", 356 sc->sc_data_iface_no, 357 sc->sc_cm_cap & USB_CDC_CM_OVER_DATA ? "" : "no ", 358 sc->sc_acm_cap & USB_CDC_ACM_HAS_BREAK ? "" : "no "); 359 360 /* get the data interface too */ 361 362 for (i = 0;; i++) { 363 struct usb_interface *iface; 364 struct usb_interface_descriptor *id; 365 366 iface = usbd_get_iface(uaa->device, i); 367 368 if (iface) { 369 370 id = usbd_get_interface_descriptor(iface); 371 372 if (id && (id->bInterfaceNumber == sc->sc_data_iface_no)) { 373 sc->sc_iface_index[0] = i; 374 usbd_set_parent_iface(uaa->device, i, uaa->info.bIfaceIndex); 375 break; 376 } 377 } else { 378 device_printf(dev, "no data interface\n"); 379 goto detach; 380 } 381 } 382 383 if (usb_test_quirk(uaa, UQ_ASSUME_CM_OVER_DATA)) { 384 sc->sc_cm_over_data = 1; 385 } else { 386 if (sc->sc_cm_cap & USB_CDC_CM_OVER_DATA) { 387 if (sc->sc_acm_cap & USB_CDC_ACM_HAS_FEATURE) { 388 389 error = umodem_set_comm_feature 390 (uaa->device, sc->sc_ctrl_iface_no, 391 UCDC_ABSTRACT_STATE, UCDC_DATA_MULTIPLEXED); 392 393 /* ignore any errors */ 394 } 395 sc->sc_cm_over_data = 1; 396 } 397 } 398 error = usbd_transfer_setup(uaa->device, 399 sc->sc_iface_index, sc->sc_xfer, 400 umodem_config, UMODEM_N_TRANSFER, 401 sc, &sc->sc_mtx); 402 if (error) { 403 goto detach; 404 } 405 406 /* clear stall at first run */ 407 mtx_lock(&sc->sc_mtx); 408 usbd_xfer_set_stall(sc->sc_xfer[UMODEM_BULK_WR]); 409 usbd_xfer_set_stall(sc->sc_xfer[UMODEM_BULK_RD]); 410 mtx_unlock(&sc->sc_mtx); 411 412 error = ucom_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc, 413 &umodem_callback, &sc->sc_mtx); 414 if (error) { 415 goto detach; 416 } 417 ucom_set_pnpinfo_usb(&sc->sc_super_ucom, dev); 418 419 return (0); 420 421 detach: 422 umodem_detach(dev); 423 return (ENXIO); 424 } 425 426 static void 427 umodem_find_data_iface(struct usb_attach_arg *uaa, 428 uint8_t iface_index, uint8_t *p_data_no, uint8_t *p_match_class) 429 { 430 struct usb_interface_descriptor *id; 431 struct usb_interface *iface; 432 433 iface = usbd_get_iface(uaa->device, iface_index); 434 435 /* check for end of interfaces */ 436 if (iface == NULL) 437 return; 438 439 id = usbd_get_interface_descriptor(iface); 440 441 /* check for non-matching interface class */ 442 if (id->bInterfaceClass != UICLASS_CDC_DATA || 443 id->bInterfaceSubClass != UISUBCLASS_DATA) { 444 /* if we got a class match then return */ 445 if (*p_match_class) 446 return; 447 } else { 448 *p_match_class = 1; 449 } 450 451 DPRINTFN(11, "Match at index %u\n", iface_index); 452 453 *p_data_no = id->bInterfaceNumber; 454 } 455 456 static void 457 umodem_start_read(struct ucom_softc *ucom) 458 { 459 struct umodem_softc *sc = ucom->sc_parent; 460 461 /* start interrupt endpoint, if any */ 462 usbd_transfer_start(sc->sc_xfer[UMODEM_INTR_RD]); 463 464 /* start read endpoint */ 465 usbd_transfer_start(sc->sc_xfer[UMODEM_BULK_RD]); 466 } 467 468 static void 469 umodem_stop_read(struct ucom_softc *ucom) 470 { 471 struct umodem_softc *sc = ucom->sc_parent; 472 473 /* stop interrupt endpoint, if any */ 474 usbd_transfer_stop(sc->sc_xfer[UMODEM_INTR_RD]); 475 476 /* stop read endpoint */ 477 usbd_transfer_stop(sc->sc_xfer[UMODEM_BULK_RD]); 478 } 479 480 static void 481 umodem_start_write(struct ucom_softc *ucom) 482 { 483 struct umodem_softc *sc = ucom->sc_parent; 484 485 usbd_transfer_start(sc->sc_xfer[UMODEM_BULK_WR]); 486 } 487 488 static void 489 umodem_stop_write(struct ucom_softc *ucom) 490 { 491 struct umodem_softc *sc = ucom->sc_parent; 492 493 usbd_transfer_stop(sc->sc_xfer[UMODEM_BULK_WR]); 494 } 495 496 static void 497 umodem_get_caps(struct usb_attach_arg *uaa, uint8_t *cm, uint8_t *acm) 498 { 499 struct usb_cdc_cm_descriptor *cmd; 500 struct usb_cdc_acm_descriptor *cad; 501 502 cmd = umodem_get_desc(uaa, UDESC_CS_INTERFACE, UDESCSUB_CDC_CM); 503 if ((cmd == NULL) || (cmd->bLength < sizeof(*cmd))) { 504 DPRINTF("no CM desc (faking one)\n"); 505 *cm = USB_CDC_CM_DOES_CM | USB_CDC_CM_OVER_DATA; 506 } else 507 *cm = cmd->bmCapabilities; 508 509 cad = umodem_get_desc(uaa, UDESC_CS_INTERFACE, UDESCSUB_CDC_ACM); 510 if ((cad == NULL) || (cad->bLength < sizeof(*cad))) { 511 DPRINTF("no ACM desc\n"); 512 *acm = 0; 513 } else 514 *acm = cad->bmCapabilities; 515 } 516 517 static void 518 umodem_cfg_get_status(struct ucom_softc *ucom, uint8_t *lsr, uint8_t *msr) 519 { 520 struct umodem_softc *sc = ucom->sc_parent; 521 522 DPRINTF("\n"); 523 524 *lsr = sc->sc_lsr; 525 *msr = sc->sc_msr; 526 } 527 528 static int 529 umodem_pre_param(struct ucom_softc *ucom, struct termios *t) 530 { 531 return (0); /* we accept anything */ 532 } 533 534 static void 535 umodem_cfg_param(struct ucom_softc *ucom, struct termios *t) 536 { 537 struct umodem_softc *sc = ucom->sc_parent; 538 struct usb_cdc_line_state ls; 539 struct usb_device_request req; 540 541 DPRINTF("sc=%p\n", sc); 542 543 memset(&ls, 0, sizeof(ls)); 544 545 USETDW(ls.dwDTERate, t->c_ospeed); 546 547 ls.bCharFormat = (t->c_cflag & CSTOPB) ? 548 UCDC_STOP_BIT_2 : UCDC_STOP_BIT_1; 549 550 ls.bParityType = (t->c_cflag & PARENB) ? 551 ((t->c_cflag & PARODD) ? 552 UCDC_PARITY_ODD : UCDC_PARITY_EVEN) : UCDC_PARITY_NONE; 553 554 switch (t->c_cflag & CSIZE) { 555 case CS5: 556 ls.bDataBits = 5; 557 break; 558 case CS6: 559 ls.bDataBits = 6; 560 break; 561 case CS7: 562 ls.bDataBits = 7; 563 break; 564 case CS8: 565 ls.bDataBits = 8; 566 break; 567 } 568 569 DPRINTF("rate=%d fmt=%d parity=%d bits=%d\n", 570 UGETDW(ls.dwDTERate), ls.bCharFormat, 571 ls.bParityType, ls.bDataBits); 572 573 req.bmRequestType = UT_WRITE_CLASS_INTERFACE; 574 req.bRequest = UCDC_SET_LINE_CODING; 575 USETW(req.wValue, 0); 576 req.wIndex[0] = sc->sc_ctrl_iface_no; 577 req.wIndex[1] = 0; 578 USETW(req.wLength, sizeof(ls)); 579 580 ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 581 &req, &ls, 0, 1000); 582 } 583 584 static int 585 umodem_ioctl(struct ucom_softc *ucom, uint32_t cmd, caddr_t data, 586 int flag, struct thread *td) 587 { 588 struct umodem_softc *sc = ucom->sc_parent; 589 int error = 0; 590 591 DPRINTF("cmd=0x%08x\n", cmd); 592 593 switch (cmd) { 594 case USB_GET_CM_OVER_DATA: 595 *(int *)data = sc->sc_cm_over_data; 596 break; 597 598 case USB_SET_CM_OVER_DATA: 599 if (*(int *)data != sc->sc_cm_over_data) { 600 /* XXX change it */ 601 } 602 break; 603 604 default: 605 DPRINTF("unknown\n"); 606 error = ENOIOCTL; 607 break; 608 } 609 610 return (error); 611 } 612 613 static void 614 umodem_cfg_set_dtr(struct ucom_softc *ucom, uint8_t onoff) 615 { 616 struct umodem_softc *sc = ucom->sc_parent; 617 struct usb_device_request req; 618 619 DPRINTF("onoff=%d\n", onoff); 620 621 if (onoff) 622 sc->sc_line |= UCDC_LINE_DTR; 623 else 624 sc->sc_line &= ~UCDC_LINE_DTR; 625 626 req.bmRequestType = UT_WRITE_CLASS_INTERFACE; 627 req.bRequest = UCDC_SET_CONTROL_LINE_STATE; 628 USETW(req.wValue, sc->sc_line); 629 req.wIndex[0] = sc->sc_ctrl_iface_no; 630 req.wIndex[1] = 0; 631 USETW(req.wLength, 0); 632 633 ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 634 &req, NULL, 0, 1000); 635 } 636 637 static void 638 umodem_cfg_set_rts(struct ucom_softc *ucom, uint8_t onoff) 639 { 640 struct umodem_softc *sc = ucom->sc_parent; 641 struct usb_device_request req; 642 643 DPRINTF("onoff=%d\n", onoff); 644 645 if (onoff) 646 sc->sc_line |= UCDC_LINE_RTS; 647 else 648 sc->sc_line &= ~UCDC_LINE_RTS; 649 650 req.bmRequestType = UT_WRITE_CLASS_INTERFACE; 651 req.bRequest = UCDC_SET_CONTROL_LINE_STATE; 652 USETW(req.wValue, sc->sc_line); 653 req.wIndex[0] = sc->sc_ctrl_iface_no; 654 req.wIndex[1] = 0; 655 USETW(req.wLength, 0); 656 657 ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 658 &req, NULL, 0, 1000); 659 } 660 661 static void 662 umodem_cfg_set_break(struct ucom_softc *ucom, uint8_t onoff) 663 { 664 struct umodem_softc *sc = ucom->sc_parent; 665 struct usb_device_request req; 666 uint16_t temp; 667 668 DPRINTF("onoff=%d\n", onoff); 669 670 if (sc->sc_acm_cap & USB_CDC_ACM_HAS_BREAK) { 671 672 temp = onoff ? UCDC_BREAK_ON : UCDC_BREAK_OFF; 673 674 req.bmRequestType = UT_WRITE_CLASS_INTERFACE; 675 req.bRequest = UCDC_SEND_BREAK; 676 USETW(req.wValue, temp); 677 req.wIndex[0] = sc->sc_ctrl_iface_no; 678 req.wIndex[1] = 0; 679 USETW(req.wLength, 0); 680 681 ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 682 &req, NULL, 0, 1000); 683 } 684 } 685 686 static void 687 umodem_intr_callback(struct usb_xfer *xfer, usb_error_t error) 688 { 689 struct usb_cdc_notification pkt; 690 struct umodem_softc *sc = usbd_xfer_softc(xfer); 691 struct usb_page_cache *pc; 692 uint16_t wLen; 693 int actlen; 694 695 usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); 696 697 switch (USB_GET_STATE(xfer)) { 698 case USB_ST_TRANSFERRED: 699 700 if (actlen < 8) { 701 DPRINTF("received short packet, " 702 "%d bytes\n", actlen); 703 goto tr_setup; 704 } 705 if (actlen > (int)sizeof(pkt)) { 706 DPRINTF("truncating message\n"); 707 actlen = sizeof(pkt); 708 } 709 pc = usbd_xfer_get_frame(xfer, 0); 710 usbd_copy_out(pc, 0, &pkt, actlen); 711 712 actlen -= 8; 713 714 wLen = UGETW(pkt.wLength); 715 if (actlen > wLen) { 716 actlen = wLen; 717 } 718 if (pkt.bmRequestType != UCDC_NOTIFICATION) { 719 DPRINTF("unknown message type, " 720 "0x%02x, on notify pipe!\n", 721 pkt.bmRequestType); 722 goto tr_setup; 723 } 724 switch (pkt.bNotification) { 725 case UCDC_N_SERIAL_STATE: 726 /* 727 * Set the serial state in ucom driver based on 728 * the bits from the notify message 729 */ 730 if (actlen < 2) { 731 DPRINTF("invalid notification " 732 "length, %d bytes!\n", actlen); 733 break; 734 } 735 DPRINTF("notify bytes = %02x%02x\n", 736 pkt.data[0], 737 pkt.data[1]); 738 739 /* Currently, lsr is always zero. */ 740 sc->sc_lsr = 0; 741 sc->sc_msr = 0; 742 743 if (pkt.data[0] & UCDC_N_SERIAL_RI) { 744 sc->sc_msr |= SER_RI; 745 } 746 if (pkt.data[0] & UCDC_N_SERIAL_DSR) { 747 sc->sc_msr |= SER_DSR; 748 } 749 if (pkt.data[0] & UCDC_N_SERIAL_DCD) { 750 sc->sc_msr |= SER_DCD; 751 } 752 ucom_status_change(&sc->sc_ucom); 753 break; 754 755 default: 756 DPRINTF("unknown notify message: 0x%02x\n", 757 pkt.bNotification); 758 break; 759 } 760 761 case USB_ST_SETUP: 762 tr_setup: 763 usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); 764 usbd_transfer_submit(xfer); 765 return; 766 767 default: /* Error */ 768 if (error != USB_ERR_CANCELLED) { 769 /* try to clear stall first */ 770 usbd_xfer_set_stall(xfer); 771 goto tr_setup; 772 } 773 return; 774 775 } 776 } 777 778 static void 779 umodem_write_callback(struct usb_xfer *xfer, usb_error_t error) 780 { 781 struct umodem_softc *sc = usbd_xfer_softc(xfer); 782 struct usb_page_cache *pc; 783 uint32_t actlen; 784 785 switch (USB_GET_STATE(xfer)) { 786 case USB_ST_SETUP: 787 case USB_ST_TRANSFERRED: 788 tr_setup: 789 pc = usbd_xfer_get_frame(xfer, 0); 790 if (ucom_get_data(&sc->sc_ucom, pc, 0, 791 UMODEM_BUF_SIZE, &actlen)) { 792 793 usbd_xfer_set_frame_len(xfer, 0, actlen); 794 usbd_transfer_submit(xfer); 795 } 796 return; 797 798 default: /* Error */ 799 if (error != USB_ERR_CANCELLED) { 800 /* try to clear stall first */ 801 usbd_xfer_set_stall(xfer); 802 goto tr_setup; 803 } 804 return; 805 } 806 } 807 808 static void 809 umodem_read_callback(struct usb_xfer *xfer, usb_error_t error) 810 { 811 struct umodem_softc *sc = usbd_xfer_softc(xfer); 812 struct usb_page_cache *pc; 813 int actlen; 814 815 usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); 816 817 switch (USB_GET_STATE(xfer)) { 818 case USB_ST_TRANSFERRED: 819 820 DPRINTF("actlen=%d\n", actlen); 821 822 pc = usbd_xfer_get_frame(xfer, 0); 823 ucom_put_data(&sc->sc_ucom, pc, 0, actlen); 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 static void * 842 umodem_get_desc(struct usb_attach_arg *uaa, uint8_t type, uint8_t subtype) 843 { 844 return (usbd_find_descriptor(uaa->device, NULL, uaa->info.bIfaceIndex, 845 type, 0xFF, subtype, 0xFF)); 846 } 847 848 static usb_error_t 849 umodem_set_comm_feature(struct usb_device *udev, uint8_t iface_no, 850 uint16_t feature, uint16_t state) 851 { 852 struct usb_device_request req; 853 struct usb_cdc_abstract_state ast; 854 855 DPRINTF("feature=%d state=%d\n", 856 feature, state); 857 858 req.bmRequestType = UT_WRITE_CLASS_INTERFACE; 859 req.bRequest = UCDC_SET_COMM_FEATURE; 860 USETW(req.wValue, feature); 861 req.wIndex[0] = iface_no; 862 req.wIndex[1] = 0; 863 USETW(req.wLength, UCDC_ABSTRACT_STATE_LENGTH); 864 USETW(ast.wState, state); 865 866 return (usbd_do_request(udev, NULL, &req, &ast)); 867 } 868 869 static int 870 umodem_detach(device_t dev) 871 { 872 struct umodem_softc *sc = device_get_softc(dev); 873 874 DPRINTF("sc=%p\n", sc); 875 876 ucom_detach(&sc->sc_super_ucom, &sc->sc_ucom); 877 usbd_transfer_unsetup(sc->sc_xfer, UMODEM_N_TRANSFER); 878 879 device_claim_softc(dev); 880 881 umodem_free_softc(sc); 882 883 return (0); 884 } 885 886 UCOM_UNLOAD_DRAIN(umodem); 887 888 static void 889 umodem_free_softc(struct umodem_softc *sc) 890 { 891 if (ucom_unref(&sc->sc_super_ucom)) { 892 mtx_destroy(&sc->sc_mtx); 893 device_free_softc(sc); 894 } 895 } 896 897 static void 898 umodem_free(struct ucom_softc *ucom) 899 { 900 umodem_free_softc(ucom->sc_parent); 901 } 902 903 static void 904 umodem_poll(struct ucom_softc *ucom) 905 { 906 struct umodem_softc *sc = ucom->sc_parent; 907 usbd_transfer_poll(sc->sc_xfer, UMODEM_N_TRANSFER); 908 } 909