uslcom.c revision 214761
1188413Sthompsa/* $OpenBSD: uslcom.c,v 1.17 2007/11/24 10:52:12 jsg Exp $ */ 2188413Sthompsa 3188413Sthompsa#include <sys/cdefs.h> 4188413Sthompsa__FBSDID("$FreeBSD: head/sys/dev/usb/serial/uslcom.c 214761 2010-11-03 21:50:49Z n_hibma $"); 5188413Sthompsa 6188413Sthompsa/* 7188413Sthompsa * Copyright (c) 2006 Jonathan Gray <jsg@openbsd.org> 8188413Sthompsa * 9188413Sthompsa * Permission to use, copy, modify, and distribute this software for any 10188413Sthompsa * purpose with or without fee is hereby granted, provided that the above 11188413Sthompsa * copyright notice and this permission notice appear in all copies. 12188413Sthompsa * 13188413Sthompsa * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 14188413Sthompsa * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 15188413Sthompsa * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 16188413Sthompsa * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 17188413Sthompsa * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 18188413Sthompsa * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 19188413Sthompsa * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 20188413Sthompsa */ 21188413Sthompsa 22194677Sthompsa#include <sys/stdint.h> 23194677Sthompsa#include <sys/stddef.h> 24194677Sthompsa#include <sys/param.h> 25194677Sthompsa#include <sys/queue.h> 26194677Sthompsa#include <sys/types.h> 27194677Sthompsa#include <sys/systm.h> 28194677Sthompsa#include <sys/kernel.h> 29194677Sthompsa#include <sys/bus.h> 30194677Sthompsa#include <sys/linker_set.h> 31194677Sthompsa#include <sys/module.h> 32194677Sthompsa#include <sys/lock.h> 33194677Sthompsa#include <sys/mutex.h> 34194677Sthompsa#include <sys/condvar.h> 35194677Sthompsa#include <sys/sysctl.h> 36194677Sthompsa#include <sys/sx.h> 37194677Sthompsa#include <sys/unistd.h> 38194677Sthompsa#include <sys/callout.h> 39194677Sthompsa#include <sys/malloc.h> 40194677Sthompsa#include <sys/priv.h> 41194677Sthompsa 42194677Sthompsa#include <dev/usb/usb.h> 43194677Sthompsa#include <dev/usb/usbdi.h> 44194677Sthompsa#include <dev/usb/usbdi_util.h> 45188746Sthompsa#include "usbdevs.h" 46188413Sthompsa 47188413Sthompsa#define USB_DEBUG_VAR uslcom_debug 48188942Sthompsa#include <dev/usb/usb_debug.h> 49188942Sthompsa#include <dev/usb/usb_process.h> 50188413Sthompsa 51188942Sthompsa#include <dev/usb/serial/usb_serial.h> 52188413Sthompsa 53207077Sthompsa#ifdef USB_DEBUG 54188413Sthompsastatic int uslcom_debug = 0; 55188413Sthompsa 56192502SthompsaSYSCTL_NODE(_hw_usb, OID_AUTO, uslcom, CTLFLAG_RW, 0, "USB uslcom"); 57192502SthompsaSYSCTL_INT(_hw_usb_uslcom, OID_AUTO, debug, CTLFLAG_RW, 58188413Sthompsa &uslcom_debug, 0, "Debug level"); 59188413Sthompsa#endif 60188413Sthompsa 61188413Sthompsa#define USLCOM_BULK_BUF_SIZE 1024 62188413Sthompsa#define USLCOM_CONFIG_INDEX 0 63188413Sthompsa#define USLCOM_IFACE_INDEX 0 64188413Sthompsa 65188413Sthompsa#define USLCOM_SET_DATA_BITS(x) ((x) << 8) 66188413Sthompsa 67188413Sthompsa#define USLCOM_WRITE 0x41 68188413Sthompsa#define USLCOM_READ 0xc1 69188413Sthompsa 70188413Sthompsa#define USLCOM_UART 0x00 71188413Sthompsa#define USLCOM_BAUD_RATE 0x01 72188413Sthompsa#define USLCOM_DATA 0x03 73188413Sthompsa#define USLCOM_BREAK 0x05 74188413Sthompsa#define USLCOM_CTRL 0x07 75188413Sthompsa 76188413Sthompsa#define USLCOM_UART_DISABLE 0x00 77188413Sthompsa#define USLCOM_UART_ENABLE 0x01 78188413Sthompsa 79188413Sthompsa#define USLCOM_CTRL_DTR_ON 0x0001 80188413Sthompsa#define USLCOM_CTRL_DTR_SET 0x0100 81188413Sthompsa#define USLCOM_CTRL_RTS_ON 0x0002 82188413Sthompsa#define USLCOM_CTRL_RTS_SET 0x0200 83188413Sthompsa#define USLCOM_CTRL_CTS 0x0010 84188413Sthompsa#define USLCOM_CTRL_DSR 0x0020 85188413Sthompsa#define USLCOM_CTRL_DCD 0x0080 86188413Sthompsa 87188413Sthompsa#define USLCOM_BAUD_REF 0x384000 88188413Sthompsa 89188413Sthompsa#define USLCOM_STOP_BITS_1 0x00 90188413Sthompsa#define USLCOM_STOP_BITS_2 0x02 91188413Sthompsa 92188413Sthompsa#define USLCOM_PARITY_NONE 0x00 93188413Sthompsa#define USLCOM_PARITY_ODD 0x10 94188413Sthompsa#define USLCOM_PARITY_EVEN 0x20 95188413Sthompsa 96188413Sthompsa#define USLCOM_PORT_NO 0xFFFF /* XXX think this should be 0 --hps */ 97188413Sthompsa 98188413Sthompsa#define USLCOM_BREAK_OFF 0x00 99188413Sthompsa#define USLCOM_BREAK_ON 0x01 100188413Sthompsa 101188413Sthompsaenum { 102188413Sthompsa USLCOM_BULK_DT_WR, 103188413Sthompsa USLCOM_BULK_DT_RD, 104188413Sthompsa USLCOM_N_TRANSFER, 105188413Sthompsa}; 106188413Sthompsa 107188413Sthompsastruct uslcom_softc { 108192984Sthompsa struct ucom_super_softc sc_super_ucom; 109192984Sthompsa struct ucom_softc sc_ucom; 110188413Sthompsa 111192984Sthompsa struct usb_xfer *sc_xfer[USLCOM_N_TRANSFER]; 112192984Sthompsa struct usb_device *sc_udev; 113189265Sthompsa struct mtx sc_mtx; 114188413Sthompsa 115188413Sthompsa uint8_t sc_msr; 116188413Sthompsa uint8_t sc_lsr; 117188413Sthompsa}; 118188413Sthompsa 119188413Sthompsastatic device_probe_t uslcom_probe; 120188413Sthompsastatic device_attach_t uslcom_attach; 121188413Sthompsastatic device_detach_t uslcom_detach; 122188413Sthompsa 123193045Sthompsastatic usb_callback_t uslcom_write_callback; 124193045Sthompsastatic usb_callback_t uslcom_read_callback; 125188413Sthompsa 126192984Sthompsastatic void uslcom_open(struct ucom_softc *); 127192984Sthompsastatic void uslcom_close(struct ucom_softc *); 128192984Sthompsastatic void uslcom_set_dtr(struct ucom_softc *, uint8_t); 129192984Sthompsastatic void uslcom_set_rts(struct ucom_softc *, uint8_t); 130192984Sthompsastatic void uslcom_set_break(struct ucom_softc *, uint8_t); 131192984Sthompsastatic int uslcom_pre_param(struct ucom_softc *, struct termios *); 132192984Sthompsastatic void uslcom_param(struct ucom_softc *, struct termios *); 133192984Sthompsastatic void uslcom_get_status(struct ucom_softc *, uint8_t *, uint8_t *); 134192984Sthompsastatic void uslcom_start_read(struct ucom_softc *); 135192984Sthompsastatic void uslcom_stop_read(struct ucom_softc *); 136192984Sthompsastatic void uslcom_start_write(struct ucom_softc *); 137192984Sthompsastatic void uslcom_stop_write(struct ucom_softc *); 138197570Sthompsastatic void uslcom_poll(struct ucom_softc *ucom); 139188413Sthompsa 140192984Sthompsastatic const struct usb_config uslcom_config[USLCOM_N_TRANSFER] = { 141188413Sthompsa 142188413Sthompsa [USLCOM_BULK_DT_WR] = { 143188413Sthompsa .type = UE_BULK, 144188413Sthompsa .endpoint = UE_ADDR_ANY, 145188413Sthompsa .direction = UE_DIR_OUT, 146190734Sthompsa .bufsize = USLCOM_BULK_BUF_SIZE, 147190734Sthompsa .flags = {.pipe_bof = 1,.force_short_xfer = 1,}, 148190734Sthompsa .callback = &uslcom_write_callback, 149188413Sthompsa }, 150188413Sthompsa 151188413Sthompsa [USLCOM_BULK_DT_RD] = { 152188413Sthompsa .type = UE_BULK, 153188413Sthompsa .endpoint = UE_ADDR_ANY, 154188413Sthompsa .direction = UE_DIR_IN, 155190734Sthompsa .bufsize = USLCOM_BULK_BUF_SIZE, 156190734Sthompsa .flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, 157190734Sthompsa .callback = &uslcom_read_callback, 158188413Sthompsa }, 159188413Sthompsa}; 160188413Sthompsa 161194099Sthompsastatic struct ucom_callback uslcom_callback = { 162194228Sthompsa .ucom_cfg_open = &uslcom_open, 163194228Sthompsa .ucom_cfg_close = &uslcom_close, 164194228Sthompsa .ucom_cfg_get_status = &uslcom_get_status, 165194228Sthompsa .ucom_cfg_set_dtr = &uslcom_set_dtr, 166194228Sthompsa .ucom_cfg_set_rts = &uslcom_set_rts, 167194228Sthompsa .ucom_cfg_set_break = &uslcom_set_break, 168194228Sthompsa .ucom_cfg_param = &uslcom_param, 169194228Sthompsa .ucom_pre_param = &uslcom_pre_param, 170194228Sthompsa .ucom_start_read = &uslcom_start_read, 171194228Sthompsa .ucom_stop_read = &uslcom_stop_read, 172194228Sthompsa .ucom_start_write = &uslcom_start_write, 173194228Sthompsa .ucom_stop_write = &uslcom_stop_write, 174197570Sthompsa .ucom_poll = &uslcom_poll, 175188413Sthompsa}; 176188413Sthompsa 177192984Sthompsastatic const struct usb_device_id uslcom_devs[] = { 178201028Sthompsa#define USLCOM_DEV(v,p) { USB_VP(USB_VENDOR_##v, USB_PRODUCT_##v##_##p) } 179201028Sthompsa USLCOM_DEV(BALTECH, CARDREADER), 180210524Sgavin USLCOM_DEV(CLIPSAL, 5500PCU), 181211022Sgavin USLCOM_DEV(DATAAPEX, MULTICOM), 182211022Sgavin USLCOM_DEV(DELL, DW700), 183211022Sgavin USLCOM_DEV(DIGIANSWER, ZIGBEE802154), 184201028Sthompsa USLCOM_DEV(DYNASTREAM, ANTDEVBOARD), 185211022Sgavin USLCOM_DEV(DYNASTREAM, ANTDEVBOARD2), 186210524Sgavin USLCOM_DEV(DYNASTREAM, ANT2USB), 187211022Sgavin USLCOM_DEV(ELV, USBI2C), 188211022Sgavin USLCOM_DEV(FOXCONN, PIRELLI_DP_L10), 189210524Sgavin USLCOM_DEV(GEMALTO, PROXPU), 190201028Sthompsa USLCOM_DEV(JABLOTRON, PC60B), 191211022Sgavin USLCOM_DEV(MEI, CASHFLOW_SC), 192211022Sgavin USLCOM_DEV(MEI, S2000), 193211022Sgavin USLCOM_DEV(JABLOTRON, PC60B), 194211022Sgavin USLCOM_DEV(OWEN, AC4), 195211022Sgavin USLCOM_DEV(PHILIPS, ACE1001), 196211022Sgavin USLCOM_DEV(PLX, CA42), 197210524Sgavin USLCOM_DEV(SILABS, AEROCOMM), 198211022Sgavin USLCOM_DEV(SILABS, AMBER_AMB2560), 199201028Sthompsa USLCOM_DEV(SILABS, ARGUSISP), 200211022Sgavin USLCOM_DEV(SILABS, ARKHAM_DS101_A), 201211022Sgavin USLCOM_DEV(SILABS, ARKHAM_DS101_M), 202211022Sgavin USLCOM_DEV(SILABS, ARYGON_MIFARE), 203211022Sgavin USLCOM_DEV(SILABS, AVIT_USB_TTL), 204211022Sgavin USLCOM_DEV(SILABS, BEI_VCP), 205210524Sgavin USLCOM_DEV(SILABS, BSM7DUSB), 206210524Sgavin USLCOM_DEV(SILABS, BURNSIDE), 207211022Sgavin USLCOM_DEV(SILABS, C2_EDGE_MODEM), 208210524Sgavin USLCOM_DEV(SILABS, CP2102), 209210524Sgavin USLCOM_DEV(SILABS, CP210X_2), 210201028Sthompsa USLCOM_DEV(SILABS, CRUMB128), 211211022Sgavin USLCOM_DEV(SILABS, CYGNAL), 212211022Sgavin USLCOM_DEV(SILABS, CYGNAL_DEBUG), 213211022Sgavin USLCOM_DEV(SILABS, CYGNAL_GPS), 214201028Sthompsa USLCOM_DEV(SILABS, DEGREE), 215211022Sgavin USLCOM_DEV(SILABS, EMS_C1007), 216201028Sthompsa USLCOM_DEV(SILABS, HELICOM), 217211022Sgavin USLCOM_DEV(SILABS, IMS_USB_RS422), 218211022Sgavin USLCOM_DEV(SILABS, INFINITY_MIC), 219211022Sgavin USLCOM_DEV(SILABS, INSYS_MODEM), 220211022Sgavin USLCOM_DEV(SILABS, KYOCERA_GPS), 221201028Sthompsa USLCOM_DEV(SILABS, LIPOWSKY_HARP), 222201028Sthompsa USLCOM_DEV(SILABS, LIPOWSKY_JTAG), 223201028Sthompsa USLCOM_DEV(SILABS, LIPOWSKY_LIN), 224210524Sgavin USLCOM_DEV(SILABS, MC35PU), 225211022Sgavin USLCOM_DEV(SILABS, MJS_TOSLINK), 226211022Sgavin USLCOM_DEV(SILABS, MSD_DASHHAWK), 227201028Sthompsa USLCOM_DEV(SILABS, POLOLU), 228211022Sgavin USLCOM_DEV(SILABS, PROCYON_AVS), 229211022Sgavin USLCOM_DEV(SILABS, SB_PARAMOUNT_ME), 230201028Sthompsa USLCOM_DEV(SILABS, SUUNTO), 231211022Sgavin USLCOM_DEV(SILABS, TAMSMASTER), 232211022Sgavin USLCOM_DEV(SILABS, TELEGESYS_ETRX2), 233210524Sgavin USLCOM_DEV(SILABS, TRACIENT), 234201028Sthompsa USLCOM_DEV(SILABS, TRAQMATE), 235210524Sgavin USLCOM_DEV(SILABS, USBCOUNT50), 236210524Sgavin USLCOM_DEV(SILABS, USBPULSE100), 237211022Sgavin USLCOM_DEV(SILABS, USBSCOPE50), 238210524Sgavin USLCOM_DEV(SILABS, USBWAVE12), 239211022Sgavin USLCOM_DEV(SILABS, VSTABI), 240211022Sgavin USLCOM_DEV(SILABS, WAVIT), 241211022Sgavin USLCOM_DEV(SILABS, WMRBATT), 242211022Sgavin USLCOM_DEV(SILABS, WMRRIGBLASTER), 243211022Sgavin USLCOM_DEV(SILABS, WMRRIGTALK), 244211022Sgavin USLCOM_DEV(SILABS, ZEPHYR_BIO), 245201028Sthompsa USLCOM_DEV(SILABS2, DCU11CLONE), 246210524Sgavin USLCOM_DEV(SILABS3, GPRS_MODEM), 247211022Sgavin USLCOM_DEV(SILABS4, 100EU_MODEM), 248211022Sgavin USLCOM_DEV(SYNTECH, CYPHERLAB100), 249201028Sthompsa USLCOM_DEV(USI, MC60), 250211022Sgavin USLCOM_DEV(VAISALA, CABLE), 251211022Sgavin USLCOM_DEV(WAVESENSE, JAZZ), 252201028Sthompsa#undef USLCOM_DEV 253188413Sthompsa}; 254188413Sthompsa 255188413Sthompsastatic device_method_t uslcom_methods[] = { 256188413Sthompsa DEVMETHOD(device_probe, uslcom_probe), 257188413Sthompsa DEVMETHOD(device_attach, uslcom_attach), 258188413Sthompsa DEVMETHOD(device_detach, uslcom_detach), 259188413Sthompsa {0, 0} 260188413Sthompsa}; 261188413Sthompsa 262188413Sthompsastatic devclass_t uslcom_devclass; 263188413Sthompsa 264188413Sthompsastatic driver_t uslcom_driver = { 265188664Sthompsa .name = "uslcom", 266188413Sthompsa .methods = uslcom_methods, 267188413Sthompsa .size = sizeof(struct uslcom_softc), 268188413Sthompsa}; 269188413Sthompsa 270189275SthompsaDRIVER_MODULE(uslcom, uhub, uslcom_driver, uslcom_devclass, NULL, 0); 271188942SthompsaMODULE_DEPEND(uslcom, ucom, 1, 1, 1); 272188942SthompsaMODULE_DEPEND(uslcom, usb, 1, 1, 1); 273188664SthompsaMODULE_VERSION(uslcom, 1); 274188413Sthompsa 275188413Sthompsastatic int 276188413Sthompsauslcom_probe(device_t dev) 277188413Sthompsa{ 278192984Sthompsa struct usb_attach_arg *uaa = device_get_ivars(dev); 279188413Sthompsa 280188413Sthompsa DPRINTFN(11, "\n"); 281188413Sthompsa 282192499Sthompsa if (uaa->usb_mode != USB_MODE_HOST) { 283188413Sthompsa return (ENXIO); 284188413Sthompsa } 285188413Sthompsa if (uaa->info.bConfigIndex != USLCOM_CONFIG_INDEX) { 286188413Sthompsa return (ENXIO); 287188413Sthompsa } 288188413Sthompsa if (uaa->info.bIfaceIndex != USLCOM_IFACE_INDEX) { 289188413Sthompsa return (ENXIO); 290188413Sthompsa } 291194228Sthompsa return (usbd_lookup_id_by_uaa(uslcom_devs, sizeof(uslcom_devs), uaa)); 292188413Sthompsa} 293188413Sthompsa 294188413Sthompsastatic int 295188413Sthompsauslcom_attach(device_t dev) 296188413Sthompsa{ 297192984Sthompsa struct usb_attach_arg *uaa = device_get_ivars(dev); 298188413Sthompsa struct uslcom_softc *sc = device_get_softc(dev); 299188413Sthompsa int error; 300188413Sthompsa 301188413Sthompsa DPRINTFN(11, "\n"); 302188413Sthompsa 303194228Sthompsa device_set_usb_desc(dev); 304189265Sthompsa mtx_init(&sc->sc_mtx, "uslcom", NULL, MTX_DEF); 305188413Sthompsa 306188413Sthompsa sc->sc_udev = uaa->device; 307188413Sthompsa 308194228Sthompsa error = usbd_transfer_setup(uaa->device, 309188413Sthompsa &uaa->info.bIfaceIndex, sc->sc_xfer, uslcom_config, 310189265Sthompsa USLCOM_N_TRANSFER, sc, &sc->sc_mtx); 311188413Sthompsa if (error) { 312188413Sthompsa DPRINTF("one or more missing USB endpoints, " 313194228Sthompsa "error=%s\n", usbd_errstr(error)); 314188413Sthompsa goto detach; 315188413Sthompsa } 316188413Sthompsa /* clear stall at first run */ 317189265Sthompsa mtx_lock(&sc->sc_mtx); 318194677Sthompsa usbd_xfer_set_stall(sc->sc_xfer[USLCOM_BULK_DT_WR]); 319194677Sthompsa usbd_xfer_set_stall(sc->sc_xfer[USLCOM_BULK_DT_RD]); 320189265Sthompsa mtx_unlock(&sc->sc_mtx); 321188413Sthompsa 322194228Sthompsa error = ucom_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc, 323189265Sthompsa &uslcom_callback, &sc->sc_mtx); 324188413Sthompsa if (error) { 325188413Sthompsa goto detach; 326188413Sthompsa } 327188413Sthompsa return (0); 328188413Sthompsa 329188413Sthompsadetach: 330188413Sthompsa uslcom_detach(dev); 331188413Sthompsa return (ENXIO); 332188413Sthompsa} 333188413Sthompsa 334188413Sthompsastatic int 335188413Sthompsauslcom_detach(device_t dev) 336188413Sthompsa{ 337188413Sthompsa struct uslcom_softc *sc = device_get_softc(dev); 338188413Sthompsa 339188413Sthompsa DPRINTF("sc=%p\n", sc); 340188413Sthompsa 341214761Sn_hibma ucom_detach(&sc->sc_super_ucom, &sc->sc_ucom); 342194228Sthompsa usbd_transfer_unsetup(sc->sc_xfer, USLCOM_N_TRANSFER); 343189265Sthompsa mtx_destroy(&sc->sc_mtx); 344188413Sthompsa 345188413Sthompsa return (0); 346188413Sthompsa} 347188413Sthompsa 348188413Sthompsastatic void 349192984Sthompsauslcom_open(struct ucom_softc *ucom) 350188413Sthompsa{ 351188413Sthompsa struct uslcom_softc *sc = ucom->sc_parent; 352192984Sthompsa struct usb_device_request req; 353188413Sthompsa 354188413Sthompsa req.bmRequestType = USLCOM_WRITE; 355188413Sthompsa req.bRequest = USLCOM_UART; 356188413Sthompsa USETW(req.wValue, USLCOM_UART_ENABLE); 357188413Sthompsa USETW(req.wIndex, USLCOM_PORT_NO); 358188413Sthompsa USETW(req.wLength, 0); 359188413Sthompsa 360194228Sthompsa if (ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 361188413Sthompsa &req, NULL, 0, 1000)) { 362188413Sthompsa DPRINTF("UART enable failed (ignored)\n"); 363188413Sthompsa } 364188413Sthompsa} 365188413Sthompsa 366188413Sthompsastatic void 367192984Sthompsauslcom_close(struct ucom_softc *ucom) 368188413Sthompsa{ 369188413Sthompsa struct uslcom_softc *sc = ucom->sc_parent; 370192984Sthompsa struct usb_device_request req; 371188413Sthompsa 372188413Sthompsa req.bmRequestType = USLCOM_WRITE; 373188413Sthompsa req.bRequest = USLCOM_UART; 374188413Sthompsa USETW(req.wValue, USLCOM_UART_DISABLE); 375188413Sthompsa USETW(req.wIndex, USLCOM_PORT_NO); 376188413Sthompsa USETW(req.wLength, 0); 377188413Sthompsa 378194228Sthompsa if (ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 379188413Sthompsa &req, NULL, 0, 1000)) { 380188413Sthompsa DPRINTF("UART disable failed (ignored)\n"); 381188413Sthompsa } 382188413Sthompsa} 383188413Sthompsa 384188413Sthompsastatic void 385192984Sthompsauslcom_set_dtr(struct ucom_softc *ucom, uint8_t onoff) 386188413Sthompsa{ 387188413Sthompsa struct uslcom_softc *sc = ucom->sc_parent; 388192984Sthompsa struct usb_device_request req; 389188413Sthompsa uint16_t ctl; 390188413Sthompsa 391188413Sthompsa DPRINTF("onoff = %d\n", onoff); 392188413Sthompsa 393188413Sthompsa ctl = onoff ? USLCOM_CTRL_DTR_ON : 0; 394188413Sthompsa ctl |= USLCOM_CTRL_DTR_SET; 395188413Sthompsa 396188413Sthompsa req.bmRequestType = USLCOM_WRITE; 397188413Sthompsa req.bRequest = USLCOM_CTRL; 398188413Sthompsa USETW(req.wValue, ctl); 399188413Sthompsa USETW(req.wIndex, USLCOM_PORT_NO); 400188413Sthompsa USETW(req.wLength, 0); 401188413Sthompsa 402194228Sthompsa if (ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 403188413Sthompsa &req, NULL, 0, 1000)) { 404188413Sthompsa DPRINTF("Setting DTR failed (ignored)\n"); 405188413Sthompsa } 406188413Sthompsa} 407188413Sthompsa 408188413Sthompsastatic void 409192984Sthompsauslcom_set_rts(struct ucom_softc *ucom, uint8_t onoff) 410188413Sthompsa{ 411188413Sthompsa struct uslcom_softc *sc = ucom->sc_parent; 412192984Sthompsa struct usb_device_request req; 413188413Sthompsa uint16_t ctl; 414188413Sthompsa 415188413Sthompsa DPRINTF("onoff = %d\n", onoff); 416188413Sthompsa 417188413Sthompsa ctl = onoff ? USLCOM_CTRL_RTS_ON : 0; 418188413Sthompsa ctl |= USLCOM_CTRL_RTS_SET; 419188413Sthompsa 420188413Sthompsa req.bmRequestType = USLCOM_WRITE; 421188413Sthompsa req.bRequest = USLCOM_CTRL; 422188413Sthompsa USETW(req.wValue, ctl); 423188413Sthompsa USETW(req.wIndex, USLCOM_PORT_NO); 424188413Sthompsa USETW(req.wLength, 0); 425188413Sthompsa 426194228Sthompsa if (ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 427188413Sthompsa &req, NULL, 0, 1000)) { 428188413Sthompsa DPRINTF("Setting DTR failed (ignored)\n"); 429188413Sthompsa } 430188413Sthompsa} 431188413Sthompsa 432188413Sthompsastatic int 433192984Sthompsauslcom_pre_param(struct ucom_softc *ucom, struct termios *t) 434188413Sthompsa{ 435188413Sthompsa if (t->c_ospeed <= 0 || t->c_ospeed > 921600) 436188413Sthompsa return (EINVAL); 437188413Sthompsa return (0); 438188413Sthompsa} 439188413Sthompsa 440188413Sthompsastatic void 441192984Sthompsauslcom_param(struct ucom_softc *ucom, struct termios *t) 442188413Sthompsa{ 443188413Sthompsa struct uslcom_softc *sc = ucom->sc_parent; 444192984Sthompsa struct usb_device_request req; 445188413Sthompsa uint16_t data; 446188413Sthompsa 447188413Sthompsa DPRINTF("\n"); 448188413Sthompsa 449188413Sthompsa req.bmRequestType = USLCOM_WRITE; 450188413Sthompsa req.bRequest = USLCOM_BAUD_RATE; 451188413Sthompsa USETW(req.wValue, USLCOM_BAUD_REF / t->c_ospeed); 452188413Sthompsa USETW(req.wIndex, USLCOM_PORT_NO); 453188413Sthompsa USETW(req.wLength, 0); 454188413Sthompsa 455194228Sthompsa if (ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 456188413Sthompsa &req, NULL, 0, 1000)) { 457188413Sthompsa DPRINTF("Set baudrate failed (ignored)\n"); 458188413Sthompsa } 459188413Sthompsa 460188413Sthompsa if (t->c_cflag & CSTOPB) 461188413Sthompsa data = USLCOM_STOP_BITS_2; 462188413Sthompsa else 463188413Sthompsa data = USLCOM_STOP_BITS_1; 464188413Sthompsa if (t->c_cflag & PARENB) { 465188413Sthompsa if (t->c_cflag & PARODD) 466188413Sthompsa data |= USLCOM_PARITY_ODD; 467188413Sthompsa else 468188413Sthompsa data |= USLCOM_PARITY_EVEN; 469188413Sthompsa } else 470188413Sthompsa data |= USLCOM_PARITY_NONE; 471188413Sthompsa switch (t->c_cflag & CSIZE) { 472188413Sthompsa case CS5: 473188413Sthompsa data |= USLCOM_SET_DATA_BITS(5); 474188413Sthompsa break; 475188413Sthompsa case CS6: 476188413Sthompsa data |= USLCOM_SET_DATA_BITS(6); 477188413Sthompsa break; 478188413Sthompsa case CS7: 479188413Sthompsa data |= USLCOM_SET_DATA_BITS(7); 480188413Sthompsa break; 481188413Sthompsa case CS8: 482188413Sthompsa data |= USLCOM_SET_DATA_BITS(8); 483188413Sthompsa break; 484188413Sthompsa } 485188413Sthompsa 486188413Sthompsa req.bmRequestType = USLCOM_WRITE; 487188413Sthompsa req.bRequest = USLCOM_DATA; 488188413Sthompsa USETW(req.wValue, data); 489188413Sthompsa USETW(req.wIndex, USLCOM_PORT_NO); 490188413Sthompsa USETW(req.wLength, 0); 491188413Sthompsa 492194228Sthompsa if (ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 493188413Sthompsa &req, NULL, 0, 1000)) { 494188413Sthompsa DPRINTF("Set format failed (ignored)\n"); 495188413Sthompsa } 496188413Sthompsa return; 497188413Sthompsa} 498188413Sthompsa 499188413Sthompsastatic void 500192984Sthompsauslcom_get_status(struct ucom_softc *ucom, uint8_t *lsr, uint8_t *msr) 501188413Sthompsa{ 502188413Sthompsa struct uslcom_softc *sc = ucom->sc_parent; 503188413Sthompsa 504188413Sthompsa DPRINTF("\n"); 505188413Sthompsa 506188413Sthompsa *lsr = sc->sc_lsr; 507188413Sthompsa *msr = sc->sc_msr; 508188413Sthompsa} 509188413Sthompsa 510188413Sthompsastatic void 511192984Sthompsauslcom_set_break(struct ucom_softc *ucom, uint8_t onoff) 512188413Sthompsa{ 513188413Sthompsa struct uslcom_softc *sc = ucom->sc_parent; 514192984Sthompsa struct usb_device_request req; 515188413Sthompsa uint16_t brk = onoff ? USLCOM_BREAK_ON : USLCOM_BREAK_OFF; 516188413Sthompsa 517188413Sthompsa req.bmRequestType = USLCOM_WRITE; 518188413Sthompsa req.bRequest = USLCOM_BREAK; 519188413Sthompsa USETW(req.wValue, brk); 520188413Sthompsa USETW(req.wIndex, USLCOM_PORT_NO); 521188413Sthompsa USETW(req.wLength, 0); 522188413Sthompsa 523194228Sthompsa if (ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 524188413Sthompsa &req, NULL, 0, 1000)) { 525188413Sthompsa DPRINTF("Set BREAK failed (ignored)\n"); 526188413Sthompsa } 527188413Sthompsa} 528188413Sthompsa 529188413Sthompsastatic void 530194677Sthompsauslcom_write_callback(struct usb_xfer *xfer, usb_error_t error) 531188413Sthompsa{ 532194677Sthompsa struct uslcom_softc *sc = usbd_xfer_softc(xfer); 533194677Sthompsa struct usb_page_cache *pc; 534188413Sthompsa uint32_t actlen; 535188413Sthompsa 536188413Sthompsa switch (USB_GET_STATE(xfer)) { 537188413Sthompsa case USB_ST_SETUP: 538188413Sthompsa case USB_ST_TRANSFERRED: 539188413Sthompsatr_setup: 540194677Sthompsa pc = usbd_xfer_get_frame(xfer, 0); 541194677Sthompsa if (ucom_get_data(&sc->sc_ucom, pc, 0, 542188413Sthompsa USLCOM_BULK_BUF_SIZE, &actlen)) { 543188413Sthompsa 544188413Sthompsa DPRINTF("actlen = %d\n", actlen); 545188413Sthompsa 546194677Sthompsa usbd_xfer_set_frame_len(xfer, 0, actlen); 547194228Sthompsa usbd_transfer_submit(xfer); 548188413Sthompsa } 549188413Sthompsa return; 550188413Sthompsa 551188413Sthompsa default: /* Error */ 552194677Sthompsa if (error != USB_ERR_CANCELLED) { 553188413Sthompsa /* try to clear stall first */ 554194677Sthompsa usbd_xfer_set_stall(xfer); 555188413Sthompsa goto tr_setup; 556188413Sthompsa } 557188413Sthompsa return; 558188413Sthompsa } 559188413Sthompsa} 560188413Sthompsa 561188413Sthompsastatic void 562194677Sthompsauslcom_read_callback(struct usb_xfer *xfer, usb_error_t error) 563188413Sthompsa{ 564194677Sthompsa struct uslcom_softc *sc = usbd_xfer_softc(xfer); 565194677Sthompsa struct usb_page_cache *pc; 566194677Sthompsa int actlen; 567188413Sthompsa 568194677Sthompsa usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); 569194677Sthompsa 570188413Sthompsa switch (USB_GET_STATE(xfer)) { 571188413Sthompsa case USB_ST_TRANSFERRED: 572194677Sthompsa pc = usbd_xfer_get_frame(xfer, 0); 573194677Sthompsa ucom_put_data(&sc->sc_ucom, pc, 0, actlen); 574188413Sthompsa 575188413Sthompsa case USB_ST_SETUP: 576188413Sthompsatr_setup: 577194677Sthompsa usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); 578194228Sthompsa usbd_transfer_submit(xfer); 579188413Sthompsa return; 580188413Sthompsa 581188413Sthompsa default: /* Error */ 582194677Sthompsa if (error != USB_ERR_CANCELLED) { 583188413Sthompsa /* try to clear stall first */ 584194677Sthompsa usbd_xfer_set_stall(xfer); 585188413Sthompsa goto tr_setup; 586188413Sthompsa } 587188413Sthompsa return; 588188413Sthompsa } 589188413Sthompsa} 590188413Sthompsa 591188413Sthompsastatic void 592192984Sthompsauslcom_start_read(struct ucom_softc *ucom) 593188413Sthompsa{ 594188413Sthompsa struct uslcom_softc *sc = ucom->sc_parent; 595188413Sthompsa 596188413Sthompsa /* start read endpoint */ 597194228Sthompsa usbd_transfer_start(sc->sc_xfer[USLCOM_BULK_DT_RD]); 598188413Sthompsa} 599188413Sthompsa 600188413Sthompsastatic void 601192984Sthompsauslcom_stop_read(struct ucom_softc *ucom) 602188413Sthompsa{ 603188413Sthompsa struct uslcom_softc *sc = ucom->sc_parent; 604188413Sthompsa 605188413Sthompsa /* stop read endpoint */ 606194228Sthompsa usbd_transfer_stop(sc->sc_xfer[USLCOM_BULK_DT_RD]); 607188413Sthompsa} 608188413Sthompsa 609188413Sthompsastatic void 610192984Sthompsauslcom_start_write(struct ucom_softc *ucom) 611188413Sthompsa{ 612188413Sthompsa struct uslcom_softc *sc = ucom->sc_parent; 613188413Sthompsa 614194228Sthompsa usbd_transfer_start(sc->sc_xfer[USLCOM_BULK_DT_WR]); 615188413Sthompsa} 616188413Sthompsa 617188413Sthompsastatic void 618192984Sthompsauslcom_stop_write(struct ucom_softc *ucom) 619188413Sthompsa{ 620188413Sthompsa struct uslcom_softc *sc = ucom->sc_parent; 621188413Sthompsa 622194228Sthompsa usbd_transfer_stop(sc->sc_xfer[USLCOM_BULK_DT_WR]); 623188413Sthompsa} 624197570Sthompsa 625197570Sthompsastatic void 626197570Sthompsauslcom_poll(struct ucom_softc *ucom) 627197570Sthompsa{ 628197570Sthompsa struct uslcom_softc *sc = ucom->sc_parent; 629197570Sthompsa usbd_transfer_poll(sc->sc_xfer, USLCOM_N_TRANSFER); 630197570Sthompsa} 631