uslcom.c revision 188746
1/* $OpenBSD: uslcom.c,v 1.17 2007/11/24 10:52:12 jsg Exp $ */ 2 3#include <sys/cdefs.h> 4__FBSDID("$FreeBSD: head/sys/dev/usb2/serial/uslcom2.c 188746 2009-02-18 06:33:10Z thompsa $"); 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/usb2/include/usb2_standard.h> 24#include <dev/usb2/include/usb2_mfunc.h> 25#include <dev/usb2/include/usb2_error.h> 26 27#define USB_DEBUG_VAR uslcom_debug 28 29#include <dev/usb2/core/usb2_core.h> 30#include <dev/usb2/core/usb2_debug.h> 31#include <dev/usb2/core/usb2_process.h> 32#include <dev/usb2/core/usb2_request.h> 33#include <dev/usb2/core/usb2_lookup.h> 34#include <dev/usb2/core/usb2_util.h> 35#include <dev/usb2/core/usb2_busdma.h> 36 37#include <dev/usb2/serial/usb2_serial.h> 38 39#if USB_DEBUG 40static int uslcom_debug = 0; 41 42SYSCTL_NODE(_hw_usb2, OID_AUTO, uslcom, CTLFLAG_RW, 0, "USB uslcom"); 43SYSCTL_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 87enum { 88 USLCOM_BULK_DT_WR, 89 USLCOM_BULK_DT_RD, 90 USLCOM_N_TRANSFER, 91}; 92 93struct 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 100 uint8_t sc_msr; 101 uint8_t sc_lsr; 102}; 103 104static device_probe_t uslcom_probe; 105static device_attach_t uslcom_attach; 106static device_detach_t uslcom_detach; 107 108static usb2_callback_t uslcom_write_callback; 109static usb2_callback_t uslcom_read_callback; 110 111static void uslcom_open(struct usb2_com_softc *); 112static void uslcom_close(struct usb2_com_softc *); 113static void uslcom_set_dtr(struct usb2_com_softc *, uint8_t); 114static void uslcom_set_rts(struct usb2_com_softc *, uint8_t); 115static void uslcom_set_break(struct usb2_com_softc *, uint8_t); 116static int uslcom_pre_param(struct usb2_com_softc *, struct termios *); 117static void uslcom_param(struct usb2_com_softc *, struct termios *); 118static void uslcom_get_status(struct usb2_com_softc *, uint8_t *, uint8_t *); 119static void uslcom_start_read(struct usb2_com_softc *); 120static void uslcom_stop_read(struct usb2_com_softc *); 121static void uslcom_start_write(struct usb2_com_softc *); 122static void uslcom_stop_write(struct usb2_com_softc *); 123 124static const struct usb2_config uslcom_config[USLCOM_N_TRANSFER] = { 125 126 [USLCOM_BULK_DT_WR] = { 127 .type = UE_BULK, 128 .endpoint = UE_ADDR_ANY, 129 .direction = UE_DIR_OUT, 130 .mh.bufsize = USLCOM_BULK_BUF_SIZE, 131 .mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,}, 132 .mh.callback = &uslcom_write_callback, 133 }, 134 135 [USLCOM_BULK_DT_RD] = { 136 .type = UE_BULK, 137 .endpoint = UE_ADDR_ANY, 138 .direction = UE_DIR_IN, 139 .mh.bufsize = USLCOM_BULK_BUF_SIZE, 140 .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, 141 .mh.callback = &uslcom_read_callback, 142 }, 143}; 144 145struct usb2_com_callback uslcom_callback = { 146 .usb2_com_cfg_open = &uslcom_open, 147 .usb2_com_cfg_close = &uslcom_close, 148 .usb2_com_cfg_get_status = &uslcom_get_status, 149 .usb2_com_cfg_set_dtr = &uslcom_set_dtr, 150 .usb2_com_cfg_set_rts = &uslcom_set_rts, 151 .usb2_com_cfg_set_break = &uslcom_set_break, 152 .usb2_com_cfg_param = &uslcom_param, 153 .usb2_com_pre_param = &uslcom_pre_param, 154 .usb2_com_start_read = &uslcom_start_read, 155 .usb2_com_stop_read = &uslcom_stop_read, 156 .usb2_com_start_write = &uslcom_start_write, 157 .usb2_com_stop_write = &uslcom_stop_write, 158}; 159 160static const struct usb2_device_id uslcom_devs[] = { 161 { USB_VPI(USB_VENDOR_BALTECH, USB_PRODUCT_BALTECH_CARDREADER, 0) }, 162 { USB_VPI(USB_VENDOR_DYNASTREAM, USB_PRODUCT_DYNASTREAM_ANTDEVBOARD, 0) }, 163 { USB_VPI(USB_VENDOR_JABLOTRON, USB_PRODUCT_JABLOTRON_PC60B, 0) }, 164 { USB_VPI(USB_VENDOR_SILABS, USB_PRODUCT_SILABS_ARGUSISP, 0) }, 165 { USB_VPI(USB_VENDOR_SILABS, USB_PRODUCT_SILABS_CRUMB128, 0) }, 166 { USB_VPI(USB_VENDOR_SILABS, USB_PRODUCT_SILABS_DEGREE, 0) }, 167 { USB_VPI(USB_VENDOR_SILABS, USB_PRODUCT_SILABS_BURNSIDE, 0) }, 168 { USB_VPI(USB_VENDOR_SILABS, USB_PRODUCT_SILABS_HELICOM, 0) }, 169 { USB_VPI(USB_VENDOR_SILABS, USB_PRODUCT_SILABS_LIPOWSKY_HARP, 0) }, 170 { USB_VPI(USB_VENDOR_SILABS, USB_PRODUCT_SILABS_LIPOWSKY_JTAG, 0) }, 171 { USB_VPI(USB_VENDOR_SILABS, USB_PRODUCT_SILABS_LIPOWSKY_LIN, 0) }, 172 { USB_VPI(USB_VENDOR_SILABS, USB_PRODUCT_SILABS_POLOLU, 0) }, 173 { USB_VPI(USB_VENDOR_SILABS, USB_PRODUCT_SILABS_CP2102, 0) }, 174 { USB_VPI(USB_VENDOR_SILABS, USB_PRODUCT_SILABS_CP210X_2, 0) }, 175 { USB_VPI(USB_VENDOR_SILABS, USB_PRODUCT_SILABS_SUUNTO, 0) }, 176 { USB_VPI(USB_VENDOR_SILABS, USB_PRODUCT_SILABS_TRAQMATE, 0) }, 177 { USB_VPI(USB_VENDOR_SILABS2, USB_PRODUCT_SILABS2_DCU11CLONE, 0) }, 178 { USB_VPI(USB_VENDOR_USI, USB_PRODUCT_USI_MC60, 0) }, 179}; 180 181static device_method_t uslcom_methods[] = { 182 DEVMETHOD(device_probe, uslcom_probe), 183 DEVMETHOD(device_attach, uslcom_attach), 184 DEVMETHOD(device_detach, uslcom_detach), 185 {0, 0} 186}; 187 188static devclass_t uslcom_devclass; 189 190static driver_t uslcom_driver = { 191 .name = "uslcom", 192 .methods = uslcom_methods, 193 .size = sizeof(struct uslcom_softc), 194}; 195 196DRIVER_MODULE(uslcom, ushub, uslcom_driver, uslcom_devclass, NULL, 0); 197MODULE_DEPEND(uslcom, usb2_serial, 1, 1, 1); 198MODULE_DEPEND(uslcom, usb2_core, 1, 1, 1); 199MODULE_VERSION(uslcom, 1); 200 201static int 202uslcom_probe(device_t dev) 203{ 204 struct usb2_attach_arg *uaa = device_get_ivars(dev); 205 206 DPRINTFN(11, "\n"); 207 208 if (uaa->usb2_mode != USB_MODE_HOST) { 209 return (ENXIO); 210 } 211 if (uaa->info.bConfigIndex != USLCOM_CONFIG_INDEX) { 212 return (ENXIO); 213 } 214 if (uaa->info.bIfaceIndex != USLCOM_IFACE_INDEX) { 215 return (ENXIO); 216 } 217 return (usb2_lookup_id_by_uaa(uslcom_devs, sizeof(uslcom_devs), uaa)); 218} 219 220static int 221uslcom_attach(device_t dev) 222{ 223 struct usb2_attach_arg *uaa = device_get_ivars(dev); 224 struct uslcom_softc *sc = device_get_softc(dev); 225 int error; 226 227 DPRINTFN(11, "\n"); 228 229 device_set_usb2_desc(dev); 230 231 sc->sc_udev = uaa->device; 232 233 error = usb2_transfer_setup(uaa->device, 234 &uaa->info.bIfaceIndex, sc->sc_xfer, uslcom_config, 235 USLCOM_N_TRANSFER, sc, &Giant); 236 if (error) { 237 DPRINTF("one or more missing USB endpoints, " 238 "error=%s\n", usb2_errstr(error)); 239 goto detach; 240 } 241 /* clear stall at first run */ 242 usb2_transfer_set_stall(sc->sc_xfer[USLCOM_BULK_DT_WR]); 243 usb2_transfer_set_stall(sc->sc_xfer[USLCOM_BULK_DT_RD]); 244 245 error = usb2_com_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc, 246 &uslcom_callback, &Giant); 247 if (error) { 248 goto detach; 249 } 250 return (0); 251 252detach: 253 uslcom_detach(dev); 254 return (ENXIO); 255} 256 257static int 258uslcom_detach(device_t dev) 259{ 260 struct uslcom_softc *sc = device_get_softc(dev); 261 262 DPRINTF("sc=%p\n", sc); 263 264 usb2_com_detach(&sc->sc_super_ucom, &sc->sc_ucom, 1); 265 266 usb2_transfer_unsetup(sc->sc_xfer, USLCOM_N_TRANSFER); 267 268 return (0); 269} 270 271static void 272uslcom_open(struct usb2_com_softc *ucom) 273{ 274 struct uslcom_softc *sc = ucom->sc_parent; 275 struct usb2_device_request req; 276 277 req.bmRequestType = USLCOM_WRITE; 278 req.bRequest = USLCOM_UART; 279 USETW(req.wValue, USLCOM_UART_ENABLE); 280 USETW(req.wIndex, USLCOM_PORT_NO); 281 USETW(req.wLength, 0); 282 283 if (usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 284 &req, NULL, 0, 1000)) { 285 DPRINTF("UART enable failed (ignored)\n"); 286 } 287} 288 289static void 290uslcom_close(struct usb2_com_softc *ucom) 291{ 292 struct uslcom_softc *sc = ucom->sc_parent; 293 struct usb2_device_request req; 294 295 req.bmRequestType = USLCOM_WRITE; 296 req.bRequest = USLCOM_UART; 297 USETW(req.wValue, USLCOM_UART_DISABLE); 298 USETW(req.wIndex, USLCOM_PORT_NO); 299 USETW(req.wLength, 0); 300 301 if (usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 302 &req, NULL, 0, 1000)) { 303 DPRINTF("UART disable failed (ignored)\n"); 304 } 305} 306 307static void 308uslcom_set_dtr(struct usb2_com_softc *ucom, uint8_t onoff) 309{ 310 struct uslcom_softc *sc = ucom->sc_parent; 311 struct usb2_device_request req; 312 uint16_t ctl; 313 314 DPRINTF("onoff = %d\n", onoff); 315 316 ctl = onoff ? USLCOM_CTRL_DTR_ON : 0; 317 ctl |= USLCOM_CTRL_DTR_SET; 318 319 req.bmRequestType = USLCOM_WRITE; 320 req.bRequest = USLCOM_CTRL; 321 USETW(req.wValue, ctl); 322 USETW(req.wIndex, USLCOM_PORT_NO); 323 USETW(req.wLength, 0); 324 325 if (usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 326 &req, NULL, 0, 1000)) { 327 DPRINTF("Setting DTR failed (ignored)\n"); 328 } 329} 330 331static void 332uslcom_set_rts(struct usb2_com_softc *ucom, uint8_t onoff) 333{ 334 struct uslcom_softc *sc = ucom->sc_parent; 335 struct usb2_device_request req; 336 uint16_t ctl; 337 338 DPRINTF("onoff = %d\n", onoff); 339 340 ctl = onoff ? USLCOM_CTRL_RTS_ON : 0; 341 ctl |= USLCOM_CTRL_RTS_SET; 342 343 req.bmRequestType = USLCOM_WRITE; 344 req.bRequest = USLCOM_CTRL; 345 USETW(req.wValue, ctl); 346 USETW(req.wIndex, USLCOM_PORT_NO); 347 USETW(req.wLength, 0); 348 349 if (usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 350 &req, NULL, 0, 1000)) { 351 DPRINTF("Setting DTR failed (ignored)\n"); 352 } 353} 354 355static int 356uslcom_pre_param(struct usb2_com_softc *ucom, struct termios *t) 357{ 358 if (t->c_ospeed <= 0 || t->c_ospeed > 921600) 359 return (EINVAL); 360 return (0); 361} 362 363static void 364uslcom_param(struct usb2_com_softc *ucom, struct termios *t) 365{ 366 struct uslcom_softc *sc = ucom->sc_parent; 367 struct usb2_device_request req; 368 uint16_t data; 369 370 DPRINTF("\n"); 371 372 req.bmRequestType = USLCOM_WRITE; 373 req.bRequest = USLCOM_BAUD_RATE; 374 USETW(req.wValue, USLCOM_BAUD_REF / t->c_ospeed); 375 USETW(req.wIndex, USLCOM_PORT_NO); 376 USETW(req.wLength, 0); 377 378 if (usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 379 &req, NULL, 0, 1000)) { 380 DPRINTF("Set baudrate failed (ignored)\n"); 381 } 382 383 if (t->c_cflag & CSTOPB) 384 data = USLCOM_STOP_BITS_2; 385 else 386 data = USLCOM_STOP_BITS_1; 387 if (t->c_cflag & PARENB) { 388 if (t->c_cflag & PARODD) 389 data |= USLCOM_PARITY_ODD; 390 else 391 data |= USLCOM_PARITY_EVEN; 392 } else 393 data |= USLCOM_PARITY_NONE; 394 switch (t->c_cflag & CSIZE) { 395 case CS5: 396 data |= USLCOM_SET_DATA_BITS(5); 397 break; 398 case CS6: 399 data |= USLCOM_SET_DATA_BITS(6); 400 break; 401 case CS7: 402 data |= USLCOM_SET_DATA_BITS(7); 403 break; 404 case CS8: 405 data |= USLCOM_SET_DATA_BITS(8); 406 break; 407 } 408 409 req.bmRequestType = USLCOM_WRITE; 410 req.bRequest = USLCOM_DATA; 411 USETW(req.wValue, data); 412 USETW(req.wIndex, USLCOM_PORT_NO); 413 USETW(req.wLength, 0); 414 415 if (usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 416 &req, NULL, 0, 1000)) { 417 DPRINTF("Set format failed (ignored)\n"); 418 } 419 return; 420} 421 422static void 423uslcom_get_status(struct usb2_com_softc *ucom, uint8_t *lsr, uint8_t *msr) 424{ 425 struct uslcom_softc *sc = ucom->sc_parent; 426 427 DPRINTF("\n"); 428 429 *lsr = sc->sc_lsr; 430 *msr = sc->sc_msr; 431} 432 433static void 434uslcom_set_break(struct usb2_com_softc *ucom, uint8_t onoff) 435{ 436 struct uslcom_softc *sc = ucom->sc_parent; 437 struct usb2_device_request req; 438 uint16_t brk = onoff ? USLCOM_BREAK_ON : USLCOM_BREAK_OFF; 439 440 req.bmRequestType = USLCOM_WRITE; 441 req.bRequest = USLCOM_BREAK; 442 USETW(req.wValue, brk); 443 USETW(req.wIndex, USLCOM_PORT_NO); 444 USETW(req.wLength, 0); 445 446 if (usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 447 &req, NULL, 0, 1000)) { 448 DPRINTF("Set BREAK failed (ignored)\n"); 449 } 450} 451 452static void 453uslcom_write_callback(struct usb2_xfer *xfer) 454{ 455 struct uslcom_softc *sc = xfer->priv_sc; 456 uint32_t actlen; 457 458 switch (USB_GET_STATE(xfer)) { 459 case USB_ST_SETUP: 460 case USB_ST_TRANSFERRED: 461tr_setup: 462 if (usb2_com_get_data(&sc->sc_ucom, xfer->frbuffers, 0, 463 USLCOM_BULK_BUF_SIZE, &actlen)) { 464 465 DPRINTF("actlen = %d\n", actlen); 466 467 xfer->frlengths[0] = actlen; 468 usb2_start_hardware(xfer); 469 } 470 return; 471 472 default: /* Error */ 473 if (xfer->error != USB_ERR_CANCELLED) { 474 /* try to clear stall first */ 475 xfer->flags.stall_pipe = 1; 476 goto tr_setup; 477 } 478 return; 479 } 480} 481 482static void 483uslcom_read_callback(struct usb2_xfer *xfer) 484{ 485 struct uslcom_softc *sc = xfer->priv_sc; 486 487 switch (USB_GET_STATE(xfer)) { 488 case USB_ST_TRANSFERRED: 489 usb2_com_put_data(&sc->sc_ucom, xfer->frbuffers, 0, xfer->actlen); 490 491 case USB_ST_SETUP: 492tr_setup: 493 xfer->frlengths[0] = xfer->max_data_length; 494 usb2_start_hardware(xfer); 495 return; 496 497 default: /* Error */ 498 if (xfer->error != USB_ERR_CANCELLED) { 499 /* try to clear stall first */ 500 xfer->flags.stall_pipe = 1; 501 goto tr_setup; 502 } 503 return; 504 } 505} 506 507static void 508uslcom_start_read(struct usb2_com_softc *ucom) 509{ 510 struct uslcom_softc *sc = ucom->sc_parent; 511 512 /* start read endpoint */ 513 usb2_transfer_start(sc->sc_xfer[USLCOM_BULK_DT_RD]); 514} 515 516static void 517uslcom_stop_read(struct usb2_com_softc *ucom) 518{ 519 struct uslcom_softc *sc = ucom->sc_parent; 520 521 /* stop read endpoint */ 522 usb2_transfer_stop(sc->sc_xfer[USLCOM_BULK_DT_RD]); 523} 524 525static void 526uslcom_start_write(struct usb2_com_softc *ucom) 527{ 528 struct uslcom_softc *sc = ucom->sc_parent; 529 530 usb2_transfer_start(sc->sc_xfer[USLCOM_BULK_DT_WR]); 531} 532 533static void 534uslcom_stop_write(struct usb2_com_softc *ucom) 535{ 536 struct uslcom_softc *sc = ucom->sc_parent; 537 538 usb2_transfer_stop(sc->sc_xfer[USLCOM_BULK_DT_WR]); 539} 540