uftdi.c revision 240659
1/* $NetBSD: uftdi.c,v 1.13 2002/09/23 05:51:23 simonb Exp $ */ 2 3/*- 4 * Copyright (c) 2000 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Lennart Augustsson (lennart@augustsson.net). 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 NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32#include <sys/cdefs.h> 33__FBSDID("$FreeBSD: stable/9/sys/dev/usb/serial/uftdi.c 240659 2012-09-18 16:28:49Z hselasky $"); 34 35/* 36 * NOTE: all function names beginning like "uftdi_cfg_" can only 37 * be called from within the config thread function ! 38 */ 39 40/* 41 * FTDI FT2232x, FT8U100AX and FT8U232AM serial adapter driver 42 */ 43 44#include <sys/stdint.h> 45#include <sys/stddef.h> 46#include <sys/param.h> 47#include <sys/queue.h> 48#include <sys/types.h> 49#include <sys/systm.h> 50#include <sys/kernel.h> 51#include <sys/bus.h> 52#include <sys/module.h> 53#include <sys/lock.h> 54#include <sys/mutex.h> 55#include <sys/condvar.h> 56#include <sys/sysctl.h> 57#include <sys/sx.h> 58#include <sys/unistd.h> 59#include <sys/callout.h> 60#include <sys/malloc.h> 61#include <sys/priv.h> 62 63#include <dev/usb/usb.h> 64#include <dev/usb/usbdi.h> 65#include <dev/usb/usbdi_util.h> 66#include "usbdevs.h" 67 68#define USB_DEBUG_VAR uftdi_debug 69#include <dev/usb/usb_debug.h> 70#include <dev/usb/usb_process.h> 71 72#include <dev/usb/serial/usb_serial.h> 73#include <dev/usb/serial/uftdi_reg.h> 74 75#ifdef USB_DEBUG 76static int uftdi_debug = 0; 77 78SYSCTL_NODE(_hw_usb, OID_AUTO, uftdi, CTLFLAG_RW, 0, "USB uftdi"); 79SYSCTL_INT(_hw_usb_uftdi, OID_AUTO, debug, CTLFLAG_RW, 80 &uftdi_debug, 0, "Debug level"); 81#endif 82 83#define UFTDI_CONFIG_INDEX 0 84#define UFTDI_IFACE_INDEX_JTAG 0 85 86#define UFTDI_OBUFSIZE 64 /* bytes, cannot be increased due to 87 * do size encoding */ 88 89enum { 90 UFTDI_BULK_DT_WR, 91 UFTDI_BULK_DT_RD, 92 UFTDI_N_TRANSFER, 93}; 94 95struct uftdi_softc { 96 struct ucom_super_softc sc_super_ucom; 97 struct ucom_softc sc_ucom; 98 99 struct usb_device *sc_udev; 100 struct usb_xfer *sc_xfer[UFTDI_N_TRANSFER]; 101 device_t sc_dev; 102 struct mtx sc_mtx; 103 104 uint32_t sc_unit; 105 106 uint16_t sc_last_lcr; 107 108 uint8_t sc_type; 109 uint8_t sc_iface_index; 110 uint8_t sc_hdrlen; 111 uint8_t sc_msr; 112 uint8_t sc_lsr; 113}; 114 115struct uftdi_param_config { 116 uint16_t rate; 117 uint16_t lcr; 118 uint8_t v_start; 119 uint8_t v_stop; 120 uint8_t v_flow; 121}; 122 123/* prototypes */ 124 125static device_probe_t uftdi_probe; 126static device_attach_t uftdi_attach; 127static device_detach_t uftdi_detach; 128static void uftdi_free_softc(struct uftdi_softc *); 129 130static usb_callback_t uftdi_write_callback; 131static usb_callback_t uftdi_read_callback; 132 133static void uftdi_free(struct ucom_softc *); 134static void uftdi_cfg_open(struct ucom_softc *); 135static void uftdi_cfg_set_dtr(struct ucom_softc *, uint8_t); 136static void uftdi_cfg_set_rts(struct ucom_softc *, uint8_t); 137static void uftdi_cfg_set_break(struct ucom_softc *, uint8_t); 138static int uftdi_set_parm_soft(struct termios *, 139 struct uftdi_param_config *, uint8_t); 140static int uftdi_pre_param(struct ucom_softc *, struct termios *); 141static void uftdi_cfg_param(struct ucom_softc *, struct termios *); 142static void uftdi_cfg_get_status(struct ucom_softc *, uint8_t *, 143 uint8_t *); 144static void uftdi_start_read(struct ucom_softc *); 145static void uftdi_stop_read(struct ucom_softc *); 146static void uftdi_start_write(struct ucom_softc *); 147static void uftdi_stop_write(struct ucom_softc *); 148static uint8_t uftdi_8u232am_getrate(uint32_t, uint16_t *); 149static void uftdi_poll(struct ucom_softc *ucom); 150 151static const struct usb_config uftdi_config[UFTDI_N_TRANSFER] = { 152 153 [UFTDI_BULK_DT_WR] = { 154 .type = UE_BULK, 155 .endpoint = UE_ADDR_ANY, 156 .direction = UE_DIR_OUT, 157 .bufsize = UFTDI_OBUFSIZE, 158 .flags = {.pipe_bof = 1,}, 159 .callback = &uftdi_write_callback, 160 }, 161 162 [UFTDI_BULK_DT_RD] = { 163 .type = UE_BULK, 164 .endpoint = UE_ADDR_ANY, 165 .direction = UE_DIR_IN, 166 .bufsize = 0, /* use wMaxPacketSize */ 167 .flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, 168 .callback = &uftdi_read_callback, 169 }, 170}; 171 172static const struct ucom_callback uftdi_callback = { 173 .ucom_cfg_get_status = &uftdi_cfg_get_status, 174 .ucom_cfg_set_dtr = &uftdi_cfg_set_dtr, 175 .ucom_cfg_set_rts = &uftdi_cfg_set_rts, 176 .ucom_cfg_set_break = &uftdi_cfg_set_break, 177 .ucom_cfg_param = &uftdi_cfg_param, 178 .ucom_cfg_open = &uftdi_cfg_open, 179 .ucom_pre_param = &uftdi_pre_param, 180 .ucom_start_read = &uftdi_start_read, 181 .ucom_stop_read = &uftdi_stop_read, 182 .ucom_start_write = &uftdi_start_write, 183 .ucom_stop_write = &uftdi_stop_write, 184 .ucom_poll = &uftdi_poll, 185 .ucom_free = &uftdi_free, 186}; 187 188static device_method_t uftdi_methods[] = { 189 /* Device interface */ 190 DEVMETHOD(device_probe, uftdi_probe), 191 DEVMETHOD(device_attach, uftdi_attach), 192 DEVMETHOD(device_detach, uftdi_detach), 193 DEVMETHOD_END 194}; 195 196static devclass_t uftdi_devclass; 197 198static driver_t uftdi_driver = { 199 .name = "uftdi", 200 .methods = uftdi_methods, 201 .size = sizeof(struct uftdi_softc), 202}; 203 204DRIVER_MODULE(uftdi, uhub, uftdi_driver, uftdi_devclass, NULL, NULL); 205MODULE_DEPEND(uftdi, ucom, 1, 1, 1); 206MODULE_DEPEND(uftdi, usb, 1, 1, 1); 207MODULE_VERSION(uftdi, 1); 208 209static const STRUCT_USB_HOST_ID uftdi_devs[] = { 210#define UFTDI_DEV(v, p, i) \ 211 { USB_VPI(USB_VENDOR_##v, USB_PRODUCT_##v##_##p, i) } 212 UFTDI_DEV(ATMEL, STK541, UFTDI_TYPE_8U232AM), 213 UFTDI_DEV(BBELECTRONICS, USOTL4, UFTDI_TYPE_8U232AM), 214 UFTDI_DEV(DRESDENELEKTRONIK, SENSORTERMINALBOARD, 215 UFTDI_TYPE_8U232AM), 216 UFTDI_DEV(DRESDENELEKTRONIK, WIRELESSHANDHELDTERMINAL, 217 UFTDI_TYPE_8U232AM), 218 UFTDI_DEV(FALCOM, TWIST, UFTDI_TYPE_8U232AM), 219 UFTDI_DEV(FTDI, BEAGLEBONE, UFTDI_TYPE_8U232AM), 220 UFTDI_DEV(FTDI, CFA_631, UFTDI_TYPE_8U232AM), 221 UFTDI_DEV(FTDI, CFA_632, UFTDI_TYPE_8U232AM), 222 UFTDI_DEV(FTDI, CFA_633, UFTDI_TYPE_8U232AM), 223 UFTDI_DEV(FTDI, CFA_634, UFTDI_TYPE_8U232AM), 224 UFTDI_DEV(FTDI, CFA_635, UFTDI_TYPE_8U232AM), 225 UFTDI_DEV(FTDI, CTI_USB_MINI_485, UFTDI_TYPE_8U232AM), 226 UFTDI_DEV(FTDI, CTI_USB_NANO_485, UFTDI_TYPE_8U232AM), 227 UFTDI_DEV(FTDI, EISCOU, UFTDI_TYPE_8U232AM), 228 UFTDI_DEV(FTDI, EMCU2D, UFTDI_TYPE_8U232AM), 229 UFTDI_DEV(FTDI, EMCU2H, UFTDI_TYPE_8U232AM), 230 UFTDI_DEV(FTDI, GAMMASCOUT, UFTDI_TYPE_8U232AM), 231 UFTDI_DEV(FTDI, KBS, UFTDI_TYPE_8U232AM), 232 UFTDI_DEV(FTDI, LK202, UFTDI_TYPE_8U232AM), 233 UFTDI_DEV(FTDI, LK204, UFTDI_TYPE_8U232AM), 234 UFTDI_DEV(FTDI, MAXSTREAM, UFTDI_TYPE_8U232AM), 235 UFTDI_DEV(FTDI, MX2_3, UFTDI_TYPE_8U232AM), 236 UFTDI_DEV(FTDI, MX4_5, UFTDI_TYPE_8U232AM), 237 UFTDI_DEV(FTDI, PCMSFU, UFTDI_TYPE_8U232AM), 238 UFTDI_DEV(FTDI, SEMC_DSS20, UFTDI_TYPE_8U232AM), 239 UFTDI_DEV(FTDI, SERIAL_2232C, UFTDI_TYPE_8U232AM), 240 UFTDI_DEV(FTDI, SERIAL_2232D, UFTDI_TYPE_8U232AM), 241 UFTDI_DEV(FTDI, SERIAL_4232H, UFTDI_TYPE_8U232AM), 242 UFTDI_DEV(FTDI, SERIAL_8U100AX, UFTDI_TYPE_SIO), 243 UFTDI_DEV(FTDI, SERIAL_8U232AM, UFTDI_TYPE_8U232AM), 244 UFTDI_DEV(FTDI, SERIAL_8U232AM4, UFTDI_TYPE_8U232AM), 245 UFTDI_DEV(FTDI, TACTRIX_OPENPORT_13M, UFTDI_TYPE_8U232AM), 246 UFTDI_DEV(FTDI, TACTRIX_OPENPORT_13S, UFTDI_TYPE_8U232AM), 247 UFTDI_DEV(FTDI, TACTRIX_OPENPORT_13U, UFTDI_TYPE_8U232AM), 248 UFTDI_DEV(FTDI, TURTELIZER2, UFTDI_TYPE_8U232AM | UFTDI_FLAG_JTAG), 249 UFTDI_DEV(FTDI, UOPTBR, UFTDI_TYPE_8U232AM), 250 UFTDI_DEV(FTDI, USBSERIAL, UFTDI_TYPE_8U232AM), 251 UFTDI_DEV(FTDI, USB_UIRT, UFTDI_TYPE_8U232AM), 252 UFTDI_DEV(INTREPIDCS, NEOVI, UFTDI_TYPE_8U232AM), 253 UFTDI_DEV(INTREPIDCS, VALUECAN, UFTDI_TYPE_8U232AM), 254 UFTDI_DEV(MARVELL, SHEEVAPLUG, UFTDI_TYPE_8U232AM), 255 UFTDI_DEV(MATRIXORBITAL, MOUA, UFTDI_TYPE_8U232AM), 256 UFTDI_DEV(MELCO, PCOPRS1, UFTDI_TYPE_8U232AM), 257 UFTDI_DEV(RATOC, REXUSB60F, UFTDI_TYPE_8U232AM), 258 UFTDI_DEV(SIIG2, US2308, UFTDI_TYPE_8U232AM) 259#undef UFTDI_DEV 260}; 261 262static int 263uftdi_probe(device_t dev) 264{ 265 struct usb_attach_arg *uaa = device_get_ivars(dev); 266 const struct usb_device_id *id; 267 268 if (uaa->usb_mode != USB_MODE_HOST) { 269 return (ENXIO); 270 } 271 if (uaa->info.bConfigIndex != UFTDI_CONFIG_INDEX) { 272 return (ENXIO); 273 } 274 275 /* 276 * Attach to all present interfaces unless this is a JTAG one, which 277 * we leave for userland. 278 */ 279 id = usbd_lookup_id_by_info(uftdi_devs, sizeof(uftdi_devs), 280 &uaa->info); 281 if (id == NULL) 282 return (ENXIO); 283 if ((id->driver_info & UFTDI_FLAG_JTAG) != 0 && 284 uaa->info.bIfaceIndex == UFTDI_IFACE_INDEX_JTAG) { 285 printf("%s: skipping JTAG interface at %u.%u\n", 286 device_get_name(dev), usbd_get_bus_index(uaa->device), 287 usbd_get_device_index(uaa->device)); 288 return (ENXIO); 289 } 290 uaa->driver_info = id->driver_info; 291 return (BUS_PROBE_SPECIFIC); 292} 293 294static int 295uftdi_attach(device_t dev) 296{ 297 struct usb_attach_arg *uaa = device_get_ivars(dev); 298 struct uftdi_softc *sc = device_get_softc(dev); 299 int error; 300 301 sc->sc_udev = uaa->device; 302 sc->sc_dev = dev; 303 sc->sc_unit = device_get_unit(dev); 304 305 device_set_usb_desc(dev); 306 mtx_init(&sc->sc_mtx, "uftdi", NULL, MTX_DEF); 307 ucom_ref(&sc->sc_super_ucom); 308 309 DPRINTF("\n"); 310 311 sc->sc_iface_index = uaa->info.bIfaceIndex; 312 sc->sc_type = USB_GET_DRIVER_INFO(uaa) & UFTDI_TYPE_MASK; 313 314 switch (sc->sc_type) { 315 case UFTDI_TYPE_SIO: 316 sc->sc_hdrlen = 1; 317 break; 318 case UFTDI_TYPE_8U232AM: 319 default: 320 sc->sc_hdrlen = 0; 321 break; 322 } 323 324 error = usbd_transfer_setup(uaa->device, 325 &sc->sc_iface_index, sc->sc_xfer, uftdi_config, 326 UFTDI_N_TRANSFER, sc, &sc->sc_mtx); 327 328 if (error) { 329 device_printf(dev, "allocating USB " 330 "transfers failed\n"); 331 goto detach; 332 } 333 sc->sc_ucom.sc_portno = FTDI_PIT_SIOA + uaa->info.bIfaceNum; 334 335 /* clear stall at first run */ 336 mtx_lock(&sc->sc_mtx); 337 usbd_xfer_set_stall(sc->sc_xfer[UFTDI_BULK_DT_WR]); 338 usbd_xfer_set_stall(sc->sc_xfer[UFTDI_BULK_DT_RD]); 339 mtx_unlock(&sc->sc_mtx); 340 341 /* set a valid "lcr" value */ 342 343 sc->sc_last_lcr = 344 (FTDI_SIO_SET_DATA_STOP_BITS_2 | 345 FTDI_SIO_SET_DATA_PARITY_NONE | 346 FTDI_SIO_SET_DATA_BITS(8)); 347 348 error = ucom_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc, 349 &uftdi_callback, &sc->sc_mtx); 350 if (error) { 351 goto detach; 352 } 353 ucom_set_pnpinfo_usb(&sc->sc_super_ucom, dev); 354 355 return (0); /* success */ 356 357detach: 358 uftdi_detach(dev); 359 return (ENXIO); 360} 361 362static int 363uftdi_detach(device_t dev) 364{ 365 struct uftdi_softc *sc = device_get_softc(dev); 366 367 ucom_detach(&sc->sc_super_ucom, &sc->sc_ucom); 368 usbd_transfer_unsetup(sc->sc_xfer, UFTDI_N_TRANSFER); 369 370 device_claim_softc(dev); 371 372 uftdi_free_softc(sc); 373 374 return (0); 375} 376 377UCOM_UNLOAD_DRAIN(uftdi); 378 379static void 380uftdi_free_softc(struct uftdi_softc *sc) 381{ 382 if (ucom_unref(&sc->sc_super_ucom)) { 383 mtx_destroy(&sc->sc_mtx); 384 device_free_softc(sc); 385 } 386} 387 388static void 389uftdi_free(struct ucom_softc *ucom) 390{ 391 uftdi_free_softc(ucom->sc_parent); 392} 393 394static void 395uftdi_cfg_open(struct ucom_softc *ucom) 396{ 397 struct uftdi_softc *sc = ucom->sc_parent; 398 uint16_t wIndex = ucom->sc_portno; 399 struct usb_device_request req; 400 401 DPRINTF(""); 402 403 /* perform a full reset on the device */ 404 405 req.bmRequestType = UT_WRITE_VENDOR_DEVICE; 406 req.bRequest = FTDI_SIO_RESET; 407 USETW(req.wValue, FTDI_SIO_RESET_SIO); 408 USETW(req.wIndex, wIndex); 409 USETW(req.wLength, 0); 410 ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 411 &req, NULL, 0, 1000); 412 413 /* turn on RTS/CTS flow control */ 414 415 req.bmRequestType = UT_WRITE_VENDOR_DEVICE; 416 req.bRequest = FTDI_SIO_SET_FLOW_CTRL; 417 USETW(req.wValue, 0); 418 USETW2(req.wIndex, FTDI_SIO_RTS_CTS_HS, wIndex); 419 USETW(req.wLength, 0); 420 ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 421 &req, NULL, 0, 1000); 422 423 /* 424 * NOTE: with the new UCOM layer there will always be a 425 * "uftdi_cfg_param()" call after "open()", so there is no need for 426 * "open()" to configure anything 427 */ 428} 429 430static void 431uftdi_write_callback(struct usb_xfer *xfer, usb_error_t error) 432{ 433 struct uftdi_softc *sc = usbd_xfer_softc(xfer); 434 struct usb_page_cache *pc; 435 uint32_t actlen; 436 uint8_t buf[1]; 437 438 switch (USB_GET_STATE(xfer)) { 439 case USB_ST_SETUP: 440 case USB_ST_TRANSFERRED: 441tr_setup: 442 pc = usbd_xfer_get_frame(xfer, 0); 443 if (ucom_get_data(&sc->sc_ucom, pc, 444 sc->sc_hdrlen, UFTDI_OBUFSIZE - sc->sc_hdrlen, 445 &actlen)) { 446 447 if (sc->sc_hdrlen > 0) { 448 buf[0] = 449 FTDI_OUT_TAG(actlen, sc->sc_ucom.sc_portno); 450 usbd_copy_in(pc, 0, buf, 1); 451 } 452 usbd_xfer_set_frame_len(xfer, 0, actlen + sc->sc_hdrlen); 453 usbd_transfer_submit(xfer); 454 } 455 return; 456 457 default: /* Error */ 458 if (error != USB_ERR_CANCELLED) { 459 /* try to clear stall first */ 460 usbd_xfer_set_stall(xfer); 461 goto tr_setup; 462 } 463 return; 464 } 465} 466 467static void 468uftdi_read_callback(struct usb_xfer *xfer, usb_error_t error) 469{ 470 struct uftdi_softc *sc = usbd_xfer_softc(xfer); 471 struct usb_page_cache *pc; 472 uint8_t buf[2]; 473 uint8_t ftdi_msr; 474 uint8_t msr; 475 uint8_t lsr; 476 int actlen; 477 478 usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); 479 480 switch (USB_GET_STATE(xfer)) { 481 case USB_ST_TRANSFERRED: 482 483 if (actlen < 2) { 484 goto tr_setup; 485 } 486 pc = usbd_xfer_get_frame(xfer, 0); 487 usbd_copy_out(pc, 0, buf, 2); 488 489 ftdi_msr = FTDI_GET_MSR(buf); 490 lsr = FTDI_GET_LSR(buf); 491 492 msr = 0; 493 if (ftdi_msr & FTDI_SIO_CTS_MASK) 494 msr |= SER_CTS; 495 if (ftdi_msr & FTDI_SIO_DSR_MASK) 496 msr |= SER_DSR; 497 if (ftdi_msr & FTDI_SIO_RI_MASK) 498 msr |= SER_RI; 499 if (ftdi_msr & FTDI_SIO_RLSD_MASK) 500 msr |= SER_DCD; 501 502 if ((sc->sc_msr != msr) || 503 ((sc->sc_lsr & FTDI_LSR_MASK) != (lsr & FTDI_LSR_MASK))) { 504 DPRINTF("status change msr=0x%02x (0x%02x) " 505 "lsr=0x%02x (0x%02x)\n", msr, sc->sc_msr, 506 lsr, sc->sc_lsr); 507 508 sc->sc_msr = msr; 509 sc->sc_lsr = lsr; 510 511 ucom_status_change(&sc->sc_ucom); 512 } 513 actlen -= 2; 514 515 if (actlen > 0) { 516 ucom_put_data(&sc->sc_ucom, pc, 2, actlen); 517 } 518 case USB_ST_SETUP: 519tr_setup: 520 usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); 521 usbd_transfer_submit(xfer); 522 return; 523 524 default: /* Error */ 525 if (error != USB_ERR_CANCELLED) { 526 /* try to clear stall first */ 527 usbd_xfer_set_stall(xfer); 528 goto tr_setup; 529 } 530 return; 531 } 532} 533 534static void 535uftdi_cfg_set_dtr(struct ucom_softc *ucom, uint8_t onoff) 536{ 537 struct uftdi_softc *sc = ucom->sc_parent; 538 uint16_t wIndex = ucom->sc_portno; 539 uint16_t wValue; 540 struct usb_device_request req; 541 542 wValue = onoff ? FTDI_SIO_SET_DTR_HIGH : FTDI_SIO_SET_DTR_LOW; 543 544 req.bmRequestType = UT_WRITE_VENDOR_DEVICE; 545 req.bRequest = FTDI_SIO_MODEM_CTRL; 546 USETW(req.wValue, wValue); 547 USETW(req.wIndex, wIndex); 548 USETW(req.wLength, 0); 549 ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 550 &req, NULL, 0, 1000); 551} 552 553static void 554uftdi_cfg_set_rts(struct ucom_softc *ucom, uint8_t onoff) 555{ 556 struct uftdi_softc *sc = ucom->sc_parent; 557 uint16_t wIndex = ucom->sc_portno; 558 uint16_t wValue; 559 struct usb_device_request req; 560 561 wValue = onoff ? FTDI_SIO_SET_RTS_HIGH : FTDI_SIO_SET_RTS_LOW; 562 563 req.bmRequestType = UT_WRITE_VENDOR_DEVICE; 564 req.bRequest = FTDI_SIO_MODEM_CTRL; 565 USETW(req.wValue, wValue); 566 USETW(req.wIndex, wIndex); 567 USETW(req.wLength, 0); 568 ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 569 &req, NULL, 0, 1000); 570} 571 572static void 573uftdi_cfg_set_break(struct ucom_softc *ucom, uint8_t onoff) 574{ 575 struct uftdi_softc *sc = ucom->sc_parent; 576 uint16_t wIndex = ucom->sc_portno; 577 uint16_t wValue; 578 struct usb_device_request req; 579 580 if (onoff) { 581 sc->sc_last_lcr |= FTDI_SIO_SET_BREAK; 582 } else { 583 sc->sc_last_lcr &= ~FTDI_SIO_SET_BREAK; 584 } 585 586 wValue = sc->sc_last_lcr; 587 588 req.bmRequestType = UT_WRITE_VENDOR_DEVICE; 589 req.bRequest = FTDI_SIO_SET_DATA; 590 USETW(req.wValue, wValue); 591 USETW(req.wIndex, wIndex); 592 USETW(req.wLength, 0); 593 ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 594 &req, NULL, 0, 1000); 595} 596 597static int 598uftdi_set_parm_soft(struct termios *t, 599 struct uftdi_param_config *cfg, uint8_t type) 600{ 601 602 memset(cfg, 0, sizeof(*cfg)); 603 604 switch (type) { 605 case UFTDI_TYPE_SIO: 606 switch (t->c_ospeed) { 607 case 300: 608 cfg->rate = ftdi_sio_b300; 609 break; 610 case 600: 611 cfg->rate = ftdi_sio_b600; 612 break; 613 case 1200: 614 cfg->rate = ftdi_sio_b1200; 615 break; 616 case 2400: 617 cfg->rate = ftdi_sio_b2400; 618 break; 619 case 4800: 620 cfg->rate = ftdi_sio_b4800; 621 break; 622 case 9600: 623 cfg->rate = ftdi_sio_b9600; 624 break; 625 case 19200: 626 cfg->rate = ftdi_sio_b19200; 627 break; 628 case 38400: 629 cfg->rate = ftdi_sio_b38400; 630 break; 631 case 57600: 632 cfg->rate = ftdi_sio_b57600; 633 break; 634 case 115200: 635 cfg->rate = ftdi_sio_b115200; 636 break; 637 default: 638 return (EINVAL); 639 } 640 break; 641 642 case UFTDI_TYPE_8U232AM: 643 if (uftdi_8u232am_getrate(t->c_ospeed, &cfg->rate)) { 644 return (EINVAL); 645 } 646 break; 647 } 648 649 if (t->c_cflag & CSTOPB) 650 cfg->lcr = FTDI_SIO_SET_DATA_STOP_BITS_2; 651 else 652 cfg->lcr = FTDI_SIO_SET_DATA_STOP_BITS_1; 653 654 if (t->c_cflag & PARENB) { 655 if (t->c_cflag & PARODD) { 656 cfg->lcr |= FTDI_SIO_SET_DATA_PARITY_ODD; 657 } else { 658 cfg->lcr |= FTDI_SIO_SET_DATA_PARITY_EVEN; 659 } 660 } else { 661 cfg->lcr |= FTDI_SIO_SET_DATA_PARITY_NONE; 662 } 663 664 switch (t->c_cflag & CSIZE) { 665 case CS5: 666 cfg->lcr |= FTDI_SIO_SET_DATA_BITS(5); 667 break; 668 669 case CS6: 670 cfg->lcr |= FTDI_SIO_SET_DATA_BITS(6); 671 break; 672 673 case CS7: 674 cfg->lcr |= FTDI_SIO_SET_DATA_BITS(7); 675 break; 676 677 case CS8: 678 cfg->lcr |= FTDI_SIO_SET_DATA_BITS(8); 679 break; 680 } 681 682 if (t->c_cflag & CRTSCTS) { 683 cfg->v_flow = FTDI_SIO_RTS_CTS_HS; 684 } else if (t->c_iflag & (IXON | IXOFF)) { 685 cfg->v_flow = FTDI_SIO_XON_XOFF_HS; 686 cfg->v_start = t->c_cc[VSTART]; 687 cfg->v_stop = t->c_cc[VSTOP]; 688 } else { 689 cfg->v_flow = FTDI_SIO_DISABLE_FLOW_CTRL; 690 } 691 692 return (0); 693} 694 695static int 696uftdi_pre_param(struct ucom_softc *ucom, struct termios *t) 697{ 698 struct uftdi_softc *sc = ucom->sc_parent; 699 struct uftdi_param_config cfg; 700 701 DPRINTF("\n"); 702 703 return (uftdi_set_parm_soft(t, &cfg, sc->sc_type)); 704} 705 706static void 707uftdi_cfg_param(struct ucom_softc *ucom, struct termios *t) 708{ 709 struct uftdi_softc *sc = ucom->sc_parent; 710 uint16_t wIndex = ucom->sc_portno; 711 struct uftdi_param_config cfg; 712 struct usb_device_request req; 713 714 if (uftdi_set_parm_soft(t, &cfg, sc->sc_type)) { 715 /* should not happen */ 716 return; 717 } 718 sc->sc_last_lcr = cfg.lcr; 719 720 DPRINTF("\n"); 721 722 req.bmRequestType = UT_WRITE_VENDOR_DEVICE; 723 req.bRequest = FTDI_SIO_SET_BAUD_RATE; 724 USETW(req.wValue, cfg.rate); 725 USETW(req.wIndex, wIndex); 726 USETW(req.wLength, 0); 727 ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 728 &req, NULL, 0, 1000); 729 730 req.bmRequestType = UT_WRITE_VENDOR_DEVICE; 731 req.bRequest = FTDI_SIO_SET_DATA; 732 USETW(req.wValue, cfg.lcr); 733 USETW(req.wIndex, wIndex); 734 USETW(req.wLength, 0); 735 ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 736 &req, NULL, 0, 1000); 737 738 req.bmRequestType = UT_WRITE_VENDOR_DEVICE; 739 req.bRequest = FTDI_SIO_SET_FLOW_CTRL; 740 USETW2(req.wValue, cfg.v_stop, cfg.v_start); 741 USETW2(req.wIndex, cfg.v_flow, wIndex); 742 USETW(req.wLength, 0); 743 ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 744 &req, NULL, 0, 1000); 745} 746 747static void 748uftdi_cfg_get_status(struct ucom_softc *ucom, uint8_t *lsr, uint8_t *msr) 749{ 750 struct uftdi_softc *sc = ucom->sc_parent; 751 752 DPRINTF("msr=0x%02x lsr=0x%02x\n", 753 sc->sc_msr, sc->sc_lsr); 754 755 *msr = sc->sc_msr; 756 *lsr = sc->sc_lsr; 757} 758 759static void 760uftdi_start_read(struct ucom_softc *ucom) 761{ 762 struct uftdi_softc *sc = ucom->sc_parent; 763 764 usbd_transfer_start(sc->sc_xfer[UFTDI_BULK_DT_RD]); 765} 766 767static void 768uftdi_stop_read(struct ucom_softc *ucom) 769{ 770 struct uftdi_softc *sc = ucom->sc_parent; 771 772 usbd_transfer_stop(sc->sc_xfer[UFTDI_BULK_DT_RD]); 773} 774 775static void 776uftdi_start_write(struct ucom_softc *ucom) 777{ 778 struct uftdi_softc *sc = ucom->sc_parent; 779 780 usbd_transfer_start(sc->sc_xfer[UFTDI_BULK_DT_WR]); 781} 782 783static void 784uftdi_stop_write(struct ucom_softc *ucom) 785{ 786 struct uftdi_softc *sc = ucom->sc_parent; 787 788 usbd_transfer_stop(sc->sc_xfer[UFTDI_BULK_DT_WR]); 789} 790 791/*------------------------------------------------------------------------* 792 * uftdi_8u232am_getrate 793 * 794 * Return values: 795 * 0: Success 796 * Else: Failure 797 *------------------------------------------------------------------------*/ 798static uint8_t 799uftdi_8u232am_getrate(uint32_t speed, uint16_t *rate) 800{ 801 /* Table of the nearest even powers-of-2 for values 0..15. */ 802 static const uint8_t roundoff[16] = { 803 0, 2, 2, 4, 4, 4, 8, 8, 804 8, 8, 8, 8, 16, 16, 16, 16, 805 }; 806 uint32_t d; 807 uint32_t freq; 808 uint16_t result; 809 810 if ((speed < 178) || (speed > ((3000000 * 100) / 97))) 811 return (1); /* prevent numerical overflow */ 812 813 /* Special cases for 2M and 3M. */ 814 if ((speed >= ((3000000 * 100) / 103)) && 815 (speed <= ((3000000 * 100) / 97))) { 816 result = 0; 817 goto done; 818 } 819 if ((speed >= ((2000000 * 100) / 103)) && 820 (speed <= ((2000000 * 100) / 97))) { 821 result = 1; 822 goto done; 823 } 824 d = (FTDI_8U232AM_FREQ << 4) / speed; 825 d = (d & ~15) + roundoff[d & 15]; 826 827 if (d < FTDI_8U232AM_MIN_DIV) 828 d = FTDI_8U232AM_MIN_DIV; 829 else if (d > FTDI_8U232AM_MAX_DIV) 830 d = FTDI_8U232AM_MAX_DIV; 831 832 /* 833 * Calculate the frequency needed for "d" to exactly divide down to 834 * our target "speed", and check that the actual frequency is within 835 * 3% of this. 836 */ 837 freq = (speed * d); 838 if ((freq < ((FTDI_8U232AM_FREQ * 1600ULL) / 103)) || 839 (freq > ((FTDI_8U232AM_FREQ * 1600ULL) / 97))) 840 return (1); 841 842 /* 843 * Pack the divisor into the resultant value. The lower 14-bits 844 * hold the integral part, while the upper 2 bits encode the 845 * fractional component: either 0, 0.5, 0.25, or 0.125. 846 */ 847 result = (d >> 4); 848 if (d & 8) 849 result |= 0x4000; 850 else if (d & 4) 851 result |= 0x8000; 852 else if (d & 2) 853 result |= 0xc000; 854 855done: 856 *rate = result; 857 return (0); 858} 859 860static void 861uftdi_poll(struct ucom_softc *ucom) 862{ 863 struct uftdi_softc *sc = ucom->sc_parent; 864 865 usbd_transfer_poll(sc->sc_xfer, UFTDI_N_TRANSFER); 866} 867