ucycom.c revision 214761
1184610Salfred#include <sys/cdefs.h> 2184610Salfred__FBSDID("$FreeBSD: head/sys/dev/usb/serial/ucycom.c 214761 2010-11-03 21:50:49Z n_hibma $"); 3184610Salfred 4184610Salfred/*- 5184610Salfred * Copyright (c) 2004 Dag-Erling Co�dan Sm�rgrav 6184610Salfred * All rights reserved. 7184610Salfred * 8184610Salfred * Redistribution and use in source and binary forms, with or without 9184610Salfred * modification, are permitted provided that the following conditions 10184610Salfred * are met: 11184610Salfred * 1. Redistributions of source code must retain the above copyright 12184610Salfred * notice, this list of conditions and the following disclaimer 13184610Salfred * in this position and unchanged. 14184610Salfred * 2. Redistributions in binary form must reproduce the above copyright 15184610Salfred * notice, this list of conditions and the following disclaimer in the 16184610Salfred * documentation and/or other materials provided with the distribution. 17184610Salfred * 3. The name of the author may not be used to endorse or promote products 18184610Salfred * derived from this software without specific prior written permission. 19184610Salfred * 20184610Salfred * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 21184610Salfred * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 22184610Salfred * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 23184610Salfred * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 24184610Salfred * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 25184610Salfred * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26184610Salfred * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27184610Salfred * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28184610Salfred * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 29184610Salfred * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30184610Salfred */ 31184610Salfred 32184610Salfred/* 33184610Salfred * Device driver for Cypress CY7C637xx and CY7C640/1xx series USB to 34184610Salfred * RS232 bridges. 35184610Salfred */ 36184610Salfred 37194677Sthompsa#include <sys/stdint.h> 38194677Sthompsa#include <sys/stddef.h> 39194677Sthompsa#include <sys/param.h> 40194677Sthompsa#include <sys/queue.h> 41194677Sthompsa#include <sys/types.h> 42194677Sthompsa#include <sys/systm.h> 43194677Sthompsa#include <sys/kernel.h> 44194677Sthompsa#include <sys/bus.h> 45194677Sthompsa#include <sys/linker_set.h> 46194677Sthompsa#include <sys/module.h> 47194677Sthompsa#include <sys/lock.h> 48194677Sthompsa#include <sys/mutex.h> 49194677Sthompsa#include <sys/condvar.h> 50194677Sthompsa#include <sys/sysctl.h> 51194677Sthompsa#include <sys/sx.h> 52194677Sthompsa#include <sys/unistd.h> 53194677Sthompsa#include <sys/callout.h> 54194677Sthompsa#include <sys/malloc.h> 55194677Sthompsa#include <sys/priv.h> 56194677Sthompsa 57188942Sthompsa#include <dev/usb/usb.h> 58194677Sthompsa#include <dev/usb/usbdi.h> 59194677Sthompsa#include <dev/usb/usbdi_util.h> 60188942Sthompsa#include <dev/usb/usbhid.h> 61194677Sthompsa#include "usbdevs.h" 62184610Salfred 63194228Sthompsa#define USB_DEBUG_VAR usb_debug 64188942Sthompsa#include <dev/usb/usb_debug.h> 65188942Sthompsa#include <dev/usb/usb_process.h> 66184610Salfred 67188942Sthompsa#include <dev/usb/serial/usb_serial.h> 68184610Salfred 69184610Salfred#define UCYCOM_MAX_IOLEN (1024 + 2) /* bytes */ 70184610Salfred 71184610Salfred#define UCYCOM_IFACE_INDEX 0 72184610Salfred 73187259Sthompsaenum { 74187259Sthompsa UCYCOM_CTRL_RD, 75187259Sthompsa UCYCOM_INTR_RD, 76188413Sthompsa UCYCOM_N_TRANSFER, 77187259Sthompsa}; 78187259Sthompsa 79184610Salfredstruct ucycom_softc { 80192984Sthompsa struct ucom_super_softc sc_super_ucom; 81192984Sthompsa struct ucom_softc sc_ucom; 82184610Salfred 83192984Sthompsa struct usb_device *sc_udev; 84192984Sthompsa struct usb_xfer *sc_xfer[UCYCOM_N_TRANSFER]; 85189265Sthompsa struct mtx sc_mtx; 86184610Salfred 87184610Salfred uint32_t sc_model; 88184610Salfred#define MODEL_CY7C63743 0x63743 89184610Salfred#define MODEL_CY7C64013 0x64013 90184610Salfred 91184610Salfred uint16_t sc_flen; /* feature report length */ 92184610Salfred uint16_t sc_ilen; /* input report length */ 93184610Salfred uint16_t sc_olen; /* output report length */ 94184610Salfred 95184610Salfred uint8_t sc_fid; /* feature report id */ 96184610Salfred uint8_t sc_iid; /* input report id */ 97184610Salfred uint8_t sc_oid; /* output report id */ 98184610Salfred uint8_t sc_cfg; 99184610Salfred#define UCYCOM_CFG_RESET 0x80 100184610Salfred#define UCYCOM_CFG_PARODD 0x20 101184610Salfred#define UCYCOM_CFG_PAREN 0x10 102184610Salfred#define UCYCOM_CFG_STOPB 0x08 103184610Salfred#define UCYCOM_CFG_DATAB 0x03 104184610Salfred uint8_t sc_ist; /* status flags from last input */ 105184610Salfred uint8_t sc_name[16]; 106184610Salfred uint8_t sc_iface_no; 107184610Salfred uint8_t sc_temp_cfg[32]; 108184610Salfred}; 109184610Salfred 110184610Salfred/* prototypes */ 111184610Salfred 112184610Salfredstatic device_probe_t ucycom_probe; 113184610Salfredstatic device_attach_t ucycom_attach; 114184610Salfredstatic device_detach_t ucycom_detach; 115184610Salfred 116193045Sthompsastatic usb_callback_t ucycom_ctrl_write_callback; 117193045Sthompsastatic usb_callback_t ucycom_intr_read_callback; 118184610Salfred 119192984Sthompsastatic void ucycom_cfg_open(struct ucom_softc *); 120192984Sthompsastatic void ucycom_start_read(struct ucom_softc *); 121192984Sthompsastatic void ucycom_stop_read(struct ucom_softc *); 122192984Sthompsastatic void ucycom_start_write(struct ucom_softc *); 123192984Sthompsastatic void ucycom_stop_write(struct ucom_softc *); 124185948Sthompsastatic void ucycom_cfg_write(struct ucycom_softc *, uint32_t, uint8_t); 125192984Sthompsastatic int ucycom_pre_param(struct ucom_softc *, struct termios *); 126192984Sthompsastatic void ucycom_cfg_param(struct ucom_softc *, struct termios *); 127197570Sthompsastatic void ucycom_poll(struct ucom_softc *ucom); 128184610Salfred 129192984Sthompsastatic const struct usb_config ucycom_config[UCYCOM_N_TRANSFER] = { 130184610Salfred 131187259Sthompsa [UCYCOM_CTRL_RD] = { 132184610Salfred .type = UE_CONTROL, 133184610Salfred .endpoint = 0x00, /* Control pipe */ 134184610Salfred .direction = UE_DIR_ANY, 135192984Sthompsa .bufsize = (sizeof(struct usb_device_request) + UCYCOM_MAX_IOLEN), 136190734Sthompsa .callback = &ucycom_ctrl_write_callback, 137190734Sthompsa .timeout = 1000, /* 1 second */ 138184610Salfred }, 139184610Salfred 140187259Sthompsa [UCYCOM_INTR_RD] = { 141184610Salfred .type = UE_INTERRUPT, 142184610Salfred .endpoint = UE_ADDR_ANY, 143184610Salfred .direction = UE_DIR_IN, 144190734Sthompsa .flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, 145190734Sthompsa .bufsize = UCYCOM_MAX_IOLEN, 146190734Sthompsa .callback = &ucycom_intr_read_callback, 147184610Salfred }, 148184610Salfred}; 149184610Salfred 150192984Sthompsastatic const struct ucom_callback ucycom_callback = { 151194228Sthompsa .ucom_cfg_param = &ucycom_cfg_param, 152194228Sthompsa .ucom_cfg_open = &ucycom_cfg_open, 153194228Sthompsa .ucom_pre_param = &ucycom_pre_param, 154194228Sthompsa .ucom_start_read = &ucycom_start_read, 155194228Sthompsa .ucom_stop_read = &ucycom_stop_read, 156194228Sthompsa .ucom_start_write = &ucycom_start_write, 157194228Sthompsa .ucom_stop_write = &ucycom_stop_write, 158197570Sthompsa .ucom_poll = &ucycom_poll, 159184610Salfred}; 160184610Salfred 161184610Salfredstatic device_method_t ucycom_methods[] = { 162184610Salfred DEVMETHOD(device_probe, ucycom_probe), 163184610Salfred DEVMETHOD(device_attach, ucycom_attach), 164184610Salfred DEVMETHOD(device_detach, ucycom_detach), 165184610Salfred {0, 0} 166184610Salfred}; 167184610Salfred 168184610Salfredstatic devclass_t ucycom_devclass; 169184610Salfred 170184610Salfredstatic driver_t ucycom_driver = { 171184610Salfred .name = "ucycom", 172184610Salfred .methods = ucycom_methods, 173184610Salfred .size = sizeof(struct ucycom_softc), 174184610Salfred}; 175184610Salfred 176189275SthompsaDRIVER_MODULE(ucycom, uhub, ucycom_driver, ucycom_devclass, NULL, 0); 177188942SthompsaMODULE_DEPEND(ucycom, ucom, 1, 1, 1); 178188942SthompsaMODULE_DEPEND(ucycom, usb, 1, 1, 1); 179212122SthompsaMODULE_VERSION(ucycom, 1); 180184610Salfred 181184610Salfred/* 182184610Salfred * Supported devices 183184610Salfred */ 184192984Sthompsastatic const struct usb_device_id ucycom_devs[] = { 185184610Salfred {USB_VPI(USB_VENDOR_DELORME, USB_PRODUCT_DELORME_EARTHMATE, MODEL_CY7C64013)}, 186184610Salfred}; 187184610Salfred 188184610Salfred#define UCYCOM_DEFAULT_RATE 4800 189184610Salfred#define UCYCOM_DEFAULT_CFG 0x03 /* N-8-1 */ 190184610Salfred 191184610Salfredstatic int 192184610Salfreducycom_probe(device_t dev) 193184610Salfred{ 194192984Sthompsa struct usb_attach_arg *uaa = device_get_ivars(dev); 195184610Salfred 196192499Sthompsa if (uaa->usb_mode != USB_MODE_HOST) { 197184610Salfred return (ENXIO); 198184610Salfred } 199184610Salfred if (uaa->info.bConfigIndex != 0) { 200184610Salfred return (ENXIO); 201184610Salfred } 202184610Salfred if (uaa->info.bIfaceIndex != UCYCOM_IFACE_INDEX) { 203184610Salfred return (ENXIO); 204184610Salfred } 205194228Sthompsa return (usbd_lookup_id_by_uaa(ucycom_devs, sizeof(ucycom_devs), uaa)); 206184610Salfred} 207184610Salfred 208184610Salfredstatic int 209184610Salfreducycom_attach(device_t dev) 210184610Salfred{ 211192984Sthompsa struct usb_attach_arg *uaa = device_get_ivars(dev); 212184610Salfred struct ucycom_softc *sc = device_get_softc(dev); 213184610Salfred void *urd_ptr = NULL; 214184610Salfred int32_t error; 215184610Salfred uint16_t urd_len; 216184610Salfred uint8_t iface_index; 217184610Salfred 218184610Salfred sc->sc_udev = uaa->device; 219184610Salfred 220194228Sthompsa device_set_usb_desc(dev); 221189265Sthompsa mtx_init(&sc->sc_mtx, "ucycom", NULL, MTX_DEF); 222184610Salfred 223184610Salfred snprintf(sc->sc_name, sizeof(sc->sc_name), 224184610Salfred "%s", device_get_nameunit(dev)); 225184610Salfred 226184610Salfred DPRINTF("\n"); 227184610Salfred 228184610Salfred /* get chip model */ 229184610Salfred sc->sc_model = USB_GET_DRIVER_INFO(uaa); 230184610Salfred if (sc->sc_model == 0) { 231184610Salfred device_printf(dev, "unsupported device\n"); 232184610Salfred goto detach; 233184610Salfred } 234184610Salfred device_printf(dev, "Cypress CY7C%X USB to RS232 bridge\n", sc->sc_model); 235184610Salfred 236184610Salfred /* get report descriptor */ 237184610Salfred 238194228Sthompsa error = usbd_req_get_hid_desc(uaa->device, NULL, 239184610Salfred &urd_ptr, &urd_len, M_USBDEV, 240184610Salfred UCYCOM_IFACE_INDEX); 241184610Salfred 242184610Salfred if (error) { 243184610Salfred device_printf(dev, "failed to get report " 244184610Salfred "descriptor: %s\n", 245194228Sthompsa usbd_errstr(error)); 246184610Salfred goto detach; 247184610Salfred } 248184610Salfred /* get report sizes */ 249184610Salfred 250184610Salfred sc->sc_flen = hid_report_size(urd_ptr, urd_len, hid_feature, &sc->sc_fid); 251184610Salfred sc->sc_ilen = hid_report_size(urd_ptr, urd_len, hid_input, &sc->sc_iid); 252184610Salfred sc->sc_olen = hid_report_size(urd_ptr, urd_len, hid_output, &sc->sc_oid); 253184610Salfred 254184610Salfred if ((sc->sc_ilen > UCYCOM_MAX_IOLEN) || (sc->sc_ilen < 1) || 255184610Salfred (sc->sc_olen > UCYCOM_MAX_IOLEN) || (sc->sc_olen < 2) || 256184610Salfred (sc->sc_flen > UCYCOM_MAX_IOLEN) || (sc->sc_flen < 5)) { 257184610Salfred device_printf(dev, "invalid report size i=%d, o=%d, f=%d, max=%d\n", 258184610Salfred sc->sc_ilen, sc->sc_olen, sc->sc_flen, 259184610Salfred UCYCOM_MAX_IOLEN); 260184610Salfred goto detach; 261184610Salfred } 262184610Salfred sc->sc_iface_no = uaa->info.bIfaceNum; 263184610Salfred 264184610Salfred iface_index = UCYCOM_IFACE_INDEX; 265194228Sthompsa error = usbd_transfer_setup(uaa->device, &iface_index, 266187259Sthompsa sc->sc_xfer, ucycom_config, UCYCOM_N_TRANSFER, 267189265Sthompsa sc, &sc->sc_mtx); 268184610Salfred if (error) { 269184610Salfred device_printf(dev, "allocating USB " 270199816Sthompsa "transfers failed\n"); 271184610Salfred goto detach; 272184610Salfred } 273194228Sthompsa error = ucom_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc, 274189265Sthompsa &ucycom_callback, &sc->sc_mtx); 275184610Salfred 276184610Salfred if (error) { 277184610Salfred goto detach; 278184610Salfred } 279184610Salfred if (urd_ptr) { 280184610Salfred free(urd_ptr, M_USBDEV); 281184610Salfred } 282184610Salfred return (0); /* success */ 283184610Salfred 284184610Salfreddetach: 285184610Salfred if (urd_ptr) { 286184610Salfred free(urd_ptr, M_USBDEV); 287184610Salfred } 288184610Salfred ucycom_detach(dev); 289184610Salfred return (ENXIO); 290184610Salfred} 291184610Salfred 292184610Salfredstatic int 293184610Salfreducycom_detach(device_t dev) 294184610Salfred{ 295184610Salfred struct ucycom_softc *sc = device_get_softc(dev); 296184610Salfred 297214761Sn_hibma ucom_detach(&sc->sc_super_ucom, &sc->sc_ucom); 298194228Sthompsa usbd_transfer_unsetup(sc->sc_xfer, UCYCOM_N_TRANSFER); 299189265Sthompsa mtx_destroy(&sc->sc_mtx); 300184610Salfred 301184610Salfred return (0); 302184610Salfred} 303184610Salfred 304184610Salfredstatic void 305192984Sthompsaucycom_cfg_open(struct ucom_softc *ucom) 306184610Salfred{ 307184610Salfred struct ucycom_softc *sc = ucom->sc_parent; 308184610Salfred 309184610Salfred /* set default configuration */ 310184610Salfred ucycom_cfg_write(sc, UCYCOM_DEFAULT_RATE, UCYCOM_DEFAULT_CFG); 311184610Salfred} 312184610Salfred 313184610Salfredstatic void 314192984Sthompsaucycom_start_read(struct ucom_softc *ucom) 315184610Salfred{ 316184610Salfred struct ucycom_softc *sc = ucom->sc_parent; 317184610Salfred 318194228Sthompsa usbd_transfer_start(sc->sc_xfer[UCYCOM_INTR_RD]); 319184610Salfred} 320184610Salfred 321184610Salfredstatic void 322192984Sthompsaucycom_stop_read(struct ucom_softc *ucom) 323184610Salfred{ 324184610Salfred struct ucycom_softc *sc = ucom->sc_parent; 325184610Salfred 326194228Sthompsa usbd_transfer_stop(sc->sc_xfer[UCYCOM_INTR_RD]); 327184610Salfred} 328184610Salfred 329184610Salfredstatic void 330192984Sthompsaucycom_start_write(struct ucom_softc *ucom) 331184610Salfred{ 332184610Salfred struct ucycom_softc *sc = ucom->sc_parent; 333184610Salfred 334194228Sthompsa usbd_transfer_start(sc->sc_xfer[UCYCOM_CTRL_RD]); 335184610Salfred} 336184610Salfred 337184610Salfredstatic void 338192984Sthompsaucycom_stop_write(struct ucom_softc *ucom) 339184610Salfred{ 340184610Salfred struct ucycom_softc *sc = ucom->sc_parent; 341184610Salfred 342194228Sthompsa usbd_transfer_stop(sc->sc_xfer[UCYCOM_CTRL_RD]); 343184610Salfred} 344184610Salfred 345184610Salfredstatic void 346194677Sthompsaucycom_ctrl_write_callback(struct usb_xfer *xfer, usb_error_t error) 347184610Salfred{ 348194677Sthompsa struct ucycom_softc *sc = usbd_xfer_softc(xfer); 349192984Sthompsa struct usb_device_request req; 350194677Sthompsa struct usb_page_cache *pc0, *pc1; 351184610Salfred uint8_t data[2]; 352184610Salfred uint8_t offset; 353184610Salfred uint32_t actlen; 354184610Salfred 355194677Sthompsa pc0 = usbd_xfer_get_frame(xfer, 0); 356194677Sthompsa pc1 = usbd_xfer_get_frame(xfer, 1); 357194677Sthompsa 358184610Salfred switch (USB_GET_STATE(xfer)) { 359184610Salfred case USB_ST_TRANSFERRED: 360184610Salfredtr_transferred: 361184610Salfred case USB_ST_SETUP: 362184610Salfred 363184610Salfred switch (sc->sc_model) { 364184610Salfred case MODEL_CY7C63743: 365184610Salfred offset = 1; 366184610Salfred break; 367184610Salfred case MODEL_CY7C64013: 368184610Salfred offset = 2; 369184610Salfred break; 370184610Salfred default: 371184610Salfred offset = 0; 372184610Salfred break; 373184610Salfred } 374184610Salfred 375194677Sthompsa if (ucom_get_data(&sc->sc_ucom, pc1, offset, 376184610Salfred sc->sc_olen - offset, &actlen)) { 377184610Salfred 378184610Salfred req.bmRequestType = UT_WRITE_CLASS_INTERFACE; 379184610Salfred req.bRequest = UR_SET_REPORT; 380184610Salfred USETW2(req.wValue, UHID_OUTPUT_REPORT, sc->sc_oid); 381184610Salfred req.wIndex[0] = sc->sc_iface_no; 382184610Salfred req.wIndex[1] = 0; 383184610Salfred USETW(req.wLength, sc->sc_olen); 384184610Salfred 385184610Salfred switch (sc->sc_model) { 386184610Salfred case MODEL_CY7C63743: 387184610Salfred data[0] = actlen; 388184610Salfred break; 389184610Salfred case MODEL_CY7C64013: 390184610Salfred data[0] = 0; 391184610Salfred data[1] = actlen; 392184610Salfred break; 393184610Salfred default: 394184610Salfred break; 395184610Salfred } 396184610Salfred 397194677Sthompsa usbd_copy_in(pc0, 0, &req, sizeof(req)); 398194677Sthompsa usbd_copy_in(pc1, 0, data, offset); 399184610Salfred 400194677Sthompsa usbd_xfer_set_frame_len(xfer, 0, sizeof(req)); 401194677Sthompsa usbd_xfer_set_frame_len(xfer, 1, sc->sc_olen); 402194677Sthompsa usbd_xfer_set_frames(xfer, sc->sc_olen ? 2 : 1); 403194228Sthompsa usbd_transfer_submit(xfer); 404184610Salfred } 405184610Salfred return; 406184610Salfred 407184610Salfred default: /* Error */ 408194677Sthompsa if (error == USB_ERR_CANCELLED) { 409184610Salfred return; 410184610Salfred } 411184610Salfred DPRINTF("error=%s\n", 412194677Sthompsa usbd_errstr(error)); 413184610Salfred goto tr_transferred; 414184610Salfred } 415184610Salfred} 416184610Salfred 417184610Salfredstatic void 418184610Salfreducycom_cfg_write(struct ucycom_softc *sc, uint32_t baud, uint8_t cfg) 419184610Salfred{ 420192984Sthompsa struct usb_device_request req; 421184610Salfred uint16_t len; 422193045Sthompsa usb_error_t err; 423184610Salfred 424184610Salfred len = sc->sc_flen; 425184610Salfred if (len > sizeof(sc->sc_temp_cfg)) { 426184610Salfred len = sizeof(sc->sc_temp_cfg); 427184610Salfred } 428184610Salfred sc->sc_cfg = cfg; 429184610Salfred 430184610Salfred req.bmRequestType = UT_WRITE_CLASS_INTERFACE; 431184610Salfred req.bRequest = UR_SET_REPORT; 432184610Salfred USETW2(req.wValue, UHID_FEATURE_REPORT, sc->sc_fid); 433184610Salfred req.wIndex[0] = sc->sc_iface_no; 434184610Salfred req.wIndex[1] = 0; 435184610Salfred USETW(req.wLength, len); 436184610Salfred 437184610Salfred sc->sc_temp_cfg[0] = (baud & 0xff); 438184610Salfred sc->sc_temp_cfg[1] = (baud >> 8) & 0xff; 439184610Salfred sc->sc_temp_cfg[2] = (baud >> 16) & 0xff; 440184610Salfred sc->sc_temp_cfg[3] = (baud >> 24) & 0xff; 441184610Salfred sc->sc_temp_cfg[4] = cfg; 442184610Salfred 443194228Sthompsa err = ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 444188413Sthompsa &req, sc->sc_temp_cfg, 0, 1000); 445184610Salfred if (err) { 446184610Salfred DPRINTFN(0, "device request failed, err=%s " 447194228Sthompsa "(ignored)\n", usbd_errstr(err)); 448184610Salfred } 449184610Salfred} 450184610Salfred 451184610Salfredstatic int 452192984Sthompsaucycom_pre_param(struct ucom_softc *ucom, struct termios *t) 453184610Salfred{ 454184610Salfred switch (t->c_ospeed) { 455184610Salfred case 600: 456184610Salfred case 1200: 457184610Salfred case 2400: 458184610Salfred case 4800: 459184610Salfred case 9600: 460184610Salfred case 19200: 461184610Salfred case 38400: 462184610Salfred case 57600: 463184610Salfred#if 0 464184610Salfred /* 465184610Salfred * Stock chips only support standard baud rates in the 600 - 57600 466184610Salfred * range, but higher rates can be achieved using custom firmware. 467184610Salfred */ 468184610Salfred case 115200: 469184610Salfred case 153600: 470184610Salfred case 192000: 471184610Salfred#endif 472184610Salfred break; 473184610Salfred default: 474184610Salfred return (EINVAL); 475184610Salfred } 476184610Salfred return (0); 477184610Salfred} 478184610Salfred 479184610Salfredstatic void 480192984Sthompsaucycom_cfg_param(struct ucom_softc *ucom, struct termios *t) 481184610Salfred{ 482184610Salfred struct ucycom_softc *sc = ucom->sc_parent; 483184610Salfred uint8_t cfg; 484184610Salfred 485184610Salfred DPRINTF("\n"); 486184610Salfred 487184610Salfred if (t->c_cflag & CIGNORE) { 488184610Salfred cfg = sc->sc_cfg; 489184610Salfred } else { 490184610Salfred cfg = 0; 491184610Salfred switch (t->c_cflag & CSIZE) { 492184610Salfred default: 493184610Salfred case CS8: 494184610Salfred ++cfg; 495184610Salfred case CS7: 496184610Salfred ++cfg; 497184610Salfred case CS6: 498184610Salfred ++cfg; 499184610Salfred case CS5: 500184610Salfred break; 501184610Salfred } 502184610Salfred 503184610Salfred if (t->c_cflag & CSTOPB) 504184610Salfred cfg |= UCYCOM_CFG_STOPB; 505184610Salfred if (t->c_cflag & PARENB) 506184610Salfred cfg |= UCYCOM_CFG_PAREN; 507184610Salfred if (t->c_cflag & PARODD) 508184610Salfred cfg |= UCYCOM_CFG_PARODD; 509184610Salfred } 510184610Salfred 511184610Salfred ucycom_cfg_write(sc, t->c_ospeed, cfg); 512184610Salfred} 513184610Salfred 514184610Salfredstatic void 515194677Sthompsaucycom_intr_read_callback(struct usb_xfer *xfer, usb_error_t error) 516184610Salfred{ 517194677Sthompsa struct ucycom_softc *sc = usbd_xfer_softc(xfer); 518194677Sthompsa struct usb_page_cache *pc; 519184610Salfred uint8_t buf[2]; 520184610Salfred uint32_t offset; 521184610Salfred uint32_t len; 522194677Sthompsa int actlen; 523184610Salfred 524194677Sthompsa usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); 525194677Sthompsa pc = usbd_xfer_get_frame(xfer, 0); 526194677Sthompsa 527184610Salfred switch (USB_GET_STATE(xfer)) { 528184610Salfred case USB_ST_TRANSFERRED: 529184610Salfred switch (sc->sc_model) { 530184610Salfred case MODEL_CY7C63743: 531194677Sthompsa if (actlen < 1) { 532184610Salfred goto tr_setup; 533184610Salfred } 534194677Sthompsa usbd_copy_out(pc, 0, buf, 1); 535184610Salfred 536184610Salfred sc->sc_ist = buf[0] & ~0x07; 537184610Salfred len = buf[0] & 0x07; 538184610Salfred 539194677Sthompsa actlen--; 540184610Salfred offset = 1; 541184610Salfred 542184610Salfred break; 543184610Salfred 544184610Salfred case MODEL_CY7C64013: 545194677Sthompsa if (actlen < 2) { 546184610Salfred goto tr_setup; 547184610Salfred } 548194677Sthompsa usbd_copy_out(pc, 0, buf, 2); 549184610Salfred 550184610Salfred sc->sc_ist = buf[0] & ~0x07; 551184610Salfred len = buf[1]; 552184610Salfred 553194677Sthompsa actlen -= 2; 554184610Salfred offset = 2; 555184610Salfred 556184610Salfred break; 557184610Salfred 558184610Salfred default: 559199816Sthompsa DPRINTFN(0, "unsupported model number\n"); 560184610Salfred goto tr_setup; 561184610Salfred } 562184610Salfred 563194677Sthompsa if (len > actlen) 564194677Sthompsa len = actlen; 565194677Sthompsa if (len) 566194677Sthompsa ucom_put_data(&sc->sc_ucom, pc, offset, len); 567194677Sthompsa /* FALLTHROUGH */ 568184610Salfred case USB_ST_SETUP: 569184610Salfredtr_setup: 570194677Sthompsa usbd_xfer_set_frame_len(xfer, 0, sc->sc_ilen); 571194228Sthompsa usbd_transfer_submit(xfer); 572184610Salfred return; 573184610Salfred 574184610Salfred default: /* Error */ 575194677Sthompsa if (error != USB_ERR_CANCELLED) { 576188413Sthompsa /* try to clear stall first */ 577194677Sthompsa usbd_xfer_set_stall(xfer); 578188413Sthompsa goto tr_setup; 579184610Salfred } 580184610Salfred return; 581184610Salfred 582184610Salfred } 583184610Salfred} 584197570Sthompsa 585197570Sthompsastatic void 586197570Sthompsaucycom_poll(struct ucom_softc *ucom) 587197570Sthompsa{ 588197570Sthompsa struct ucycom_softc *sc = ucom->sc_parent; 589197570Sthompsa usbd_transfer_poll(sc->sc_xfer, UCYCOM_N_TRANSFER); 590197570Sthompsa} 591