1184610Salfred#include <sys/cdefs.h> 2184610Salfred__FBSDID("$FreeBSD$"); 3184610Salfred 4184610Salfred/*- 5184610Salfred * Copyright (c) 2003 Scott Long 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 * 2. Redistributions in binary form must reproduce the above copyright 14184610Salfred * notice, this list of conditions and the following disclaimer in the 15184610Salfred * documentation and/or other materials provided with the distribution. 16184610Salfred * 17184610Salfred * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18184610Salfred * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19184610Salfred * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20184610Salfred * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21184610Salfred * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22184610Salfred * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23184610Salfred * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24184610Salfred * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25184610Salfred * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26184610Salfred * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27184610Salfred * SUCH DAMAGE. 28184610Salfred * 29184610Salfred */ 30184610Salfred 31184610Salfred/* 32184610Salfred * Driver for the MCT (Magic Control Technology) USB-RS232 Converter. 33184610Salfred * Based on the superb documentation from the linux mct_u232 driver by 34184610Salfred * Wolfgang Grandeggar <wolfgang@cec.ch>. 35184610Salfred * This device smells a lot like the Belkin F5U103, except that it has 36184610Salfred * suffered some mild brain-damage. This driver is based off of the ubsa.c 37189002Sed * driver from Alexander Kabaev <kan@FreeBSD.org>. Merging the two together 38184610Salfred * might be useful, though the subtle differences might lead to lots of 39184610Salfred * #ifdef's. 40184610Salfred */ 41184610Salfred 42184610Salfred/* 43184610Salfred * NOTE: all function names beginning like "umct_cfg_" can only 44184610Salfred * be called from within the config thread function ! 45184610Salfred */ 46184610Salfred 47194677Sthompsa#include <sys/stdint.h> 48194677Sthompsa#include <sys/stddef.h> 49194677Sthompsa#include <sys/param.h> 50194677Sthompsa#include <sys/queue.h> 51194677Sthompsa#include <sys/types.h> 52194677Sthompsa#include <sys/systm.h> 53194677Sthompsa#include <sys/kernel.h> 54194677Sthompsa#include <sys/bus.h> 55194677Sthompsa#include <sys/module.h> 56194677Sthompsa#include <sys/lock.h> 57194677Sthompsa#include <sys/mutex.h> 58194677Sthompsa#include <sys/condvar.h> 59194677Sthompsa#include <sys/sysctl.h> 60194677Sthompsa#include <sys/sx.h> 61194677Sthompsa#include <sys/unistd.h> 62194677Sthompsa#include <sys/callout.h> 63194677Sthompsa#include <sys/malloc.h> 64194677Sthompsa#include <sys/priv.h> 65194677Sthompsa 66194677Sthompsa#include <dev/usb/usb.h> 67194677Sthompsa#include <dev/usb/usbdi.h> 68194677Sthompsa#include <dev/usb/usbdi_util.h> 69188746Sthompsa#include "usbdevs.h" 70184610Salfred 71194228Sthompsa#define USB_DEBUG_VAR usb_debug 72188942Sthompsa#include <dev/usb/usb_debug.h> 73188942Sthompsa#include <dev/usb/usb_process.h> 74184610Salfred 75188942Sthompsa#include <dev/usb/serial/usb_serial.h> 76184610Salfred 77184610Salfred/* The UMCT advertises the standard 8250 UART registers */ 78184610Salfred#define UMCT_GET_MSR 2 /* Get Modem Status Register */ 79184610Salfred#define UMCT_GET_MSR_SIZE 1 80184610Salfred#define UMCT_GET_LCR 6 /* Get Line Control Register */ 81184610Salfred#define UMCT_GET_LCR_SIZE 1 82184610Salfred#define UMCT_SET_BAUD 5 /* Set the Baud Rate Divisor */ 83184610Salfred#define UMCT_SET_BAUD_SIZE 4 84184610Salfred#define UMCT_SET_LCR 7 /* Set Line Control Register */ 85184610Salfred#define UMCT_SET_LCR_SIZE 1 86184610Salfred#define UMCT_SET_MCR 10 /* Set Modem Control Register */ 87184610Salfred#define UMCT_SET_MCR_SIZE 1 88184610Salfred 89184610Salfred#define UMCT_INTR_INTERVAL 100 90184610Salfred#define UMCT_IFACE_INDEX 0 91192556Sthompsa#define UMCT_CONFIG_INDEX 0 92184610Salfred 93187259Sthompsaenum { 94187259Sthompsa UMCT_BULK_DT_WR, 95187259Sthompsa UMCT_BULK_DT_RD, 96187259Sthompsa UMCT_INTR_DT_RD, 97188413Sthompsa UMCT_N_TRANSFER, 98187259Sthompsa}; 99184610Salfred 100184610Salfredstruct umct_softc { 101192984Sthompsa struct ucom_super_softc sc_super_ucom; 102192984Sthompsa struct ucom_softc sc_ucom; 103184610Salfred 104192984Sthompsa struct usb_device *sc_udev; 105192984Sthompsa struct usb_xfer *sc_xfer[UMCT_N_TRANSFER]; 106189265Sthompsa struct mtx sc_mtx; 107184610Salfred 108184610Salfred uint32_t sc_unit; 109184610Salfred 110184610Salfred uint16_t sc_obufsize; 111184610Salfred 112184610Salfred uint8_t sc_lsr; 113184610Salfred uint8_t sc_msr; 114184610Salfred uint8_t sc_lcr; 115184610Salfred uint8_t sc_mcr; 116188413Sthompsa uint8_t sc_iface_no; 117197573Sthompsa uint8_t sc_swap_cb; 118184610Salfred}; 119184610Salfred 120184610Salfred/* prototypes */ 121184610Salfred 122184610Salfredstatic device_probe_t umct_probe; 123184610Salfredstatic device_attach_t umct_attach; 124184610Salfredstatic device_detach_t umct_detach; 125239299Shselaskystatic void umct_free_softc(struct umct_softc *); 126184610Salfred 127193045Sthompsastatic usb_callback_t umct_intr_callback; 128197573Sthompsastatic usb_callback_t umct_intr_callback_sub; 129197573Sthompsastatic usb_callback_t umct_read_callback; 130197573Sthompsastatic usb_callback_t umct_read_callback_sub; 131193045Sthompsastatic usb_callback_t umct_write_callback; 132184610Salfred 133188413Sthompsastatic void umct_cfg_do_request(struct umct_softc *sc, uint8_t request, 134188413Sthompsa uint16_t len, uint32_t value); 135239180Shselaskystatic void umct_free(struct ucom_softc *); 136192984Sthompsastatic void umct_cfg_get_status(struct ucom_softc *, uint8_t *, 137185948Sthompsa uint8_t *); 138192984Sthompsastatic void umct_cfg_set_break(struct ucom_softc *, uint8_t); 139192984Sthompsastatic void umct_cfg_set_dtr(struct ucom_softc *, uint8_t); 140192984Sthompsastatic void umct_cfg_set_rts(struct ucom_softc *, uint8_t); 141185948Sthompsastatic uint8_t umct_calc_baud(uint32_t); 142192984Sthompsastatic int umct_pre_param(struct ucom_softc *, struct termios *); 143192984Sthompsastatic void umct_cfg_param(struct ucom_softc *, struct termios *); 144192984Sthompsastatic void umct_start_read(struct ucom_softc *); 145192984Sthompsastatic void umct_stop_read(struct ucom_softc *); 146192984Sthompsastatic void umct_start_write(struct ucom_softc *); 147192984Sthompsastatic void umct_stop_write(struct ucom_softc *); 148197570Sthompsastatic void umct_poll(struct ucom_softc *ucom); 149184610Salfred 150192984Sthompsastatic const struct usb_config umct_config[UMCT_N_TRANSFER] = { 151184610Salfred 152187259Sthompsa [UMCT_BULK_DT_WR] = { 153184610Salfred .type = UE_BULK, 154184610Salfred .endpoint = UE_ADDR_ANY, 155184610Salfred .direction = UE_DIR_OUT, 156190734Sthompsa .bufsize = 0, /* use wMaxPacketSize */ 157190734Sthompsa .flags = {.pipe_bof = 1,.force_short_xfer = 1,}, 158190734Sthompsa .callback = &umct_write_callback, 159184610Salfred }, 160184610Salfred 161187259Sthompsa [UMCT_BULK_DT_RD] = { 162184610Salfred .type = UE_INTERRUPT, 163184610Salfred .endpoint = UE_ADDR_ANY, 164184610Salfred .direction = UE_DIR_IN, 165190734Sthompsa .flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, 166190734Sthompsa .bufsize = 0, /* use wMaxPacketSize */ 167190734Sthompsa .callback = &umct_read_callback, 168184610Salfred .ep_index = 0, /* first interrupt endpoint */ 169184610Salfred }, 170184610Salfred 171187259Sthompsa [UMCT_INTR_DT_RD] = { 172184610Salfred .type = UE_INTERRUPT, 173184610Salfred .endpoint = UE_ADDR_ANY, 174184610Salfred .direction = UE_DIR_IN, 175190734Sthompsa .flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, 176190734Sthompsa .bufsize = 0, /* use wMaxPacketSize */ 177190734Sthompsa .callback = &umct_intr_callback, 178184610Salfred .ep_index = 1, /* second interrupt endpoint */ 179184610Salfred }, 180184610Salfred}; 181184610Salfred 182192984Sthompsastatic const struct ucom_callback umct_callback = { 183194228Sthompsa .ucom_cfg_get_status = &umct_cfg_get_status, 184194228Sthompsa .ucom_cfg_set_dtr = &umct_cfg_set_dtr, 185194228Sthompsa .ucom_cfg_set_rts = &umct_cfg_set_rts, 186194228Sthompsa .ucom_cfg_set_break = &umct_cfg_set_break, 187194228Sthompsa .ucom_cfg_param = &umct_cfg_param, 188194228Sthompsa .ucom_pre_param = &umct_pre_param, 189194228Sthompsa .ucom_start_read = &umct_start_read, 190194228Sthompsa .ucom_stop_read = &umct_stop_read, 191194228Sthompsa .ucom_start_write = &umct_start_write, 192194228Sthompsa .ucom_stop_write = &umct_stop_write, 193197570Sthompsa .ucom_poll = &umct_poll, 194239180Shselasky .ucom_free = &umct_free, 195184610Salfred}; 196184610Salfred 197223486Shselaskystatic const STRUCT_USB_HOST_ID umct_devs[] = { 198184610Salfred {USB_VPI(USB_VENDOR_MCT, USB_PRODUCT_MCT_USB232, 0)}, 199184610Salfred {USB_VPI(USB_VENDOR_MCT, USB_PRODUCT_MCT_SITECOM_USB232, 0)}, 200184610Salfred {USB_VPI(USB_VENDOR_MCT, USB_PRODUCT_MCT_DU_H3SP_USB232, 0)}, 201184610Salfred {USB_VPI(USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F5U109, 0)}, 202184610Salfred {USB_VPI(USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F5U409, 0)}, 203184610Salfred}; 204184610Salfred 205184610Salfredstatic device_method_t umct_methods[] = { 206184610Salfred DEVMETHOD(device_probe, umct_probe), 207184610Salfred DEVMETHOD(device_attach, umct_attach), 208184610Salfred DEVMETHOD(device_detach, umct_detach), 209239180Shselasky DEVMETHOD_END 210184610Salfred}; 211184610Salfred 212184610Salfredstatic devclass_t umct_devclass; 213184610Salfred 214184610Salfredstatic driver_t umct_driver = { 215184610Salfred .name = "umct", 216184610Salfred .methods = umct_methods, 217184610Salfred .size = sizeof(struct umct_softc), 218184610Salfred}; 219184610Salfred 220189275SthompsaDRIVER_MODULE(umct, uhub, umct_driver, umct_devclass, NULL, 0); 221188942SthompsaMODULE_DEPEND(umct, ucom, 1, 1, 1); 222188942SthompsaMODULE_DEPEND(umct, usb, 1, 1, 1); 223212122SthompsaMODULE_VERSION(umct, 1); 224184610Salfred 225184610Salfredstatic int 226184610Salfredumct_probe(device_t dev) 227184610Salfred{ 228192984Sthompsa struct usb_attach_arg *uaa = device_get_ivars(dev); 229184610Salfred 230192499Sthompsa if (uaa->usb_mode != USB_MODE_HOST) { 231184610Salfred return (ENXIO); 232184610Salfred } 233184610Salfred if (uaa->info.bConfigIndex != UMCT_CONFIG_INDEX) { 234184610Salfred return (ENXIO); 235184610Salfred } 236184610Salfred if (uaa->info.bIfaceIndex != UMCT_IFACE_INDEX) { 237184610Salfred return (ENXIO); 238184610Salfred } 239194228Sthompsa return (usbd_lookup_id_by_uaa(umct_devs, sizeof(umct_devs), uaa)); 240184610Salfred} 241184610Salfred 242184610Salfredstatic int 243184610Salfredumct_attach(device_t dev) 244184610Salfred{ 245192984Sthompsa struct usb_attach_arg *uaa = device_get_ivars(dev); 246184610Salfred struct umct_softc *sc = device_get_softc(dev); 247184610Salfred int32_t error; 248197573Sthompsa uint16_t maxp; 249184610Salfred uint8_t iface_index; 250184610Salfred 251184610Salfred sc->sc_udev = uaa->device; 252184610Salfred sc->sc_unit = device_get_unit(dev); 253184610Salfred 254194228Sthompsa device_set_usb_desc(dev); 255189265Sthompsa mtx_init(&sc->sc_mtx, "umct", NULL, MTX_DEF); 256239180Shselasky ucom_ref(&sc->sc_super_ucom); 257184610Salfred 258184610Salfred sc->sc_iface_no = uaa->info.bIfaceNum; 259184610Salfred 260184610Salfred iface_index = UMCT_IFACE_INDEX; 261194228Sthompsa error = usbd_transfer_setup(uaa->device, &iface_index, 262189265Sthompsa sc->sc_xfer, umct_config, UMCT_N_TRANSFER, sc, &sc->sc_mtx); 263184610Salfred 264184610Salfred if (error) { 265184610Salfred device_printf(dev, "allocating USB " 266199816Sthompsa "transfers failed\n"); 267184610Salfred goto detach; 268184610Salfred } 269197573Sthompsa 270184610Salfred /* 271184610Salfred * The real bulk-in endpoint is also marked as an interrupt. 272184610Salfred * The only way to differentiate it from the real interrupt 273184610Salfred * endpoint is to look at the wMaxPacketSize field. 274184610Salfred */ 275197573Sthompsa maxp = usbd_xfer_max_framelen(sc->sc_xfer[UMCT_BULK_DT_RD]); 276184610Salfred if (maxp == 0x2) { 277184610Salfred 278184610Salfred /* guessed wrong - switch around endpoints */ 279184610Salfred 280192984Sthompsa struct usb_xfer *temp = sc->sc_xfer[UMCT_INTR_DT_RD]; 281184610Salfred 282187259Sthompsa sc->sc_xfer[UMCT_INTR_DT_RD] = sc->sc_xfer[UMCT_BULK_DT_RD]; 283187259Sthompsa sc->sc_xfer[UMCT_BULK_DT_RD] = temp; 284197573Sthompsa sc->sc_swap_cb = 1; 285197573Sthompsa } 286184610Salfred 287194677Sthompsa sc->sc_obufsize = usbd_xfer_max_len(sc->sc_xfer[UMCT_BULK_DT_WR]); 288184610Salfred 289184610Salfred if (uaa->info.idProduct == USB_PRODUCT_MCT_SITECOM_USB232) { 290184610Salfred if (sc->sc_obufsize > 16) { 291184610Salfred sc->sc_obufsize = 16; 292184610Salfred } 293184610Salfred } 294194228Sthompsa error = ucom_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc, 295189265Sthompsa &umct_callback, &sc->sc_mtx); 296184610Salfred if (error) { 297184610Salfred goto detach; 298184610Salfred } 299214843Sn_hibma ucom_set_pnpinfo_usb(&sc->sc_super_ucom, dev); 300214843Sn_hibma 301184610Salfred return (0); /* success */ 302184610Salfred 303184610Salfreddetach: 304184610Salfred umct_detach(dev); 305184610Salfred return (ENXIO); /* failure */ 306184610Salfred} 307184610Salfred 308184610Salfredstatic int 309184610Salfredumct_detach(device_t dev) 310184610Salfred{ 311184610Salfred struct umct_softc *sc = device_get_softc(dev); 312184610Salfred 313214761Sn_hibma ucom_detach(&sc->sc_super_ucom, &sc->sc_ucom); 314194228Sthompsa usbd_transfer_unsetup(sc->sc_xfer, UMCT_N_TRANSFER); 315184610Salfred 316239299Shselasky device_claim_softc(dev); 317239299Shselasky 318239299Shselasky umct_free_softc(sc); 319239299Shselasky 320184610Salfred return (0); 321184610Salfred} 322184610Salfred 323239180ShselaskyUCOM_UNLOAD_DRAIN(umct); 324239180Shselasky 325184610Salfredstatic void 326239299Shselaskyumct_free_softc(struct umct_softc *sc) 327239180Shselasky{ 328239180Shselasky if (ucom_unref(&sc->sc_super_ucom)) { 329239299Shselasky mtx_destroy(&sc->sc_mtx); 330239299Shselasky device_free_softc(sc); 331239180Shselasky } 332239180Shselasky} 333239180Shselasky 334239180Shselaskystatic void 335239180Shselaskyumct_free(struct ucom_softc *ucom) 336239180Shselasky{ 337239299Shselasky umct_free_softc(ucom->sc_parent); 338239180Shselasky} 339239180Shselasky 340239180Shselaskystatic void 341184610Salfredumct_cfg_do_request(struct umct_softc *sc, uint8_t request, 342184610Salfred uint16_t len, uint32_t value) 343184610Salfred{ 344192984Sthompsa struct usb_device_request req; 345193045Sthompsa usb_error_t err; 346184610Salfred uint8_t temp[4]; 347184610Salfred 348188413Sthompsa if (len > 4) 349184610Salfred len = 4; 350184610Salfred req.bmRequestType = UT_WRITE_VENDOR_DEVICE; 351184610Salfred req.bRequest = request; 352184610Salfred USETW(req.wValue, 0); 353184610Salfred req.wIndex[0] = sc->sc_iface_no; 354184610Salfred req.wIndex[1] = 0; 355184610Salfred USETW(req.wLength, len); 356184610Salfred USETDW(temp, value); 357184610Salfred 358194228Sthompsa err = ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 359188413Sthompsa &req, temp, 0, 1000); 360184610Salfred if (err) { 361184610Salfred DPRINTFN(0, "device request failed, err=%s " 362194228Sthompsa "(ignored)\n", usbd_errstr(err)); 363184610Salfred } 364184610Salfred return; 365184610Salfred} 366184610Salfred 367184610Salfredstatic void 368197573Sthompsaumct_intr_callback_sub(struct usb_xfer *xfer, usb_error_t error) 369184610Salfred{ 370194677Sthompsa struct umct_softc *sc = usbd_xfer_softc(xfer); 371194677Sthompsa struct usb_page_cache *pc; 372184610Salfred uint8_t buf[2]; 373194677Sthompsa int actlen; 374184610Salfred 375194677Sthompsa usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); 376194677Sthompsa 377184610Salfred switch (USB_GET_STATE(xfer)) { 378184610Salfred case USB_ST_TRANSFERRED: 379194677Sthompsa if (actlen < 2) { 380184610Salfred DPRINTF("too short message\n"); 381184610Salfred goto tr_setup; 382184610Salfred } 383194677Sthompsa pc = usbd_xfer_get_frame(xfer, 0); 384194677Sthompsa usbd_copy_out(pc, 0, buf, sizeof(buf)); 385184610Salfred 386184610Salfred sc->sc_msr = buf[0]; 387184610Salfred sc->sc_lsr = buf[1]; 388184610Salfred 389194228Sthompsa ucom_status_change(&sc->sc_ucom); 390184610Salfred 391184610Salfred case USB_ST_SETUP: 392184610Salfredtr_setup: 393194677Sthompsa usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); 394194228Sthompsa usbd_transfer_submit(xfer); 395184610Salfred return; 396184610Salfred 397184610Salfred default: /* Error */ 398194677Sthompsa if (error != USB_ERR_CANCELLED) { 399188413Sthompsa /* try to clear stall first */ 400194677Sthompsa usbd_xfer_set_stall(xfer); 401188413Sthompsa goto tr_setup; 402184610Salfred } 403184610Salfred return; 404184610Salfred } 405184610Salfred} 406184610Salfred 407184610Salfredstatic void 408192984Sthompsaumct_cfg_get_status(struct ucom_softc *ucom, uint8_t *lsr, uint8_t *msr) 409184610Salfred{ 410184610Salfred struct umct_softc *sc = ucom->sc_parent; 411184610Salfred 412184610Salfred *lsr = sc->sc_lsr; 413184610Salfred *msr = sc->sc_msr; 414184610Salfred} 415184610Salfred 416184610Salfredstatic void 417192984Sthompsaumct_cfg_set_break(struct ucom_softc *ucom, uint8_t onoff) 418184610Salfred{ 419184610Salfred struct umct_softc *sc = ucom->sc_parent; 420184610Salfred 421184610Salfred if (onoff) 422184610Salfred sc->sc_lcr |= 0x40; 423184610Salfred else 424184610Salfred sc->sc_lcr &= ~0x40; 425184610Salfred 426184610Salfred umct_cfg_do_request(sc, UMCT_SET_LCR, UMCT_SET_LCR_SIZE, sc->sc_lcr); 427184610Salfred} 428184610Salfred 429184610Salfredstatic void 430192984Sthompsaumct_cfg_set_dtr(struct ucom_softc *ucom, uint8_t onoff) 431184610Salfred{ 432184610Salfred struct umct_softc *sc = ucom->sc_parent; 433184610Salfred 434184610Salfred if (onoff) 435184610Salfred sc->sc_mcr |= 0x01; 436184610Salfred else 437184610Salfred sc->sc_mcr &= ~0x01; 438184610Salfred 439184610Salfred umct_cfg_do_request(sc, UMCT_SET_MCR, UMCT_SET_MCR_SIZE, sc->sc_mcr); 440184610Salfred} 441184610Salfred 442184610Salfredstatic void 443192984Sthompsaumct_cfg_set_rts(struct ucom_softc *ucom, uint8_t onoff) 444184610Salfred{ 445184610Salfred struct umct_softc *sc = ucom->sc_parent; 446184610Salfred 447184610Salfred if (onoff) 448184610Salfred sc->sc_mcr |= 0x02; 449184610Salfred else 450184610Salfred sc->sc_mcr &= ~0x02; 451184610Salfred 452184610Salfred umct_cfg_do_request(sc, UMCT_SET_MCR, UMCT_SET_MCR_SIZE, sc->sc_mcr); 453184610Salfred} 454184610Salfred 455184610Salfredstatic uint8_t 456184610Salfredumct_calc_baud(uint32_t baud) 457184610Salfred{ 458184610Salfred switch (baud) { 459184610Salfred case B300:return (0x1); 460184610Salfred case B600: 461184610Salfred return (0x2); 462184610Salfred case B1200: 463184610Salfred return (0x3); 464184610Salfred case B2400: 465184610Salfred return (0x4); 466184610Salfred case B4800: 467184610Salfred return (0x6); 468184610Salfred case B9600: 469184610Salfred return (0x8); 470184610Salfred case B19200: 471184610Salfred return (0x9); 472184610Salfred case B38400: 473184610Salfred return (0xa); 474184610Salfred case B57600: 475184610Salfred return (0xb); 476184610Salfred case 115200: 477184610Salfred return (0xc); 478184610Salfred case B0: 479184610Salfred default: 480184610Salfred break; 481184610Salfred } 482184610Salfred return (0x0); 483184610Salfred} 484184610Salfred 485184610Salfredstatic int 486192984Sthompsaumct_pre_param(struct ucom_softc *ucom, struct termios *t) 487184610Salfred{ 488184610Salfred return (0); /* we accept anything */ 489184610Salfred} 490184610Salfred 491184610Salfredstatic void 492192984Sthompsaumct_cfg_param(struct ucom_softc *ucom, struct termios *t) 493184610Salfred{ 494184610Salfred struct umct_softc *sc = ucom->sc_parent; 495184610Salfred uint32_t value; 496184610Salfred 497184610Salfred value = umct_calc_baud(t->c_ospeed); 498184610Salfred umct_cfg_do_request(sc, UMCT_SET_BAUD, UMCT_SET_BAUD_SIZE, value); 499184610Salfred 500184610Salfred value = (sc->sc_lcr & 0x40); 501184610Salfred 502184610Salfred switch (t->c_cflag & CSIZE) { 503184610Salfred case CS5: 504184610Salfred value |= 0x0; 505184610Salfred break; 506184610Salfred case CS6: 507184610Salfred value |= 0x1; 508184610Salfred break; 509184610Salfred case CS7: 510184610Salfred value |= 0x2; 511184610Salfred break; 512184610Salfred default: 513184610Salfred case CS8: 514184610Salfred value |= 0x3; 515184610Salfred break; 516184610Salfred } 517184610Salfred 518184610Salfred value |= (t->c_cflag & CSTOPB) ? 0x4 : 0; 519184610Salfred if (t->c_cflag & PARENB) { 520184610Salfred value |= 0x8; 521184610Salfred value |= (t->c_cflag & PARODD) ? 0x0 : 0x10; 522184610Salfred } 523184610Salfred /* 524184610Salfred * XXX There doesn't seem to be a way to tell the device 525184610Salfred * to use flow control. 526184610Salfred */ 527184610Salfred 528184610Salfred sc->sc_lcr = value; 529184610Salfred umct_cfg_do_request(sc, UMCT_SET_LCR, UMCT_SET_LCR_SIZE, value); 530184610Salfred} 531184610Salfred 532184610Salfredstatic void 533192984Sthompsaumct_start_read(struct ucom_softc *ucom) 534184610Salfred{ 535184610Salfred struct umct_softc *sc = ucom->sc_parent; 536184610Salfred 537184610Salfred /* start interrupt endpoint */ 538194228Sthompsa usbd_transfer_start(sc->sc_xfer[UMCT_INTR_DT_RD]); 539184610Salfred 540184610Salfred /* start read endpoint */ 541194228Sthompsa usbd_transfer_start(sc->sc_xfer[UMCT_BULK_DT_RD]); 542184610Salfred} 543184610Salfred 544184610Salfredstatic void 545192984Sthompsaumct_stop_read(struct ucom_softc *ucom) 546184610Salfred{ 547184610Salfred struct umct_softc *sc = ucom->sc_parent; 548184610Salfred 549184610Salfred /* stop interrupt endpoint */ 550194228Sthompsa usbd_transfer_stop(sc->sc_xfer[UMCT_INTR_DT_RD]); 551184610Salfred 552184610Salfred /* stop read endpoint */ 553194228Sthompsa usbd_transfer_stop(sc->sc_xfer[UMCT_BULK_DT_RD]); 554184610Salfred} 555184610Salfred 556184610Salfredstatic void 557192984Sthompsaumct_start_write(struct ucom_softc *ucom) 558184610Salfred{ 559184610Salfred struct umct_softc *sc = ucom->sc_parent; 560184610Salfred 561194228Sthompsa usbd_transfer_start(sc->sc_xfer[UMCT_BULK_DT_WR]); 562184610Salfred} 563184610Salfred 564184610Salfredstatic void 565192984Sthompsaumct_stop_write(struct ucom_softc *ucom) 566184610Salfred{ 567184610Salfred struct umct_softc *sc = ucom->sc_parent; 568184610Salfred 569194228Sthompsa usbd_transfer_stop(sc->sc_xfer[UMCT_BULK_DT_WR]); 570184610Salfred} 571184610Salfred 572184610Salfredstatic void 573197573Sthompsaumct_read_callback(struct usb_xfer *xfer, usb_error_t error) 574197573Sthompsa{ 575197573Sthompsa struct umct_softc *sc = usbd_xfer_softc(xfer); 576197573Sthompsa 577197573Sthompsa if (sc->sc_swap_cb) 578197573Sthompsa umct_intr_callback_sub(xfer, error); 579197573Sthompsa else 580197573Sthompsa umct_read_callback_sub(xfer, error); 581197573Sthompsa} 582197573Sthompsa 583197573Sthompsastatic void 584197573Sthompsaumct_intr_callback(struct usb_xfer *xfer, usb_error_t error) 585197573Sthompsa{ 586197573Sthompsa struct umct_softc *sc = usbd_xfer_softc(xfer); 587197573Sthompsa 588197573Sthompsa if (sc->sc_swap_cb) 589197573Sthompsa umct_read_callback_sub(xfer, error); 590197573Sthompsa else 591197573Sthompsa umct_intr_callback_sub(xfer, error); 592197573Sthompsa} 593197573Sthompsa 594197573Sthompsastatic void 595194677Sthompsaumct_write_callback(struct usb_xfer *xfer, usb_error_t error) 596184610Salfred{ 597194677Sthompsa struct umct_softc *sc = usbd_xfer_softc(xfer); 598194677Sthompsa struct usb_page_cache *pc; 599184610Salfred uint32_t actlen; 600184610Salfred 601184610Salfred switch (USB_GET_STATE(xfer)) { 602184610Salfred case USB_ST_SETUP: 603184610Salfred case USB_ST_TRANSFERRED: 604188413Sthompsatr_setup: 605194677Sthompsa pc = usbd_xfer_get_frame(xfer, 0); 606194677Sthompsa if (ucom_get_data(&sc->sc_ucom, pc, 0, 607184610Salfred sc->sc_obufsize, &actlen)) { 608184610Salfred 609194677Sthompsa usbd_xfer_set_frame_len(xfer, 0, actlen); 610194228Sthompsa usbd_transfer_submit(xfer); 611184610Salfred } 612184610Salfred return; 613184610Salfred 614184610Salfred default: /* Error */ 615194677Sthompsa if (error != USB_ERR_CANCELLED) { 616188413Sthompsa /* try to clear stall first */ 617194677Sthompsa usbd_xfer_set_stall(xfer); 618188413Sthompsa goto tr_setup; 619184610Salfred } 620184610Salfred return; 621184610Salfred } 622184610Salfred} 623184610Salfred 624184610Salfredstatic void 625197573Sthompsaumct_read_callback_sub(struct usb_xfer *xfer, usb_error_t error) 626184610Salfred{ 627194677Sthompsa struct umct_softc *sc = usbd_xfer_softc(xfer); 628194677Sthompsa struct usb_page_cache *pc; 629194677Sthompsa int actlen; 630184610Salfred 631194677Sthompsa usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); 632194677Sthompsa 633184610Salfred switch (USB_GET_STATE(xfer)) { 634184610Salfred case USB_ST_TRANSFERRED: 635194677Sthompsa pc = usbd_xfer_get_frame(xfer, 0); 636194677Sthompsa ucom_put_data(&sc->sc_ucom, pc, 0, actlen); 637184610Salfred 638184610Salfred case USB_ST_SETUP: 639188413Sthompsatr_setup: 640194677Sthompsa usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); 641194228Sthompsa usbd_transfer_submit(xfer); 642184610Salfred return; 643184610Salfred 644184610Salfred default: /* Error */ 645194677Sthompsa if (error != USB_ERR_CANCELLED) { 646188413Sthompsa /* try to clear stall first */ 647194677Sthompsa usbd_xfer_set_stall(xfer); 648188413Sthompsa goto tr_setup; 649184610Salfred } 650184610Salfred return; 651184610Salfred } 652184610Salfred} 653197570Sthompsa 654197570Sthompsastatic void 655197570Sthompsaumct_poll(struct ucom_softc *ucom) 656197570Sthompsa{ 657197570Sthompsa struct umct_softc *sc = ucom->sc_parent; 658197570Sthompsa usbd_transfer_poll(sc->sc_xfer, UMCT_N_TRANSFER); 659197570Sthompsa} 660