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