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