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