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