| 1 |
/* $OpenBSD: uslcom.c,v 1.17 2007/11/24 10:52:12 jsg Exp $ */ |
| 2 |
|
| 3 |
#include <sys/cdefs.h> |
| 4 |
__FBSDID("$FreeBSD$"); |
| 5 |
|
| 6 |
/* |
| 7 |
* Copyright (c) 2006 Jonathan Gray <jsg@openbsd.org> |
| 8 |
* |
| 9 |
* Permission to use, copy, modify, and distribute this software for any |
| 10 |
* purpose with or without fee is hereby granted, provided that the above |
| 11 |
* copyright notice and this permission notice appear in all copies. |
| 12 |
* |
| 13 |
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
| 14 |
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
| 15 |
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
| 16 |
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
| 17 |
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
| 18 |
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
| 19 |
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
| 20 |
*/ |
| 21 |
|
| 22 |
/* |
| 23 |
* Driver for Silicon Laboratories CP2101/CP2102/CP2103/CP2104/CP2105 |
| 24 |
* USB-Serial adapters. Based on datasheet AN571, publicly available from |
| 25 |
* http://www.silabs.com/Support%20Documents/TechnicalDocs/AN571.pdf |
| 26 |
*/ |
| 27 |
|
| 28 |
#include <sys/stdint.h> |
| 29 |
#include <sys/stddef.h> |
| 30 |
#include <sys/param.h> |
| 31 |
#include <sys/queue.h> |
| 32 |
#include <sys/types.h> |
| 33 |
#include <sys/systm.h> |
| 34 |
#include <sys/kernel.h> |
| 35 |
#include <sys/bus.h> |
| 36 |
#include <sys/module.h> |
| 37 |
#include <sys/lock.h> |
| 38 |
#include <sys/mutex.h> |
| 39 |
#include <sys/condvar.h> |
| 40 |
#include <sys/sysctl.h> |
| 41 |
#include <sys/sx.h> |
| 42 |
#include <sys/unistd.h> |
| 43 |
#include <sys/callout.h> |
| 44 |
#include <sys/malloc.h> |
| 45 |
#include <sys/priv.h> |
| 46 |
|
| 47 |
#include <dev/usb/usb.h> |
| 48 |
#include <dev/usb/usbdi.h> |
| 49 |
#include <dev/usb/usbdi_util.h> |
| 50 |
#include <dev/usb/usb_ioctl.h> |
| 51 |
#include "usbdevs.h" |
| 52 |
|
| 53 |
#define USB_DEBUG_VAR uslcom_debug |
| 54 |
#include <dev/usb/usb_debug.h> |
| 55 |
#include <dev/usb/usb_process.h> |
| 56 |
|
| 57 |
#include <dev/usb/serial/usb_serial.h> |
| 58 |
|
| 59 |
#ifdef USB_DEBUG |
| 60 |
static int uslcom_debug = 0; |
| 61 |
|
| 62 |
static SYSCTL_NODE(_hw_usb, OID_AUTO, uslcom, CTLFLAG_RW, 0, "USB uslcom"); |
| 63 |
SYSCTL_INT(_hw_usb_uslcom, OID_AUTO, debug, CTLFLAG_RWTUN, |
| 64 |
&uslcom_debug, 0, "Debug level"); |
| 65 |
#endif |
| 66 |
|
| 67 |
#define USLCOM_BULK_BUF_SIZE 1024 |
| 68 |
#define USLCOM_CONFIG_INDEX 0 |
| 69 |
|
| 70 |
/* Request types */ |
| 71 |
#define USLCOM_WRITE 0x41 |
| 72 |
#define USLCOM_READ 0xc1 |
| 73 |
|
| 74 |
/* Request codes */ |
| 75 |
#define USLCOM_IFC_ENABLE 0x00 |
| 76 |
#define USLCOM_SET_BAUDDIV 0x01 |
| 77 |
#define USLCOM_SET_LINE_CTL 0x03 |
| 78 |
#define USLCOM_SET_BREAK 0x05 |
| 79 |
#define USLCOM_SET_MHS 0x07 |
| 80 |
#define USLCOM_GET_MDMSTS 0x08 |
| 81 |
#define USLCOM_SET_FLOW 0x13 |
| 82 |
#define USLCOM_SET_BAUDRATE 0x1e |
| 83 |
#define USLCOM_VENDOR_SPECIFIC 0xff |
| 84 |
|
| 85 |
/* USLCOM_IFC_ENABLE values */ |
| 86 |
#define USLCOM_IFC_ENABLE_DIS 0x00 |
| 87 |
#define USLCOM_IFC_ENABLE_EN 0x01 |
| 88 |
|
| 89 |
/* USLCOM_SET_MHS/USLCOM_GET_MDMSTS values */ |
| 90 |
#define USLCOM_MHS_DTR_ON 0x0001 |
| 91 |
#define USLCOM_MHS_DTR_SET 0x0100 |
| 92 |
#define USLCOM_MHS_RTS_ON 0x0002 |
| 93 |
#define USLCOM_MHS_RTS_SET 0x0200 |
| 94 |
#define USLCOM_MHS_CTS 0x0010 |
| 95 |
#define USLCOM_MHS_DSR 0x0020 |
| 96 |
#define USLCOM_MHS_RI 0x0040 |
| 97 |
#define USLCOM_MHS_DCD 0x0080 |
| 98 |
|
| 99 |
/* USLCOM_SET_BAUDDIV values */ |
| 100 |
#define USLCOM_BAUDDIV_REF 3686400 /* 3.6864 MHz */ |
| 101 |
|
| 102 |
/* USLCOM_SET_LINE_CTL values */ |
| 103 |
#define USLCOM_STOP_BITS_1 0x00 |
| 104 |
#define USLCOM_STOP_BITS_2 0x02 |
| 105 |
#define USLCOM_PARITY_NONE 0x00 |
| 106 |
#define USLCOM_PARITY_ODD 0x10 |
| 107 |
#define USLCOM_PARITY_EVEN 0x20 |
| 108 |
#define USLCOM_SET_DATA_BITS(x) ((x) << 8) |
| 109 |
|
| 110 |
/* USLCOM_SET_BREAK values */ |
| 111 |
#define USLCOM_SET_BREAK_OFF 0x00 |
| 112 |
#define USLCOM_SET_BREAK_ON 0x01 |
| 113 |
|
| 114 |
/* USLCOM_SET_FLOW values - 1st word */ |
| 115 |
#define USLCOM_FLOW_DTR_ON 0x00000001 /* DTR static active */ |
| 116 |
#define USLCOM_FLOW_CTS_HS 0x00000008 /* CTS handshake */ |
| 117 |
/* USLCOM_SET_FLOW values - 2nd word */ |
| 118 |
#define USLCOM_FLOW_RTS_ON 0x00000040 /* RTS static active */ |
| 119 |
#define USLCOM_FLOW_RTS_HS 0x00000080 /* RTS handshake */ |
| 120 |
|
| 121 |
/* USLCOM_VENDOR_SPECIFIC values */ |
| 122 |
#define USLCOM_GET_PARTNUM 0x370B |
| 123 |
#define USLCOM_WRITE_LATCH 0x37E1 |
| 124 |
#define USLCOM_READ_LATCH 0x00C2 |
| 125 |
|
| 126 |
/* USLCOM_GET_PARTNUM values from hardware */ |
| 127 |
#define USLCOM_PARTNUM_CP2101 1 |
| 128 |
#define USLCOM_PARTNUM_CP2102 2 |
| 129 |
#define USLCOM_PARTNUM_CP2103 3 |
| 130 |
#define USLCOM_PARTNUM_CP2104 4 |
| 131 |
#define USLCOM_PARTNUM_CP2105 5 |
| 132 |
|
| 133 |
enum { |
| 134 |
USLCOM_BULK_DT_WR, |
| 135 |
USLCOM_BULK_DT_RD, |
| 136 |
USLCOM_CTRL_DT_RD, |
| 137 |
USLCOM_N_TRANSFER, |
| 138 |
}; |
| 139 |
|
| 140 |
struct uslcom_softc { |
| 141 |
struct ucom_super_softc sc_super_ucom; |
| 142 |
struct ucom_softc sc_ucom; |
| 143 |
struct usb_callout sc_watchdog; |
| 144 |
|
| 145 |
struct usb_xfer *sc_xfer[USLCOM_N_TRANSFER]; |
| 146 |
struct usb_device *sc_udev; |
| 147 |
struct mtx sc_mtx; |
| 148 |
|
| 149 |
uint8_t sc_msr; |
| 150 |
uint8_t sc_lsr; |
| 151 |
uint8_t sc_iface_no; |
| 152 |
uint8_t sc_partnum; |
| 153 |
}; |
| 154 |
|
| 155 |
static device_probe_t uslcom_probe; |
| 156 |
static device_attach_t uslcom_attach; |
| 157 |
static device_detach_t uslcom_detach; |
| 158 |
static void uslcom_free_softc(struct uslcom_softc *); |
| 159 |
|
| 160 |
static usb_callback_t uslcom_write_callback; |
| 161 |
static usb_callback_t uslcom_read_callback; |
| 162 |
static usb_callback_t uslcom_control_callback; |
| 163 |
|
| 164 |
static void uslcom_free(struct ucom_softc *); |
| 165 |
static uint8_t uslcom_get_partnum(struct uslcom_softc *); |
| 166 |
static void uslcom_cfg_open(struct ucom_softc *); |
| 167 |
static void uslcom_cfg_close(struct ucom_softc *); |
| 168 |
static void uslcom_cfg_set_dtr(struct ucom_softc *, uint8_t); |
| 169 |
static void uslcom_cfg_set_rts(struct ucom_softc *, uint8_t); |
| 170 |
static void uslcom_cfg_set_break(struct ucom_softc *, uint8_t); |
| 171 |
static void uslcom_cfg_param(struct ucom_softc *, struct termios *); |
| 172 |
static void uslcom_cfg_get_status(struct ucom_softc *, uint8_t *, uint8_t *); |
| 173 |
static int uslcom_ioctl(struct ucom_softc *, uint32_t, caddr_t, int, |
| 174 |
struct thread *); |
| 175 |
static int uslcom_pre_param(struct ucom_softc *, struct termios *); |
| 176 |
static void uslcom_start_read(struct ucom_softc *); |
| 177 |
static void uslcom_stop_read(struct ucom_softc *); |
| 178 |
static void uslcom_start_write(struct ucom_softc *); |
| 179 |
static void uslcom_stop_write(struct ucom_softc *); |
| 180 |
static void uslcom_poll(struct ucom_softc *ucom); |
| 181 |
|
| 182 |
static const struct usb_config uslcom_config[USLCOM_N_TRANSFER] = { |
| 183 |
[USLCOM_BULK_DT_WR] = { |
| 184 |
.type = UE_BULK, |
| 185 |
.endpoint = UE_ADDR_ANY, |
| 186 |
.direction = UE_DIR_OUT, |
| 187 |
.bufsize = USLCOM_BULK_BUF_SIZE, |
| 188 |
.flags = {.pipe_bof = 1,}, |
| 189 |
.callback = &uslcom_write_callback, |
| 190 |
}, |
| 191 |
|
| 192 |
[USLCOM_BULK_DT_RD] = { |
| 193 |
.type = UE_BULK, |
| 194 |
.endpoint = UE_ADDR_ANY, |
| 195 |
.direction = UE_DIR_IN, |
| 196 |
.bufsize = USLCOM_BULK_BUF_SIZE, |
| 197 |
.flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, |
| 198 |
.callback = &uslcom_read_callback, |
| 199 |
}, |
| 200 |
[USLCOM_CTRL_DT_RD] = { |
| 201 |
.type = UE_CONTROL, |
| 202 |
.endpoint = 0x00, |
| 203 |
.direction = UE_DIR_ANY, |
| 204 |
.bufsize = sizeof(struct usb_device_request) + 8, |
| 205 |
.flags = {.pipe_bof = 1,}, |
| 206 |
.callback = &uslcom_control_callback, |
| 207 |
.timeout = 1000, /* 1 second timeout */ |
| 208 |
}, |
| 209 |
}; |
| 210 |
|
| 211 |
static struct ucom_callback uslcom_callback = { |
| 212 |
.ucom_cfg_get_status = &uslcom_cfg_get_status, |
| 213 |
.ucom_cfg_set_dtr = &uslcom_cfg_set_dtr, |
| 214 |
.ucom_cfg_set_rts = &uslcom_cfg_set_rts, |
| 215 |
.ucom_cfg_set_break = &uslcom_cfg_set_break, |
| 216 |
.ucom_cfg_open = &uslcom_cfg_open, |
| 217 |
.ucom_cfg_close = &uslcom_cfg_close, |
| 218 |
.ucom_cfg_param = &uslcom_cfg_param, |
| 219 |
.ucom_pre_param = &uslcom_pre_param, |
| 220 |
.ucom_ioctl = &uslcom_ioctl, |
| 221 |
.ucom_start_read = &uslcom_start_read, |
| 222 |
.ucom_stop_read = &uslcom_stop_read, |
| 223 |
.ucom_start_write = &uslcom_start_write, |
| 224 |
.ucom_stop_write = &uslcom_stop_write, |
| 225 |
.ucom_poll = &uslcom_poll, |
| 226 |
.ucom_free = &uslcom_free, |
| 227 |
}; |
| 228 |
|
| 229 |
static const STRUCT_USB_HOST_ID uslcom_devs[] = { |
| 230 |
#define USLCOM_DEV(v,p) { USB_VP(USB_VENDOR_##v, USB_PRODUCT_##v##_##p) } |
| 231 |
USLCOM_DEV(BALTECH, CARDREADER), |
| 232 |
USLCOM_DEV(CLIPSAL, 5000CT2), |
| 233 |
USLCOM_DEV(CLIPSAL, 5500PACA), |
| 234 |
USLCOM_DEV(CLIPSAL, 5500PCU), |
| 235 |
USLCOM_DEV(CLIPSAL, 560884), |
| 236 |
USLCOM_DEV(CLIPSAL, 5800PC), |
| 237 |
USLCOM_DEV(CLIPSAL, C5000CT2), |
| 238 |
USLCOM_DEV(CLIPSAL, L51xx), |
| 239 |
USLCOM_DEV(DATAAPEX, MULTICOM), |
| 240 |
USLCOM_DEV(DELL, DW700), |
| 241 |
USLCOM_DEV(DIGIANSWER, ZIGBEE802154), |
| 242 |
USLCOM_DEV(DYNASTREAM, ANTDEVBOARD), |
| 243 |
USLCOM_DEV(DYNASTREAM, ANTDEVBOARD2), |
| 244 |
USLCOM_DEV(DYNASTREAM, ANT2USB), |
| 245 |
USLCOM_DEV(ELV, USBI2C), |
| 246 |
USLCOM_DEV(FESTO, CMSP), |
| 247 |
USLCOM_DEV(FESTO, CPX_USB), |
| 248 |
USLCOM_DEV(FOXCONN, PIRELLI_DP_L10), |
| 249 |
USLCOM_DEV(FOXCONN, TCOM_TC_300), |
| 250 |
USLCOM_DEV(GEMALTO, PROXPU), |
| 251 |
USLCOM_DEV(JABLOTRON, PC60B), |
| 252 |
USLCOM_DEV(KAMSTRUP, OPTICALEYE), |
| 253 |
USLCOM_DEV(KAMSTRUP, MBUS_250D), |
| 254 |
USLCOM_DEV(LAKESHORE, 121), |
| 255 |
USLCOM_DEV(LAKESHORE, 218A), |
| 256 |
USLCOM_DEV(LAKESHORE, 219), |
| 257 |
USLCOM_DEV(LAKESHORE, 233), |
| 258 |
USLCOM_DEV(LAKESHORE, 235), |
| 259 |
USLCOM_DEV(LAKESHORE, 335), |
| 260 |
USLCOM_DEV(LAKESHORE, 336), |
| 261 |
USLCOM_DEV(LAKESHORE, 350), |
| 262 |
USLCOM_DEV(LAKESHORE, 371), |
| 263 |
USLCOM_DEV(LAKESHORE, 411), |
| 264 |
USLCOM_DEV(LAKESHORE, 425), |
| 265 |
USLCOM_DEV(LAKESHORE, 455A), |
| 266 |
USLCOM_DEV(LAKESHORE, 465), |
| 267 |
USLCOM_DEV(LAKESHORE, 475A), |
| 268 |
USLCOM_DEV(LAKESHORE, 625A), |
| 269 |
USLCOM_DEV(LAKESHORE, 642A), |
| 270 |
USLCOM_DEV(LAKESHORE, 648), |
| 271 |
USLCOM_DEV(LAKESHORE, 737), |
| 272 |
USLCOM_DEV(LAKESHORE, 776), |
| 273 |
USLCOM_DEV(LINKINSTRUMENTS, MSO19), |
| 274 |
USLCOM_DEV(LINKINSTRUMENTS, MSO28), |
| 275 |
USLCOM_DEV(LINKINSTRUMENTS, MSO28_2), |
| 276 |
USLCOM_DEV(MEI, CASHFLOW_SC), |
| 277 |
USLCOM_DEV(MEI, S2000), |
| 278 |
USLCOM_DEV(NETGEAR, M4100), |
| 279 |
USLCOM_DEV(OWEN, AC4), |
| 280 |
USLCOM_DEV(OWL, CM_160), |
| 281 |
USLCOM_DEV(PHILIPS, ACE1001), |
| 282 |
USLCOM_DEV(PLX, CA42), |
| 283 |
USLCOM_DEV(RENESAS, RX610), |
| 284 |
USLCOM_DEV(SEL, C662), |
| 285 |
USLCOM_DEV(SILABS, AC_SERV_CAN), |
| 286 |
USLCOM_DEV(SILABS, AC_SERV_CIS), |
| 287 |
USLCOM_DEV(SILABS, AC_SERV_IBUS), |
| 288 |
USLCOM_DEV(SILABS, AC_SERV_OBD), |
| 289 |
USLCOM_DEV(SILABS, AEROCOMM), |
| 290 |
USLCOM_DEV(SILABS, AMBER_AMB2560), |
| 291 |
USLCOM_DEV(SILABS, ARGUSISP), |
| 292 |
USLCOM_DEV(SILABS, ARKHAM_DS101_A), |
| 293 |
USLCOM_DEV(SILABS, ARKHAM_DS101_M), |
| 294 |
USLCOM_DEV(SILABS, ARYGON_MIFARE), |
| 295 |
USLCOM_DEV(SILABS, AVIT_USB_TTL), |
| 296 |
USLCOM_DEV(SILABS, B_G_H3000), |
| 297 |
USLCOM_DEV(SILABS, BALLUFF_RFID), |
| 298 |
USLCOM_DEV(SILABS, BEI_VCP), |
| 299 |
USLCOM_DEV(SILABS, BSM7DUSB), |
| 300 |
USLCOM_DEV(SILABS, BURNSIDE), |
| 301 |
USLCOM_DEV(SILABS, C2_EDGE_MODEM), |
| 302 |
USLCOM_DEV(SILABS, CP2102), |
| 303 |
USLCOM_DEV(SILABS, CP210X_2), |
| 304 |
USLCOM_DEV(SILABS, CP210X_3), |
| 305 |
USLCOM_DEV(SILABS, CP210X_4), |
| 306 |
USLCOM_DEV(SILABS, CRUMB128), |
| 307 |
USLCOM_DEV(SILABS, CYGNAL), |
| 308 |
USLCOM_DEV(SILABS, CYGNAL_DEBUG), |
| 309 |
USLCOM_DEV(SILABS, CYGNAL_GPS), |
| 310 |
USLCOM_DEV(SILABS, DEGREE), |
| 311 |
USLCOM_DEV(SILABS, DEKTEK_DTAPLUS), |
| 312 |
USLCOM_DEV(SILABS, EMS_C1007), |
| 313 |
USLCOM_DEV(SILABS, HAMLINKUSB), |
| 314 |
USLCOM_DEV(SILABS, HELICOM), |
| 315 |
USLCOM_DEV(SILABS, HUBZ), |
| 316 |
USLCOM_DEV(SILABS, BV_AV2010_10), |
| 317 |
USLCOM_DEV(SILABS, IMS_USB_RS422), |
| 318 |
USLCOM_DEV(SILABS, INFINITY_MIC), |
| 319 |
USLCOM_DEV(SILABS, INGENI_ZIGBEE), |
| 320 |
USLCOM_DEV(SILABS, INSYS_MODEM), |
| 321 |
USLCOM_DEV(SILABS, IRZ_SG10), |
| 322 |
USLCOM_DEV(SILABS, KYOCERA_GPS), |
| 323 |
USLCOM_DEV(SILABS, LIPOWSKY_HARP), |
| 324 |
USLCOM_DEV(SILABS, LIPOWSKY_JTAG), |
| 325 |
USLCOM_DEV(SILABS, LIPOWSKY_LIN), |
| 326 |
USLCOM_DEV(SILABS, MC35PU), |
| 327 |
USLCOM_DEV(SILABS, MMB_ZIGBEE), |
| 328 |
USLCOM_DEV(SILABS, MJS_TOSLINK), |
| 329 |
USLCOM_DEV(SILABS, MSD_DASHHAWK), |
| 330 |
USLCOM_DEV(SILABS, MULTIPLEX_RC), |
| 331 |
USLCOM_DEV(SILABS, OPTRIS_MSPRO), |
| 332 |
USLCOM_DEV(SILABS, POLOLU), |
| 333 |
USLCOM_DEV(SILABS, PROCYON_AVS), |
| 334 |
USLCOM_DEV(SILABS, SB_PARAMOUNT_ME), |
| 335 |
USLCOM_DEV(SILABS, SUUNTO), |
| 336 |
USLCOM_DEV(SILABS, TAMSMASTER), |
| 337 |
USLCOM_DEV(SILABS, TELEGESIS_ETRX2), |
| 338 |
USLCOM_DEV(SILABS, TRACIENT), |
| 339 |
USLCOM_DEV(SILABS, TRAQMATE), |
| 340 |
USLCOM_DEV(SILABS, USBCOUNT50), |
| 341 |
USLCOM_DEV(SILABS, USBPULSE100), |
| 342 |
USLCOM_DEV(SILABS, USBSCOPE50), |
| 343 |
USLCOM_DEV(SILABS, USBWAVE12), |
| 344 |
USLCOM_DEV(SILABS, V_PREON32), |
| 345 |
USLCOM_DEV(SILABS, VSTABI), |
| 346 |
USLCOM_DEV(SILABS, WAVIT), |
| 347 |
USLCOM_DEV(SILABS, WMRBATT), |
| 348 |
USLCOM_DEV(SILABS, WMRRIGBLASTER), |
| 349 |
USLCOM_DEV(SILABS, WMRRIGTALK), |
| 350 |
USLCOM_DEV(SILABS, ZEPHYR_BIO), |
| 351 |
USLCOM_DEV(SILABS2, DCU11CLONE), |
| 352 |
USLCOM_DEV(SILABS3, GPRS_MODEM), |
| 353 |
USLCOM_DEV(SILABS4, 100EU_MODEM), |
| 354 |
USLCOM_DEV(SYNTECH, CYPHERLAB100), |
| 355 |
USLCOM_DEV(USI, MC60), |
| 356 |
USLCOM_DEV(VAISALA, CABLE), |
| 357 |
USLCOM_DEV(WAGO, SERVICECABLE), |
| 358 |
USLCOM_DEV(WAVESENSE, JAZZ), |
| 359 |
USLCOM_DEV(WESTMOUNTAIN, RIGBLASTER_ADVANTAGE), |
| 360 |
USLCOM_DEV(WIENERPLEINBAUS, PL512), |
| 361 |
USLCOM_DEV(WIENERPLEINBAUS, RCM), |
| 362 |
USLCOM_DEV(WIENERPLEINBAUS, MPOD), |
| 363 |
USLCOM_DEV(WIENERPLEINBAUS, CML), |
| 364 |
#undef USLCOM_DEV |
| 365 |
}; |
| 366 |
|
| 367 |
static device_method_t uslcom_methods[] = { |
| 368 |
DEVMETHOD(device_probe, uslcom_probe), |
| 369 |
DEVMETHOD(device_attach, uslcom_attach), |
| 370 |
DEVMETHOD(device_detach, uslcom_detach), |
| 371 |
DEVMETHOD_END |
| 372 |
}; |
| 373 |
|
| 374 |
static devclass_t uslcom_devclass; |
| 375 |
|
| 376 |
static driver_t uslcom_driver = { |
| 377 |
.name = "uslcom", |
| 378 |
.methods = uslcom_methods, |
| 379 |
.size = sizeof(struct uslcom_softc), |
| 380 |
}; |
| 381 |
|
| 382 |
DRIVER_MODULE(uslcom, uhub, uslcom_driver, uslcom_devclass, NULL, 0); |
| 383 |
MODULE_DEPEND(uslcom, ucom, 1, 1, 1); |
| 384 |
MODULE_DEPEND(uslcom, usb, 1, 1, 1); |
| 385 |
MODULE_VERSION(uslcom, 1); |
| 386 |
USB_PNP_HOST_INFO(uslcom_devs); |
| 387 |
|
| 388 |
static void |
| 389 |
uslcom_watchdog(void *arg) |
| 390 |
{ |
| 391 |
struct uslcom_softc *sc = arg; |
| 392 |
|
| 393 |
mtx_assert(&sc->sc_mtx, MA_OWNED); |
| 394 |
|
| 395 |
usbd_transfer_start(sc->sc_xfer[USLCOM_CTRL_DT_RD]); |
| 396 |
|
| 397 |
usb_callout_reset(&sc->sc_watchdog, |
| 398 |
hz / 4, &uslcom_watchdog, sc); |
| 399 |
} |
| 400 |
|
| 401 |
static int |
| 402 |
uslcom_probe(device_t dev) |
| 403 |
{ |
| 404 |
struct usb_attach_arg *uaa = device_get_ivars(dev); |
| 405 |
|
| 406 |
DPRINTFN(11, "\n"); |
| 407 |
|
| 408 |
if (uaa->usb_mode != USB_MODE_HOST) { |
| 409 |
return (ENXIO); |
| 410 |
} |
| 411 |
if (uaa->info.bConfigIndex != USLCOM_CONFIG_INDEX) { |
| 412 |
return (ENXIO); |
| 413 |
} |
| 414 |
return (usbd_lookup_id_by_uaa(uslcom_devs, sizeof(uslcom_devs), uaa)); |
| 415 |
} |
| 416 |
|
| 417 |
static int |
| 418 |
uslcom_attach(device_t dev) |
| 419 |
{ |
| 420 |
struct usb_attach_arg *uaa = device_get_ivars(dev); |
| 421 |
struct uslcom_softc *sc = device_get_softc(dev); |
| 422 |
int error; |
| 423 |
|
| 424 |
DPRINTFN(11, "\n"); |
| 425 |
|
| 426 |
device_set_usb_desc(dev); |
| 427 |
mtx_init(&sc->sc_mtx, "uslcom", NULL, MTX_DEF); |
| 428 |
ucom_ref(&sc->sc_super_ucom); |
| 429 |
usb_callout_init_mtx(&sc->sc_watchdog, &sc->sc_mtx, 0); |
| 430 |
|
| 431 |
sc->sc_udev = uaa->device; |
| 432 |
/* use the interface number from the USB interface descriptor */ |
| 433 |
sc->sc_iface_no = uaa->info.bIfaceNum; |
| 434 |
|
| 435 |
error = usbd_transfer_setup(uaa->device, |
| 436 |
&uaa->info.bIfaceIndex, sc->sc_xfer, uslcom_config, |
| 437 |
USLCOM_N_TRANSFER, sc, &sc->sc_mtx); |
| 438 |
if (error) { |
| 439 |
DPRINTF("one or more missing USB endpoints, " |
| 440 |
"error=%s\n", usbd_errstr(error)); |
| 441 |
goto detach; |
| 442 |
} |
| 443 |
/* clear stall at first run */ |
| 444 |
mtx_lock(&sc->sc_mtx); |
| 445 |
usbd_xfer_set_stall(sc->sc_xfer[USLCOM_BULK_DT_WR]); |
| 446 |
usbd_xfer_set_stall(sc->sc_xfer[USLCOM_BULK_DT_RD]); |
| 447 |
mtx_unlock(&sc->sc_mtx); |
| 448 |
|
| 449 |
sc->sc_partnum = uslcom_get_partnum(sc); |
| 450 |
|
| 451 |
error = ucom_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc, |
| 452 |
&uslcom_callback, &sc->sc_mtx); |
| 453 |
if (error) { |
| 454 |
goto detach; |
| 455 |
} |
| 456 |
ucom_set_pnpinfo_usb(&sc->sc_super_ucom, dev); |
| 457 |
|
| 458 |
return (0); |
| 459 |
|
| 460 |
detach: |
| 461 |
uslcom_detach(dev); |
| 462 |
return (ENXIO); |
| 463 |
} |
| 464 |
|
| 465 |
static int |
| 466 |
uslcom_detach(device_t dev) |
| 467 |
{ |
| 468 |
struct uslcom_softc *sc = device_get_softc(dev); |
| 469 |
|
| 470 |
DPRINTF("sc=%p\n", sc); |
| 471 |
|
| 472 |
ucom_detach(&sc->sc_super_ucom, &sc->sc_ucom); |
| 473 |
usbd_transfer_unsetup(sc->sc_xfer, USLCOM_N_TRANSFER); |
| 474 |
|
| 475 |
usb_callout_drain(&sc->sc_watchdog); |
| 476 |
|
| 477 |
device_claim_softc(dev); |
| 478 |
|
| 479 |
uslcom_free_softc(sc); |
| 480 |
|
| 481 |
return (0); |
| 482 |
} |
| 483 |
|
| 484 |
UCOM_UNLOAD_DRAIN(uslcom); |
| 485 |
|
| 486 |
static void |
| 487 |
uslcom_free_softc(struct uslcom_softc *sc) |
| 488 |
{ |
| 489 |
if (ucom_unref(&sc->sc_super_ucom)) { |
| 490 |
mtx_destroy(&sc->sc_mtx); |
| 491 |
device_free_softc(sc); |
| 492 |
} |
| 493 |
} |
| 494 |
|
| 495 |
static void |
| 496 |
uslcom_free(struct ucom_softc *ucom) |
| 497 |
{ |
| 498 |
uslcom_free_softc(ucom->sc_parent); |
| 499 |
} |
| 500 |
|
| 501 |
static void |
| 502 |
uslcom_cfg_open(struct ucom_softc *ucom) |
| 503 |
{ |
| 504 |
struct uslcom_softc *sc = ucom->sc_parent; |
| 505 |
struct usb_device_request req; |
| 506 |
|
| 507 |
req.bmRequestType = USLCOM_WRITE; |
| 508 |
req.bRequest = USLCOM_IFC_ENABLE; |
| 509 |
USETW(req.wValue, USLCOM_IFC_ENABLE_EN); |
| 510 |
USETW(req.wIndex, sc->sc_iface_no); |
| 511 |
USETW(req.wLength, 0); |
| 512 |
|
| 513 |
if (ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, |
| 514 |
&req, NULL, 0, 1000)) { |
| 515 |
DPRINTF("UART enable failed (ignored)\n"); |
| 516 |
} |
| 517 |
|
| 518 |
/* start polling status */ |
| 519 |
uslcom_watchdog(sc); |
| 520 |
} |
| 521 |
|
| 522 |
static void |
| 523 |
uslcom_cfg_close(struct ucom_softc *ucom) |
| 524 |
{ |
| 525 |
struct uslcom_softc *sc = ucom->sc_parent; |
| 526 |
struct usb_device_request req; |
| 527 |
|
| 528 |
/* stop polling status */ |
| 529 |
usb_callout_stop(&sc->sc_watchdog); |
| 530 |
|
| 531 |
req.bmRequestType = USLCOM_WRITE; |
| 532 |
req.bRequest = USLCOM_IFC_ENABLE; |
| 533 |
USETW(req.wValue, USLCOM_IFC_ENABLE_DIS); |
| 534 |
USETW(req.wIndex, sc->sc_iface_no); |
| 535 |
USETW(req.wLength, 0); |
| 536 |
|
| 537 |
if (ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, |
| 538 |
&req, NULL, 0, 1000)) { |
| 539 |
DPRINTF("UART disable failed (ignored)\n"); |
| 540 |
} |
| 541 |
} |
| 542 |
|
| 543 |
static uint8_t |
| 544 |
uslcom_get_partnum(struct uslcom_softc *sc) |
| 545 |
{ |
| 546 |
struct usb_device_request req; |
| 547 |
uint8_t partnum; |
| 548 |
|
| 549 |
/* Find specific chip type */ |
| 550 |
partnum = 0; |
| 551 |
req.bmRequestType = USLCOM_READ; |
| 552 |
req.bRequest = USLCOM_VENDOR_SPECIFIC; |
| 553 |
USETW(req.wValue, USLCOM_GET_PARTNUM); |
| 554 |
USETW(req.wIndex, sc->sc_iface_no); |
| 555 |
USETW(req.wLength, sizeof(partnum)); |
| 556 |
|
| 557 |
if (usbd_do_request_flags(sc->sc_udev, NULL, |
| 558 |
&req, &partnum, 0, NULL, 1000)) { |
| 559 |
DPRINTF("GET_PARTNUM failed\n"); |
| 560 |
} |
| 561 |
|
| 562 |
return(partnum); |
| 563 |
} |
| 564 |
|
| 565 |
static void |
| 566 |
uslcom_cfg_set_dtr(struct ucom_softc *ucom, uint8_t onoff) |
| 567 |
{ |
| 568 |
struct uslcom_softc *sc = ucom->sc_parent; |
| 569 |
struct usb_device_request req; |
| 570 |
uint16_t ctl; |
| 571 |
|
| 572 |
DPRINTF("onoff = %d\n", onoff); |
| 573 |
|
| 574 |
ctl = onoff ? USLCOM_MHS_DTR_ON : 0; |
| 575 |
ctl |= USLCOM_MHS_DTR_SET; |
| 576 |
|
| 577 |
req.bmRequestType = USLCOM_WRITE; |
| 578 |
req.bRequest = USLCOM_SET_MHS; |
| 579 |
USETW(req.wValue, ctl); |
| 580 |
USETW(req.wIndex, sc->sc_iface_no); |
| 581 |
USETW(req.wLength, 0); |
| 582 |
|
| 583 |
if (ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, |
| 584 |
&req, NULL, 0, 1000)) { |
| 585 |
DPRINTF("Setting DTR failed (ignored)\n"); |
| 586 |
} |
| 587 |
} |
| 588 |
|
| 589 |
static void |
| 590 |
uslcom_cfg_set_rts(struct ucom_softc *ucom, uint8_t onoff) |
| 591 |
{ |
| 592 |
struct uslcom_softc *sc = ucom->sc_parent; |
| 593 |
struct usb_device_request req; |
| 594 |
uint16_t ctl; |
| 595 |
|
| 596 |
DPRINTF("onoff = %d\n", onoff); |
| 597 |
|
| 598 |
ctl = onoff ? USLCOM_MHS_RTS_ON : 0; |
| 599 |
ctl |= USLCOM_MHS_RTS_SET; |
| 600 |
|
| 601 |
req.bmRequestType = USLCOM_WRITE; |
| 602 |
req.bRequest = USLCOM_SET_MHS; |
| 603 |
USETW(req.wValue, ctl); |
| 604 |
USETW(req.wIndex, sc->sc_iface_no); |
| 605 |
USETW(req.wLength, 0); |
| 606 |
|
| 607 |
if (ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, |
| 608 |
&req, NULL, 0, 1000)) { |
| 609 |
DPRINTF("Setting DTR failed (ignored)\n"); |
| 610 |
} |
| 611 |
} |
| 612 |
|
| 613 |
static int |
| 614 |
uslcom_pre_param(struct ucom_softc *ucom, struct termios *t) |
| 615 |
{ |
| 616 |
struct uslcom_softc *sc = ucom->sc_parent; |
| 617 |
uint32_t maxspeed; |
| 618 |
|
| 619 |
switch (sc->sc_partnum) { |
| 620 |
case USLCOM_PARTNUM_CP2104: |
| 621 |
case USLCOM_PARTNUM_CP2105: |
| 622 |
maxspeed = 2000000; |
| 623 |
break; |
| 624 |
case USLCOM_PARTNUM_CP2101: |
| 625 |
case USLCOM_PARTNUM_CP2102: |
| 626 |
case USLCOM_PARTNUM_CP2103: |
| 627 |
default: |
| 628 |
/* |
| 629 |
* Datasheet for cp2102 says 921600 max. Testing shows that |
| 630 |
* 1228800 and 1843200 work fine. |
| 631 |
*/ |
| 632 |
maxspeed = 1843200; |
| 633 |
break; |
| 634 |
} |
| 635 |
if (t->c_ospeed <= 0 || t->c_ospeed > maxspeed) |
| 636 |
return (EINVAL); |
| 637 |
return (0); |
| 638 |
} |
| 639 |
|
| 640 |
static void |
| 641 |
uslcom_cfg_param(struct ucom_softc *ucom, struct termios *t) |
| 642 |
{ |
| 643 |
struct uslcom_softc *sc = ucom->sc_parent; |
| 644 |
struct usb_device_request req; |
| 645 |
uint32_t baudrate, flowctrl[4]; |
| 646 |
uint16_t data; |
| 647 |
|
| 648 |
DPRINTF("\n"); |
| 649 |
|
| 650 |
baudrate = t->c_ospeed; |
| 651 |
req.bmRequestType = USLCOM_WRITE; |
| 652 |
req.bRequest = USLCOM_SET_BAUDRATE; |
| 653 |
USETW(req.wValue, 0); |
| 654 |
USETW(req.wIndex, sc->sc_iface_no); |
| 655 |
USETW(req.wLength, sizeof(baudrate)); |
| 656 |
|
| 657 |
if (ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, |
| 658 |
&req, &baudrate, 0, 1000)) { |
| 659 |
printf("Set baudrate failed (ignored)\n"); |
| 660 |
} |
| 661 |
|
| 662 |
if (t->c_cflag & CSTOPB) |
| 663 |
data = USLCOM_STOP_BITS_2; |
| 664 |
else |
| 665 |
data = USLCOM_STOP_BITS_1; |
| 666 |
if (t->c_cflag & PARENB) { |
| 667 |
if (t->c_cflag & PARODD) |
| 668 |
data |= USLCOM_PARITY_ODD; |
| 669 |
else |
| 670 |
data |= USLCOM_PARITY_EVEN; |
| 671 |
} else |
| 672 |
data |= USLCOM_PARITY_NONE; |
| 673 |
switch (t->c_cflag & CSIZE) { |
| 674 |
case CS5: |
| 675 |
data |= USLCOM_SET_DATA_BITS(5); |
| 676 |
break; |
| 677 |
case CS6: |
| 678 |
data |= USLCOM_SET_DATA_BITS(6); |
| 679 |
break; |
| 680 |
case CS7: |
| 681 |
data |= USLCOM_SET_DATA_BITS(7); |
| 682 |
break; |
| 683 |
case CS8: |
| 684 |
data |= USLCOM_SET_DATA_BITS(8); |
| 685 |
break; |
| 686 |
} |
| 687 |
|
| 688 |
req.bmRequestType = USLCOM_WRITE; |
| 689 |
req.bRequest = USLCOM_SET_LINE_CTL; |
| 690 |
USETW(req.wValue, data); |
| 691 |
USETW(req.wIndex, sc->sc_iface_no); |
| 692 |
USETW(req.wLength, 0); |
| 693 |
|
| 694 |
if (ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, |
| 695 |
&req, NULL, 0, 1000)) { |
| 696 |
DPRINTF("Set format failed (ignored)\n"); |
| 697 |
} |
| 698 |
|
| 699 |
if (t->c_cflag & CRTSCTS) { |
| 700 |
flowctrl[0] = htole32(USLCOM_FLOW_DTR_ON | USLCOM_FLOW_CTS_HS); |
| 701 |
flowctrl[1] = htole32(USLCOM_FLOW_RTS_HS); |
| 702 |
} else { |
| 703 |
flowctrl[0] = htole32(USLCOM_FLOW_DTR_ON); |
| 704 |
flowctrl[1] = htole32(USLCOM_FLOW_RTS_ON); |
| 705 |
} |
| 706 |
flowctrl[2] = 0; |
| 707 |
flowctrl[3] = 0; |
| 708 |
req.bmRequestType = USLCOM_WRITE; |
| 709 |
req.bRequest = USLCOM_SET_FLOW; |
| 710 |
USETW(req.wValue, 0); |
| 711 |
USETW(req.wIndex, sc->sc_iface_no); |
| 712 |
USETW(req.wLength, sizeof(flowctrl)); |
| 713 |
|
| 714 |
if (ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, |
| 715 |
&req, flowctrl, 0, 1000)) { |
| 716 |
DPRINTF("Set flowcontrol failed (ignored)\n"); |
| 717 |
} |
| 718 |
} |
| 719 |
|
| 720 |
static void |
| 721 |
uslcom_cfg_get_status(struct ucom_softc *ucom, uint8_t *lsr, uint8_t *msr) |
| 722 |
{ |
| 723 |
struct uslcom_softc *sc = ucom->sc_parent; |
| 724 |
|
| 725 |
DPRINTF("\n"); |
| 726 |
|
| 727 |
/* XXX Note: sc_lsr is always zero */ |
| 728 |
*lsr = sc->sc_lsr; |
| 729 |
*msr = sc->sc_msr; |
| 730 |
} |
| 731 |
|
| 732 |
static void |
| 733 |
uslcom_cfg_set_break(struct ucom_softc *ucom, uint8_t onoff) |
| 734 |
{ |
| 735 |
struct uslcom_softc *sc = ucom->sc_parent; |
| 736 |
struct usb_device_request req; |
| 737 |
uint16_t brk = onoff ? USLCOM_SET_BREAK_ON : USLCOM_SET_BREAK_OFF; |
| 738 |
|
| 739 |
req.bmRequestType = USLCOM_WRITE; |
| 740 |
req.bRequest = USLCOM_SET_BREAK; |
| 741 |
USETW(req.wValue, brk); |
| 742 |
USETW(req.wIndex, sc->sc_iface_no); |
| 743 |
USETW(req.wLength, 0); |
| 744 |
|
| 745 |
if (ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, |
| 746 |
&req, NULL, 0, 1000)) { |
| 747 |
DPRINTF("Set BREAK failed (ignored)\n"); |
| 748 |
} |
| 749 |
} |
| 750 |
|
| 751 |
static int |
| 752 |
uslcom_ioctl(struct ucom_softc *ucom, uint32_t cmd, caddr_t data, |
| 753 |
int flag, struct thread *td) |
| 754 |
{ |
| 755 |
struct uslcom_softc *sc = ucom->sc_parent; |
| 756 |
struct usb_device_request req; |
| 757 |
int error = 0; |
| 758 |
uint8_t latch; |
| 759 |
|
| 760 |
DPRINTF("cmd=0x%08x\n", cmd); |
| 761 |
|
| 762 |
switch (cmd) { |
| 763 |
case USB_GET_GPIO: |
| 764 |
if (sc->sc_partnum < USLCOM_PARTNUM_CP2103) { |
| 765 |
error = ENODEV; |
| 766 |
break; |
| 767 |
} |
| 768 |
req.bmRequestType = USLCOM_READ; |
| 769 |
req.bRequest = USLCOM_VENDOR_SPECIFIC; |
| 770 |
USETW(req.wValue, USLCOM_READ_LATCH); |
| 771 |
USETW(req.wIndex, sc->sc_iface_no); |
| 772 |
USETW(req.wLength, sizeof(latch)); |
| 773 |
|
| 774 |
if (ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, |
| 775 |
&req, &latch, 0, 1000)) { |
| 776 |
DPRINTF("Get LATCH failed\n"); |
| 777 |
error = EIO; |
| 778 |
} |
| 779 |
*(int *)data = latch; |
| 780 |
break; |
| 781 |
|
| 782 |
case USB_SET_GPIO: |
| 783 |
if (sc->sc_partnum < USLCOM_PARTNUM_CP2103) |
| 784 |
error = ENODEV; |
| 785 |
else if ((sc->sc_partnum == USLCOM_PARTNUM_CP2103) || |
| 786 |
(sc->sc_partnum == USLCOM_PARTNUM_CP2104)) { |
| 787 |
req.bmRequestType = USLCOM_WRITE; |
| 788 |
req.bRequest = USLCOM_VENDOR_SPECIFIC; |
| 789 |
USETW(req.wValue, USLCOM_WRITE_LATCH); |
| 790 |
USETW(req.wIndex, (*(int *)data)); |
| 791 |
USETW(req.wLength, 0); |
| 792 |
|
| 793 |
if (ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, |
| 794 |
&req, NULL, 0, 1000)) { |
| 795 |
DPRINTF("Set LATCH failed\n"); |
| 796 |
error = EIO; |
| 797 |
} |
| 798 |
} else |
| 799 |
error = ENODEV; /* Not yet */ |
| 800 |
break; |
| 801 |
|
| 802 |
default: |
| 803 |
DPRINTF("Unknown IOCTL\n"); |
| 804 |
error = ENOIOCTL; |
| 805 |
break; |
| 806 |
} |
| 807 |
return (error); |
| 808 |
} |
| 809 |
|
| 810 |
static void |
| 811 |
uslcom_write_callback(struct usb_xfer *xfer, usb_error_t error) |
| 812 |
{ |
| 813 |
struct uslcom_softc *sc = usbd_xfer_softc(xfer); |
| 814 |
struct usb_page_cache *pc; |
| 815 |
uint32_t actlen; |
| 816 |
|
| 817 |
switch (USB_GET_STATE(xfer)) { |
| 818 |
case USB_ST_SETUP: |
| 819 |
case USB_ST_TRANSFERRED: |
| 820 |
tr_setup: |
| 821 |
pc = usbd_xfer_get_frame(xfer, 0); |
| 822 |
if (ucom_get_data(&sc->sc_ucom, pc, 0, |
| 823 |
USLCOM_BULK_BUF_SIZE, &actlen)) { |
| 824 |
|
| 825 |
DPRINTF("actlen = %d\n", actlen); |
| 826 |
|
| 827 |
usbd_xfer_set_frame_len(xfer, 0, actlen); |
| 828 |
usbd_transfer_submit(xfer); |
| 829 |
} |
| 830 |
return; |
| 831 |
|
| 832 |
default: /* Error */ |
| 833 |
if (error != USB_ERR_CANCELLED) { |
| 834 |
/* try to clear stall first */ |
| 835 |
usbd_xfer_set_stall(xfer); |
| 836 |
goto tr_setup; |
| 837 |
} |
| 838 |
return; |
| 839 |
} |
| 840 |
} |
| 841 |
|
| 842 |
static void |
| 843 |
uslcom_read_callback(struct usb_xfer *xfer, usb_error_t error) |
| 844 |
{ |
| 845 |
struct uslcom_softc *sc = usbd_xfer_softc(xfer); |
| 846 |
struct usb_page_cache *pc; |
| 847 |
int actlen; |
| 848 |
|
| 849 |
usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); |
| 850 |
|
| 851 |
switch (USB_GET_STATE(xfer)) { |
| 852 |
case USB_ST_TRANSFERRED: |
| 853 |
pc = usbd_xfer_get_frame(xfer, 0); |
| 854 |
ucom_put_data(&sc->sc_ucom, pc, 0, actlen); |
| 855 |
|
| 856 |
case USB_ST_SETUP: |
| 857 |
tr_setup: |
| 858 |
usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); |
| 859 |
usbd_transfer_submit(xfer); |
| 860 |
return; |
| 861 |
|
| 862 |
default: /* Error */ |
| 863 |
if (error != USB_ERR_CANCELLED) { |
| 864 |
/* try to clear stall first */ |
| 865 |
usbd_xfer_set_stall(xfer); |
| 866 |
goto tr_setup; |
| 867 |
} |
| 868 |
return; |
| 869 |
} |
| 870 |
} |
| 871 |
|
| 872 |
static void |
| 873 |
uslcom_control_callback(struct usb_xfer *xfer, usb_error_t error) |
| 874 |
{ |
| 875 |
struct uslcom_softc *sc = usbd_xfer_softc(xfer); |
| 876 |
struct usb_page_cache *pc; |
| 877 |
struct usb_device_request req; |
| 878 |
uint8_t msr = 0; |
| 879 |
uint8_t buf; |
| 880 |
|
| 881 |
switch (USB_GET_STATE(xfer)) { |
| 882 |
case USB_ST_TRANSFERRED: |
| 883 |
pc = usbd_xfer_get_frame(xfer, 1); |
| 884 |
usbd_copy_out(pc, 0, &buf, sizeof(buf)); |
| 885 |
if (buf & USLCOM_MHS_CTS) |
| 886 |
msr |= SER_CTS; |
| 887 |
if (buf & USLCOM_MHS_DSR) |
| 888 |
msr |= SER_DSR; |
| 889 |
if (buf & USLCOM_MHS_RI) |
| 890 |
msr |= SER_RI; |
| 891 |
if (buf & USLCOM_MHS_DCD) |
| 892 |
msr |= SER_DCD; |
| 893 |
|
| 894 |
if (msr != sc->sc_msr) { |
| 895 |
DPRINTF("status change msr=0x%02x " |
| 896 |
"(was 0x%02x)\n", msr, sc->sc_msr); |
| 897 |
sc->sc_msr = msr; |
| 898 |
ucom_status_change(&sc->sc_ucom); |
| 899 |
} |
| 900 |
break; |
| 901 |
|
| 902 |
case USB_ST_SETUP: |
| 903 |
req.bmRequestType = USLCOM_READ; |
| 904 |
req.bRequest = USLCOM_GET_MDMSTS; |
| 905 |
USETW(req.wValue, 0); |
| 906 |
USETW(req.wIndex, sc->sc_iface_no); |
| 907 |
USETW(req.wLength, sizeof(buf)); |
| 908 |
|
| 909 |
usbd_xfer_set_frames(xfer, 2); |
| 910 |
usbd_xfer_set_frame_len(xfer, 0, sizeof(req)); |
| 911 |
usbd_xfer_set_frame_len(xfer, 1, sizeof(buf)); |
| 912 |
|
| 913 |
pc = usbd_xfer_get_frame(xfer, 0); |
| 914 |
usbd_copy_in(pc, 0, &req, sizeof(req)); |
| 915 |
usbd_transfer_submit(xfer); |
| 916 |
break; |
| 917 |
|
| 918 |
default: /* error */ |
| 919 |
if (error != USB_ERR_CANCELLED) |
| 920 |
DPRINTF("error=%s\n", usbd_errstr(error)); |
| 921 |
break; |
| 922 |
} |
| 923 |
} |
| 924 |
|
| 925 |
static void |
| 926 |
uslcom_start_read(struct ucom_softc *ucom) |
| 927 |
{ |
| 928 |
struct uslcom_softc *sc = ucom->sc_parent; |
| 929 |
|
| 930 |
/* start read endpoint */ |
| 931 |
usbd_transfer_start(sc->sc_xfer[USLCOM_BULK_DT_RD]); |
| 932 |
} |
| 933 |
|
| 934 |
static void |
| 935 |
uslcom_stop_read(struct ucom_softc *ucom) |
| 936 |
{ |
| 937 |
struct uslcom_softc *sc = ucom->sc_parent; |
| 938 |
|
| 939 |
/* stop read endpoint */ |
| 940 |
usbd_transfer_stop(sc->sc_xfer[USLCOM_BULK_DT_RD]); |
| 941 |
} |
| 942 |
|
| 943 |
static void |
| 944 |
uslcom_start_write(struct ucom_softc *ucom) |
| 945 |
{ |
| 946 |
struct uslcom_softc *sc = ucom->sc_parent; |
| 947 |
|
| 948 |
usbd_transfer_start(sc->sc_xfer[USLCOM_BULK_DT_WR]); |
| 949 |
} |
| 950 |
|
| 951 |
static void |
| 952 |
uslcom_stop_write(struct ucom_softc *ucom) |
| 953 |
{ |
| 954 |
struct uslcom_softc *sc = ucom->sc_parent; |
| 955 |
|
| 956 |
usbd_transfer_stop(sc->sc_xfer[USLCOM_BULK_DT_WR]); |
| 957 |
} |
| 958 |
|
| 959 |
static void |
| 960 |
uslcom_poll(struct ucom_softc *ucom) |
| 961 |
{ |
| 962 |
struct uslcom_softc *sc = ucom->sc_parent; |
| 963 |
usbd_transfer_poll(sc->sc_xfer, USLCOM_N_TRANSFER); |
| 964 |
} |