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