1188413Sthompsa/* $OpenBSD: uslcom.c,v 1.17 2007/11/24 10:52:12 jsg Exp $ */ 2188413Sthompsa 3188413Sthompsa#include <sys/cdefs.h> 4188413Sthompsa__FBSDID("$FreeBSD: releng/11.0/sys/dev/usb/serial/uslcom.c 292080 2015-12-11 05:28:00Z imp $"); 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 22239260Sgavin/* 23239260Sgavin * Driver for Silicon Laboratories CP2101/CP2102/CP2103/CP2104/CP2105 24239260Sgavin * USB-Serial adapters. Based on datasheet AN571, publicly available from 25239260Sgavin * http://www.silabs.com/Support%20Documents/TechnicalDocs/AN571.pdf 26239260Sgavin */ 27239260Sgavin 28194677Sthompsa#include <sys/stdint.h> 29194677Sthompsa#include <sys/stddef.h> 30194677Sthompsa#include <sys/param.h> 31194677Sthompsa#include <sys/queue.h> 32194677Sthompsa#include <sys/types.h> 33194677Sthompsa#include <sys/systm.h> 34194677Sthompsa#include <sys/kernel.h> 35194677Sthompsa#include <sys/bus.h> 36194677Sthompsa#include <sys/module.h> 37194677Sthompsa#include <sys/lock.h> 38194677Sthompsa#include <sys/mutex.h> 39194677Sthompsa#include <sys/condvar.h> 40194677Sthompsa#include <sys/sysctl.h> 41194677Sthompsa#include <sys/sx.h> 42194677Sthompsa#include <sys/unistd.h> 43194677Sthompsa#include <sys/callout.h> 44194677Sthompsa#include <sys/malloc.h> 45194677Sthompsa#include <sys/priv.h> 46194677Sthompsa 47194677Sthompsa#include <dev/usb/usb.h> 48194677Sthompsa#include <dev/usb/usbdi.h> 49194677Sthompsa#include <dev/usb/usbdi_util.h> 50227463Shselasky#include <dev/usb/usb_ioctl.h> 51188746Sthompsa#include "usbdevs.h" 52188413Sthompsa 53188413Sthompsa#define USB_DEBUG_VAR uslcom_debug 54188942Sthompsa#include <dev/usb/usb_debug.h> 55188942Sthompsa#include <dev/usb/usb_process.h> 56188413Sthompsa 57188942Sthompsa#include <dev/usb/serial/usb_serial.h> 58188413Sthompsa 59207077Sthompsa#ifdef USB_DEBUG 60188413Sthompsastatic int uslcom_debug = 0; 61188413Sthompsa 62227309Sedstatic SYSCTL_NODE(_hw_usb, OID_AUTO, uslcom, CTLFLAG_RW, 0, "USB uslcom"); 63276701ShselaskySYSCTL_INT(_hw_usb_uslcom, OID_AUTO, debug, CTLFLAG_RWTUN, 64188413Sthompsa &uslcom_debug, 0, "Debug level"); 65188413Sthompsa#endif 66188413Sthompsa 67188413Sthompsa#define USLCOM_BULK_BUF_SIZE 1024 68188413Sthompsa#define USLCOM_CONFIG_INDEX 0 69188413Sthompsa 70227108Shselasky/* Request types */ 71188413Sthompsa#define USLCOM_WRITE 0x41 72188413Sthompsa#define USLCOM_READ 0xc1 73188413Sthompsa 74227108Shselasky/* Request codes */ 75239260Sgavin#define USLCOM_IFC_ENABLE 0x00 76239260Sgavin#define USLCOM_SET_BAUDDIV 0x01 77239260Sgavin#define USLCOM_SET_LINE_CTL 0x03 78239260Sgavin#define USLCOM_SET_BREAK 0x05 79239260Sgavin#define USLCOM_SET_MHS 0x07 80239260Sgavin#define USLCOM_GET_MDMSTS 0x08 81239260Sgavin#define USLCOM_SET_FLOW 0x13 82239260Sgavin#define USLCOM_SET_BAUDRATE 0x1e 83227463Shselasky#define USLCOM_VENDOR_SPECIFIC 0xff 84188413Sthompsa 85239260Sgavin/* USLCOM_IFC_ENABLE values */ 86239260Sgavin#define USLCOM_IFC_ENABLE_DIS 0x00 87239260Sgavin#define USLCOM_IFC_ENABLE_EN 0x01 88188413Sthompsa 89239260Sgavin/* USLCOM_SET_MHS/USLCOM_GET_MDMSTS values */ 90239260Sgavin#define USLCOM_MHS_DTR_ON 0x0001 91239260Sgavin#define USLCOM_MHS_DTR_SET 0x0100 92239260Sgavin#define USLCOM_MHS_RTS_ON 0x0002 93239260Sgavin#define USLCOM_MHS_RTS_SET 0x0200 94239260Sgavin#define USLCOM_MHS_CTS 0x0010 95239260Sgavin#define USLCOM_MHS_DSR 0x0020 96239260Sgavin#define USLCOM_MHS_RI 0x0040 97239260Sgavin#define USLCOM_MHS_DCD 0x0080 98188413Sthompsa 99239260Sgavin/* USLCOM_SET_BAUDDIV values */ 100239260Sgavin#define USLCOM_BAUDDIV_REF 3686400 /* 3.6864 MHz */ 101188413Sthompsa 102239260Sgavin/* USLCOM_SET_LINE_CTL values */ 103188413Sthompsa#define USLCOM_STOP_BITS_1 0x00 104188413Sthompsa#define USLCOM_STOP_BITS_2 0x02 105188413Sthompsa#define USLCOM_PARITY_NONE 0x00 106188413Sthompsa#define USLCOM_PARITY_ODD 0x10 107188413Sthompsa#define USLCOM_PARITY_EVEN 0x20 108239260Sgavin#define USLCOM_SET_DATA_BITS(x) ((x) << 8) 109188413Sthompsa 110239260Sgavin/* USLCOM_SET_BREAK values */ 111239260Sgavin#define USLCOM_SET_BREAK_OFF 0x00 112239260Sgavin#define USLCOM_SET_BREAK_ON 0x01 113188413Sthompsa 114239260Sgavin/* USLCOM_SET_FLOW values - 1st word */ 115227383Shselasky#define USLCOM_FLOW_DTR_ON 0x00000001 /* DTR static active */ 116227108Shselasky#define USLCOM_FLOW_CTS_HS 0x00000008 /* CTS handshake */ 117239260Sgavin/* USLCOM_SET_FLOW values - 2nd word */ 118227383Shselasky#define USLCOM_FLOW_RTS_ON 0x00000040 /* RTS static active */ 119227108Shselasky#define USLCOM_FLOW_RTS_HS 0x00000080 /* RTS handshake */ 120227108Shselasky 121227463Shselasky/* USLCOM_VENDOR_SPECIFIC values */ 122250749Sgavin#define USLCOM_GET_PARTNUM 0x370B 123227463Shselasky#define USLCOM_WRITE_LATCH 0x37E1 124227463Shselasky#define USLCOM_READ_LATCH 0x00C2 125227463Shselasky 126250749Sgavin/* USLCOM_GET_PARTNUM values from hardware */ 127250749Sgavin#define USLCOM_PARTNUM_CP2101 1 128250749Sgavin#define USLCOM_PARTNUM_CP2102 2 129250749Sgavin#define USLCOM_PARTNUM_CP2103 3 130250749Sgavin#define USLCOM_PARTNUM_CP2104 4 131250749Sgavin#define USLCOM_PARTNUM_CP2105 5 132250749Sgavin 133188413Sthompsaenum { 134188413Sthompsa USLCOM_BULK_DT_WR, 135188413Sthompsa USLCOM_BULK_DT_RD, 136227108Shselasky USLCOM_CTRL_DT_RD, 137188413Sthompsa USLCOM_N_TRANSFER, 138188413Sthompsa}; 139188413Sthompsa 140188413Sthompsastruct uslcom_softc { 141192984Sthompsa struct ucom_super_softc sc_super_ucom; 142192984Sthompsa struct ucom_softc sc_ucom; 143227463Shselasky struct usb_callout sc_watchdog; 144188413Sthompsa 145192984Sthompsa struct usb_xfer *sc_xfer[USLCOM_N_TRANSFER]; 146192984Sthompsa struct usb_device *sc_udev; 147189265Sthompsa struct mtx sc_mtx; 148188413Sthompsa 149188413Sthompsa uint8_t sc_msr; 150188413Sthompsa uint8_t sc_lsr; 151239050Shselasky uint8_t sc_iface_no; 152250749Sgavin uint8_t sc_partnum; 153188413Sthompsa}; 154188413Sthompsa 155188413Sthompsastatic device_probe_t uslcom_probe; 156188413Sthompsastatic device_attach_t uslcom_attach; 157188413Sthompsastatic device_detach_t uslcom_detach; 158239299Shselaskystatic void uslcom_free_softc(struct uslcom_softc *); 159188413Sthompsa 160193045Sthompsastatic usb_callback_t uslcom_write_callback; 161193045Sthompsastatic usb_callback_t uslcom_read_callback; 162227108Shselaskystatic usb_callback_t uslcom_control_callback; 163188413Sthompsa 164239180Shselaskystatic void uslcom_free(struct ucom_softc *); 165192984Sthompsastatic void uslcom_open(struct ucom_softc *); 166192984Sthompsastatic void uslcom_close(struct ucom_softc *); 167250749Sgavinstatic uint8_t uslcom_get_partnum(struct uslcom_softc *); 168192984Sthompsastatic void uslcom_set_dtr(struct ucom_softc *, uint8_t); 169192984Sthompsastatic void uslcom_set_rts(struct ucom_softc *, uint8_t); 170192984Sthompsastatic void uslcom_set_break(struct ucom_softc *, uint8_t); 171227463Shselaskystatic int uslcom_ioctl(struct ucom_softc *, uint32_t, caddr_t, int, 172227463Shselasky struct thread *); 173192984Sthompsastatic int uslcom_pre_param(struct ucom_softc *, struct termios *); 174192984Sthompsastatic void uslcom_param(struct ucom_softc *, struct termios *); 175192984Sthompsastatic void uslcom_get_status(struct ucom_softc *, uint8_t *, uint8_t *); 176192984Sthompsastatic void uslcom_start_read(struct ucom_softc *); 177192984Sthompsastatic void uslcom_stop_read(struct ucom_softc *); 178192984Sthompsastatic void uslcom_start_write(struct ucom_softc *); 179192984Sthompsastatic void uslcom_stop_write(struct ucom_softc *); 180197570Sthompsastatic void uslcom_poll(struct ucom_softc *ucom); 181188413Sthompsa 182192984Sthompsastatic const struct usb_config uslcom_config[USLCOM_N_TRANSFER] = { 183188413Sthompsa 184188413Sthompsa [USLCOM_BULK_DT_WR] = { 185188413Sthompsa .type = UE_BULK, 186188413Sthompsa .endpoint = UE_ADDR_ANY, 187188413Sthompsa .direction = UE_DIR_OUT, 188190734Sthompsa .bufsize = USLCOM_BULK_BUF_SIZE, 189227108Shselasky .flags = {.pipe_bof = 1,}, 190190734Sthompsa .callback = &uslcom_write_callback, 191188413Sthompsa }, 192188413Sthompsa 193188413Sthompsa [USLCOM_BULK_DT_RD] = { 194188413Sthompsa .type = UE_BULK, 195188413Sthompsa .endpoint = UE_ADDR_ANY, 196188413Sthompsa .direction = UE_DIR_IN, 197190734Sthompsa .bufsize = USLCOM_BULK_BUF_SIZE, 198190734Sthompsa .flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, 199190734Sthompsa .callback = &uslcom_read_callback, 200188413Sthompsa }, 201227108Shselasky [USLCOM_CTRL_DT_RD] = { 202227108Shselasky .type = UE_CONTROL, 203227108Shselasky .endpoint = 0x00, 204227108Shselasky .direction = UE_DIR_ANY, 205227108Shselasky .bufsize = sizeof(struct usb_device_request) + 8, 206227108Shselasky .flags = {.pipe_bof = 1,}, 207227108Shselasky .callback = &uslcom_control_callback, 208227108Shselasky .timeout = 1000, /* 1 second timeout */ 209227108Shselasky }, 210188413Sthompsa}; 211188413Sthompsa 212194099Sthompsastatic struct ucom_callback uslcom_callback = { 213194228Sthompsa .ucom_cfg_open = &uslcom_open, 214194228Sthompsa .ucom_cfg_close = &uslcom_close, 215194228Sthompsa .ucom_cfg_get_status = &uslcom_get_status, 216194228Sthompsa .ucom_cfg_set_dtr = &uslcom_set_dtr, 217194228Sthompsa .ucom_cfg_set_rts = &uslcom_set_rts, 218194228Sthompsa .ucom_cfg_set_break = &uslcom_set_break, 219227463Shselasky .ucom_ioctl = &uslcom_ioctl, 220194228Sthompsa .ucom_cfg_param = &uslcom_param, 221194228Sthompsa .ucom_pre_param = &uslcom_pre_param, 222194228Sthompsa .ucom_start_read = &uslcom_start_read, 223194228Sthompsa .ucom_stop_read = &uslcom_stop_read, 224194228Sthompsa .ucom_start_write = &uslcom_start_write, 225194228Sthompsa .ucom_stop_write = &uslcom_stop_write, 226197570Sthompsa .ucom_poll = &uslcom_poll, 227239180Shselasky .ucom_free = &uslcom_free, 228188413Sthompsa}; 229188413Sthompsa 230223486Shselaskystatic const STRUCT_USB_HOST_ID uslcom_devs[] = { 231201028Sthompsa#define USLCOM_DEV(v,p) { USB_VP(USB_VENDOR_##v, USB_PRODUCT_##v##_##p) } 232201028Sthompsa USLCOM_DEV(BALTECH, CARDREADER), 233238803Sgavin USLCOM_DEV(CLIPSAL, 5000CT2), 234238803Sgavin USLCOM_DEV(CLIPSAL, 5500PACA), 235210524Sgavin USLCOM_DEV(CLIPSAL, 5500PCU), 236238803Sgavin USLCOM_DEV(CLIPSAL, 560884), 237238803Sgavin USLCOM_DEV(CLIPSAL, 5800PC), 238238803Sgavin USLCOM_DEV(CLIPSAL, C5000CT2), 239238803Sgavin USLCOM_DEV(CLIPSAL, L51xx), 240211022Sgavin USLCOM_DEV(DATAAPEX, MULTICOM), 241211022Sgavin USLCOM_DEV(DELL, DW700), 242211022Sgavin USLCOM_DEV(DIGIANSWER, ZIGBEE802154), 243201028Sthompsa USLCOM_DEV(DYNASTREAM, ANTDEVBOARD), 244211022Sgavin USLCOM_DEV(DYNASTREAM, ANTDEVBOARD2), 245210524Sgavin USLCOM_DEV(DYNASTREAM, ANT2USB), 246211022Sgavin USLCOM_DEV(ELV, USBI2C), 247238803Sgavin USLCOM_DEV(FESTO, CMSP), 248238803Sgavin USLCOM_DEV(FESTO, CPX_USB), 249211022Sgavin USLCOM_DEV(FOXCONN, PIRELLI_DP_L10), 250217200Sgavin USLCOM_DEV(FOXCONN, TCOM_TC_300), 251210524Sgavin USLCOM_DEV(GEMALTO, PROXPU), 252201028Sthompsa USLCOM_DEV(JABLOTRON, PC60B), 253238803Sgavin USLCOM_DEV(KAMSTRUP, OPTICALEYE), 254238803Sgavin USLCOM_DEV(KAMSTRUP, MBUS_250D), 255256782Sgavin USLCOM_DEV(LAKESHORE, 121), 256256782Sgavin USLCOM_DEV(LAKESHORE, 218A), 257256782Sgavin USLCOM_DEV(LAKESHORE, 219), 258256782Sgavin USLCOM_DEV(LAKESHORE, 233), 259256782Sgavin USLCOM_DEV(LAKESHORE, 235), 260256782Sgavin USLCOM_DEV(LAKESHORE, 335), 261256782Sgavin USLCOM_DEV(LAKESHORE, 336), 262256782Sgavin USLCOM_DEV(LAKESHORE, 350), 263256782Sgavin USLCOM_DEV(LAKESHORE, 371), 264256782Sgavin USLCOM_DEV(LAKESHORE, 411), 265256782Sgavin USLCOM_DEV(LAKESHORE, 425), 266256782Sgavin USLCOM_DEV(LAKESHORE, 455A), 267256782Sgavin USLCOM_DEV(LAKESHORE, 465), 268256782Sgavin USLCOM_DEV(LAKESHORE, 475A), 269256782Sgavin USLCOM_DEV(LAKESHORE, 625A), 270256782Sgavin USLCOM_DEV(LAKESHORE, 642A), 271256782Sgavin USLCOM_DEV(LAKESHORE, 648), 272256782Sgavin USLCOM_DEV(LAKESHORE, 737), 273256782Sgavin USLCOM_DEV(LAKESHORE, 776), 274238803Sgavin USLCOM_DEV(LINKINSTRUMENTS, MSO19), 275238803Sgavin USLCOM_DEV(LINKINSTRUMENTS, MSO28), 276238803Sgavin USLCOM_DEV(LINKINSTRUMENTS, MSO28_2), 277211022Sgavin USLCOM_DEV(MEI, CASHFLOW_SC), 278211022Sgavin USLCOM_DEV(MEI, S2000), 279256782Sgavin USLCOM_DEV(NETGEAR, M4100), 280211022Sgavin USLCOM_DEV(OWEN, AC4), 281256782Sgavin USLCOM_DEV(OWL, CM_160), 282211022Sgavin USLCOM_DEV(PHILIPS, ACE1001), 283211022Sgavin USLCOM_DEV(PLX, CA42), 284217200Sgavin USLCOM_DEV(RENESAS, RX610), 285256782Sgavin USLCOM_DEV(SEL, C662), 286238803Sgavin USLCOM_DEV(SILABS, AC_SERV_CAN), 287238803Sgavin USLCOM_DEV(SILABS, AC_SERV_CIS), 288238803Sgavin USLCOM_DEV(SILABS, AC_SERV_IBUS), 289238803Sgavin USLCOM_DEV(SILABS, AC_SERV_OBD), 290210524Sgavin USLCOM_DEV(SILABS, AEROCOMM), 291211022Sgavin USLCOM_DEV(SILABS, AMBER_AMB2560), 292201028Sthompsa USLCOM_DEV(SILABS, ARGUSISP), 293211022Sgavin USLCOM_DEV(SILABS, ARKHAM_DS101_A), 294211022Sgavin USLCOM_DEV(SILABS, ARKHAM_DS101_M), 295211022Sgavin USLCOM_DEV(SILABS, ARYGON_MIFARE), 296211022Sgavin USLCOM_DEV(SILABS, AVIT_USB_TTL), 297217200Sgavin USLCOM_DEV(SILABS, B_G_H3000), 298217200Sgavin USLCOM_DEV(SILABS, BALLUFF_RFID), 299211022Sgavin USLCOM_DEV(SILABS, BEI_VCP), 300210524Sgavin USLCOM_DEV(SILABS, BSM7DUSB), 301210524Sgavin USLCOM_DEV(SILABS, BURNSIDE), 302211022Sgavin USLCOM_DEV(SILABS, C2_EDGE_MODEM), 303210524Sgavin USLCOM_DEV(SILABS, CP2102), 304210524Sgavin USLCOM_DEV(SILABS, CP210X_2), 305238803Sgavin USLCOM_DEV(SILABS, CP210X_3), 306238803Sgavin USLCOM_DEV(SILABS, CP210X_4), 307201028Sthompsa USLCOM_DEV(SILABS, CRUMB128), 308211022Sgavin USLCOM_DEV(SILABS, CYGNAL), 309211022Sgavin USLCOM_DEV(SILABS, CYGNAL_DEBUG), 310211022Sgavin USLCOM_DEV(SILABS, CYGNAL_GPS), 311201028Sthompsa USLCOM_DEV(SILABS, DEGREE), 312238803Sgavin USLCOM_DEV(SILABS, DEKTEK_DTAPLUS), 313211022Sgavin USLCOM_DEV(SILABS, EMS_C1007), 314238803Sgavin USLCOM_DEV(SILABS, HAMLINKUSB), 315201028Sthompsa USLCOM_DEV(SILABS, HELICOM), 316211022Sgavin USLCOM_DEV(SILABS, IMS_USB_RS422), 317211022Sgavin USLCOM_DEV(SILABS, INFINITY_MIC), 318256782Sgavin USLCOM_DEV(SILABS, INGENI_ZIGBEE), 319211022Sgavin USLCOM_DEV(SILABS, INSYS_MODEM), 320238803Sgavin USLCOM_DEV(SILABS, IRZ_SG10), 321211022Sgavin USLCOM_DEV(SILABS, KYOCERA_GPS), 322201028Sthompsa USLCOM_DEV(SILABS, LIPOWSKY_HARP), 323201028Sthompsa USLCOM_DEV(SILABS, LIPOWSKY_JTAG), 324201028Sthompsa USLCOM_DEV(SILABS, LIPOWSKY_LIN), 325210524Sgavin USLCOM_DEV(SILABS, MC35PU), 326256782Sgavin USLCOM_DEV(SILABS, MMB_ZIGBEE), 327211022Sgavin USLCOM_DEV(SILABS, MJS_TOSLINK), 328211022Sgavin USLCOM_DEV(SILABS, MSD_DASHHAWK), 329238803Sgavin USLCOM_DEV(SILABS, MULTIPLEX_RC), 330238803Sgavin USLCOM_DEV(SILABS, OPTRIS_MSPRO), 331201028Sthompsa USLCOM_DEV(SILABS, POLOLU), 332211022Sgavin USLCOM_DEV(SILABS, PROCYON_AVS), 333211022Sgavin USLCOM_DEV(SILABS, SB_PARAMOUNT_ME), 334201028Sthompsa USLCOM_DEV(SILABS, SUUNTO), 335211022Sgavin USLCOM_DEV(SILABS, TAMSMASTER), 336238804Sgavin USLCOM_DEV(SILABS, TELEGESIS_ETRX2), 337210524Sgavin USLCOM_DEV(SILABS, TRACIENT), 338201028Sthompsa USLCOM_DEV(SILABS, TRAQMATE), 339210524Sgavin USLCOM_DEV(SILABS, USBCOUNT50), 340210524Sgavin USLCOM_DEV(SILABS, USBPULSE100), 341211022Sgavin USLCOM_DEV(SILABS, USBSCOPE50), 342210524Sgavin USLCOM_DEV(SILABS, USBWAVE12), 343256782Sgavin USLCOM_DEV(SILABS, V_PREON32), 344211022Sgavin USLCOM_DEV(SILABS, VSTABI), 345211022Sgavin USLCOM_DEV(SILABS, WAVIT), 346211022Sgavin USLCOM_DEV(SILABS, WMRBATT), 347211022Sgavin USLCOM_DEV(SILABS, WMRRIGBLASTER), 348211022Sgavin USLCOM_DEV(SILABS, WMRRIGTALK), 349211022Sgavin USLCOM_DEV(SILABS, ZEPHYR_BIO), 350201028Sthompsa USLCOM_DEV(SILABS2, DCU11CLONE), 351210524Sgavin USLCOM_DEV(SILABS3, GPRS_MODEM), 352211022Sgavin USLCOM_DEV(SILABS4, 100EU_MODEM), 353211022Sgavin USLCOM_DEV(SYNTECH, CYPHERLAB100), 354201028Sthompsa USLCOM_DEV(USI, MC60), 355211022Sgavin USLCOM_DEV(VAISALA, CABLE), 356217200Sgavin USLCOM_DEV(WAGO, SERVICECABLE), 357211022Sgavin USLCOM_DEV(WAVESENSE, JAZZ), 358256782Sgavin USLCOM_DEV(WESTMOUNTAIN, RIGBLASTER_ADVANTAGE), 359217200Sgavin USLCOM_DEV(WIENERPLEINBAUS, PL512), 360217200Sgavin USLCOM_DEV(WIENERPLEINBAUS, RCM), 361217200Sgavin USLCOM_DEV(WIENERPLEINBAUS, MPOD), 362217200Sgavin USLCOM_DEV(WIENERPLEINBAUS, CML), 363201028Sthompsa#undef USLCOM_DEV 364188413Sthompsa}; 365188413Sthompsa 366188413Sthompsastatic device_method_t uslcom_methods[] = { 367188413Sthompsa DEVMETHOD(device_probe, uslcom_probe), 368188413Sthompsa DEVMETHOD(device_attach, uslcom_attach), 369188413Sthompsa DEVMETHOD(device_detach, uslcom_detach), 370239180Shselasky DEVMETHOD_END 371188413Sthompsa}; 372188413Sthompsa 373188413Sthompsastatic devclass_t uslcom_devclass; 374188413Sthompsa 375188413Sthompsastatic driver_t uslcom_driver = { 376188664Sthompsa .name = "uslcom", 377188413Sthompsa .methods = uslcom_methods, 378188413Sthompsa .size = sizeof(struct uslcom_softc), 379188413Sthompsa}; 380188413Sthompsa 381189275SthompsaDRIVER_MODULE(uslcom, uhub, uslcom_driver, uslcom_devclass, NULL, 0); 382188942SthompsaMODULE_DEPEND(uslcom, ucom, 1, 1, 1); 383188942SthompsaMODULE_DEPEND(uslcom, usb, 1, 1, 1); 384188664SthompsaMODULE_VERSION(uslcom, 1); 385292080SimpUSB_PNP_HOST_INFO(uslcom_devs); 386188413Sthompsa 387227463Shselaskystatic void 388227463Shselaskyuslcom_watchdog(void *arg) 389227463Shselasky{ 390227463Shselasky struct uslcom_softc *sc = arg; 391227463Shselasky 392227463Shselasky mtx_assert(&sc->sc_mtx, MA_OWNED); 393227463Shselasky 394227463Shselasky usbd_transfer_start(sc->sc_xfer[USLCOM_CTRL_DT_RD]); 395227463Shselasky 396227463Shselasky usb_callout_reset(&sc->sc_watchdog, 397227463Shselasky hz / 4, &uslcom_watchdog, sc); 398227463Shselasky} 399227463Shselasky 400188413Sthompsastatic int 401188413Sthompsauslcom_probe(device_t dev) 402188413Sthompsa{ 403192984Sthompsa struct usb_attach_arg *uaa = device_get_ivars(dev); 404188413Sthompsa 405188413Sthompsa DPRINTFN(11, "\n"); 406188413Sthompsa 407192499Sthompsa if (uaa->usb_mode != USB_MODE_HOST) { 408188413Sthompsa return (ENXIO); 409188413Sthompsa } 410188413Sthompsa if (uaa->info.bConfigIndex != USLCOM_CONFIG_INDEX) { 411188413Sthompsa return (ENXIO); 412188413Sthompsa } 413194228Sthompsa return (usbd_lookup_id_by_uaa(uslcom_devs, sizeof(uslcom_devs), uaa)); 414188413Sthompsa} 415188413Sthompsa 416188413Sthompsastatic int 417188413Sthompsauslcom_attach(device_t dev) 418188413Sthompsa{ 419192984Sthompsa struct usb_attach_arg *uaa = device_get_ivars(dev); 420188413Sthompsa struct uslcom_softc *sc = device_get_softc(dev); 421188413Sthompsa int error; 422188413Sthompsa 423188413Sthompsa DPRINTFN(11, "\n"); 424188413Sthompsa 425194228Sthompsa device_set_usb_desc(dev); 426189265Sthompsa mtx_init(&sc->sc_mtx, "uslcom", NULL, MTX_DEF); 427239180Shselasky ucom_ref(&sc->sc_super_ucom); 428227463Shselasky usb_callout_init_mtx(&sc->sc_watchdog, &sc->sc_mtx, 0); 429188413Sthompsa 430188413Sthompsa sc->sc_udev = uaa->device; 431239050Shselasky /* use the interface number from the USB interface descriptor */ 432239050Shselasky sc->sc_iface_no = uaa->info.bIfaceNum; 433188413Sthompsa 434194228Sthompsa error = usbd_transfer_setup(uaa->device, 435188413Sthompsa &uaa->info.bIfaceIndex, sc->sc_xfer, uslcom_config, 436189265Sthompsa USLCOM_N_TRANSFER, sc, &sc->sc_mtx); 437188413Sthompsa if (error) { 438188413Sthompsa DPRINTF("one or more missing USB endpoints, " 439194228Sthompsa "error=%s\n", usbd_errstr(error)); 440188413Sthompsa goto detach; 441188413Sthompsa } 442188413Sthompsa /* clear stall at first run */ 443189265Sthompsa mtx_lock(&sc->sc_mtx); 444194677Sthompsa usbd_xfer_set_stall(sc->sc_xfer[USLCOM_BULK_DT_WR]); 445194677Sthompsa usbd_xfer_set_stall(sc->sc_xfer[USLCOM_BULK_DT_RD]); 446189265Sthompsa mtx_unlock(&sc->sc_mtx); 447188413Sthompsa 448250749Sgavin sc->sc_partnum = uslcom_get_partnum(sc); 449250749Sgavin 450194228Sthompsa error = ucom_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc, 451189265Sthompsa &uslcom_callback, &sc->sc_mtx); 452188413Sthompsa if (error) { 453188413Sthompsa goto detach; 454188413Sthompsa } 455214843Sn_hibma ucom_set_pnpinfo_usb(&sc->sc_super_ucom, dev); 456214843Sn_hibma 457188413Sthompsa return (0); 458188413Sthompsa 459188413Sthompsadetach: 460188413Sthompsa uslcom_detach(dev); 461188413Sthompsa return (ENXIO); 462188413Sthompsa} 463188413Sthompsa 464188413Sthompsastatic int 465188413Sthompsauslcom_detach(device_t dev) 466188413Sthompsa{ 467188413Sthompsa struct uslcom_softc *sc = device_get_softc(dev); 468188413Sthompsa 469188413Sthompsa DPRINTF("sc=%p\n", sc); 470188413Sthompsa 471214761Sn_hibma ucom_detach(&sc->sc_super_ucom, &sc->sc_ucom); 472194228Sthompsa usbd_transfer_unsetup(sc->sc_xfer, USLCOM_N_TRANSFER); 473227463Shselasky 474227463Shselasky usb_callout_drain(&sc->sc_watchdog); 475188413Sthompsa 476239299Shselasky device_claim_softc(dev); 477239299Shselasky 478239299Shselasky uslcom_free_softc(sc); 479239299Shselasky 480188413Sthompsa return (0); 481188413Sthompsa} 482188413Sthompsa 483239180ShselaskyUCOM_UNLOAD_DRAIN(uslcom); 484239180Shselasky 485188413Sthompsastatic void 486239299Shselaskyuslcom_free_softc(struct uslcom_softc *sc) 487239180Shselasky{ 488239180Shselasky if (ucom_unref(&sc->sc_super_ucom)) { 489239299Shselasky mtx_destroy(&sc->sc_mtx); 490239299Shselasky device_free_softc(sc); 491239180Shselasky } 492239180Shselasky} 493239180Shselasky 494239180Shselaskystatic void 495239180Shselaskyuslcom_free(struct ucom_softc *ucom) 496239180Shselasky{ 497239299Shselasky uslcom_free_softc(ucom->sc_parent); 498239180Shselasky} 499239180Shselasky 500239180Shselaskystatic void 501192984Sthompsauslcom_open(struct ucom_softc *ucom) 502188413Sthompsa{ 503188413Sthompsa struct uslcom_softc *sc = ucom->sc_parent; 504192984Sthompsa struct usb_device_request req; 505188413Sthompsa 506188413Sthompsa req.bmRequestType = USLCOM_WRITE; 507239260Sgavin req.bRequest = USLCOM_IFC_ENABLE; 508239260Sgavin USETW(req.wValue, USLCOM_IFC_ENABLE_EN); 509239050Shselasky USETW(req.wIndex, sc->sc_iface_no); 510188413Sthompsa USETW(req.wLength, 0); 511188413Sthompsa 512194228Sthompsa if (ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 513188413Sthompsa &req, NULL, 0, 1000)) { 514188413Sthompsa DPRINTF("UART enable failed (ignored)\n"); 515188413Sthompsa } 516227463Shselasky 517227463Shselasky /* start polling status */ 518227463Shselasky uslcom_watchdog(sc); 519188413Sthompsa} 520188413Sthompsa 521188413Sthompsastatic void 522192984Sthompsauslcom_close(struct ucom_softc *ucom) 523188413Sthompsa{ 524188413Sthompsa struct uslcom_softc *sc = ucom->sc_parent; 525192984Sthompsa struct usb_device_request req; 526188413Sthompsa 527227463Shselasky /* stop polling status */ 528227463Shselasky usb_callout_stop(&sc->sc_watchdog); 529227108Shselasky 530188413Sthompsa req.bmRequestType = USLCOM_WRITE; 531239260Sgavin req.bRequest = USLCOM_IFC_ENABLE; 532239260Sgavin USETW(req.wValue, USLCOM_IFC_ENABLE_DIS); 533239050Shselasky USETW(req.wIndex, sc->sc_iface_no); 534188413Sthompsa USETW(req.wLength, 0); 535188413Sthompsa 536227108Shselasky if (ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 537188413Sthompsa &req, NULL, 0, 1000)) { 538188413Sthompsa DPRINTF("UART disable failed (ignored)\n"); 539188413Sthompsa } 540188413Sthompsa} 541188413Sthompsa 542250749Sgavinstatic uint8_t 543250749Sgavinuslcom_get_partnum(struct uslcom_softc *sc) 544250749Sgavin{ 545250749Sgavin struct usb_device_request req; 546250749Sgavin uint8_t partnum; 547250749Sgavin 548250749Sgavin /* Find specific chip type */ 549250749Sgavin partnum = 0; 550250749Sgavin req.bmRequestType = USLCOM_READ; 551250749Sgavin req.bRequest = USLCOM_VENDOR_SPECIFIC; 552250749Sgavin USETW(req.wValue, USLCOM_GET_PARTNUM); 553250749Sgavin USETW(req.wIndex, sc->sc_iface_no); 554250749Sgavin USETW(req.wLength, sizeof(partnum)); 555250749Sgavin 556250749Sgavin if (usbd_do_request_flags(sc->sc_udev, NULL, 557250749Sgavin &req, &partnum, 0, NULL, 1000)) { 558250749Sgavin DPRINTF("GET_PARTNUM failed\n"); 559250749Sgavin } 560250749Sgavin 561250749Sgavin return(partnum); 562250749Sgavin} 563250749Sgavin 564188413Sthompsastatic void 565192984Sthompsauslcom_set_dtr(struct ucom_softc *ucom, uint8_t onoff) 566188413Sthompsa{ 567188413Sthompsa struct uslcom_softc *sc = ucom->sc_parent; 568192984Sthompsa struct usb_device_request req; 569188413Sthompsa uint16_t ctl; 570188413Sthompsa 571188413Sthompsa DPRINTF("onoff = %d\n", onoff); 572188413Sthompsa 573239260Sgavin ctl = onoff ? USLCOM_MHS_DTR_ON : 0; 574239260Sgavin ctl |= USLCOM_MHS_DTR_SET; 575188413Sthompsa 576188413Sthompsa req.bmRequestType = USLCOM_WRITE; 577239260Sgavin req.bRequest = USLCOM_SET_MHS; 578188413Sthompsa USETW(req.wValue, ctl); 579239050Shselasky USETW(req.wIndex, sc->sc_iface_no); 580188413Sthompsa USETW(req.wLength, 0); 581188413Sthompsa 582194228Sthompsa if (ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 583188413Sthompsa &req, NULL, 0, 1000)) { 584188413Sthompsa DPRINTF("Setting DTR failed (ignored)\n"); 585188413Sthompsa } 586188413Sthompsa} 587188413Sthompsa 588188413Sthompsastatic void 589192984Sthompsauslcom_set_rts(struct ucom_softc *ucom, uint8_t onoff) 590188413Sthompsa{ 591188413Sthompsa struct uslcom_softc *sc = ucom->sc_parent; 592192984Sthompsa struct usb_device_request req; 593188413Sthompsa uint16_t ctl; 594188413Sthompsa 595188413Sthompsa DPRINTF("onoff = %d\n", onoff); 596188413Sthompsa 597239260Sgavin ctl = onoff ? USLCOM_MHS_RTS_ON : 0; 598239260Sgavin ctl |= USLCOM_MHS_RTS_SET; 599188413Sthompsa 600188413Sthompsa req.bmRequestType = USLCOM_WRITE; 601239260Sgavin req.bRequest = USLCOM_SET_MHS; 602188413Sthompsa USETW(req.wValue, ctl); 603239050Shselasky USETW(req.wIndex, sc->sc_iface_no); 604188413Sthompsa USETW(req.wLength, 0); 605188413Sthompsa 606194228Sthompsa if (ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 607188413Sthompsa &req, NULL, 0, 1000)) { 608188413Sthompsa DPRINTF("Setting DTR failed (ignored)\n"); 609188413Sthompsa } 610188413Sthompsa} 611188413Sthompsa 612188413Sthompsastatic int 613192984Sthompsauslcom_pre_param(struct ucom_softc *ucom, struct termios *t) 614188413Sthompsa{ 615188413Sthompsa if (t->c_ospeed <= 0 || t->c_ospeed > 921600) 616188413Sthompsa return (EINVAL); 617188413Sthompsa return (0); 618188413Sthompsa} 619188413Sthompsa 620188413Sthompsastatic void 621192984Sthompsauslcom_param(struct ucom_softc *ucom, struct termios *t) 622188413Sthompsa{ 623188413Sthompsa struct uslcom_softc *sc = ucom->sc_parent; 624192984Sthompsa struct usb_device_request req; 625238778Sgavin uint32_t baudrate, flowctrl[4]; 626188413Sthompsa uint16_t data; 627188413Sthompsa 628188413Sthompsa DPRINTF("\n"); 629188413Sthompsa 630238778Sgavin baudrate = t->c_ospeed; 631188413Sthompsa req.bmRequestType = USLCOM_WRITE; 632239260Sgavin req.bRequest = USLCOM_SET_BAUDRATE; 633238778Sgavin USETW(req.wValue, 0); 634239050Shselasky USETW(req.wIndex, sc->sc_iface_no); 635238778Sgavin USETW(req.wLength, sizeof(baudrate)); 636188413Sthompsa 637238778Sgavin if (ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 638238778Sgavin &req, &baudrate, 0, 1000)) { 639188413Sthompsa DPRINTF("Set baudrate failed (ignored)\n"); 640188413Sthompsa } 641188413Sthompsa 642188413Sthompsa if (t->c_cflag & CSTOPB) 643188413Sthompsa data = USLCOM_STOP_BITS_2; 644188413Sthompsa else 645188413Sthompsa data = USLCOM_STOP_BITS_1; 646188413Sthompsa if (t->c_cflag & PARENB) { 647188413Sthompsa if (t->c_cflag & PARODD) 648188413Sthompsa data |= USLCOM_PARITY_ODD; 649188413Sthompsa else 650188413Sthompsa data |= USLCOM_PARITY_EVEN; 651188413Sthompsa } else 652188413Sthompsa data |= USLCOM_PARITY_NONE; 653188413Sthompsa switch (t->c_cflag & CSIZE) { 654188413Sthompsa case CS5: 655188413Sthompsa data |= USLCOM_SET_DATA_BITS(5); 656188413Sthompsa break; 657188413Sthompsa case CS6: 658188413Sthompsa data |= USLCOM_SET_DATA_BITS(6); 659188413Sthompsa break; 660188413Sthompsa case CS7: 661188413Sthompsa data |= USLCOM_SET_DATA_BITS(7); 662188413Sthompsa break; 663188413Sthompsa case CS8: 664188413Sthompsa data |= USLCOM_SET_DATA_BITS(8); 665188413Sthompsa break; 666188413Sthompsa } 667188413Sthompsa 668188413Sthompsa req.bmRequestType = USLCOM_WRITE; 669239260Sgavin req.bRequest = USLCOM_SET_LINE_CTL; 670188413Sthompsa USETW(req.wValue, data); 671239050Shselasky USETW(req.wIndex, sc->sc_iface_no); 672188413Sthompsa USETW(req.wLength, 0); 673188413Sthompsa 674194228Sthompsa if (ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 675188413Sthompsa &req, NULL, 0, 1000)) { 676188413Sthompsa DPRINTF("Set format failed (ignored)\n"); 677188413Sthompsa } 678227108Shselasky 679227108Shselasky if (t->c_cflag & CRTSCTS) { 680227383Shselasky flowctrl[0] = htole32(USLCOM_FLOW_DTR_ON | USLCOM_FLOW_CTS_HS); 681227108Shselasky flowctrl[1] = htole32(USLCOM_FLOW_RTS_HS); 682227108Shselasky } else { 683227383Shselasky flowctrl[0] = htole32(USLCOM_FLOW_DTR_ON); 684227108Shselasky flowctrl[1] = htole32(USLCOM_FLOW_RTS_ON); 685227108Shselasky } 686239260Sgavin flowctrl[2] = 0; 687239260Sgavin flowctrl[3] = 0; 688227108Shselasky req.bmRequestType = USLCOM_WRITE; 689239260Sgavin req.bRequest = USLCOM_SET_FLOW; 690227108Shselasky USETW(req.wValue, 0); 691239050Shselasky USETW(req.wIndex, sc->sc_iface_no); 692227108Shselasky USETW(req.wLength, sizeof(flowctrl)); 693227108Shselasky 694227108Shselasky if (ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 695227108Shselasky &req, flowctrl, 0, 1000)) { 696227108Shselasky DPRINTF("Set flowcontrol failed (ignored)\n"); 697227108Shselasky } 698188413Sthompsa} 699188413Sthompsa 700188413Sthompsastatic void 701192984Sthompsauslcom_get_status(struct ucom_softc *ucom, uint8_t *lsr, uint8_t *msr) 702188413Sthompsa{ 703188413Sthompsa struct uslcom_softc *sc = ucom->sc_parent; 704188413Sthompsa 705188413Sthompsa DPRINTF("\n"); 706188413Sthompsa 707188413Sthompsa *lsr = sc->sc_lsr; 708188413Sthompsa *msr = sc->sc_msr; 709188413Sthompsa} 710188413Sthompsa 711188413Sthompsastatic void 712192984Sthompsauslcom_set_break(struct ucom_softc *ucom, uint8_t onoff) 713188413Sthompsa{ 714188413Sthompsa struct uslcom_softc *sc = ucom->sc_parent; 715192984Sthompsa struct usb_device_request req; 716239260Sgavin uint16_t brk = onoff ? USLCOM_SET_BREAK_ON : USLCOM_SET_BREAK_OFF; 717188413Sthompsa 718188413Sthompsa req.bmRequestType = USLCOM_WRITE; 719239260Sgavin req.bRequest = USLCOM_SET_BREAK; 720188413Sthompsa USETW(req.wValue, brk); 721239050Shselasky USETW(req.wIndex, sc->sc_iface_no); 722188413Sthompsa USETW(req.wLength, 0); 723188413Sthompsa 724194228Sthompsa if (ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 725188413Sthompsa &req, NULL, 0, 1000)) { 726188413Sthompsa DPRINTF("Set BREAK failed (ignored)\n"); 727188413Sthompsa } 728188413Sthompsa} 729188413Sthompsa 730227463Shselaskystatic int 731227463Shselaskyuslcom_ioctl(struct ucom_softc *ucom, uint32_t cmd, caddr_t data, 732227463Shselasky int flag, struct thread *td) 733227463Shselasky{ 734227463Shselasky struct uslcom_softc *sc = ucom->sc_parent; 735227463Shselasky struct usb_device_request req; 736227463Shselasky int error = 0; 737227463Shselasky uint8_t latch; 738227463Shselasky 739227463Shselasky DPRINTF("cmd=0x%08x\n", cmd); 740227463Shselasky 741227463Shselasky switch (cmd) { 742227463Shselasky case USB_GET_GPIO: 743250749Sgavin if (sc->sc_partnum < USLCOM_PARTNUM_CP2103) { 744250749Sgavin error = ENODEV; 745250749Sgavin break; 746250749Sgavin } 747227463Shselasky req.bmRequestType = USLCOM_READ; 748227463Shselasky req.bRequest = USLCOM_VENDOR_SPECIFIC; 749227463Shselasky USETW(req.wValue, USLCOM_READ_LATCH); 750250749Sgavin USETW(req.wIndex, sc->sc_iface_no); 751227463Shselasky USETW(req.wLength, sizeof(latch)); 752227463Shselasky 753227463Shselasky if (ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 754227463Shselasky &req, &latch, 0, 1000)) { 755227463Shselasky DPRINTF("Get LATCH failed\n"); 756227463Shselasky error = EIO; 757227463Shselasky } 758227463Shselasky *(int *)data = latch; 759227463Shselasky break; 760227463Shselasky 761227463Shselasky case USB_SET_GPIO: 762250749Sgavin if (sc->sc_partnum < USLCOM_PARTNUM_CP2103) 763250749Sgavin error = ENODEV; 764250749Sgavin else if ((sc->sc_partnum == USLCOM_PARTNUM_CP2103) || 765250749Sgavin (sc->sc_partnum == USLCOM_PARTNUM_CP2104)) { 766250749Sgavin req.bmRequestType = USLCOM_WRITE; 767250749Sgavin req.bRequest = USLCOM_VENDOR_SPECIFIC; 768250749Sgavin USETW(req.wValue, USLCOM_WRITE_LATCH); 769250749Sgavin USETW(req.wIndex, (*(int *)data)); 770250749Sgavin USETW(req.wLength, 0); 771250749Sgavin 772250749Sgavin if (ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 773250749Sgavin &req, NULL, 0, 1000)) { 774250749Sgavin DPRINTF("Set LATCH failed\n"); 775250749Sgavin error = EIO; 776250749Sgavin } 777250749Sgavin } else 778250749Sgavin error = ENODEV; /* Not yet */ 779227463Shselasky break; 780227463Shselasky 781227463Shselasky default: 782227463Shselasky DPRINTF("Unknown IOCTL\n"); 783227463Shselasky error = ENOIOCTL; 784227463Shselasky break; 785227463Shselasky } 786227463Shselasky return (error); 787227463Shselasky} 788227463Shselasky 789188413Sthompsastatic void 790194677Sthompsauslcom_write_callback(struct usb_xfer *xfer, usb_error_t error) 791188413Sthompsa{ 792194677Sthompsa struct uslcom_softc *sc = usbd_xfer_softc(xfer); 793194677Sthompsa struct usb_page_cache *pc; 794188413Sthompsa uint32_t actlen; 795188413Sthompsa 796188413Sthompsa switch (USB_GET_STATE(xfer)) { 797188413Sthompsa case USB_ST_SETUP: 798188413Sthompsa case USB_ST_TRANSFERRED: 799188413Sthompsatr_setup: 800194677Sthompsa pc = usbd_xfer_get_frame(xfer, 0); 801194677Sthompsa if (ucom_get_data(&sc->sc_ucom, pc, 0, 802188413Sthompsa USLCOM_BULK_BUF_SIZE, &actlen)) { 803188413Sthompsa 804188413Sthompsa DPRINTF("actlen = %d\n", actlen); 805188413Sthompsa 806194677Sthompsa usbd_xfer_set_frame_len(xfer, 0, actlen); 807194228Sthompsa usbd_transfer_submit(xfer); 808188413Sthompsa } 809188413Sthompsa return; 810188413Sthompsa 811188413Sthompsa default: /* Error */ 812194677Sthompsa if (error != USB_ERR_CANCELLED) { 813188413Sthompsa /* try to clear stall first */ 814194677Sthompsa usbd_xfer_set_stall(xfer); 815188413Sthompsa goto tr_setup; 816188413Sthompsa } 817188413Sthompsa return; 818188413Sthompsa } 819188413Sthompsa} 820188413Sthompsa 821188413Sthompsastatic void 822194677Sthompsauslcom_read_callback(struct usb_xfer *xfer, usb_error_t error) 823188413Sthompsa{ 824194677Sthompsa struct uslcom_softc *sc = usbd_xfer_softc(xfer); 825194677Sthompsa struct usb_page_cache *pc; 826194677Sthompsa int actlen; 827188413Sthompsa 828194677Sthompsa usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); 829194677Sthompsa 830188413Sthompsa switch (USB_GET_STATE(xfer)) { 831188413Sthompsa case USB_ST_TRANSFERRED: 832194677Sthompsa pc = usbd_xfer_get_frame(xfer, 0); 833194677Sthompsa ucom_put_data(&sc->sc_ucom, pc, 0, actlen); 834188413Sthompsa 835188413Sthompsa case USB_ST_SETUP: 836188413Sthompsatr_setup: 837194677Sthompsa usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); 838194228Sthompsa usbd_transfer_submit(xfer); 839188413Sthompsa return; 840188413Sthompsa 841188413Sthompsa default: /* Error */ 842194677Sthompsa if (error != USB_ERR_CANCELLED) { 843188413Sthompsa /* try to clear stall first */ 844194677Sthompsa usbd_xfer_set_stall(xfer); 845188413Sthompsa goto tr_setup; 846188413Sthompsa } 847188413Sthompsa return; 848188413Sthompsa } 849188413Sthompsa} 850188413Sthompsa 851188413Sthompsastatic void 852227108Shselaskyuslcom_control_callback(struct usb_xfer *xfer, usb_error_t error) 853227108Shselasky{ 854227108Shselasky struct uslcom_softc *sc = usbd_xfer_softc(xfer); 855227108Shselasky struct usb_page_cache *pc; 856227108Shselasky struct usb_device_request req; 857227108Shselasky uint8_t msr = 0; 858227108Shselasky uint8_t buf; 859227108Shselasky 860227108Shselasky switch (USB_GET_STATE(xfer)) { 861227108Shselasky case USB_ST_TRANSFERRED: 862227108Shselasky pc = usbd_xfer_get_frame(xfer, 1); 863227108Shselasky usbd_copy_out(pc, 0, &buf, sizeof(buf)); 864239260Sgavin if (buf & USLCOM_MHS_CTS) 865227108Shselasky msr |= SER_CTS; 866239260Sgavin if (buf & USLCOM_MHS_DSR) 867227108Shselasky msr |= SER_DSR; 868239260Sgavin if (buf & USLCOM_MHS_RI) 869227108Shselasky msr |= SER_RI; 870239260Sgavin if (buf & USLCOM_MHS_DCD) 871227108Shselasky msr |= SER_DCD; 872227108Shselasky 873227108Shselasky if (msr != sc->sc_msr) { 874227108Shselasky DPRINTF("status change msr=0x%02x " 875227108Shselasky "(was 0x%02x)\n", msr, sc->sc_msr); 876227108Shselasky sc->sc_msr = msr; 877227108Shselasky ucom_status_change(&sc->sc_ucom); 878227108Shselasky } 879227463Shselasky break; 880227108Shselasky 881227108Shselasky case USB_ST_SETUP: 882227108Shselasky req.bmRequestType = USLCOM_READ; 883239260Sgavin req.bRequest = USLCOM_GET_MDMSTS; 884227108Shselasky USETW(req.wValue, 0); 885239050Shselasky USETW(req.wIndex, sc->sc_iface_no); 886227108Shselasky USETW(req.wLength, sizeof(buf)); 887227108Shselasky 888227108Shselasky usbd_xfer_set_frames(xfer, 2); 889227108Shselasky usbd_xfer_set_frame_len(xfer, 0, sizeof(req)); 890227108Shselasky usbd_xfer_set_frame_len(xfer, 1, sizeof(buf)); 891227108Shselasky 892227108Shselasky pc = usbd_xfer_get_frame(xfer, 0); 893227108Shselasky usbd_copy_in(pc, 0, &req, sizeof(req)); 894227108Shselasky usbd_transfer_submit(xfer); 895227108Shselasky break; 896227108Shselasky 897227108Shselasky default: /* error */ 898227463Shselasky if (error != USB_ERR_CANCELLED) 899227108Shselasky DPRINTF("error=%s\n", usbd_errstr(error)); 900227108Shselasky break; 901227108Shselasky } 902227108Shselasky} 903227108Shselasky 904227108Shselaskystatic void 905192984Sthompsauslcom_start_read(struct ucom_softc *ucom) 906188413Sthompsa{ 907188413Sthompsa struct uslcom_softc *sc = ucom->sc_parent; 908188413Sthompsa 909188413Sthompsa /* start read endpoint */ 910194228Sthompsa usbd_transfer_start(sc->sc_xfer[USLCOM_BULK_DT_RD]); 911188413Sthompsa} 912188413Sthompsa 913188413Sthompsastatic void 914192984Sthompsauslcom_stop_read(struct ucom_softc *ucom) 915188413Sthompsa{ 916188413Sthompsa struct uslcom_softc *sc = ucom->sc_parent; 917188413Sthompsa 918188413Sthompsa /* stop read endpoint */ 919194228Sthompsa usbd_transfer_stop(sc->sc_xfer[USLCOM_BULK_DT_RD]); 920188413Sthompsa} 921188413Sthompsa 922188413Sthompsastatic void 923192984Sthompsauslcom_start_write(struct ucom_softc *ucom) 924188413Sthompsa{ 925188413Sthompsa struct uslcom_softc *sc = ucom->sc_parent; 926188413Sthompsa 927194228Sthompsa usbd_transfer_start(sc->sc_xfer[USLCOM_BULK_DT_WR]); 928188413Sthompsa} 929188413Sthompsa 930188413Sthompsastatic void 931192984Sthompsauslcom_stop_write(struct ucom_softc *ucom) 932188413Sthompsa{ 933188413Sthompsa struct uslcom_softc *sc = ucom->sc_parent; 934188413Sthompsa 935194228Sthompsa usbd_transfer_stop(sc->sc_xfer[USLCOM_BULK_DT_WR]); 936188413Sthompsa} 937197570Sthompsa 938197570Sthompsastatic void 939197570Sthompsauslcom_poll(struct ucom_softc *ucom) 940197570Sthompsa{ 941197570Sthompsa struct uslcom_softc *sc = ucom->sc_parent; 942197570Sthompsa usbd_transfer_poll(sc->sc_xfer, USLCOM_N_TRANSFER); 943197570Sthompsa} 944