1 #include <sys/cdefs.h> 2 __FBSDID("$FreeBSD$"); 3 4 /*- 5 * Copyright (c) 2004 Dag-Erling Co�dan Sm�rgrav 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer 13 * in this position and unchanged. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 3. The name of the author may not be used to endorse or promote products 18 * derived from this software without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 21 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 22 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 23 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 24 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 25 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 29 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 /* 33 * Device driver for Cypress CY7C637xx and CY7C640/1xx series USB to 34 * RS232 bridges. 35 */ 36 37 #include "usbdevs.h" 38 #include <dev/usb/usb.h> 39 #include <dev/usb/usb_mfunc.h> 40 #include <dev/usb/usb_error.h> 41 #include <dev/usb/usb_cdc.h> 42 #include <dev/usb/usb_ioctl.h> 43 #include <dev/usb/usbhid.h> 44 45 #define USB_DEBUG_VAR usb2_debug 46 47 #include <dev/usb/usb_core.h> 48 #include <dev/usb/usb_debug.h> 49 #include <dev/usb/usb_process.h> 50 #include <dev/usb/usb_request.h> 51 #include <dev/usb/usb_lookup.h> 52 #include <dev/usb/usb_util.h> 53 #include <dev/usb/usb_busdma.h> 54 #include <dev/usb/usb_hid.h> 55 56 #include <dev/usb/serial/usb_serial.h> 57 58 #define UCYCOM_MAX_IOLEN (1024 + 2) /* bytes */ 59 60 #define UCYCOM_IFACE_INDEX 0 61 62 enum { 63 UCYCOM_CTRL_RD, 64 UCYCOM_INTR_RD, 65 UCYCOM_N_TRANSFER, 66 }; 67 68 struct ucycom_softc { 69 struct usb2_com_super_softc sc_super_ucom; 70 struct usb2_com_softc sc_ucom; 71 72 struct usb2_device *sc_udev; 73 struct usb2_xfer *sc_xfer[UCYCOM_N_TRANSFER]; 74 75 uint32_t sc_model; 76 #define MODEL_CY7C63743 0x63743 77 #define MODEL_CY7C64013 0x64013 78 79 uint16_t sc_flen; /* feature report length */ 80 uint16_t sc_ilen; /* input report length */ 81 uint16_t sc_olen; /* output report length */ 82 83 uint8_t sc_fid; /* feature report id */ 84 uint8_t sc_iid; /* input report id */ 85 uint8_t sc_oid; /* output report id */ 86 uint8_t sc_cfg; 87 #define UCYCOM_CFG_RESET 0x80 88 #define UCYCOM_CFG_PARODD 0x20 89 #define UCYCOM_CFG_PAREN 0x10 90 #define UCYCOM_CFG_STOPB 0x08 91 #define UCYCOM_CFG_DATAB 0x03 92 uint8_t sc_ist; /* status flags from last input */ 93 uint8_t sc_name[16]; 94 uint8_t sc_iface_no; 95 uint8_t sc_temp_cfg[32]; 96 }; 97 98 /* prototypes */ 99 100 static device_probe_t ucycom_probe; 101 static device_attach_t ucycom_attach; 102 static device_detach_t ucycom_detach; 103 104 static usb2_callback_t ucycom_ctrl_write_callback; 105 static usb2_callback_t ucycom_intr_read_callback; 106 107 static void ucycom_cfg_open(struct usb2_com_softc *); 108 static void ucycom_start_read(struct usb2_com_softc *); 109 static void ucycom_stop_read(struct usb2_com_softc *); 110 static void ucycom_start_write(struct usb2_com_softc *); 111 static void ucycom_stop_write(struct usb2_com_softc *); 112 static void ucycom_cfg_write(struct ucycom_softc *, uint32_t, uint8_t); 113 static int ucycom_pre_param(struct usb2_com_softc *, struct termios *); 114 static void ucycom_cfg_param(struct usb2_com_softc *, struct termios *); 115 116 static const struct usb2_config ucycom_config[UCYCOM_N_TRANSFER] = { 117 118 [UCYCOM_CTRL_RD] = { 119 .type = UE_CONTROL, 120 .endpoint = 0x00, /* Control pipe */ 121 .direction = UE_DIR_ANY, 122 .mh.bufsize = (sizeof(struct usb2_device_request) + UCYCOM_MAX_IOLEN), 123 .mh.flags = {}, 124 .mh.callback = &ucycom_ctrl_write_callback, 125 .mh.timeout = 1000, /* 1 second */ 126 }, 127 128 [UCYCOM_INTR_RD] = { 129 .type = UE_INTERRUPT, 130 .endpoint = UE_ADDR_ANY, 131 .direction = UE_DIR_IN, 132 .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, 133 .mh.bufsize = UCYCOM_MAX_IOLEN, 134 .mh.callback = &ucycom_intr_read_callback, 135 }, 136 }; 137 138 static const struct usb2_com_callback ucycom_callback = { 139 .usb2_com_cfg_param = &ucycom_cfg_param, 140 .usb2_com_cfg_open = &ucycom_cfg_open, 141 .usb2_com_pre_param = &ucycom_pre_param, 142 .usb2_com_start_read = &ucycom_start_read, 143 .usb2_com_stop_read = &ucycom_stop_read, 144 .usb2_com_start_write = &ucycom_start_write, 145 .usb2_com_stop_write = &ucycom_stop_write, 146 }; 147 148 static device_method_t ucycom_methods[] = { 149 DEVMETHOD(device_probe, ucycom_probe), 150 DEVMETHOD(device_attach, ucycom_attach), 151 DEVMETHOD(device_detach, ucycom_detach), 152 {0, 0} 153 }; 154 155 static devclass_t ucycom_devclass; 156 157 static driver_t ucycom_driver = { 158 .name = "ucycom", 159 .methods = ucycom_methods, 160 .size = sizeof(struct ucycom_softc), 161 }; 162 163 DRIVER_MODULE(ucycom, ushub, ucycom_driver, ucycom_devclass, NULL, 0); 164 MODULE_DEPEND(ucycom, ucom, 1, 1, 1); 165 MODULE_DEPEND(ucycom, usb, 1, 1, 1); 166 167 /* 168 * Supported devices 169 */ 170 static const struct usb2_device_id ucycom_devs[] = { 171 {USB_VPI(USB_VENDOR_DELORME, USB_PRODUCT_DELORME_EARTHMATE, MODEL_CY7C64013)}, 172 }; 173 174 #define UCYCOM_DEFAULT_RATE 4800 175 #define UCYCOM_DEFAULT_CFG 0x03 /* N-8-1 */ 176 177 static int 178 ucycom_probe(device_t dev) 179 { 180 struct usb2_attach_arg *uaa = device_get_ivars(dev); 181 182 if (uaa->usb2_mode != USB_MODE_HOST) { 183 return (ENXIO); 184 } 185 if (uaa->info.bConfigIndex != 0) { 186 return (ENXIO); 187 } 188 if (uaa->info.bIfaceIndex != UCYCOM_IFACE_INDEX) { 189 return (ENXIO); 190 } 191 return (usb2_lookup_id_by_uaa(ucycom_devs, sizeof(ucycom_devs), uaa)); 192 } 193 194 static int 195 ucycom_attach(device_t dev) 196 { 197 struct usb2_attach_arg *uaa = device_get_ivars(dev); 198 struct ucycom_softc *sc = device_get_softc(dev); 199 void *urd_ptr = NULL; 200 int32_t error; 201 uint16_t urd_len; 202 uint8_t iface_index; 203 204 sc->sc_udev = uaa->device; 205 206 device_set_usb2_desc(dev); 207 208 snprintf(sc->sc_name, sizeof(sc->sc_name), 209 "%s", device_get_nameunit(dev)); 210 211 DPRINTF("\n"); 212 213 /* get chip model */ 214 sc->sc_model = USB_GET_DRIVER_INFO(uaa); 215 if (sc->sc_model == 0) { 216 device_printf(dev, "unsupported device\n"); 217 goto detach; 218 } 219 device_printf(dev, "Cypress CY7C%X USB to RS232 bridge\n", sc->sc_model); 220 221 /* get report descriptor */ 222 223 error = usb2_req_get_hid_desc 224 (uaa->device, &Giant, 225 &urd_ptr, &urd_len, M_USBDEV, 226 UCYCOM_IFACE_INDEX); 227 228 if (error) { 229 device_printf(dev, "failed to get report " 230 "descriptor: %s\n", 231 usb2_errstr(error)); 232 goto detach; 233 } 234 /* get report sizes */ 235 236 sc->sc_flen = hid_report_size(urd_ptr, urd_len, hid_feature, &sc->sc_fid); 237 sc->sc_ilen = hid_report_size(urd_ptr, urd_len, hid_input, &sc->sc_iid); 238 sc->sc_olen = hid_report_size(urd_ptr, urd_len, hid_output, &sc->sc_oid); 239 240 if ((sc->sc_ilen > UCYCOM_MAX_IOLEN) || (sc->sc_ilen < 1) || 241 (sc->sc_olen > UCYCOM_MAX_IOLEN) || (sc->sc_olen < 2) || 242 (sc->sc_flen > UCYCOM_MAX_IOLEN) || (sc->sc_flen < 5)) { 243 device_printf(dev, "invalid report size i=%d, o=%d, f=%d, max=%d\n", 244 sc->sc_ilen, sc->sc_olen, sc->sc_flen, 245 UCYCOM_MAX_IOLEN); 246 goto detach; 247 } 248 sc->sc_iface_no = uaa->info.bIfaceNum; 249 250 iface_index = UCYCOM_IFACE_INDEX; 251 error = usb2_transfer_setup(uaa->device, &iface_index, 252 sc->sc_xfer, ucycom_config, UCYCOM_N_TRANSFER, 253 sc, &Giant); 254 if (error) { 255 device_printf(dev, "allocating USB " 256 "transfers failed!\n"); 257 goto detach; 258 } 259 error = usb2_com_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc, 260 &ucycom_callback, &Giant); 261 262 if (error) { 263 goto detach; 264 } 265 if (urd_ptr) { 266 free(urd_ptr, M_USBDEV); 267 } 268 return (0); /* success */ 269 270 detach: 271 if (urd_ptr) { 272 free(urd_ptr, M_USBDEV); 273 } 274 ucycom_detach(dev); 275 return (ENXIO); 276 } 277 278 static int 279 ucycom_detach(device_t dev) 280 { 281 struct ucycom_softc *sc = device_get_softc(dev); 282 283 usb2_com_detach(&sc->sc_super_ucom, &sc->sc_ucom, 1); 284 285 usb2_transfer_unsetup(sc->sc_xfer, UCYCOM_N_TRANSFER); 286 287 return (0); 288 } 289 290 static void 291 ucycom_cfg_open(struct usb2_com_softc *ucom) 292 { 293 struct ucycom_softc *sc = ucom->sc_parent; 294 295 /* set default configuration */ 296 ucycom_cfg_write(sc, UCYCOM_DEFAULT_RATE, UCYCOM_DEFAULT_CFG); 297 } 298 299 static void 300 ucycom_start_read(struct usb2_com_softc *ucom) 301 { 302 struct ucycom_softc *sc = ucom->sc_parent; 303 304 usb2_transfer_start(sc->sc_xfer[UCYCOM_INTR_RD]); 305 } 306 307 static void 308 ucycom_stop_read(struct usb2_com_softc *ucom) 309 { 310 struct ucycom_softc *sc = ucom->sc_parent; 311 312 usb2_transfer_stop(sc->sc_xfer[UCYCOM_INTR_RD]); 313 } 314 315 static void 316 ucycom_start_write(struct usb2_com_softc *ucom) 317 { 318 struct ucycom_softc *sc = ucom->sc_parent; 319 320 usb2_transfer_start(sc->sc_xfer[UCYCOM_CTRL_RD]); 321 } 322 323 static void 324 ucycom_stop_write(struct usb2_com_softc *ucom) 325 { 326 struct ucycom_softc *sc = ucom->sc_parent; 327 328 usb2_transfer_stop(sc->sc_xfer[UCYCOM_CTRL_RD]); 329 } 330 331 static void 332 ucycom_ctrl_write_callback(struct usb2_xfer *xfer) 333 { 334 struct ucycom_softc *sc = xfer->priv_sc; 335 struct usb2_device_request req; 336 uint8_t data[2]; 337 uint8_t offset; 338 uint32_t actlen; 339 340 switch (USB_GET_STATE(xfer)) { 341 case USB_ST_TRANSFERRED: 342 tr_transferred: 343 case USB_ST_SETUP: 344 345 switch (sc->sc_model) { 346 case MODEL_CY7C63743: 347 offset = 1; 348 break; 349 case MODEL_CY7C64013: 350 offset = 2; 351 break; 352 default: 353 offset = 0; 354 break; 355 } 356 357 if (usb2_com_get_data(&sc->sc_ucom, xfer->frbuffers + 1, offset, 358 sc->sc_olen - offset, &actlen)) { 359 360 req.bmRequestType = UT_WRITE_CLASS_INTERFACE; 361 req.bRequest = UR_SET_REPORT; 362 USETW2(req.wValue, UHID_OUTPUT_REPORT, sc->sc_oid); 363 req.wIndex[0] = sc->sc_iface_no; 364 req.wIndex[1] = 0; 365 USETW(req.wLength, sc->sc_olen); 366 367 switch (sc->sc_model) { 368 case MODEL_CY7C63743: 369 data[0] = actlen; 370 break; 371 case MODEL_CY7C64013: 372 data[0] = 0; 373 data[1] = actlen; 374 break; 375 default: 376 break; 377 } 378 379 usb2_copy_in(xfer->frbuffers, 0, &req, sizeof(req)); 380 usb2_copy_in(xfer->frbuffers + 1, 0, data, offset); 381 382 xfer->frlengths[0] = sizeof(req); 383 xfer->frlengths[1] = sc->sc_olen; 384 xfer->nframes = xfer->frlengths[1] ? 2 : 1; 385 usb2_start_hardware(xfer); 386 } 387 return; 388 389 default: /* Error */ 390 if (xfer->error == USB_ERR_CANCELLED) { 391 return; 392 } 393 DPRINTF("error=%s\n", 394 usb2_errstr(xfer->error)); 395 goto tr_transferred; 396 } 397 } 398 399 static void 400 ucycom_cfg_write(struct ucycom_softc *sc, uint32_t baud, uint8_t cfg) 401 { 402 struct usb2_device_request req; 403 uint16_t len; 404 usb2_error_t err; 405 406 len = sc->sc_flen; 407 if (len > sizeof(sc->sc_temp_cfg)) { 408 len = sizeof(sc->sc_temp_cfg); 409 } 410 sc->sc_cfg = cfg; 411 412 req.bmRequestType = UT_WRITE_CLASS_INTERFACE; 413 req.bRequest = UR_SET_REPORT; 414 USETW2(req.wValue, UHID_FEATURE_REPORT, sc->sc_fid); 415 req.wIndex[0] = sc->sc_iface_no; 416 req.wIndex[1] = 0; 417 USETW(req.wLength, len); 418 419 sc->sc_temp_cfg[0] = (baud & 0xff); 420 sc->sc_temp_cfg[1] = (baud >> 8) & 0xff; 421 sc->sc_temp_cfg[2] = (baud >> 16) & 0xff; 422 sc->sc_temp_cfg[3] = (baud >> 24) & 0xff; 423 sc->sc_temp_cfg[4] = cfg; 424 425 err = usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 426 &req, sc->sc_temp_cfg, 0, 1000); 427 if (err) { 428 DPRINTFN(0, "device request failed, err=%s " 429 "(ignored)\n", usb2_errstr(err)); 430 } 431 } 432 433 static int 434 ucycom_pre_param(struct usb2_com_softc *ucom, struct termios *t) 435 { 436 switch (t->c_ospeed) { 437 case 600: 438 case 1200: 439 case 2400: 440 case 4800: 441 case 9600: 442 case 19200: 443 case 38400: 444 case 57600: 445 #if 0 446 /* 447 * Stock chips only support standard baud rates in the 600 - 57600 448 * range, but higher rates can be achieved using custom firmware. 449 */ 450 case 115200: 451 case 153600: 452 case 192000: 453 #endif 454 break; 455 default: 456 return (EINVAL); 457 } 458 return (0); 459 } 460 461 static void 462 ucycom_cfg_param(struct usb2_com_softc *ucom, struct termios *t) 463 { 464 struct ucycom_softc *sc = ucom->sc_parent; 465 uint8_t cfg; 466 467 DPRINTF("\n"); 468 469 if (t->c_cflag & CIGNORE) { 470 cfg = sc->sc_cfg; 471 } else { 472 cfg = 0; 473 switch (t->c_cflag & CSIZE) { 474 default: 475 case CS8: 476 ++cfg; 477 case CS7: 478 ++cfg; 479 case CS6: 480 ++cfg; 481 case CS5: 482 break; 483 } 484 485 if (t->c_cflag & CSTOPB) 486 cfg |= UCYCOM_CFG_STOPB; 487 if (t->c_cflag & PARENB) 488 cfg |= UCYCOM_CFG_PAREN; 489 if (t->c_cflag & PARODD) 490 cfg |= UCYCOM_CFG_PARODD; 491 } 492 493 ucycom_cfg_write(sc, t->c_ospeed, cfg); 494 } 495 496 static void 497 ucycom_intr_read_callback(struct usb2_xfer *xfer) 498 { 499 struct ucycom_softc *sc = xfer->priv_sc; 500 uint8_t buf[2]; 501 uint32_t offset; 502 uint32_t len; 503 504 switch (USB_GET_STATE(xfer)) { 505 case USB_ST_TRANSFERRED: 506 switch (sc->sc_model) { 507 case MODEL_CY7C63743: 508 if (xfer->actlen < 1) { 509 goto tr_setup; 510 } 511 usb2_copy_out(xfer->frbuffers, 0, buf, 1); 512 513 sc->sc_ist = buf[0] & ~0x07; 514 len = buf[0] & 0x07; 515 516 (xfer->actlen)--; 517 518 offset = 1; 519 520 break; 521 522 case MODEL_CY7C64013: 523 if (xfer->actlen < 2) { 524 goto tr_setup; 525 } 526 usb2_copy_out(xfer->frbuffers, 0, buf, 2); 527 528 sc->sc_ist = buf[0] & ~0x07; 529 len = buf[1]; 530 531 (xfer->actlen) -= 2; 532 533 offset = 2; 534 535 break; 536 537 default: 538 DPRINTFN(0, "unsupported model number!\n"); 539 goto tr_setup; 540 } 541 542 if (len > xfer->actlen) { 543 len = xfer->actlen; 544 } 545 if (len) { 546 usb2_com_put_data(&sc->sc_ucom, xfer->frbuffers, 547 offset, len); 548 } 549 case USB_ST_SETUP: 550 tr_setup: 551 xfer->frlengths[0] = sc->sc_ilen; 552 usb2_start_hardware(xfer); 553 return; 554 555 default: /* Error */ 556 if (xfer->error != USB_ERR_CANCELLED) { 557 /* try to clear stall first */ 558 xfer->flags.stall_pipe = 1; 559 goto tr_setup; 560 } 561 return; 562 563 } 564 } 565