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