1184610Salfred#include <sys/cdefs.h> 2184610Salfred__FBSDID("$FreeBSD$"); 3184610Salfred 4184610Salfred/*- 5230132Suqs * 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/module.h> 46194677Sthompsa#include <sys/lock.h> 47194677Sthompsa#include <sys/mutex.h> 48194677Sthompsa#include <sys/condvar.h> 49194677Sthompsa#include <sys/sysctl.h> 50194677Sthompsa#include <sys/sx.h> 51194677Sthompsa#include <sys/unistd.h> 52194677Sthompsa#include <sys/callout.h> 53194677Sthompsa#include <sys/malloc.h> 54194677Sthompsa#include <sys/priv.h> 55194677Sthompsa 56188942Sthompsa#include <dev/usb/usb.h> 57194677Sthompsa#include <dev/usb/usbdi.h> 58194677Sthompsa#include <dev/usb/usbdi_util.h> 59188942Sthompsa#include <dev/usb/usbhid.h> 60194677Sthompsa#include "usbdevs.h" 61184610Salfred 62194228Sthompsa#define USB_DEBUG_VAR usb_debug 63188942Sthompsa#include <dev/usb/usb_debug.h> 64188942Sthompsa#include <dev/usb/usb_process.h> 65184610Salfred 66188942Sthompsa#include <dev/usb/serial/usb_serial.h> 67184610Salfred 68184610Salfred#define UCYCOM_MAX_IOLEN (1024 + 2) /* bytes */ 69184610Salfred 70184610Salfred#define UCYCOM_IFACE_INDEX 0 71184610Salfred 72187259Sthompsaenum { 73187259Sthompsa UCYCOM_CTRL_RD, 74187259Sthompsa UCYCOM_INTR_RD, 75188413Sthompsa UCYCOM_N_TRANSFER, 76187259Sthompsa}; 77187259Sthompsa 78184610Salfredstruct ucycom_softc { 79192984Sthompsa struct ucom_super_softc sc_super_ucom; 80192984Sthompsa struct ucom_softc sc_ucom; 81184610Salfred 82192984Sthompsa struct usb_device *sc_udev; 83192984Sthompsa struct usb_xfer *sc_xfer[UCYCOM_N_TRANSFER]; 84189265Sthompsa struct mtx sc_mtx; 85184610Salfred 86184610Salfred uint32_t sc_model; 87184610Salfred#define MODEL_CY7C63743 0x63743 88184610Salfred#define MODEL_CY7C64013 0x64013 89184610Salfred 90184610Salfred uint16_t sc_flen; /* feature report length */ 91184610Salfred uint16_t sc_ilen; /* input report length */ 92184610Salfred uint16_t sc_olen; /* output report length */ 93184610Salfred 94184610Salfred uint8_t sc_fid; /* feature report id */ 95184610Salfred uint8_t sc_iid; /* input report id */ 96184610Salfred uint8_t sc_oid; /* output report id */ 97184610Salfred uint8_t sc_cfg; 98184610Salfred#define UCYCOM_CFG_RESET 0x80 99184610Salfred#define UCYCOM_CFG_PARODD 0x20 100184610Salfred#define UCYCOM_CFG_PAREN 0x10 101184610Salfred#define UCYCOM_CFG_STOPB 0x08 102184610Salfred#define UCYCOM_CFG_DATAB 0x03 103184610Salfred uint8_t sc_ist; /* status flags from last input */ 104184610Salfred uint8_t sc_iface_no; 105184610Salfred uint8_t sc_temp_cfg[32]; 106184610Salfred}; 107184610Salfred 108184610Salfred/* prototypes */ 109184610Salfred 110184610Salfredstatic device_probe_t ucycom_probe; 111184610Salfredstatic device_attach_t ucycom_attach; 112184610Salfredstatic device_detach_t ucycom_detach; 113239299Shselaskystatic void ucycom_free_softc(struct ucycom_softc *); 114184610Salfred 115193045Sthompsastatic usb_callback_t ucycom_ctrl_write_callback; 116193045Sthompsastatic usb_callback_t ucycom_intr_read_callback; 117184610Salfred 118239180Shselaskystatic void ucycom_free(struct ucom_softc *); 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, 159239180Shselasky .ucom_free = &ucycom_free, 160184610Salfred}; 161184610Salfred 162184610Salfredstatic device_method_t ucycom_methods[] = { 163184610Salfred DEVMETHOD(device_probe, ucycom_probe), 164184610Salfred DEVMETHOD(device_attach, ucycom_attach), 165184610Salfred DEVMETHOD(device_detach, ucycom_detach), 166239180Shselasky DEVMETHOD_END 167184610Salfred}; 168184610Salfred 169184610Salfredstatic devclass_t ucycom_devclass; 170184610Salfred 171184610Salfredstatic driver_t ucycom_driver = { 172184610Salfred .name = "ucycom", 173184610Salfred .methods = ucycom_methods, 174184610Salfred .size = sizeof(struct ucycom_softc), 175184610Salfred}; 176184610Salfred 177184610Salfred/* 178184610Salfred * Supported devices 179184610Salfred */ 180223486Shselaskystatic const STRUCT_USB_HOST_ID ucycom_devs[] = { 181184610Salfred {USB_VPI(USB_VENDOR_DELORME, USB_PRODUCT_DELORME_EARTHMATE, MODEL_CY7C64013)}, 182184610Salfred}; 183184610Salfred 184292080SimpDRIVER_MODULE(ucycom, uhub, ucycom_driver, ucycom_devclass, NULL, 0); 185292080SimpMODULE_DEPEND(ucycom, ucom, 1, 1, 1); 186292080SimpMODULE_DEPEND(ucycom, usb, 1, 1, 1); 187292080SimpMODULE_VERSION(ucycom, 1); 188292080SimpUSB_PNP_HOST_INFO(ucycom_devs); 189292080Simp 190184610Salfred#define UCYCOM_DEFAULT_RATE 4800 191184610Salfred#define UCYCOM_DEFAULT_CFG 0x03 /* N-8-1 */ 192184610Salfred 193184610Salfredstatic int 194184610Salfreducycom_probe(device_t dev) 195184610Salfred{ 196192984Sthompsa struct usb_attach_arg *uaa = device_get_ivars(dev); 197184610Salfred 198192499Sthompsa if (uaa->usb_mode != USB_MODE_HOST) { 199184610Salfred return (ENXIO); 200184610Salfred } 201184610Salfred if (uaa->info.bConfigIndex != 0) { 202184610Salfred return (ENXIO); 203184610Salfred } 204184610Salfred if (uaa->info.bIfaceIndex != UCYCOM_IFACE_INDEX) { 205184610Salfred return (ENXIO); 206184610Salfred } 207194228Sthompsa return (usbd_lookup_id_by_uaa(ucycom_devs, sizeof(ucycom_devs), uaa)); 208184610Salfred} 209184610Salfred 210184610Salfredstatic int 211184610Salfreducycom_attach(device_t dev) 212184610Salfred{ 213192984Sthompsa struct usb_attach_arg *uaa = device_get_ivars(dev); 214184610Salfred struct ucycom_softc *sc = device_get_softc(dev); 215184610Salfred void *urd_ptr = NULL; 216184610Salfred int32_t error; 217184610Salfred uint16_t urd_len; 218184610Salfred uint8_t iface_index; 219184610Salfred 220184610Salfred sc->sc_udev = uaa->device; 221184610Salfred 222194228Sthompsa device_set_usb_desc(dev); 223189265Sthompsa mtx_init(&sc->sc_mtx, "ucycom", NULL, MTX_DEF); 224239180Shselasky ucom_ref(&sc->sc_super_ucom); 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 if (error) { 276184610Salfred goto detach; 277184610Salfred } 278214843Sn_hibma ucom_set_pnpinfo_usb(&sc->sc_super_ucom, dev); 279214843Sn_hibma 280184610Salfred if (urd_ptr) { 281184610Salfred free(urd_ptr, M_USBDEV); 282184610Salfred } 283214843Sn_hibma 284184610Salfred return (0); /* success */ 285184610Salfred 286184610Salfreddetach: 287184610Salfred if (urd_ptr) { 288184610Salfred free(urd_ptr, M_USBDEV); 289184610Salfred } 290184610Salfred ucycom_detach(dev); 291184610Salfred return (ENXIO); 292184610Salfred} 293184610Salfred 294184610Salfredstatic int 295184610Salfreducycom_detach(device_t dev) 296184610Salfred{ 297184610Salfred struct ucycom_softc *sc = device_get_softc(dev); 298184610Salfred 299214761Sn_hibma ucom_detach(&sc->sc_super_ucom, &sc->sc_ucom); 300194228Sthompsa usbd_transfer_unsetup(sc->sc_xfer, UCYCOM_N_TRANSFER); 301184610Salfred 302239299Shselasky device_claim_softc(dev); 303239299Shselasky 304239299Shselasky ucycom_free_softc(sc); 305239299Shselasky 306184610Salfred return (0); 307184610Salfred} 308184610Salfred 309239180ShselaskyUCOM_UNLOAD_DRAIN(ucycom); 310239180Shselasky 311184610Salfredstatic void 312239299Shselaskyucycom_free_softc(struct ucycom_softc *sc) 313239180Shselasky{ 314239180Shselasky if (ucom_unref(&sc->sc_super_ucom)) { 315239299Shselasky mtx_destroy(&sc->sc_mtx); 316239299Shselasky device_free_softc(sc); 317239180Shselasky } 318239180Shselasky} 319239180Shselasky 320239180Shselaskystatic void 321239180Shselaskyucycom_free(struct ucom_softc *ucom) 322239180Shselasky{ 323239299Shselasky ucycom_free_softc(ucom->sc_parent); 324239180Shselasky} 325239180Shselasky 326239180Shselaskystatic void 327192984Sthompsaucycom_cfg_open(struct ucom_softc *ucom) 328184610Salfred{ 329184610Salfred struct ucycom_softc *sc = ucom->sc_parent; 330184610Salfred 331184610Salfred /* set default configuration */ 332184610Salfred ucycom_cfg_write(sc, UCYCOM_DEFAULT_RATE, UCYCOM_DEFAULT_CFG); 333184610Salfred} 334184610Salfred 335184610Salfredstatic void 336192984Sthompsaucycom_start_read(struct ucom_softc *ucom) 337184610Salfred{ 338184610Salfred struct ucycom_softc *sc = ucom->sc_parent; 339184610Salfred 340194228Sthompsa usbd_transfer_start(sc->sc_xfer[UCYCOM_INTR_RD]); 341184610Salfred} 342184610Salfred 343184610Salfredstatic void 344192984Sthompsaucycom_stop_read(struct ucom_softc *ucom) 345184610Salfred{ 346184610Salfred struct ucycom_softc *sc = ucom->sc_parent; 347184610Salfred 348194228Sthompsa usbd_transfer_stop(sc->sc_xfer[UCYCOM_INTR_RD]); 349184610Salfred} 350184610Salfred 351184610Salfredstatic void 352192984Sthompsaucycom_start_write(struct ucom_softc *ucom) 353184610Salfred{ 354184610Salfred struct ucycom_softc *sc = ucom->sc_parent; 355184610Salfred 356194228Sthompsa usbd_transfer_start(sc->sc_xfer[UCYCOM_CTRL_RD]); 357184610Salfred} 358184610Salfred 359184610Salfredstatic void 360192984Sthompsaucycom_stop_write(struct ucom_softc *ucom) 361184610Salfred{ 362184610Salfred struct ucycom_softc *sc = ucom->sc_parent; 363184610Salfred 364194228Sthompsa usbd_transfer_stop(sc->sc_xfer[UCYCOM_CTRL_RD]); 365184610Salfred} 366184610Salfred 367184610Salfredstatic void 368194677Sthompsaucycom_ctrl_write_callback(struct usb_xfer *xfer, usb_error_t error) 369184610Salfred{ 370194677Sthompsa struct ucycom_softc *sc = usbd_xfer_softc(xfer); 371192984Sthompsa struct usb_device_request req; 372194677Sthompsa struct usb_page_cache *pc0, *pc1; 373184610Salfred uint8_t data[2]; 374184610Salfred uint8_t offset; 375184610Salfred uint32_t actlen; 376184610Salfred 377194677Sthompsa pc0 = usbd_xfer_get_frame(xfer, 0); 378194677Sthompsa pc1 = usbd_xfer_get_frame(xfer, 1); 379194677Sthompsa 380184610Salfred switch (USB_GET_STATE(xfer)) { 381184610Salfred case USB_ST_TRANSFERRED: 382184610Salfredtr_transferred: 383184610Salfred case USB_ST_SETUP: 384184610Salfred 385184610Salfred switch (sc->sc_model) { 386184610Salfred case MODEL_CY7C63743: 387184610Salfred offset = 1; 388184610Salfred break; 389184610Salfred case MODEL_CY7C64013: 390184610Salfred offset = 2; 391184610Salfred break; 392184610Salfred default: 393184610Salfred offset = 0; 394184610Salfred break; 395184610Salfred } 396184610Salfred 397194677Sthompsa if (ucom_get_data(&sc->sc_ucom, pc1, offset, 398184610Salfred sc->sc_olen - offset, &actlen)) { 399184610Salfred 400184610Salfred req.bmRequestType = UT_WRITE_CLASS_INTERFACE; 401184610Salfred req.bRequest = UR_SET_REPORT; 402184610Salfred USETW2(req.wValue, UHID_OUTPUT_REPORT, sc->sc_oid); 403184610Salfred req.wIndex[0] = sc->sc_iface_no; 404184610Salfred req.wIndex[1] = 0; 405184610Salfred USETW(req.wLength, sc->sc_olen); 406184610Salfred 407184610Salfred switch (sc->sc_model) { 408184610Salfred case MODEL_CY7C63743: 409184610Salfred data[0] = actlen; 410184610Salfred break; 411184610Salfred case MODEL_CY7C64013: 412184610Salfred data[0] = 0; 413184610Salfred data[1] = actlen; 414184610Salfred break; 415184610Salfred default: 416184610Salfred break; 417184610Salfred } 418184610Salfred 419194677Sthompsa usbd_copy_in(pc0, 0, &req, sizeof(req)); 420194677Sthompsa usbd_copy_in(pc1, 0, data, offset); 421184610Salfred 422194677Sthompsa usbd_xfer_set_frame_len(xfer, 0, sizeof(req)); 423194677Sthompsa usbd_xfer_set_frame_len(xfer, 1, sc->sc_olen); 424194677Sthompsa usbd_xfer_set_frames(xfer, sc->sc_olen ? 2 : 1); 425194228Sthompsa usbd_transfer_submit(xfer); 426184610Salfred } 427184610Salfred return; 428184610Salfred 429184610Salfred default: /* Error */ 430194677Sthompsa if (error == USB_ERR_CANCELLED) { 431184610Salfred return; 432184610Salfred } 433184610Salfred DPRINTF("error=%s\n", 434194677Sthompsa usbd_errstr(error)); 435184610Salfred goto tr_transferred; 436184610Salfred } 437184610Salfred} 438184610Salfred 439184610Salfredstatic void 440184610Salfreducycom_cfg_write(struct ucycom_softc *sc, uint32_t baud, uint8_t cfg) 441184610Salfred{ 442192984Sthompsa struct usb_device_request req; 443184610Salfred uint16_t len; 444193045Sthompsa usb_error_t err; 445184610Salfred 446184610Salfred len = sc->sc_flen; 447184610Salfred if (len > sizeof(sc->sc_temp_cfg)) { 448184610Salfred len = sizeof(sc->sc_temp_cfg); 449184610Salfred } 450184610Salfred sc->sc_cfg = cfg; 451184610Salfred 452184610Salfred req.bmRequestType = UT_WRITE_CLASS_INTERFACE; 453184610Salfred req.bRequest = UR_SET_REPORT; 454184610Salfred USETW2(req.wValue, UHID_FEATURE_REPORT, sc->sc_fid); 455184610Salfred req.wIndex[0] = sc->sc_iface_no; 456184610Salfred req.wIndex[1] = 0; 457184610Salfred USETW(req.wLength, len); 458184610Salfred 459184610Salfred sc->sc_temp_cfg[0] = (baud & 0xff); 460184610Salfred sc->sc_temp_cfg[1] = (baud >> 8) & 0xff; 461184610Salfred sc->sc_temp_cfg[2] = (baud >> 16) & 0xff; 462184610Salfred sc->sc_temp_cfg[3] = (baud >> 24) & 0xff; 463184610Salfred sc->sc_temp_cfg[4] = cfg; 464184610Salfred 465194228Sthompsa err = ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 466188413Sthompsa &req, sc->sc_temp_cfg, 0, 1000); 467184610Salfred if (err) { 468184610Salfred DPRINTFN(0, "device request failed, err=%s " 469194228Sthompsa "(ignored)\n", usbd_errstr(err)); 470184610Salfred } 471184610Salfred} 472184610Salfred 473184610Salfredstatic int 474192984Sthompsaucycom_pre_param(struct ucom_softc *ucom, struct termios *t) 475184610Salfred{ 476184610Salfred switch (t->c_ospeed) { 477184610Salfred case 600: 478184610Salfred case 1200: 479184610Salfred case 2400: 480184610Salfred case 4800: 481184610Salfred case 9600: 482184610Salfred case 19200: 483184610Salfred case 38400: 484184610Salfred case 57600: 485184610Salfred#if 0 486184610Salfred /* 487184610Salfred * Stock chips only support standard baud rates in the 600 - 57600 488184610Salfred * range, but higher rates can be achieved using custom firmware. 489184610Salfred */ 490184610Salfred case 115200: 491184610Salfred case 153600: 492184610Salfred case 192000: 493184610Salfred#endif 494184610Salfred break; 495184610Salfred default: 496184610Salfred return (EINVAL); 497184610Salfred } 498184610Salfred return (0); 499184610Salfred} 500184610Salfred 501184610Salfredstatic void 502192984Sthompsaucycom_cfg_param(struct ucom_softc *ucom, struct termios *t) 503184610Salfred{ 504184610Salfred struct ucycom_softc *sc = ucom->sc_parent; 505184610Salfred uint8_t cfg; 506184610Salfred 507184610Salfred DPRINTF("\n"); 508184610Salfred 509184610Salfred if (t->c_cflag & CIGNORE) { 510184610Salfred cfg = sc->sc_cfg; 511184610Salfred } else { 512184610Salfred cfg = 0; 513184610Salfred switch (t->c_cflag & CSIZE) { 514184610Salfred default: 515184610Salfred case CS8: 516184610Salfred ++cfg; 517184610Salfred case CS7: 518184610Salfred ++cfg; 519184610Salfred case CS6: 520184610Salfred ++cfg; 521184610Salfred case CS5: 522184610Salfred break; 523184610Salfred } 524184610Salfred 525184610Salfred if (t->c_cflag & CSTOPB) 526184610Salfred cfg |= UCYCOM_CFG_STOPB; 527184610Salfred if (t->c_cflag & PARENB) 528184610Salfred cfg |= UCYCOM_CFG_PAREN; 529184610Salfred if (t->c_cflag & PARODD) 530184610Salfred cfg |= UCYCOM_CFG_PARODD; 531184610Salfred } 532184610Salfred 533184610Salfred ucycom_cfg_write(sc, t->c_ospeed, cfg); 534184610Salfred} 535184610Salfred 536184610Salfredstatic void 537194677Sthompsaucycom_intr_read_callback(struct usb_xfer *xfer, usb_error_t error) 538184610Salfred{ 539194677Sthompsa struct ucycom_softc *sc = usbd_xfer_softc(xfer); 540194677Sthompsa struct usb_page_cache *pc; 541184610Salfred uint8_t buf[2]; 542184610Salfred uint32_t offset; 543233774Shselasky int len; 544194677Sthompsa int actlen; 545184610Salfred 546194677Sthompsa usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); 547194677Sthompsa pc = usbd_xfer_get_frame(xfer, 0); 548194677Sthompsa 549184610Salfred switch (USB_GET_STATE(xfer)) { 550184610Salfred case USB_ST_TRANSFERRED: 551184610Salfred switch (sc->sc_model) { 552184610Salfred case MODEL_CY7C63743: 553194677Sthompsa if (actlen < 1) { 554184610Salfred goto tr_setup; 555184610Salfred } 556194677Sthompsa usbd_copy_out(pc, 0, buf, 1); 557184610Salfred 558184610Salfred sc->sc_ist = buf[0] & ~0x07; 559184610Salfred len = buf[0] & 0x07; 560184610Salfred 561194677Sthompsa actlen--; 562184610Salfred offset = 1; 563184610Salfred 564184610Salfred break; 565184610Salfred 566184610Salfred case MODEL_CY7C64013: 567194677Sthompsa if (actlen < 2) { 568184610Salfred goto tr_setup; 569184610Salfred } 570194677Sthompsa usbd_copy_out(pc, 0, buf, 2); 571184610Salfred 572184610Salfred sc->sc_ist = buf[0] & ~0x07; 573184610Salfred len = buf[1]; 574184610Salfred 575194677Sthompsa actlen -= 2; 576184610Salfred offset = 2; 577184610Salfred 578184610Salfred break; 579184610Salfred 580184610Salfred default: 581199816Sthompsa DPRINTFN(0, "unsupported model number\n"); 582184610Salfred goto tr_setup; 583184610Salfred } 584184610Salfred 585194677Sthompsa if (len > actlen) 586194677Sthompsa len = actlen; 587194677Sthompsa if (len) 588194677Sthompsa ucom_put_data(&sc->sc_ucom, pc, offset, len); 589194677Sthompsa /* FALLTHROUGH */ 590184610Salfred case USB_ST_SETUP: 591184610Salfredtr_setup: 592194677Sthompsa usbd_xfer_set_frame_len(xfer, 0, sc->sc_ilen); 593194228Sthompsa usbd_transfer_submit(xfer); 594184610Salfred return; 595184610Salfred 596184610Salfred default: /* Error */ 597194677Sthompsa if (error != USB_ERR_CANCELLED) { 598188413Sthompsa /* try to clear stall first */ 599194677Sthompsa usbd_xfer_set_stall(xfer); 600188413Sthompsa goto tr_setup; 601184610Salfred } 602184610Salfred return; 603184610Salfred 604184610Salfred } 605184610Salfred} 606197570Sthompsa 607197570Sthompsastatic void 608197570Sthompsaucycom_poll(struct ucom_softc *ucom) 609197570Sthompsa{ 610197570Sthompsa struct ucycom_softc *sc = ucom->sc_parent; 611197570Sthompsa usbd_transfer_poll(sc->sc_xfer, UCYCOM_N_TRANSFER); 612197570Sthompsa} 613