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