1 /* $OpenBSD: uslcom.c,v 1.17 2007/11/24 10:52:12 jsg Exp $ */ 2 3 #include <sys/cdefs.h> 4 __FBSDID("$FreeBSD$"); 5 6 /* 7 * Copyright (c) 2006 Jonathan Gray <jsg@openbsd.org> 8 * 9 * Permission to use, copy, modify, and distribute this software for any 10 * purpose with or without fee is hereby granted, provided that the above 11 * copyright notice and this permission notice appear in all copies. 12 * 13 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 14 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 15 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 16 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 17 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 18 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 19 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 20 */ 21 22 #include "usbdevs.h" 23 #include <dev/usb/usb.h> 24 #include <dev/usb/usb_mfunc.h> 25 #include <dev/usb/usb_error.h> 26 27 #define USB_DEBUG_VAR uslcom_debug 28 29 #include <dev/usb/usb_core.h> 30 #include <dev/usb/usb_debug.h> 31 #include <dev/usb/usb_process.h> 32 #include <dev/usb/usb_request.h> 33 #include <dev/usb/usb_lookup.h> 34 #include <dev/usb/usb_util.h> 35 #include <dev/usb/usb_busdma.h> 36 37 #include <dev/usb/serial/usb_serial.h> 38 39 #if USB_DEBUG 40 static int uslcom_debug = 0; 41 42 SYSCTL_NODE(_hw_usb2, OID_AUTO, uslcom, CTLFLAG_RW, 0, "USB uslcom"); 43 SYSCTL_INT(_hw_usb2_uslcom, OID_AUTO, debug, CTLFLAG_RW, 44 &uslcom_debug, 0, "Debug level"); 45 #endif 46 47 #define USLCOM_BULK_BUF_SIZE 1024 48 #define USLCOM_CONFIG_INDEX 0 49 #define USLCOM_IFACE_INDEX 0 50 51 #define USLCOM_SET_DATA_BITS(x) ((x) << 8) 52 53 #define USLCOM_WRITE 0x41 54 #define USLCOM_READ 0xc1 55 56 #define USLCOM_UART 0x00 57 #define USLCOM_BAUD_RATE 0x01 58 #define USLCOM_DATA 0x03 59 #define USLCOM_BREAK 0x05 60 #define USLCOM_CTRL 0x07 61 62 #define USLCOM_UART_DISABLE 0x00 63 #define USLCOM_UART_ENABLE 0x01 64 65 #define USLCOM_CTRL_DTR_ON 0x0001 66 #define USLCOM_CTRL_DTR_SET 0x0100 67 #define USLCOM_CTRL_RTS_ON 0x0002 68 #define USLCOM_CTRL_RTS_SET 0x0200 69 #define USLCOM_CTRL_CTS 0x0010 70 #define USLCOM_CTRL_DSR 0x0020 71 #define USLCOM_CTRL_DCD 0x0080 72 73 #define USLCOM_BAUD_REF 0x384000 74 75 #define USLCOM_STOP_BITS_1 0x00 76 #define USLCOM_STOP_BITS_2 0x02 77 78 #define USLCOM_PARITY_NONE 0x00 79 #define USLCOM_PARITY_ODD 0x10 80 #define USLCOM_PARITY_EVEN 0x20 81 82 #define USLCOM_PORT_NO 0xFFFF /* XXX think this should be 0 --hps */ 83 84 #define USLCOM_BREAK_OFF 0x00 85 #define USLCOM_BREAK_ON 0x01 86 87 enum { 88 USLCOM_BULK_DT_WR, 89 USLCOM_BULK_DT_RD, 90 USLCOM_N_TRANSFER, 91 }; 92 93 struct uslcom_softc { 94 struct usb2_com_super_softc sc_super_ucom; 95 struct usb2_com_softc sc_ucom; 96 97 struct usb2_xfer *sc_xfer[USLCOM_N_TRANSFER]; 98 struct usb2_device *sc_udev; 99 struct mtx sc_mtx; 100 101 uint8_t sc_msr; 102 uint8_t sc_lsr; 103 }; 104 105 static device_probe_t uslcom_probe; 106 static device_attach_t uslcom_attach; 107 static device_detach_t uslcom_detach; 108 109 static usb2_callback_t uslcom_write_callback; 110 static usb2_callback_t uslcom_read_callback; 111 112 static void uslcom_open(struct usb2_com_softc *); 113 static void uslcom_close(struct usb2_com_softc *); 114 static void uslcom_set_dtr(struct usb2_com_softc *, uint8_t); 115 static void uslcom_set_rts(struct usb2_com_softc *, uint8_t); 116 static void uslcom_set_break(struct usb2_com_softc *, uint8_t); 117 static int uslcom_pre_param(struct usb2_com_softc *, struct termios *); 118 static void uslcom_param(struct usb2_com_softc *, struct termios *); 119 static void uslcom_get_status(struct usb2_com_softc *, uint8_t *, uint8_t *); 120 static void uslcom_start_read(struct usb2_com_softc *); 121 static void uslcom_stop_read(struct usb2_com_softc *); 122 static void uslcom_start_write(struct usb2_com_softc *); 123 static void uslcom_stop_write(struct usb2_com_softc *); 124 125 static const struct usb2_config uslcom_config[USLCOM_N_TRANSFER] = { 126 127 [USLCOM_BULK_DT_WR] = { 128 .type = UE_BULK, 129 .endpoint = UE_ADDR_ANY, 130 .direction = UE_DIR_OUT, 131 .mh.bufsize = USLCOM_BULK_BUF_SIZE, 132 .mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,}, 133 .mh.callback = &uslcom_write_callback, 134 }, 135 136 [USLCOM_BULK_DT_RD] = { 137 .type = UE_BULK, 138 .endpoint = UE_ADDR_ANY, 139 .direction = UE_DIR_IN, 140 .mh.bufsize = USLCOM_BULK_BUF_SIZE, 141 .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, 142 .mh.callback = &uslcom_read_callback, 143 }, 144 }; 145 146 struct usb2_com_callback uslcom_callback = { 147 .usb2_com_cfg_open = &uslcom_open, 148 .usb2_com_cfg_close = &uslcom_close, 149 .usb2_com_cfg_get_status = &uslcom_get_status, 150 .usb2_com_cfg_set_dtr = &uslcom_set_dtr, 151 .usb2_com_cfg_set_rts = &uslcom_set_rts, 152 .usb2_com_cfg_set_break = &uslcom_set_break, 153 .usb2_com_cfg_param = &uslcom_param, 154 .usb2_com_pre_param = &uslcom_pre_param, 155 .usb2_com_start_read = &uslcom_start_read, 156 .usb2_com_stop_read = &uslcom_stop_read, 157 .usb2_com_start_write = &uslcom_start_write, 158 .usb2_com_stop_write = &uslcom_stop_write, 159 }; 160 161 static const struct usb2_device_id uslcom_devs[] = { 162 { USB_VPI(USB_VENDOR_BALTECH, USB_PRODUCT_BALTECH_CARDREADER, 0) }, 163 { USB_VPI(USB_VENDOR_DYNASTREAM, USB_PRODUCT_DYNASTREAM_ANTDEVBOARD, 0) }, 164 { USB_VPI(USB_VENDOR_JABLOTRON, USB_PRODUCT_JABLOTRON_PC60B, 0) }, 165 { USB_VPI(USB_VENDOR_SILABS, USB_PRODUCT_SILABS_ARGUSISP, 0) }, 166 { USB_VPI(USB_VENDOR_SILABS, USB_PRODUCT_SILABS_CRUMB128, 0) }, 167 { USB_VPI(USB_VENDOR_SILABS, USB_PRODUCT_SILABS_DEGREE, 0) }, 168 { USB_VPI(USB_VENDOR_SILABS, USB_PRODUCT_SILABS_BURNSIDE, 0) }, 169 { USB_VPI(USB_VENDOR_SILABS, USB_PRODUCT_SILABS_HELICOM, 0) }, 170 { USB_VPI(USB_VENDOR_SILABS, USB_PRODUCT_SILABS_LIPOWSKY_HARP, 0) }, 171 { USB_VPI(USB_VENDOR_SILABS, USB_PRODUCT_SILABS_LIPOWSKY_JTAG, 0) }, 172 { USB_VPI(USB_VENDOR_SILABS, USB_PRODUCT_SILABS_LIPOWSKY_LIN, 0) }, 173 { USB_VPI(USB_VENDOR_SILABS, USB_PRODUCT_SILABS_POLOLU, 0) }, 174 { USB_VPI(USB_VENDOR_SILABS, USB_PRODUCT_SILABS_CP2102, 0) }, 175 { USB_VPI(USB_VENDOR_SILABS, USB_PRODUCT_SILABS_CP210X_2, 0) }, 176 { USB_VPI(USB_VENDOR_SILABS, USB_PRODUCT_SILABS_SUUNTO, 0) }, 177 { USB_VPI(USB_VENDOR_SILABS, USB_PRODUCT_SILABS_TRAQMATE, 0) }, 178 { USB_VPI(USB_VENDOR_SILABS2, USB_PRODUCT_SILABS2_DCU11CLONE, 0) }, 179 { USB_VPI(USB_VENDOR_USI, USB_PRODUCT_USI_MC60, 0) }, 180 }; 181 182 static device_method_t uslcom_methods[] = { 183 DEVMETHOD(device_probe, uslcom_probe), 184 DEVMETHOD(device_attach, uslcom_attach), 185 DEVMETHOD(device_detach, uslcom_detach), 186 {0, 0} 187 }; 188 189 static devclass_t uslcom_devclass; 190 191 static driver_t uslcom_driver = { 192 .name = "uslcom", 193 .methods = uslcom_methods, 194 .size = sizeof(struct uslcom_softc), 195 }; 196 197 DRIVER_MODULE(uslcom, uhub, uslcom_driver, uslcom_devclass, NULL, 0); 198 MODULE_DEPEND(uslcom, ucom, 1, 1, 1); 199 MODULE_DEPEND(uslcom, usb, 1, 1, 1); 200 MODULE_VERSION(uslcom, 1); 201 202 static int 203 uslcom_probe(device_t dev) 204 { 205 struct usb2_attach_arg *uaa = device_get_ivars(dev); 206 207 DPRINTFN(11, "\n"); 208 209 if (uaa->usb2_mode != USB_MODE_HOST) { 210 return (ENXIO); 211 } 212 if (uaa->info.bConfigIndex != USLCOM_CONFIG_INDEX) { 213 return (ENXIO); 214 } 215 if (uaa->info.bIfaceIndex != USLCOM_IFACE_INDEX) { 216 return (ENXIO); 217 } 218 return (usb2_lookup_id_by_uaa(uslcom_devs, sizeof(uslcom_devs), uaa)); 219 } 220 221 static int 222 uslcom_attach(device_t dev) 223 { 224 struct usb2_attach_arg *uaa = device_get_ivars(dev); 225 struct uslcom_softc *sc = device_get_softc(dev); 226 int error; 227 228 DPRINTFN(11, "\n"); 229 230 device_set_usb2_desc(dev); 231 mtx_init(&sc->sc_mtx, "uslcom", NULL, MTX_DEF); 232 233 sc->sc_udev = uaa->device; 234 235 error = usb2_transfer_setup(uaa->device, 236 &uaa->info.bIfaceIndex, sc->sc_xfer, uslcom_config, 237 USLCOM_N_TRANSFER, sc, &sc->sc_mtx); 238 if (error) { 239 DPRINTF("one or more missing USB endpoints, " 240 "error=%s\n", usb2_errstr(error)); 241 goto detach; 242 } 243 /* clear stall at first run */ 244 mtx_lock(&sc->sc_mtx); 245 usb2_transfer_set_stall(sc->sc_xfer[USLCOM_BULK_DT_WR]); 246 usb2_transfer_set_stall(sc->sc_xfer[USLCOM_BULK_DT_RD]); 247 mtx_unlock(&sc->sc_mtx); 248 249 error = usb2_com_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc, 250 &uslcom_callback, &sc->sc_mtx); 251 if (error) { 252 goto detach; 253 } 254 return (0); 255 256 detach: 257 uslcom_detach(dev); 258 return (ENXIO); 259 } 260 261 static int 262 uslcom_detach(device_t dev) 263 { 264 struct uslcom_softc *sc = device_get_softc(dev); 265 266 DPRINTF("sc=%p\n", sc); 267 268 usb2_com_detach(&sc->sc_super_ucom, &sc->sc_ucom, 1); 269 usb2_transfer_unsetup(sc->sc_xfer, USLCOM_N_TRANSFER); 270 mtx_destroy(&sc->sc_mtx); 271 272 return (0); 273 } 274 275 static void 276 uslcom_open(struct usb2_com_softc *ucom) 277 { 278 struct uslcom_softc *sc = ucom->sc_parent; 279 struct usb2_device_request req; 280 281 req.bmRequestType = USLCOM_WRITE; 282 req.bRequest = USLCOM_UART; 283 USETW(req.wValue, USLCOM_UART_ENABLE); 284 USETW(req.wIndex, USLCOM_PORT_NO); 285 USETW(req.wLength, 0); 286 287 if (usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 288 &req, NULL, 0, 1000)) { 289 DPRINTF("UART enable failed (ignored)\n"); 290 } 291 } 292 293 static void 294 uslcom_close(struct usb2_com_softc *ucom) 295 { 296 struct uslcom_softc *sc = ucom->sc_parent; 297 struct usb2_device_request req; 298 299 req.bmRequestType = USLCOM_WRITE; 300 req.bRequest = USLCOM_UART; 301 USETW(req.wValue, USLCOM_UART_DISABLE); 302 USETW(req.wIndex, USLCOM_PORT_NO); 303 USETW(req.wLength, 0); 304 305 if (usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 306 &req, NULL, 0, 1000)) { 307 DPRINTF("UART disable failed (ignored)\n"); 308 } 309 } 310 311 static void 312 uslcom_set_dtr(struct usb2_com_softc *ucom, uint8_t onoff) 313 { 314 struct uslcom_softc *sc = ucom->sc_parent; 315 struct usb2_device_request req; 316 uint16_t ctl; 317 318 DPRINTF("onoff = %d\n", onoff); 319 320 ctl = onoff ? USLCOM_CTRL_DTR_ON : 0; 321 ctl |= USLCOM_CTRL_DTR_SET; 322 323 req.bmRequestType = USLCOM_WRITE; 324 req.bRequest = USLCOM_CTRL; 325 USETW(req.wValue, ctl); 326 USETW(req.wIndex, USLCOM_PORT_NO); 327 USETW(req.wLength, 0); 328 329 if (usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 330 &req, NULL, 0, 1000)) { 331 DPRINTF("Setting DTR failed (ignored)\n"); 332 } 333 } 334 335 static void 336 uslcom_set_rts(struct usb2_com_softc *ucom, uint8_t onoff) 337 { 338 struct uslcom_softc *sc = ucom->sc_parent; 339 struct usb2_device_request req; 340 uint16_t ctl; 341 342 DPRINTF("onoff = %d\n", onoff); 343 344 ctl = onoff ? USLCOM_CTRL_RTS_ON : 0; 345 ctl |= USLCOM_CTRL_RTS_SET; 346 347 req.bmRequestType = USLCOM_WRITE; 348 req.bRequest = USLCOM_CTRL; 349 USETW(req.wValue, ctl); 350 USETW(req.wIndex, USLCOM_PORT_NO); 351 USETW(req.wLength, 0); 352 353 if (usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 354 &req, NULL, 0, 1000)) { 355 DPRINTF("Setting DTR failed (ignored)\n"); 356 } 357 } 358 359 static int 360 uslcom_pre_param(struct usb2_com_softc *ucom, struct termios *t) 361 { 362 if (t->c_ospeed <= 0 || t->c_ospeed > 921600) 363 return (EINVAL); 364 return (0); 365 } 366 367 static void 368 uslcom_param(struct usb2_com_softc *ucom, struct termios *t) 369 { 370 struct uslcom_softc *sc = ucom->sc_parent; 371 struct usb2_device_request req; 372 uint16_t data; 373 374 DPRINTF("\n"); 375 376 req.bmRequestType = USLCOM_WRITE; 377 req.bRequest = USLCOM_BAUD_RATE; 378 USETW(req.wValue, USLCOM_BAUD_REF / t->c_ospeed); 379 USETW(req.wIndex, USLCOM_PORT_NO); 380 USETW(req.wLength, 0); 381 382 if (usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 383 &req, NULL, 0, 1000)) { 384 DPRINTF("Set baudrate failed (ignored)\n"); 385 } 386 387 if (t->c_cflag & CSTOPB) 388 data = USLCOM_STOP_BITS_2; 389 else 390 data = USLCOM_STOP_BITS_1; 391 if (t->c_cflag & PARENB) { 392 if (t->c_cflag & PARODD) 393 data |= USLCOM_PARITY_ODD; 394 else 395 data |= USLCOM_PARITY_EVEN; 396 } else 397 data |= USLCOM_PARITY_NONE; 398 switch (t->c_cflag & CSIZE) { 399 case CS5: 400 data |= USLCOM_SET_DATA_BITS(5); 401 break; 402 case CS6: 403 data |= USLCOM_SET_DATA_BITS(6); 404 break; 405 case CS7: 406 data |= USLCOM_SET_DATA_BITS(7); 407 break; 408 case CS8: 409 data |= USLCOM_SET_DATA_BITS(8); 410 break; 411 } 412 413 req.bmRequestType = USLCOM_WRITE; 414 req.bRequest = USLCOM_DATA; 415 USETW(req.wValue, data); 416 USETW(req.wIndex, USLCOM_PORT_NO); 417 USETW(req.wLength, 0); 418 419 if (usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 420 &req, NULL, 0, 1000)) { 421 DPRINTF("Set format failed (ignored)\n"); 422 } 423 return; 424 } 425 426 static void 427 uslcom_get_status(struct usb2_com_softc *ucom, uint8_t *lsr, uint8_t *msr) 428 { 429 struct uslcom_softc *sc = ucom->sc_parent; 430 431 DPRINTF("\n"); 432 433 *lsr = sc->sc_lsr; 434 *msr = sc->sc_msr; 435 } 436 437 static void 438 uslcom_set_break(struct usb2_com_softc *ucom, uint8_t onoff) 439 { 440 struct uslcom_softc *sc = ucom->sc_parent; 441 struct usb2_device_request req; 442 uint16_t brk = onoff ? USLCOM_BREAK_ON : USLCOM_BREAK_OFF; 443 444 req.bmRequestType = USLCOM_WRITE; 445 req.bRequest = USLCOM_BREAK; 446 USETW(req.wValue, brk); 447 USETW(req.wIndex, USLCOM_PORT_NO); 448 USETW(req.wLength, 0); 449 450 if (usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 451 &req, NULL, 0, 1000)) { 452 DPRINTF("Set BREAK failed (ignored)\n"); 453 } 454 } 455 456 static void 457 uslcom_write_callback(struct usb2_xfer *xfer) 458 { 459 struct uslcom_softc *sc = xfer->priv_sc; 460 uint32_t actlen; 461 462 switch (USB_GET_STATE(xfer)) { 463 case USB_ST_SETUP: 464 case USB_ST_TRANSFERRED: 465 tr_setup: 466 if (usb2_com_get_data(&sc->sc_ucom, xfer->frbuffers, 0, 467 USLCOM_BULK_BUF_SIZE, &actlen)) { 468 469 DPRINTF("actlen = %d\n", actlen); 470 471 xfer->frlengths[0] = actlen; 472 usb2_start_hardware(xfer); 473 } 474 return; 475 476 default: /* Error */ 477 if (xfer->error != USB_ERR_CANCELLED) { 478 /* try to clear stall first */ 479 xfer->flags.stall_pipe = 1; 480 goto tr_setup; 481 } 482 return; 483 } 484 } 485 486 static void 487 uslcom_read_callback(struct usb2_xfer *xfer) 488 { 489 struct uslcom_softc *sc = xfer->priv_sc; 490 491 switch (USB_GET_STATE(xfer)) { 492 case USB_ST_TRANSFERRED: 493 usb2_com_put_data(&sc->sc_ucom, xfer->frbuffers, 0, xfer->actlen); 494 495 case USB_ST_SETUP: 496 tr_setup: 497 xfer->frlengths[0] = xfer->max_data_length; 498 usb2_start_hardware(xfer); 499 return; 500 501 default: /* Error */ 502 if (xfer->error != USB_ERR_CANCELLED) { 503 /* try to clear stall first */ 504 xfer->flags.stall_pipe = 1; 505 goto tr_setup; 506 } 507 return; 508 } 509 } 510 511 static void 512 uslcom_start_read(struct usb2_com_softc *ucom) 513 { 514 struct uslcom_softc *sc = ucom->sc_parent; 515 516 /* start read endpoint */ 517 usb2_transfer_start(sc->sc_xfer[USLCOM_BULK_DT_RD]); 518 } 519 520 static void 521 uslcom_stop_read(struct usb2_com_softc *ucom) 522 { 523 struct uslcom_softc *sc = ucom->sc_parent; 524 525 /* stop read endpoint */ 526 usb2_transfer_stop(sc->sc_xfer[USLCOM_BULK_DT_RD]); 527 } 528 529 static void 530 uslcom_start_write(struct usb2_com_softc *ucom) 531 { 532 struct uslcom_softc *sc = ucom->sc_parent; 533 534 usb2_transfer_start(sc->sc_xfer[USLCOM_BULK_DT_WR]); 535 } 536 537 static void 538 uslcom_stop_write(struct usb2_com_softc *ucom) 539 { 540 struct uslcom_softc *sc = ucom->sc_parent; 541 542 usb2_transfer_stop(sc->sc_xfer[USLCOM_BULK_DT_WR]); 543 } 544