uftdi.c revision 187259
1244197Sgonzo/* $NetBSD: uftdi.c,v 1.13 2002/09/23 05:51:23 simonb Exp $ */ 2244197Sgonzo 3244197Sgonzo/*- 4244197Sgonzo * Copyright (c) 2000 The NetBSD Foundation, Inc. 5244197Sgonzo * All rights reserved. 6244197Sgonzo * 7244197Sgonzo * This code is derived from software contributed to The NetBSD Foundation 8244197Sgonzo * by Lennart Augustsson (lennart@augustsson.net). 9244197Sgonzo * 10244197Sgonzo * Redistribution and use in source and binary forms, with or without 11244197Sgonzo * modification, are permitted provided that the following conditions 12244197Sgonzo * are met: 13244197Sgonzo * 1. Redistributions of source code must retain the above copyright 14244197Sgonzo * notice, this list of conditions and the following disclaimer. 15244197Sgonzo * 2. Redistributions in binary form must reproduce the above copyright 16244197Sgonzo * notice, this list of conditions and the following disclaimer in the 17244197Sgonzo * documentation and/or other materials provided with the distribution. 18244197Sgonzo * 3. All advertising materials mentioning features or use of this software 19244197Sgonzo * must display the following acknowledgement: 20244197Sgonzo * This product includes software developed by the NetBSD 21244197Sgonzo * Foundation, Inc. and its contributors. 22244197Sgonzo * 4. Neither the name of The NetBSD Foundation nor the names of its 23244197Sgonzo * contributors may be used to endorse or promote products derived 24244197Sgonzo * from this software without specific prior written permission. 25244197Sgonzo * 26244197Sgonzo * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 27244197Sgonzo * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 28244197Sgonzo * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 29244197Sgonzo * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 30244197Sgonzo * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 31244197Sgonzo * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 32244197Sgonzo * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 33244197Sgonzo * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 34244197Sgonzo * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 35244197Sgonzo * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 36244197Sgonzo * POSSIBILITY OF SUCH DAMAGE. 37244197Sgonzo */ 38244197Sgonzo 39244197Sgonzo#include <sys/cdefs.h> 40244197Sgonzo__FBSDID("$FreeBSD: head/sys/dev/usb2/serial/uftdi2.c 187259 2009-01-15 02:35:40Z thompsa $"); 41244197Sgonzo 42244197Sgonzo/* 43244197Sgonzo * NOTE: all function names beginning like "uftdi_cfg_" can only 44244197Sgonzo * be called from within the config thread function ! 45244197Sgonzo */ 46244197Sgonzo 47244197Sgonzo/* 48244197Sgonzo * FTDI FT8U100AX serial adapter driver 49244197Sgonzo */ 50244197Sgonzo 51244197Sgonzo#include <dev/usb2/include/usb2_devid.h> 52244197Sgonzo#include <dev/usb2/include/usb2_standard.h> 53244197Sgonzo#include <dev/usb2/include/usb2_mfunc.h> 54244197Sgonzo#include <dev/usb2/include/usb2_error.h> 55244197Sgonzo#include <dev/usb2/include/usb2_cdc.h> 56244197Sgonzo 57244197Sgonzo#define USB_DEBUG_VAR uftdi_debug 58244197Sgonzo 59244197Sgonzo#include <dev/usb2/core/usb2_core.h> 60244197Sgonzo#include <dev/usb2/core/usb2_debug.h> 61244197Sgonzo#include <dev/usb2/core/usb2_process.h> 62244197Sgonzo#include <dev/usb2/core/usb2_request.h> 63244197Sgonzo#include <dev/usb2/core/usb2_lookup.h> 64244197Sgonzo#include <dev/usb2/core/usb2_util.h> 65244197Sgonzo#include <dev/usb2/core/usb2_busdma.h> 66244197Sgonzo 67244197Sgonzo#include <dev/usb2/serial/usb2_serial.h> 68244197Sgonzo#include <dev/usb2/serial/uftdi2_reg.h> 69244197Sgonzo 70244197Sgonzo#if USB_DEBUG 71244197Sgonzostatic int uftdi_debug = 0; 72244197Sgonzo 73244197SgonzoSYSCTL_NODE(_hw_usb2, OID_AUTO, uftdi, CTLFLAG_RW, 0, "USB uftdi"); 74244197SgonzoSYSCTL_INT(_hw_usb2_uftdi, OID_AUTO, debug, CTLFLAG_RW, 75244197Sgonzo &uftdi_debug, 0, "Debug level"); 76244197Sgonzo#endif 77244197Sgonzo 78244197Sgonzo#define UFTDI_CONFIG_INDEX 0 79244197Sgonzo#define UFTDI_IFACE_INDEX 0 80244197Sgonzo 81244197Sgonzo#define UFTDI_IBUFSIZE 64 /* bytes, maximum number of bytes per 82244197Sgonzo * frame */ 83244197Sgonzo#define UFTDI_OBUFSIZE 64 /* bytes, cannot be increased due to 84244197Sgonzo * do size encoding */ 85244197Sgonzo 86244197Sgonzoenum { 87244197Sgonzo UFTDI_BULK_DT_WR, 88244197Sgonzo UFTDI_BULK_DT_RD, 89244197Sgonzo UFTDI_BULK_CS_WR, 90244197Sgonzo UFTDI_BULK_CS_RD, 91244197Sgonzo UFTDI_N_TRANSFER = 4, 92244197Sgonzo}; 93244197Sgonzo 94244197Sgonzostruct uftdi_softc { 95244197Sgonzo struct usb2_com_super_softc sc_super_ucom; 96244197Sgonzo struct usb2_com_softc sc_ucom; 97244197Sgonzo 98244197Sgonzo struct usb2_device *sc_udev; 99244197Sgonzo struct usb2_xfer *sc_xfer[UFTDI_N_TRANSFER]; 100244197Sgonzo device_t sc_dev; 101244197Sgonzo 102244197Sgonzo uint32_t sc_unit; 103244197Sgonzo enum uftdi_type sc_type; 104244197Sgonzo 105244197Sgonzo uint16_t sc_last_lcr; 106244197Sgonzo 107244197Sgonzo uint8_t sc_iface_index; 108244197Sgonzo uint8_t sc_hdrlen; 109244197Sgonzo 110244197Sgonzo uint8_t sc_msr; 111244197Sgonzo uint8_t sc_lsr; 112244197Sgonzo 113244197Sgonzo uint8_t sc_flag; 114244197Sgonzo#define UFTDI_FLAG_WRITE_STALL 0x01 115244197Sgonzo#define UFTDI_FLAG_READ_STALL 0x02 116244197Sgonzo 117244197Sgonzo uint8_t sc_name[16]; 118244197Sgonzo}; 119244197Sgonzo 120244197Sgonzostruct uftdi_param_config { 121244197Sgonzo uint16_t rate; 122244197Sgonzo uint16_t lcr; 123244197Sgonzo uint8_t v_start; 124244197Sgonzo uint8_t v_stop; 125244197Sgonzo uint8_t v_flow; 126244197Sgonzo}; 127244197Sgonzo 128244197Sgonzo/* prototypes */ 129244197Sgonzo 130244197Sgonzostatic device_probe_t uftdi_probe; 131244197Sgonzostatic device_attach_t uftdi_attach; 132244197Sgonzostatic device_detach_t uftdi_detach; 133244197Sgonzo 134244197Sgonzostatic usb2_callback_t uftdi_write_callback; 135244197Sgonzostatic usb2_callback_t uftdi_write_clear_stall_callback; 136244197Sgonzostatic usb2_callback_t uftdi_read_callback; 137244197Sgonzostatic usb2_callback_t uftdi_read_clear_stall_callback; 138244197Sgonzo 139244197Sgonzostatic void uftdi_cfg_do_request(struct uftdi_softc *, 140244197Sgonzo struct usb2_device_request *, void *); 141244197Sgonzostatic void uftdi_cfg_open(struct usb2_com_softc *); 142244197Sgonzostatic void uftdi_cfg_set_dtr(struct usb2_com_softc *, uint8_t); 143244197Sgonzostatic void uftdi_cfg_set_rts(struct usb2_com_softc *, uint8_t); 144244197Sgonzostatic void uftdi_cfg_set_break(struct usb2_com_softc *, uint8_t); 145244197Sgonzostatic int uftdi_set_parm_soft(struct termios *, 146244197Sgonzo struct uftdi_param_config *, uint8_t); 147244197Sgonzostatic int uftdi_pre_param(struct usb2_com_softc *, struct termios *); 148244197Sgonzostatic void uftdi_cfg_param(struct usb2_com_softc *, struct termios *); 149244197Sgonzostatic void uftdi_cfg_get_status(struct usb2_com_softc *, uint8_t *, 150244197Sgonzo uint8_t *); 151244197Sgonzostatic void uftdi_start_read(struct usb2_com_softc *); 152244197Sgonzostatic void uftdi_stop_read(struct usb2_com_softc *); 153244197Sgonzostatic void uftdi_start_write(struct usb2_com_softc *); 154244197Sgonzostatic void uftdi_stop_write(struct usb2_com_softc *); 155244197Sgonzostatic uint8_t uftdi_8u232am_getrate(uint32_t, uint16_t *); 156244197Sgonzo 157244197Sgonzostatic const struct usb2_config uftdi_config[UFTDI_N_TRANSFER] = { 158244197Sgonzo 159244197Sgonzo [UFTDI_BULK_DT_WR] = { 160244197Sgonzo .type = UE_BULK, 161244197Sgonzo .endpoint = UE_ADDR_ANY, 162244197Sgonzo .direction = UE_DIR_OUT, 163244197Sgonzo .mh.bufsize = UFTDI_OBUFSIZE, 164244197Sgonzo .mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,}, 165244197Sgonzo .mh.callback = &uftdi_write_callback, 166244197Sgonzo }, 167244197Sgonzo 168244197Sgonzo [UFTDI_BULK_DT_RD] = { 169244197Sgonzo .type = UE_BULK, 170244197Sgonzo .endpoint = UE_ADDR_ANY, 171244197Sgonzo .direction = UE_DIR_IN, 172244197Sgonzo .mh.bufsize = UFTDI_IBUFSIZE, 173244197Sgonzo .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, 174244197Sgonzo .mh.callback = &uftdi_read_callback, 175244197Sgonzo }, 176244197Sgonzo 177244197Sgonzo [UFTDI_BULK_CS_WR] = { 178244197Sgonzo .type = UE_CONTROL, 179244197Sgonzo .endpoint = 0x00, /* Control pipe */ 180244197Sgonzo .direction = UE_DIR_ANY, 181244197Sgonzo .mh.bufsize = sizeof(struct usb2_device_request), 182244197Sgonzo .mh.flags = {}, 183244197Sgonzo .mh.callback = &uftdi_write_clear_stall_callback, 184244197Sgonzo .mh.timeout = 1000, /* 1 second */ 185244197Sgonzo .mh.interval = 50, /* 50ms */ 186244197Sgonzo }, 187244197Sgonzo 188244197Sgonzo [UFTDI_BULK_CS_RD] = { 189244197Sgonzo .type = UE_CONTROL, 190244197Sgonzo .endpoint = 0x00, /* Control pipe */ 191244197Sgonzo .direction = UE_DIR_ANY, 192244197Sgonzo .mh.bufsize = sizeof(struct usb2_device_request), 193244197Sgonzo .mh.flags = {}, 194244197Sgonzo .mh.callback = &uftdi_read_clear_stall_callback, 195244197Sgonzo .mh.timeout = 1000, /* 1 second */ 196244197Sgonzo .mh.interval = 50, /* 50ms */ 197244197Sgonzo }, 198244197Sgonzo}; 199244197Sgonzo 200244197Sgonzostatic const struct usb2_com_callback uftdi_callback = { 201244197Sgonzo .usb2_com_cfg_get_status = &uftdi_cfg_get_status, 202244197Sgonzo .usb2_com_cfg_set_dtr = &uftdi_cfg_set_dtr, 203244197Sgonzo .usb2_com_cfg_set_rts = &uftdi_cfg_set_rts, 204244197Sgonzo .usb2_com_cfg_set_break = &uftdi_cfg_set_break, 205244197Sgonzo .usb2_com_cfg_param = &uftdi_cfg_param, 206244197Sgonzo .usb2_com_cfg_open = &uftdi_cfg_open, 207244197Sgonzo .usb2_com_pre_param = &uftdi_pre_param, 208244197Sgonzo .usb2_com_start_read = &uftdi_start_read, 209244197Sgonzo .usb2_com_stop_read = &uftdi_stop_read, 210244197Sgonzo .usb2_com_start_write = &uftdi_start_write, 211244197Sgonzo .usb2_com_stop_write = &uftdi_stop_write, 212244197Sgonzo}; 213244197Sgonzo 214244197Sgonzostatic device_method_t uftdi_methods[] = { 215244197Sgonzo /* Device interface */ 216244197Sgonzo DEVMETHOD(device_probe, uftdi_probe), 217244197Sgonzo DEVMETHOD(device_attach, uftdi_attach), 218244197Sgonzo DEVMETHOD(device_detach, uftdi_detach), 219244197Sgonzo 220244197Sgonzo {0, 0} 221244197Sgonzo}; 222244197Sgonzo 223244197Sgonzostatic devclass_t uftdi_devclass; 224244197Sgonzo 225244197Sgonzostatic driver_t uftdi_driver = { 226244197Sgonzo .name = "uftdi", 227244197Sgonzo .methods = uftdi_methods, 228244197Sgonzo .size = sizeof(struct uftdi_softc), 229244197Sgonzo}; 230244197Sgonzo 231244197SgonzoDRIVER_MODULE(uftdi, ushub, uftdi_driver, uftdi_devclass, NULL, 0); 232244197SgonzoMODULE_DEPEND(uftdi, usb2_serial, 1, 1, 1); 233244197SgonzoMODULE_DEPEND(uftdi, usb2_core, 1, 1, 1); 234244197Sgonzo 235244197Sgonzostatic struct usb2_device_id uftdi_devs[] = { 236244197Sgonzo {USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_SERIAL_8U100AX, UFTDI_TYPE_SIO)}, 237244197Sgonzo {USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_SERIAL_2232C, UFTDI_TYPE_8U232AM)}, 238244197Sgonzo {USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_SERIAL_8U232AM, UFTDI_TYPE_8U232AM)}, 239244197Sgonzo {USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_SEMC_DSS20, UFTDI_TYPE_8U232AM)}, 240244197Sgonzo {USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_CFA_631, UFTDI_TYPE_8U232AM)}, 241244197Sgonzo {USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_CFA_632, UFTDI_TYPE_8U232AM)}, 242244197Sgonzo {USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_CFA_633, UFTDI_TYPE_8U232AM)}, 243244197Sgonzo {USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_CFA_634, UFTDI_TYPE_8U232AM)}, 244244197Sgonzo {USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_CFA_635, UFTDI_TYPE_8U232AM)}, 245244197Sgonzo {USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_USBSERIAL, UFTDI_TYPE_8U232AM)}, 246244197Sgonzo {USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_MX2_3, UFTDI_TYPE_8U232AM)}, 247244197Sgonzo {USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_MX4_5, UFTDI_TYPE_8U232AM)}, 248244197Sgonzo {USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_LK202, UFTDI_TYPE_8U232AM)}, 249244197Sgonzo {USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_LK204, UFTDI_TYPE_8U232AM)}, 250244197Sgonzo {USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_TACTRIX_OPENPORT_13M, UFTDI_TYPE_8U232AM)}, 251244197Sgonzo {USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_TACTRIX_OPENPORT_13S, UFTDI_TYPE_8U232AM)}, 252244197Sgonzo {USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_TACTRIX_OPENPORT_13U, UFTDI_TYPE_8U232AM)}, 253244197Sgonzo {USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_EISCOU, UFTDI_TYPE_8U232AM)}, 254244197Sgonzo {USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_UOPTBR, UFTDI_TYPE_8U232AM)}, 255244197Sgonzo {USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_EMCU2D, UFTDI_TYPE_8U232AM)}, 256244197Sgonzo {USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_PCMSFU, UFTDI_TYPE_8U232AM)}, 257244197Sgonzo {USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_EMCU2H, UFTDI_TYPE_8U232AM)}, 258244197Sgonzo {USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_MAXSTREAM, UFTDI_TYPE_8U232AM)}, 259244197Sgonzo {USB_VPI(USB_VENDOR_SIIG2, USB_PRODUCT_SIIG2_US2308, UFTDI_TYPE_8U232AM)}, 260244197Sgonzo {USB_VPI(USB_VENDOR_INTREPIDCS, USB_PRODUCT_INTREPIDCS_VALUECAN, UFTDI_TYPE_8U232AM)}, 261244197Sgonzo {USB_VPI(USB_VENDOR_INTREPIDCS, USB_PRODUCT_INTREPIDCS_NEOVI, UFTDI_TYPE_8U232AM)}, 262244197Sgonzo {USB_VPI(USB_VENDOR_BBELECTRONICS, USB_PRODUCT_BBELECTRONICS_USOTL4, UFTDI_TYPE_8U232AM)}, 263244197Sgonzo {USB_VPI(USB_VENDOR_MELCO, USB_PRODUCT_MELCO_PCOPRS1, UFTDI_TYPE_8U232AM)}, 264244197Sgonzo}; 265244197Sgonzo 266244197Sgonzostatic int 267244197Sgonzouftdi_probe(device_t dev) 268244197Sgonzo{ 269244197Sgonzo struct usb2_attach_arg *uaa = device_get_ivars(dev); 270244197Sgonzo 271244197Sgonzo if (uaa->usb2_mode != USB_MODE_HOST) { 272244197Sgonzo return (ENXIO); 273244197Sgonzo } 274244197Sgonzo if (uaa->info.bConfigIndex != UFTDI_CONFIG_INDEX) { 275244197Sgonzo return (ENXIO); 276244197Sgonzo } 277244197Sgonzo /* attach to all present interfaces */ 278244197Sgonzo 279244197Sgonzo return (usb2_lookup_id_by_uaa(uftdi_devs, sizeof(uftdi_devs), uaa)); 280244197Sgonzo} 281244197Sgonzo 282244197Sgonzostatic int 283244197Sgonzouftdi_attach(device_t dev) 284244197Sgonzo{ 285244197Sgonzo struct usb2_attach_arg *uaa = device_get_ivars(dev); 286244197Sgonzo struct uftdi_softc *sc = device_get_softc(dev); 287244197Sgonzo int error; 288244197Sgonzo 289244197Sgonzo if (sc == NULL) { 290244197Sgonzo return (ENOMEM); 291244197Sgonzo } 292244197Sgonzo sc->sc_udev = uaa->device; 293244197Sgonzo sc->sc_dev = dev; 294244197Sgonzo sc->sc_unit = device_get_unit(dev); 295244197Sgonzo 296244197Sgonzo device_set_usb2_desc(dev); 297244197Sgonzo 298244197Sgonzo snprintf(sc->sc_name, sizeof(sc->sc_name), 299244197Sgonzo "%s", device_get_nameunit(dev)); 300244197Sgonzo 301244197Sgonzo DPRINTF("\n"); 302244197Sgonzo 303244197Sgonzo sc->sc_iface_index = uaa->info.bIfaceIndex; 304244197Sgonzo sc->sc_type = USB_GET_DRIVER_INFO(uaa); 305244197Sgonzo 306244197Sgonzo switch (sc->sc_type) { 307244197Sgonzo case UFTDI_TYPE_SIO: 308244197Sgonzo sc->sc_hdrlen = 1; 309244197Sgonzo break; 310244197Sgonzo case UFTDI_TYPE_8U232AM: 311244197Sgonzo default: 312244197Sgonzo sc->sc_hdrlen = 0; 313244197Sgonzo break; 314244197Sgonzo } 315244197Sgonzo 316244197Sgonzo error = usb2_transfer_setup(uaa->device, 317244197Sgonzo &sc->sc_iface_index, sc->sc_xfer, uftdi_config, 318244197Sgonzo UFTDI_N_TRANSFER, sc, &Giant); 319244197Sgonzo 320244197Sgonzo if (error) { 321244197Sgonzo device_printf(dev, "allocating USB " 322244197Sgonzo "transfers failed!\n"); 323244197Sgonzo goto detach; 324244197Sgonzo } 325244197Sgonzo sc->sc_ucom.sc_portno = FTDI_PIT_SIOA + uaa->info.bIfaceNum; 326244197Sgonzo 327244197Sgonzo /* clear stall at first run */ 328244197Sgonzo 329244197Sgonzo sc->sc_flag |= (UFTDI_FLAG_WRITE_STALL | 330244197Sgonzo UFTDI_FLAG_READ_STALL); 331244197Sgonzo 332244197Sgonzo /* set a valid "lcr" value */ 333244197Sgonzo 334244197Sgonzo sc->sc_last_lcr = 335244197Sgonzo (FTDI_SIO_SET_DATA_STOP_BITS_2 | 336244197Sgonzo FTDI_SIO_SET_DATA_PARITY_NONE | 337244197Sgonzo FTDI_SIO_SET_DATA_BITS(8)); 338244197Sgonzo 339244197Sgonzo error = usb2_com_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc, 340244197Sgonzo &uftdi_callback, &Giant); 341244197Sgonzo if (error) { 342244197Sgonzo goto detach; 343244197Sgonzo } 344244197Sgonzo return (0); /* success */ 345244197Sgonzo 346244197Sgonzodetach: 347244197Sgonzo uftdi_detach(dev); 348244197Sgonzo return (ENXIO); 349244197Sgonzo} 350244197Sgonzo 351244197Sgonzostatic int 352244197Sgonzouftdi_detach(device_t dev) 353244197Sgonzo{ 354244197Sgonzo struct uftdi_softc *sc = device_get_softc(dev); 355244197Sgonzo 356244197Sgonzo usb2_com_detach(&sc->sc_super_ucom, &sc->sc_ucom, 1); 357244197Sgonzo 358244197Sgonzo usb2_transfer_unsetup(sc->sc_xfer, UFTDI_N_TRANSFER); 359244197Sgonzo 360244197Sgonzo return (0); 361244197Sgonzo} 362244197Sgonzo 363244197Sgonzostatic void 364244197Sgonzouftdi_cfg_do_request(struct uftdi_softc *sc, struct usb2_device_request *req, 365244197Sgonzo void *data) 366244197Sgonzo{ 367244197Sgonzo uint16_t length; 368244197Sgonzo usb2_error_t err; 369244197Sgonzo 370244197Sgonzo if (usb2_com_cfg_is_gone(&sc->sc_ucom)) { 371244197Sgonzo goto error; 372244197Sgonzo } 373244197Sgonzo err = usb2_do_request_flags 374244197Sgonzo (sc->sc_udev, &Giant, req, data, 0, NULL, 1000); 375244197Sgonzo 376244197Sgonzo if (err) { 377244197Sgonzo 378244197Sgonzo DPRINTFN(0, "device request failed, err=%s " 379244197Sgonzo "(ignored)\n", usb2_errstr(err)); 380244197Sgonzo 381244197Sgonzoerror: 382244197Sgonzo length = UGETW(req->wLength); 383244197Sgonzo 384244197Sgonzo if ((req->bmRequestType & UT_READ) && length) { 385244197Sgonzo bzero(data, length); 386244197Sgonzo } 387244197Sgonzo } 388244197Sgonzo} 389244197Sgonzo 390244197Sgonzostatic void 391244197Sgonzouftdi_cfg_open(struct usb2_com_softc *ucom) 392244197Sgonzo{ 393244197Sgonzo struct uftdi_softc *sc = ucom->sc_parent; 394244197Sgonzo uint16_t wIndex = ucom->sc_portno; 395244197Sgonzo struct usb2_device_request req; 396244197Sgonzo 397244197Sgonzo DPRINTF(""); 398244197Sgonzo 399244197Sgonzo /* perform a full reset on the device */ 400244197Sgonzo 401244197Sgonzo req.bmRequestType = UT_WRITE_VENDOR_DEVICE; 402244197Sgonzo req.bRequest = FTDI_SIO_RESET; 403244197Sgonzo USETW(req.wValue, FTDI_SIO_RESET_SIO); 404244197Sgonzo USETW(req.wIndex, wIndex); 405244197Sgonzo USETW(req.wLength, 0); 406244197Sgonzo uftdi_cfg_do_request(sc, &req, NULL); 407244197Sgonzo 408244197Sgonzo /* turn on RTS/CTS flow control */ 409244197Sgonzo 410244197Sgonzo req.bmRequestType = UT_WRITE_VENDOR_DEVICE; 411244197Sgonzo req.bRequest = FTDI_SIO_SET_FLOW_CTRL; 412244197Sgonzo USETW(req.wValue, 0); 413244197Sgonzo USETW2(req.wIndex, FTDI_SIO_RTS_CTS_HS, wIndex); 414244197Sgonzo USETW(req.wLength, 0); 415244197Sgonzo uftdi_cfg_do_request(sc, &req, NULL); 416244197Sgonzo 417244197Sgonzo /* 418244197Sgonzo * NOTE: with the new UCOM layer there will always be a 419244197Sgonzo * "uftdi_cfg_param()" call after "open()", so there is no need for 420244197Sgonzo * "open()" to configure anything 421244197Sgonzo */ 422244197Sgonzo} 423244197Sgonzo 424244197Sgonzostatic void 425244197Sgonzouftdi_write_callback(struct usb2_xfer *xfer) 426244197Sgonzo{ 427244197Sgonzo struct uftdi_softc *sc = xfer->priv_sc; 428244197Sgonzo uint32_t actlen; 429244197Sgonzo uint8_t buf[1]; 430244197Sgonzo 431244197Sgonzo switch (USB_GET_STATE(xfer)) { 432244197Sgonzo case USB_ST_SETUP: 433244197Sgonzo case USB_ST_TRANSFERRED: 434244197Sgonzo if (sc->sc_flag & UFTDI_FLAG_WRITE_STALL) { 435244197Sgonzo usb2_transfer_start(sc->sc_xfer[UFTDI_BULK_CS_WR]); 436244197Sgonzo return; 437244197Sgonzo } 438244197Sgonzo if (usb2_com_get_data(&sc->sc_ucom, xfer->frbuffers, 439244197Sgonzo sc->sc_hdrlen, UFTDI_OBUFSIZE - sc->sc_hdrlen, 440244197Sgonzo &actlen)) { 441244197Sgonzo 442244197Sgonzo if (sc->sc_hdrlen > 0) { 443244197Sgonzo buf[0] = 444244197Sgonzo FTDI_OUT_TAG(actlen, sc->sc_ucom.sc_portno); 445244197Sgonzo usb2_copy_in(xfer->frbuffers, 0, buf, 1); 446244197Sgonzo } 447244197Sgonzo xfer->frlengths[0] = actlen + sc->sc_hdrlen; 448244197Sgonzo usb2_start_hardware(xfer); 449244197Sgonzo } 450244197Sgonzo return; 451244197Sgonzo 452244197Sgonzo default: /* Error */ 453244197Sgonzo if (xfer->error != USB_ERR_CANCELLED) { 454244197Sgonzo sc->sc_flag |= UFTDI_FLAG_WRITE_STALL; 455244197Sgonzo usb2_transfer_start(sc->sc_xfer[UFTDI_BULK_CS_WR]); 456244197Sgonzo } 457244197Sgonzo return; 458244197Sgonzo 459244197Sgonzo } 460244197Sgonzo} 461244197Sgonzo 462244197Sgonzostatic void 463244197Sgonzouftdi_write_clear_stall_callback(struct usb2_xfer *xfer) 464244197Sgonzo{ 465244197Sgonzo struct uftdi_softc *sc = xfer->priv_sc; 466244197Sgonzo struct usb2_xfer *xfer_other = sc->sc_xfer[UFTDI_BULK_DT_WR]; 467244197Sgonzo 468244197Sgonzo if (usb2_clear_stall_callback(xfer, xfer_other)) { 469244197Sgonzo DPRINTF("stall cleared\n"); 470244197Sgonzo sc->sc_flag &= ~UFTDI_FLAG_WRITE_STALL; 471244197Sgonzo usb2_transfer_start(xfer_other); 472244197Sgonzo } 473244197Sgonzo} 474244197Sgonzo 475244197Sgonzostatic void 476244197Sgonzouftdi_read_callback(struct usb2_xfer *xfer) 477244197Sgonzo{ 478244197Sgonzo struct uftdi_softc *sc = xfer->priv_sc; 479244197Sgonzo uint8_t buf[2]; 480244197Sgonzo uint8_t ftdi_msr; 481244197Sgonzo uint8_t msr; 482244197Sgonzo uint8_t lsr; 483244197Sgonzo 484244197Sgonzo switch (USB_GET_STATE(xfer)) { 485244197Sgonzo case USB_ST_TRANSFERRED: 486244197Sgonzo 487244197Sgonzo if (xfer->actlen < 2) { 488244197Sgonzo goto tr_setup; 489244197Sgonzo } 490244197Sgonzo usb2_copy_out(xfer->frbuffers, 0, buf, 2); 491244197Sgonzo 492244197Sgonzo ftdi_msr = FTDI_GET_MSR(buf); 493244197Sgonzo lsr = FTDI_GET_LSR(buf); 494244197Sgonzo 495244197Sgonzo msr = 0; 496244197Sgonzo if (ftdi_msr & FTDI_SIO_CTS_MASK) 497244197Sgonzo msr |= SER_CTS; 498244197Sgonzo if (ftdi_msr & FTDI_SIO_DSR_MASK) 499244197Sgonzo msr |= SER_DSR; 500244197Sgonzo if (ftdi_msr & FTDI_SIO_RI_MASK) 501244197Sgonzo msr |= SER_RI; 502244197Sgonzo if (ftdi_msr & FTDI_SIO_RLSD_MASK) 503244197Sgonzo msr |= SER_DCD; 504244197Sgonzo 505244197Sgonzo if ((sc->sc_msr != msr) || 506244197Sgonzo ((sc->sc_lsr & FTDI_LSR_MASK) != (lsr & FTDI_LSR_MASK))) { 507244197Sgonzo DPRINTF("status change msr=0x%02x (0x%02x) " 508244197Sgonzo "lsr=0x%02x (0x%02x)\n", msr, sc->sc_msr, 509244197Sgonzo lsr, sc->sc_lsr); 510244197Sgonzo 511244197Sgonzo sc->sc_msr = msr; 512244197Sgonzo sc->sc_lsr = lsr; 513244197Sgonzo 514244197Sgonzo usb2_com_status_change(&sc->sc_ucom); 515244197Sgonzo } 516244197Sgonzo xfer->actlen -= 2; 517244197Sgonzo 518244197Sgonzo if (xfer->actlen > 0) { 519244197Sgonzo usb2_com_put_data(&sc->sc_ucom, xfer->frbuffers, 2, 520244197Sgonzo xfer->actlen); 521244197Sgonzo } 522244197Sgonzo case USB_ST_SETUP: 523244197Sgonzotr_setup: 524244197Sgonzo if (sc->sc_flag & UFTDI_FLAG_READ_STALL) { 525244197Sgonzo usb2_transfer_start(sc->sc_xfer[UFTDI_BULK_CS_RD]); 526244197Sgonzo } else { 527244197Sgonzo xfer->frlengths[0] = xfer->max_data_length; 528244197Sgonzo usb2_start_hardware(xfer); 529244197Sgonzo } 530244197Sgonzo return; 531244197Sgonzo 532244197Sgonzo default: /* Error */ 533244197Sgonzo if (xfer->error != USB_ERR_CANCELLED) { 534244197Sgonzo sc->sc_flag |= UFTDI_FLAG_READ_STALL; 535244197Sgonzo usb2_transfer_start(sc->sc_xfer[UFTDI_BULK_CS_RD]); 536244197Sgonzo } 537244197Sgonzo return; 538244197Sgonzo 539244197Sgonzo } 540244197Sgonzo} 541244197Sgonzo 542244197Sgonzostatic void 543244197Sgonzouftdi_read_clear_stall_callback(struct usb2_xfer *xfer) 544244197Sgonzo{ 545244197Sgonzo struct uftdi_softc *sc = xfer->priv_sc; 546244197Sgonzo struct usb2_xfer *xfer_other = sc->sc_xfer[UFTDI_BULK_DT_RD]; 547244197Sgonzo 548244197Sgonzo if (usb2_clear_stall_callback(xfer, xfer_other)) { 549244197Sgonzo DPRINTF("stall cleared\n"); 550244197Sgonzo sc->sc_flag &= ~UFTDI_FLAG_READ_STALL; 551244197Sgonzo usb2_transfer_start(xfer_other); 552244197Sgonzo } 553244197Sgonzo} 554244197Sgonzo 555244197Sgonzostatic void 556244197Sgonzouftdi_cfg_set_dtr(struct usb2_com_softc *ucom, uint8_t onoff) 557244197Sgonzo{ 558244197Sgonzo struct uftdi_softc *sc = ucom->sc_parent; 559244197Sgonzo uint16_t wIndex = ucom->sc_portno; 560244197Sgonzo uint16_t wValue; 561244197Sgonzo struct usb2_device_request req; 562244197Sgonzo 563244197Sgonzo wValue = onoff ? FTDI_SIO_SET_DTR_HIGH : FTDI_SIO_SET_DTR_LOW; 564244197Sgonzo 565244197Sgonzo req.bmRequestType = UT_WRITE_VENDOR_DEVICE; 566244197Sgonzo req.bRequest = FTDI_SIO_MODEM_CTRL; 567244197Sgonzo USETW(req.wValue, wValue); 568244197Sgonzo USETW(req.wIndex, wIndex); 569244197Sgonzo USETW(req.wLength, 0); 570244197Sgonzo uftdi_cfg_do_request(sc, &req, NULL); 571244197Sgonzo} 572244197Sgonzo 573244197Sgonzostatic void 574244197Sgonzouftdi_cfg_set_rts(struct usb2_com_softc *ucom, uint8_t onoff) 575244197Sgonzo{ 576244197Sgonzo struct uftdi_softc *sc = ucom->sc_parent; 577244197Sgonzo uint16_t wIndex = ucom->sc_portno; 578244197Sgonzo uint16_t wValue; 579244197Sgonzo struct usb2_device_request req; 580244197Sgonzo 581244197Sgonzo wValue = onoff ? FTDI_SIO_SET_RTS_HIGH : FTDI_SIO_SET_RTS_LOW; 582244197Sgonzo 583244197Sgonzo req.bmRequestType = UT_WRITE_VENDOR_DEVICE; 584244197Sgonzo req.bRequest = FTDI_SIO_MODEM_CTRL; 585244197Sgonzo USETW(req.wValue, wValue); 586244197Sgonzo USETW(req.wIndex, wIndex); 587244197Sgonzo USETW(req.wLength, 0); 588244197Sgonzo uftdi_cfg_do_request(sc, &req, NULL); 589244197Sgonzo} 590244197Sgonzo 591244197Sgonzostatic void 592244197Sgonzouftdi_cfg_set_break(struct usb2_com_softc *ucom, uint8_t onoff) 593244197Sgonzo{ 594244197Sgonzo struct uftdi_softc *sc = ucom->sc_parent; 595244197Sgonzo uint16_t wIndex = ucom->sc_portno; 596244197Sgonzo uint16_t wValue; 597244197Sgonzo struct usb2_device_request req; 598244197Sgonzo 599244197Sgonzo if (onoff) { 600244197Sgonzo sc->sc_last_lcr |= FTDI_SIO_SET_BREAK; 601244197Sgonzo } else { 602244197Sgonzo sc->sc_last_lcr &= ~FTDI_SIO_SET_BREAK; 603244197Sgonzo } 604244197Sgonzo 605244197Sgonzo wValue = sc->sc_last_lcr; 606244197Sgonzo 607244197Sgonzo req.bmRequestType = UT_WRITE_VENDOR_DEVICE; 608244197Sgonzo req.bRequest = FTDI_SIO_SET_DATA; 609244197Sgonzo USETW(req.wValue, wValue); 610244197Sgonzo USETW(req.wIndex, wIndex); 611244197Sgonzo USETW(req.wLength, 0); 612266152Sian uftdi_cfg_do_request(sc, &req, NULL); 613266152Sian} 614266152Sian 615244197Sgonzostatic int 616244197Sgonzouftdi_set_parm_soft(struct termios *t, 617244197Sgonzo struct uftdi_param_config *cfg, uint8_t type) 618244197Sgonzo{ 619244197Sgonzo bzero(cfg, sizeof(*cfg)); 620244197Sgonzo 621244197Sgonzo switch (type) { 622244197Sgonzo case UFTDI_TYPE_SIO: 623244197Sgonzo switch (t->c_ospeed) { 624244197Sgonzo case 300: 625244197Sgonzo cfg->rate = ftdi_sio_b300; 626244197Sgonzo break; 627244197Sgonzo case 600: 628244197Sgonzo cfg->rate = ftdi_sio_b600; 629244197Sgonzo break; 630244197Sgonzo case 1200: 631244197Sgonzo cfg->rate = ftdi_sio_b1200; 632244197Sgonzo break; 633244197Sgonzo case 2400: 634244197Sgonzo cfg->rate = ftdi_sio_b2400; 635244197Sgonzo break; 636244197Sgonzo case 4800: 637244197Sgonzo cfg->rate = ftdi_sio_b4800; 638244197Sgonzo break; 639244197Sgonzo case 9600: 640244197Sgonzo cfg->rate = ftdi_sio_b9600; 641244197Sgonzo break; 642244197Sgonzo case 19200: 643244197Sgonzo cfg->rate = ftdi_sio_b19200; 644244197Sgonzo break; 645244197Sgonzo case 38400: 646244197Sgonzo cfg->rate = ftdi_sio_b38400; 647244197Sgonzo break; 648244197Sgonzo case 57600: 649244197Sgonzo cfg->rate = ftdi_sio_b57600; 650244197Sgonzo break; 651244197Sgonzo case 115200: 652244197Sgonzo cfg->rate = ftdi_sio_b115200; 653244197Sgonzo break; 654244197Sgonzo default: 655244197Sgonzo return (EINVAL); 656244197Sgonzo } 657244197Sgonzo break; 658244197Sgonzo 659244197Sgonzo case UFTDI_TYPE_8U232AM: 660244197Sgonzo if (uftdi_8u232am_getrate(t->c_ospeed, &cfg->rate)) { 661244197Sgonzo return (EINVAL); 662244197Sgonzo } 663244197Sgonzo break; 664244197Sgonzo } 665244197Sgonzo 666244197Sgonzo if (t->c_cflag & CSTOPB) 667244197Sgonzo cfg->lcr = FTDI_SIO_SET_DATA_STOP_BITS_2; 668244197Sgonzo else 669244197Sgonzo cfg->lcr = FTDI_SIO_SET_DATA_STOP_BITS_1; 670244197Sgonzo 671244197Sgonzo if (t->c_cflag & PARENB) { 672244197Sgonzo if (t->c_cflag & PARODD) { 673244197Sgonzo cfg->lcr |= FTDI_SIO_SET_DATA_PARITY_ODD; 674244197Sgonzo } else { 675244197Sgonzo cfg->lcr |= FTDI_SIO_SET_DATA_PARITY_EVEN; 676244197Sgonzo } 677244197Sgonzo } else { 678244197Sgonzo cfg->lcr |= FTDI_SIO_SET_DATA_PARITY_NONE; 679244197Sgonzo } 680244197Sgonzo 681244197Sgonzo switch (t->c_cflag & CSIZE) { 682244197Sgonzo case CS5: 683244197Sgonzo cfg->lcr |= FTDI_SIO_SET_DATA_BITS(5); 684244197Sgonzo break; 685244197Sgonzo 686244197Sgonzo case CS6: 687244197Sgonzo cfg->lcr |= FTDI_SIO_SET_DATA_BITS(6); 688244197Sgonzo break; 689244197Sgonzo 690244197Sgonzo case CS7: 691244197Sgonzo cfg->lcr |= FTDI_SIO_SET_DATA_BITS(7); 692244197Sgonzo break; 693244197Sgonzo 694244197Sgonzo case CS8: 695244197Sgonzo cfg->lcr |= FTDI_SIO_SET_DATA_BITS(8); 696244197Sgonzo break; 697244197Sgonzo } 698244197Sgonzo 699244197Sgonzo if (t->c_cflag & CRTSCTS) { 700244197Sgonzo cfg->v_flow = FTDI_SIO_RTS_CTS_HS; 701244197Sgonzo } else if (t->c_iflag & (IXON | IXOFF)) { 702244197Sgonzo cfg->v_flow = FTDI_SIO_XON_XOFF_HS; 703244197Sgonzo cfg->v_start = t->c_cc[VSTART]; 704244197Sgonzo cfg->v_stop = t->c_cc[VSTOP]; 705244197Sgonzo } else { 706244197Sgonzo cfg->v_flow = FTDI_SIO_DISABLE_FLOW_CTRL; 707244197Sgonzo } 708244197Sgonzo 709244197Sgonzo return (0); 710244197Sgonzo} 711244197Sgonzo 712244197Sgonzostatic int 713244197Sgonzouftdi_pre_param(struct usb2_com_softc *ucom, struct termios *t) 714244197Sgonzo{ 715244197Sgonzo struct uftdi_softc *sc = ucom->sc_parent; 716 struct uftdi_param_config cfg; 717 718 DPRINTF("\n"); 719 720 return (uftdi_set_parm_soft(t, &cfg, sc->sc_type)); 721} 722 723static void 724uftdi_cfg_param(struct usb2_com_softc *ucom, struct termios *t) 725{ 726 struct uftdi_softc *sc = ucom->sc_parent; 727 uint16_t wIndex = ucom->sc_portno; 728 struct uftdi_param_config cfg; 729 struct usb2_device_request req; 730 731 if (uftdi_set_parm_soft(t, &cfg, sc->sc_type)) { 732 /* should not happen */ 733 return; 734 } 735 sc->sc_last_lcr = cfg.lcr; 736 737 DPRINTF("\n"); 738 739 req.bmRequestType = UT_WRITE_VENDOR_DEVICE; 740 req.bRequest = FTDI_SIO_SET_BAUD_RATE; 741 USETW(req.wValue, cfg.rate); 742 USETW(req.wIndex, wIndex); 743 USETW(req.wLength, 0); 744 uftdi_cfg_do_request(sc, &req, NULL); 745 746 req.bmRequestType = UT_WRITE_VENDOR_DEVICE; 747 req.bRequest = FTDI_SIO_SET_DATA; 748 USETW(req.wValue, cfg.lcr); 749 USETW(req.wIndex, wIndex); 750 USETW(req.wLength, 0); 751 uftdi_cfg_do_request(sc, &req, NULL); 752 753 req.bmRequestType = UT_WRITE_VENDOR_DEVICE; 754 req.bRequest = FTDI_SIO_SET_FLOW_CTRL; 755 USETW2(req.wValue, cfg.v_stop, cfg.v_start); 756 USETW2(req.wIndex, cfg.v_flow, wIndex); 757 USETW(req.wLength, 0); 758 uftdi_cfg_do_request(sc, &req, NULL); 759} 760 761static void 762uftdi_cfg_get_status(struct usb2_com_softc *ucom, uint8_t *lsr, uint8_t *msr) 763{ 764 struct uftdi_softc *sc = ucom->sc_parent; 765 766 DPRINTF("msr=0x%02x lsr=0x%02x\n", 767 sc->sc_msr, sc->sc_lsr); 768 769 *msr = sc->sc_msr; 770 *lsr = sc->sc_lsr; 771} 772 773static void 774uftdi_start_read(struct usb2_com_softc *ucom) 775{ 776 struct uftdi_softc *sc = ucom->sc_parent; 777 778 usb2_transfer_start(sc->sc_xfer[UFTDI_BULK_DT_RD]); 779} 780 781static void 782uftdi_stop_read(struct usb2_com_softc *ucom) 783{ 784 struct uftdi_softc *sc = ucom->sc_parent; 785 786 usb2_transfer_stop(sc->sc_xfer[UFTDI_BULK_CS_RD]); 787 usb2_transfer_stop(sc->sc_xfer[UFTDI_BULK_DT_RD]); 788} 789 790static void 791uftdi_start_write(struct usb2_com_softc *ucom) 792{ 793 struct uftdi_softc *sc = ucom->sc_parent; 794 795 usb2_transfer_start(sc->sc_xfer[UFTDI_BULK_DT_WR]); 796} 797 798static void 799uftdi_stop_write(struct usb2_com_softc *ucom) 800{ 801 struct uftdi_softc *sc = ucom->sc_parent; 802 803 usb2_transfer_stop(sc->sc_xfer[UFTDI_BULK_CS_WR]); 804 usb2_transfer_stop(sc->sc_xfer[UFTDI_BULK_DT_WR]); 805} 806 807/*------------------------------------------------------------------------* 808 * uftdi_8u232am_getrate 809 * 810 * Return values: 811 * 0: Success 812 * Else: Failure 813 *------------------------------------------------------------------------*/ 814static uint8_t 815uftdi_8u232am_getrate(uint32_t speed, uint16_t *rate) 816{ 817 /* Table of the nearest even powers-of-2 for values 0..15. */ 818 static const uint8_t roundoff[16] = { 819 0, 2, 2, 4, 4, 4, 8, 8, 820 8, 8, 8, 8, 16, 16, 16, 16, 821 }; 822 uint32_t d; 823 uint32_t freq; 824 uint16_t result; 825 826 if ((speed < 178) || (speed > ((3000000 * 100) / 97))) 827 return (1); /* prevent numerical overflow */ 828 829 /* Special cases for 2M and 3M. */ 830 if ((speed >= ((3000000 * 100) / 103)) && 831 (speed <= ((3000000 * 100) / 97))) { 832 result = 0; 833 goto done; 834 } 835 if ((speed >= ((2000000 * 100) / 103)) && 836 (speed <= ((2000000 * 100) / 97))) { 837 result = 1; 838 goto done; 839 } 840 d = (FTDI_8U232AM_FREQ << 4) / speed; 841 d = (d & ~15) + roundoff[d & 15]; 842 843 if (d < FTDI_8U232AM_MIN_DIV) 844 d = FTDI_8U232AM_MIN_DIV; 845 else if (d > FTDI_8U232AM_MAX_DIV) 846 d = FTDI_8U232AM_MAX_DIV; 847 848 /* 849 * Calculate the frequency needed for "d" to exactly divide down to 850 * our target "speed", and check that the actual frequency is within 851 * 3% of this. 852 */ 853 freq = (speed * d); 854 if ((freq < ((FTDI_8U232AM_FREQ * 1600ULL) / 103)) || 855 (freq > ((FTDI_8U232AM_FREQ * 1600ULL) / 97))) 856 return (1); 857 858 /* 859 * Pack the divisor into the resultant value. The lower 14-bits 860 * hold the integral part, while the upper 2 bits encode the 861 * fractional component: either 0, 0.5, 0.25, or 0.125. 862 */ 863 result = (d >> 4); 864 if (d & 8) 865 result |= 0x4000; 866 else if (d & 4) 867 result |= 0x8000; 868 else if (d & 2) 869 result |= 0xc000; 870 871done: 872 *rate = result; 873 return (0); 874} 875