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