1 /* $NetBSD: umodem.c,v 1.45 2002/09/23 05:51:23 simonb Exp $ */ 2 3 #include <sys/cdefs.h> 4 __FBSDID("$FreeBSD$"); 5 6 /*- 7 * Copyright (c) 2003, M. Warner Losh <imp@FreeBSD.org>. 8 * All rights reserved. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32 /*- 33 * Copyright (c) 1998 The NetBSD Foundation, Inc. 34 * All rights reserved. 35 * 36 * This code is derived from software contributed to The NetBSD Foundation 37 * by Lennart Augustsson (lennart@augustsson.net) at 38 * Carlstedt Research & Technology. 39 * 40 * Redistribution and use in source and binary forms, with or without 41 * modification, are permitted provided that the following conditions 42 * are met: 43 * 1. Redistributions of source code must retain the above copyright 44 * notice, this list of conditions and the following disclaimer. 45 * 2. Redistributions in binary form must reproduce the above copyright 46 * notice, this list of conditions and the following disclaimer in the 47 * documentation and/or other materials provided with the distribution. 48 * 3. All advertising materials mentioning features or use of this software 49 * must display the following acknowledgement: 50 * This product includes software developed by the NetBSD 51 * Foundation, Inc. and its contributors. 52 * 4. Neither the name of The NetBSD Foundation nor the names of its 53 * contributors may be used to endorse or promote products derived 54 * from this software without specific prior written permission. 55 * 56 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 57 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 58 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 59 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 60 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 61 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 62 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 63 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 64 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 65 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 66 * POSSIBILITY OF SUCH DAMAGE. 67 */ 68 69 /* 70 * Comm Class spec: http://www.usb.org/developers/devclass_docs/usbccs10.pdf 71 * http://www.usb.org/developers/devclass_docs/usbcdc11.pdf 72 */ 73 74 /* 75 * TODO: 76 * - Add error recovery in various places; the big problem is what 77 * to do in a callback if there is an error. 78 * - Implement a Call Device for modems without multiplexed commands. 79 * 80 */ 81 82 #include "usbdevs.h" 83 #include <dev/usb/usb.h> 84 #include <dev/usb/usb_mfunc.h> 85 #include <dev/usb/usb_error.h> 86 #include <dev/usb/usb_cdc.h> 87 #include <dev/usb/usb_ioctl.h> 88 #include <dev/usb/usb_defs.h> 89 90 #define USB_DEBUG_VAR umodem_debug 91 92 #include <dev/usb/usb_core.h> 93 #include <dev/usb/usb_debug.h> 94 #include <dev/usb/usb_process.h> 95 #include <dev/usb/usb_request.h> 96 #include <dev/usb/usb_lookup.h> 97 #include <dev/usb/usb_util.h> 98 #include <dev/usb/usb_busdma.h> 99 #include <dev/usb/usb_device.h> 100 101 #include <dev/usb/serial/usb_serial.h> 102 103 #if USB_DEBUG 104 static int umodem_debug = 0; 105 106 SYSCTL_NODE(_hw_usb2, OID_AUTO, umodem, CTLFLAG_RW, 0, "USB umodem"); 107 SYSCTL_INT(_hw_usb2_umodem, OID_AUTO, debug, CTLFLAG_RW, 108 &umodem_debug, 0, "Debug level"); 109 #endif 110 111 static const struct usb2_device_id umodem_devs[] = { 112 /* Generic Modem class match */ 113 {USB_IFACE_CLASS(UICLASS_CDC), 114 USB_IFACE_SUBCLASS(UISUBCLASS_ABSTRACT_CONTROL_MODEL), 115 USB_IFACE_PROTOCOL(UIPROTO_CDC_AT)}, 116 /* Kyocera AH-K3001V */ 117 {USB_VPI(USB_VENDOR_KYOCERA, USB_PRODUCT_KYOCERA_AHK3001V, 1)}, 118 {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC5720, 1)}, 119 {USB_VPI(USB_VENDOR_CURITEL, USB_PRODUCT_CURITEL_PC5740, 1)}, 120 }; 121 122 /* 123 * As speeds for umodem deivces increase, these numbers will need to 124 * be increased. They should be good for G3 speeds and below. 125 * 126 * TODO: The TTY buffers should be increased! 127 */ 128 #define UMODEM_BUF_SIZE 1024 129 130 enum { 131 UMODEM_BULK_WR, 132 UMODEM_BULK_RD, 133 UMODEM_INTR_RD, 134 UMODEM_N_TRANSFER, 135 }; 136 137 #define UMODEM_MODVER 1 /* module version */ 138 139 struct umodem_softc { 140 struct usb2_com_super_softc sc_super_ucom; 141 struct usb2_com_softc sc_ucom; 142 143 struct usb2_xfer *sc_xfer[UMODEM_N_TRANSFER]; 144 struct usb2_device *sc_udev; 145 146 uint16_t sc_line; 147 148 uint8_t sc_lsr; /* local status register */ 149 uint8_t sc_msr; /* modem status register */ 150 uint8_t sc_ctrl_iface_no; 151 uint8_t sc_data_iface_no; 152 uint8_t sc_iface_index[2]; 153 uint8_t sc_cm_over_data; 154 uint8_t sc_cm_cap; /* CM capabilities */ 155 uint8_t sc_acm_cap; /* ACM capabilities */ 156 }; 157 158 static device_probe_t umodem_probe; 159 static device_attach_t umodem_attach; 160 static device_detach_t umodem_detach; 161 162 static usb2_callback_t umodem_intr_callback; 163 static usb2_callback_t umodem_write_callback; 164 static usb2_callback_t umodem_read_callback; 165 166 static void umodem_start_read(struct usb2_com_softc *); 167 static void umodem_stop_read(struct usb2_com_softc *); 168 static void umodem_start_write(struct usb2_com_softc *); 169 static void umodem_stop_write(struct usb2_com_softc *); 170 static void umodem_get_caps(struct usb2_attach_arg *, uint8_t *, uint8_t *); 171 static void umodem_cfg_get_status(struct usb2_com_softc *, uint8_t *, 172 uint8_t *); 173 static int umodem_pre_param(struct usb2_com_softc *, struct termios *); 174 static void umodem_cfg_param(struct usb2_com_softc *, struct termios *); 175 static int umodem_ioctl(struct usb2_com_softc *, uint32_t, caddr_t, int, 176 struct thread *); 177 static void umodem_cfg_set_dtr(struct usb2_com_softc *, uint8_t); 178 static void umodem_cfg_set_rts(struct usb2_com_softc *, uint8_t); 179 static void umodem_cfg_set_break(struct usb2_com_softc *, uint8_t); 180 static void *umodem_get_desc(struct usb2_attach_arg *, uint8_t, uint8_t); 181 static usb2_error_t umodem_set_comm_feature(struct usb2_device *, uint8_t, 182 uint16_t, uint16_t); 183 184 static const struct usb2_config umodem_config[UMODEM_N_TRANSFER] = { 185 186 [UMODEM_BULK_WR] = { 187 .type = UE_BULK, 188 .endpoint = UE_ADDR_ANY, 189 .direction = UE_DIR_OUT, 190 .if_index = 0, 191 .mh.bufsize = UMODEM_BUF_SIZE, 192 .mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,}, 193 .mh.callback = &umodem_write_callback, 194 }, 195 196 [UMODEM_BULK_RD] = { 197 .type = UE_BULK, 198 .endpoint = UE_ADDR_ANY, 199 .direction = UE_DIR_IN, 200 .if_index = 0, 201 .mh.bufsize = UMODEM_BUF_SIZE, 202 .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, 203 .mh.callback = &umodem_read_callback, 204 }, 205 206 [UMODEM_INTR_RD] = { 207 .type = UE_INTERRUPT, 208 .endpoint = UE_ADDR_ANY, 209 .direction = UE_DIR_IN, 210 .if_index = 1, 211 .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,.no_pipe_ok = 1,}, 212 .mh.bufsize = 0, /* use wMaxPacketSize */ 213 .mh.callback = &umodem_intr_callback, 214 }, 215 }; 216 217 static const struct usb2_com_callback umodem_callback = { 218 .usb2_com_cfg_get_status = &umodem_cfg_get_status, 219 .usb2_com_cfg_set_dtr = &umodem_cfg_set_dtr, 220 .usb2_com_cfg_set_rts = &umodem_cfg_set_rts, 221 .usb2_com_cfg_set_break = &umodem_cfg_set_break, 222 .usb2_com_cfg_param = &umodem_cfg_param, 223 .usb2_com_pre_param = &umodem_pre_param, 224 .usb2_com_ioctl = &umodem_ioctl, 225 .usb2_com_start_read = &umodem_start_read, 226 .usb2_com_stop_read = &umodem_stop_read, 227 .usb2_com_start_write = &umodem_start_write, 228 .usb2_com_stop_write = &umodem_stop_write, 229 }; 230 231 static device_method_t umodem_methods[] = { 232 DEVMETHOD(device_probe, umodem_probe), 233 DEVMETHOD(device_attach, umodem_attach), 234 DEVMETHOD(device_detach, umodem_detach), 235 {0, 0} 236 }; 237 238 static devclass_t umodem_devclass; 239 240 static driver_t umodem_driver = { 241 .name = "umodem", 242 .methods = umodem_methods, 243 .size = sizeof(struct umodem_softc), 244 }; 245 246 DRIVER_MODULE(umodem, ushub, umodem_driver, umodem_devclass, NULL, 0); 247 MODULE_DEPEND(umodem, ucom, 1, 1, 1); 248 MODULE_DEPEND(umodem, usb, 1, 1, 1); 249 MODULE_VERSION(umodem, UMODEM_MODVER); 250 251 static int 252 umodem_probe(device_t dev) 253 { 254 struct usb2_attach_arg *uaa = device_get_ivars(dev); 255 uint8_t cm; 256 uint8_t acm; 257 int error; 258 259 DPRINTFN(11, "\n"); 260 261 if (uaa->usb2_mode != USB_MODE_HOST) { 262 return (ENXIO); 263 } 264 error = usb2_lookup_id_by_uaa(umodem_devs, sizeof(umodem_devs), uaa); 265 if (error) { 266 return (error); 267 } 268 if (uaa->driver_info == NULL) { 269 /* some modems do not have any capabilities */ 270 return (error); 271 } 272 umodem_get_caps(uaa, &cm, &acm); 273 if (!(cm & USB_CDC_CM_DOES_CM) || 274 !(cm & USB_CDC_CM_OVER_DATA) || 275 !(acm & USB_CDC_ACM_HAS_LINE)) { 276 error = ENXIO; 277 } 278 return (error); 279 } 280 281 static int 282 umodem_attach(device_t dev) 283 { 284 struct usb2_attach_arg *uaa = device_get_ivars(dev); 285 struct umodem_softc *sc = device_get_softc(dev); 286 struct usb2_cdc_cm_descriptor *cmd; 287 uint8_t i; 288 int error; 289 290 device_set_usb2_desc(dev); 291 292 sc->sc_ctrl_iface_no = uaa->info.bIfaceNum; 293 sc->sc_iface_index[1] = uaa->info.bIfaceIndex; 294 sc->sc_udev = uaa->device; 295 296 umodem_get_caps(uaa, &sc->sc_cm_cap, &sc->sc_acm_cap); 297 298 /* get the data interface number */ 299 300 cmd = umodem_get_desc(uaa, UDESC_CS_INTERFACE, UDESCSUB_CDC_CM); 301 302 if ((cmd == NULL) || (cmd->bLength < sizeof(*cmd))) { 303 device_printf(dev, "no CM descriptor!\n"); 304 goto detach; 305 } 306 sc->sc_data_iface_no = cmd->bDataInterface; 307 308 device_printf(dev, "data interface %d, has %sCM over " 309 "data, has %sbreak\n", 310 sc->sc_data_iface_no, 311 sc->sc_cm_cap & USB_CDC_CM_OVER_DATA ? "" : "no ", 312 sc->sc_acm_cap & USB_CDC_ACM_HAS_BREAK ? "" : "no "); 313 314 /* get the data interface too */ 315 316 for (i = 0;; i++) { 317 struct usb2_interface *iface; 318 struct usb2_interface_descriptor *id; 319 320 iface = usb2_get_iface(uaa->device, i); 321 322 if (iface) { 323 324 id = usb2_get_interface_descriptor(iface); 325 326 if (id && (id->bInterfaceNumber == sc->sc_data_iface_no)) { 327 sc->sc_iface_index[0] = i; 328 usb2_set_parent_iface(uaa->device, i, uaa->info.bIfaceIndex); 329 break; 330 } 331 } else { 332 device_printf(dev, "no data interface!\n"); 333 goto detach; 334 } 335 } 336 337 if (sc->sc_cm_cap & USB_CDC_CM_OVER_DATA) { 338 if (sc->sc_acm_cap & USB_CDC_ACM_HAS_FEATURE) { 339 340 error = umodem_set_comm_feature 341 (uaa->device, sc->sc_ctrl_iface_no, 342 UCDC_ABSTRACT_STATE, UCDC_DATA_MULTIPLEXED); 343 344 /* ignore any errors */ 345 } 346 sc->sc_cm_over_data = 1; 347 } 348 error = usb2_transfer_setup(uaa->device, 349 sc->sc_iface_index, sc->sc_xfer, 350 umodem_config, UMODEM_N_TRANSFER, 351 sc, &Giant); 352 if (error) { 353 goto detach; 354 } 355 356 /* clear stall at first run */ 357 usb2_transfer_set_stall(sc->sc_xfer[UMODEM_BULK_WR]); 358 usb2_transfer_set_stall(sc->sc_xfer[UMODEM_BULK_RD]); 359 360 error = usb2_com_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc, 361 &umodem_callback, &Giant); 362 if (error) { 363 goto detach; 364 } 365 return (0); 366 367 detach: 368 umodem_detach(dev); 369 return (ENXIO); 370 } 371 372 static void 373 umodem_start_read(struct usb2_com_softc *ucom) 374 { 375 struct umodem_softc *sc = ucom->sc_parent; 376 377 /* start interrupt endpoint, if any */ 378 usb2_transfer_start(sc->sc_xfer[UMODEM_INTR_RD]); 379 380 /* start read endpoint */ 381 usb2_transfer_start(sc->sc_xfer[UMODEM_BULK_RD]); 382 } 383 384 static void 385 umodem_stop_read(struct usb2_com_softc *ucom) 386 { 387 struct umodem_softc *sc = ucom->sc_parent; 388 389 /* stop interrupt endpoint, if any */ 390 usb2_transfer_stop(sc->sc_xfer[UMODEM_INTR_RD]); 391 392 /* stop read endpoint */ 393 usb2_transfer_stop(sc->sc_xfer[UMODEM_BULK_RD]); 394 } 395 396 static void 397 umodem_start_write(struct usb2_com_softc *ucom) 398 { 399 struct umodem_softc *sc = ucom->sc_parent; 400 401 usb2_transfer_start(sc->sc_xfer[UMODEM_BULK_WR]); 402 } 403 404 static void 405 umodem_stop_write(struct usb2_com_softc *ucom) 406 { 407 struct umodem_softc *sc = ucom->sc_parent; 408 409 usb2_transfer_stop(sc->sc_xfer[UMODEM_BULK_WR]); 410 } 411 412 static void 413 umodem_get_caps(struct usb2_attach_arg *uaa, uint8_t *cm, uint8_t *acm) 414 { 415 struct usb2_cdc_cm_descriptor *cmd; 416 struct usb2_cdc_acm_descriptor *cad; 417 418 *cm = *acm = 0; 419 420 cmd = umodem_get_desc(uaa, UDESC_CS_INTERFACE, UDESCSUB_CDC_CM); 421 if ((cmd == NULL) || (cmd->bLength < sizeof(*cmd))) { 422 DPRINTF("no CM desc\n"); 423 return; 424 } 425 *cm = cmd->bmCapabilities; 426 427 cad = umodem_get_desc(uaa, UDESC_CS_INTERFACE, UDESCSUB_CDC_ACM); 428 if ((cad == NULL) || (cad->bLength < sizeof(*cad))) { 429 DPRINTF("no ACM desc\n"); 430 return; 431 } 432 *acm = cad->bmCapabilities; 433 } 434 435 static void 436 umodem_cfg_get_status(struct usb2_com_softc *ucom, uint8_t *lsr, uint8_t *msr) 437 { 438 struct umodem_softc *sc = ucom->sc_parent; 439 440 DPRINTF("\n"); 441 442 *lsr = sc->sc_lsr; 443 *msr = sc->sc_msr; 444 } 445 446 static int 447 umodem_pre_param(struct usb2_com_softc *ucom, struct termios *t) 448 { 449 return (0); /* we accept anything */ 450 } 451 452 static void 453 umodem_cfg_param(struct usb2_com_softc *ucom, struct termios *t) 454 { 455 struct umodem_softc *sc = ucom->sc_parent; 456 struct usb2_cdc_line_state ls; 457 struct usb2_device_request req; 458 459 DPRINTF("sc=%p\n", sc); 460 461 bzero(&ls, sizeof(ls)); 462 463 USETDW(ls.dwDTERate, t->c_ospeed); 464 465 ls.bCharFormat = (t->c_cflag & CSTOPB) ? 466 UCDC_STOP_BIT_2 : UCDC_STOP_BIT_1; 467 468 ls.bParityType = (t->c_cflag & PARENB) ? 469 ((t->c_cflag & PARODD) ? 470 UCDC_PARITY_ODD : UCDC_PARITY_EVEN) : UCDC_PARITY_NONE; 471 472 switch (t->c_cflag & CSIZE) { 473 case CS5: 474 ls.bDataBits = 5; 475 break; 476 case CS6: 477 ls.bDataBits = 6; 478 break; 479 case CS7: 480 ls.bDataBits = 7; 481 break; 482 case CS8: 483 ls.bDataBits = 8; 484 break; 485 } 486 487 DPRINTF("rate=%d fmt=%d parity=%d bits=%d\n", 488 UGETDW(ls.dwDTERate), ls.bCharFormat, 489 ls.bParityType, ls.bDataBits); 490 491 req.bmRequestType = UT_WRITE_CLASS_INTERFACE; 492 req.bRequest = UCDC_SET_LINE_CODING; 493 USETW(req.wValue, 0); 494 req.wIndex[0] = sc->sc_ctrl_iface_no; 495 req.wIndex[1] = 0; 496 USETW(req.wLength, sizeof(ls)); 497 498 usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 499 &req, &ls, 0, 1000); 500 } 501 502 static int 503 umodem_ioctl(struct usb2_com_softc *ucom, uint32_t cmd, caddr_t data, 504 int flag, struct thread *td) 505 { 506 struct umodem_softc *sc = ucom->sc_parent; 507 int error = 0; 508 509 DPRINTF("cmd=0x%08x\n", cmd); 510 511 switch (cmd) { 512 case USB_GET_CM_OVER_DATA: 513 *(int *)data = sc->sc_cm_over_data; 514 break; 515 516 case USB_SET_CM_OVER_DATA: 517 if (*(int *)data != sc->sc_cm_over_data) { 518 /* XXX change it */ 519 } 520 break; 521 522 default: 523 DPRINTF("unknown\n"); 524 error = ENOIOCTL; 525 break; 526 } 527 528 return (error); 529 } 530 531 static void 532 umodem_cfg_set_dtr(struct usb2_com_softc *ucom, uint8_t onoff) 533 { 534 struct umodem_softc *sc = ucom->sc_parent; 535 struct usb2_device_request req; 536 537 DPRINTF("onoff=%d\n", onoff); 538 539 if (onoff) 540 sc->sc_line |= UCDC_LINE_DTR; 541 else 542 sc->sc_line &= ~UCDC_LINE_DTR; 543 544 req.bmRequestType = UT_WRITE_CLASS_INTERFACE; 545 req.bRequest = UCDC_SET_CONTROL_LINE_STATE; 546 USETW(req.wValue, sc->sc_line); 547 req.wIndex[0] = sc->sc_ctrl_iface_no; 548 req.wIndex[1] = 0; 549 USETW(req.wLength, 0); 550 551 usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 552 &req, NULL, 0, 1000); 553 } 554 555 static void 556 umodem_cfg_set_rts(struct usb2_com_softc *ucom, uint8_t onoff) 557 { 558 struct umodem_softc *sc = ucom->sc_parent; 559 struct usb2_device_request req; 560 561 DPRINTF("onoff=%d\n", onoff); 562 563 if (onoff) 564 sc->sc_line |= UCDC_LINE_RTS; 565 else 566 sc->sc_line &= ~UCDC_LINE_RTS; 567 568 req.bmRequestType = UT_WRITE_CLASS_INTERFACE; 569 req.bRequest = UCDC_SET_CONTROL_LINE_STATE; 570 USETW(req.wValue, sc->sc_line); 571 req.wIndex[0] = sc->sc_ctrl_iface_no; 572 req.wIndex[1] = 0; 573 USETW(req.wLength, 0); 574 575 usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 576 &req, NULL, 0, 1000); 577 } 578 579 static void 580 umodem_cfg_set_break(struct usb2_com_softc *ucom, uint8_t onoff) 581 { 582 struct umodem_softc *sc = ucom->sc_parent; 583 struct usb2_device_request req; 584 uint16_t temp; 585 586 DPRINTF("onoff=%d\n", onoff); 587 588 if (sc->sc_acm_cap & USB_CDC_ACM_HAS_BREAK) { 589 590 temp = onoff ? UCDC_BREAK_ON : UCDC_BREAK_OFF; 591 592 req.bmRequestType = UT_WRITE_CLASS_INTERFACE; 593 req.bRequest = UCDC_SEND_BREAK; 594 USETW(req.wValue, temp); 595 req.wIndex[0] = sc->sc_ctrl_iface_no; 596 req.wIndex[1] = 0; 597 USETW(req.wLength, 0); 598 599 usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 600 &req, NULL, 0, 1000); 601 } 602 } 603 604 static void 605 umodem_intr_callback(struct usb2_xfer *xfer) 606 { 607 struct usb2_cdc_notification pkt; 608 struct umodem_softc *sc = xfer->priv_sc; 609 uint16_t wLen; 610 611 switch (USB_GET_STATE(xfer)) { 612 case USB_ST_TRANSFERRED: 613 614 if (xfer->actlen < 8) { 615 DPRINTF("received short packet, " 616 "%d bytes\n", xfer->actlen); 617 goto tr_setup; 618 } 619 if (xfer->actlen > sizeof(pkt)) { 620 DPRINTF("truncating message\n"); 621 xfer->actlen = sizeof(pkt); 622 } 623 usb2_copy_out(xfer->frbuffers, 0, &pkt, xfer->actlen); 624 625 xfer->actlen -= 8; 626 627 wLen = UGETW(pkt.wLength); 628 if (xfer->actlen > wLen) { 629 xfer->actlen = wLen; 630 } 631 if (pkt.bmRequestType != UCDC_NOTIFICATION) { 632 DPRINTF("unknown message type, " 633 "0x%02x, on notify pipe!\n", 634 pkt.bmRequestType); 635 goto tr_setup; 636 } 637 switch (pkt.bNotification) { 638 case UCDC_N_SERIAL_STATE: 639 /* 640 * Set the serial state in ucom driver based on 641 * the bits from the notify message 642 */ 643 if (xfer->actlen < 2) { 644 DPRINTF("invalid notification " 645 "length, %d bytes!\n", xfer->actlen); 646 break; 647 } 648 DPRINTF("notify bytes = %02x%02x\n", 649 pkt.data[0], 650 pkt.data[1]); 651 652 /* Currently, lsr is always zero. */ 653 sc->sc_lsr = 0; 654 sc->sc_msr = 0; 655 656 if (pkt.data[0] & UCDC_N_SERIAL_RI) { 657 sc->sc_msr |= SER_RI; 658 } 659 if (pkt.data[0] & UCDC_N_SERIAL_DSR) { 660 sc->sc_msr |= SER_DSR; 661 } 662 if (pkt.data[0] & UCDC_N_SERIAL_DCD) { 663 sc->sc_msr |= SER_DCD; 664 } 665 usb2_com_status_change(&sc->sc_ucom); 666 break; 667 668 default: 669 DPRINTF("unknown notify message: 0x%02x\n", 670 pkt.bNotification); 671 break; 672 } 673 674 case USB_ST_SETUP: 675 tr_setup: 676 xfer->frlengths[0] = xfer->max_data_length; 677 usb2_start_hardware(xfer); 678 return; 679 680 default: /* Error */ 681 if (xfer->error != USB_ERR_CANCELLED) { 682 /* try to clear stall first */ 683 xfer->flags.stall_pipe = 1; 684 goto tr_setup; 685 } 686 return; 687 688 } 689 } 690 691 static void 692 umodem_write_callback(struct usb2_xfer *xfer) 693 { 694 struct umodem_softc *sc = xfer->priv_sc; 695 uint32_t actlen; 696 697 switch (USB_GET_STATE(xfer)) { 698 case USB_ST_SETUP: 699 case USB_ST_TRANSFERRED: 700 tr_setup: 701 if (usb2_com_get_data(&sc->sc_ucom, xfer->frbuffers, 0, 702 UMODEM_BUF_SIZE, &actlen)) { 703 704 xfer->frlengths[0] = actlen; 705 usb2_start_hardware(xfer); 706 } 707 return; 708 709 default: /* Error */ 710 if (xfer->error != USB_ERR_CANCELLED) { 711 /* try to clear stall first */ 712 xfer->flags.stall_pipe = 1; 713 goto tr_setup; 714 } 715 return; 716 } 717 } 718 719 static void 720 umodem_read_callback(struct usb2_xfer *xfer) 721 { 722 struct umodem_softc *sc = xfer->priv_sc; 723 724 switch (USB_GET_STATE(xfer)) { 725 case USB_ST_TRANSFERRED: 726 727 DPRINTF("actlen=%d\n", xfer->actlen); 728 729 usb2_com_put_data(&sc->sc_ucom, xfer->frbuffers, 0, 730 xfer->actlen); 731 732 case USB_ST_SETUP: 733 tr_setup: 734 xfer->frlengths[0] = xfer->max_data_length; 735 usb2_start_hardware(xfer); 736 return; 737 738 default: /* Error */ 739 if (xfer->error != USB_ERR_CANCELLED) { 740 /* try to clear stall first */ 741 xfer->flags.stall_pipe = 1; 742 goto tr_setup; 743 } 744 return; 745 } 746 } 747 748 static void * 749 umodem_get_desc(struct usb2_attach_arg *uaa, uint8_t type, uint8_t subtype) 750 { 751 return (usb2_find_descriptor(uaa->device, NULL, uaa->info.bIfaceIndex, 752 type, 0 - 1, subtype, 0 - 1)); 753 } 754 755 static usb2_error_t 756 umodem_set_comm_feature(struct usb2_device *udev, uint8_t iface_no, 757 uint16_t feature, uint16_t state) 758 { 759 struct usb2_device_request req; 760 struct usb2_cdc_abstract_state ast; 761 762 DPRINTF("feature=%d state=%d\n", 763 feature, state); 764 765 req.bmRequestType = UT_WRITE_CLASS_INTERFACE; 766 req.bRequest = UCDC_SET_COMM_FEATURE; 767 USETW(req.wValue, feature); 768 req.wIndex[0] = iface_no; 769 req.wIndex[1] = 0; 770 USETW(req.wLength, UCDC_ABSTRACT_STATE_LENGTH); 771 USETW(ast.wState, state); 772 773 return (usb2_do_request(udev, &Giant, &req, &ast)); 774 } 775 776 static int 777 umodem_detach(device_t dev) 778 { 779 struct umodem_softc *sc = device_get_softc(dev); 780 781 DPRINTF("sc=%p\n", sc); 782 783 usb2_com_detach(&sc->sc_super_ucom, &sc->sc_ucom, 1); 784 785 usb2_transfer_unsetup(sc->sc_xfer, UMODEM_N_TRANSFER); 786 787 return (0); 788 } 789