uslcom.c revision 223486
1204431Sraj/*	$OpenBSD: uslcom.c,v 1.17 2007/11/24 10:52:12 jsg Exp $	*/
2204431Sraj
3204431Sraj#include <sys/cdefs.h>
4204431Sraj__FBSDID("$FreeBSD: head/sys/dev/usb/serial/uslcom.c 223486 2011-06-24 02:30:02Z hselasky $");
5204431Sraj
6204431Sraj/*
7204431Sraj * Copyright (c) 2006 Jonathan Gray <jsg@openbsd.org>
8204431Sraj *
9204431Sraj * Permission to use, copy, modify, and distribute this software for any
10204431Sraj * purpose with or without fee is hereby granted, provided that the above
11204431Sraj * copyright notice and this permission notice appear in all copies.
12204431Sraj *
13204431Sraj * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
14204431Sraj * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
15204431Sraj * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
16204431Sraj * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
17204431Sraj * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
18204431Sraj * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
19204431Sraj * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
20204431Sraj */
21204431Sraj
22204431Sraj#include <sys/stdint.h>
23204431Sraj#include <sys/stddef.h>
24204431Sraj#include <sys/param.h>
25204431Sraj#include <sys/queue.h>
26204431Sraj#include <sys/types.h>
27204431Sraj#include <sys/systm.h>
28204431Sraj#include <sys/kernel.h>
29204431Sraj#include <sys/bus.h>
30204431Sraj#include <sys/module.h>
31204431Sraj#include <sys/lock.h>
32204431Sraj#include <sys/mutex.h>
33204431Sraj#include <sys/condvar.h>
34204431Sraj#include <sys/sysctl.h>
35204431Sraj#include <sys/sx.h>
36204431Sraj#include <sys/unistd.h>
37204431Sraj#include <sys/callout.h>
38204431Sraj#include <sys/malloc.h>
39204431Sraj#include <sys/priv.h>
40204431Sraj
41204431Sraj#include <dev/usb/usb.h>
42204431Sraj#include <dev/usb/usbdi.h>
43204431Sraj#include <dev/usb/usbdi_util.h>
44204431Sraj#include "usbdevs.h"
45204431Sraj
46204431Sraj#define	USB_DEBUG_VAR uslcom_debug
47204431Sraj#include <dev/usb/usb_debug.h>
48204431Sraj#include <dev/usb/usb_process.h>
49204431Sraj
50204431Sraj#include <dev/usb/serial/usb_serial.h>
51204431Sraj
52204431Sraj#ifdef USB_DEBUG
53204431Srajstatic int uslcom_debug = 0;
54204431Sraj
55204431SrajSYSCTL_NODE(_hw_usb, OID_AUTO, uslcom, CTLFLAG_RW, 0, "USB uslcom");
56204431SrajSYSCTL_INT(_hw_usb_uslcom, OID_AUTO, debug, CTLFLAG_RW,
57204431Sraj    &uslcom_debug, 0, "Debug level");
58204431Sraj#endif
59204431Sraj
60204431Sraj#define	USLCOM_BULK_BUF_SIZE		1024
61204431Sraj#define	USLCOM_CONFIG_INDEX	0
62204431Sraj#define	USLCOM_IFACE_INDEX	0
63204431Sraj
64204431Sraj#define	USLCOM_SET_DATA_BITS(x)	((x) << 8)
65204431Sraj
66204431Sraj#define	USLCOM_WRITE		0x41
67204431Sraj#define	USLCOM_READ		0xc1
68204431Sraj
69204431Sraj#define	USLCOM_UART		0x00
70204431Sraj#define	USLCOM_BAUD_RATE	0x01
71204431Sraj#define	USLCOM_DATA		0x03
72204431Sraj#define	USLCOM_BREAK		0x05
73204431Sraj#define	USLCOM_CTRL		0x07
74204431Sraj
75204431Sraj#define	USLCOM_UART_DISABLE	0x00
76204431Sraj#define	USLCOM_UART_ENABLE	0x01
77204431Sraj
78204431Sraj#define	USLCOM_CTRL_DTR_ON	0x0001
79204431Sraj#define	USLCOM_CTRL_DTR_SET	0x0100
80204431Sraj#define	USLCOM_CTRL_RTS_ON	0x0002
81204431Sraj#define	USLCOM_CTRL_RTS_SET	0x0200
82204431Sraj#define	USLCOM_CTRL_CTS		0x0010
83204431Sraj#define	USLCOM_CTRL_DSR		0x0020
84204431Sraj#define	USLCOM_CTRL_DCD		0x0080
85204431Sraj
86204431Sraj#define	USLCOM_BAUD_REF		0x384000
87204431Sraj
88204431Sraj#define	USLCOM_STOP_BITS_1	0x00
89204431Sraj#define	USLCOM_STOP_BITS_2	0x02
90204431Sraj
91204431Sraj#define	USLCOM_PARITY_NONE	0x00
92204431Sraj#define	USLCOM_PARITY_ODD	0x10
93204431Sraj#define	USLCOM_PARITY_EVEN	0x20
94204431Sraj
95204431Sraj#define	USLCOM_PORT_NO		0xFFFF /* XXX think this should be 0 --hps */
96204431Sraj
97204431Sraj#define	USLCOM_BREAK_OFF	0x00
98204431Sraj#define	USLCOM_BREAK_ON		0x01
99204431Sraj
100204431Srajenum {
101204431Sraj	USLCOM_BULK_DT_WR,
102204431Sraj	USLCOM_BULK_DT_RD,
103204431Sraj	USLCOM_N_TRANSFER,
104204431Sraj};
105204431Sraj
106204431Srajstruct uslcom_softc {
107204431Sraj	struct ucom_super_softc sc_super_ucom;
108204431Sraj	struct ucom_softc sc_ucom;
109204431Sraj
110204431Sraj	struct usb_xfer *sc_xfer[USLCOM_N_TRANSFER];
111204431Sraj	struct usb_device *sc_udev;
112204431Sraj	struct mtx sc_mtx;
113204431Sraj
114204431Sraj	uint8_t		 sc_msr;
115204431Sraj	uint8_t		 sc_lsr;
116204431Sraj};
117204431Sraj
118204431Srajstatic device_probe_t uslcom_probe;
119204431Srajstatic device_attach_t uslcom_attach;
120204431Srajstatic device_detach_t uslcom_detach;
121204431Sraj
122204431Srajstatic usb_callback_t uslcom_write_callback;
123204431Srajstatic usb_callback_t uslcom_read_callback;
124204431Sraj
125204433Srajstatic void uslcom_open(struct ucom_softc *);
126204431Srajstatic void uslcom_close(struct ucom_softc *);
127204431Srajstatic void uslcom_set_dtr(struct ucom_softc *, uint8_t);
128204431Srajstatic void uslcom_set_rts(struct ucom_softc *, uint8_t);
129204431Srajstatic void uslcom_set_break(struct ucom_softc *, uint8_t);
130204431Srajstatic int uslcom_pre_param(struct ucom_softc *, struct termios *);
131204431Srajstatic void uslcom_param(struct ucom_softc *, struct termios *);
132204431Srajstatic void uslcom_get_status(struct ucom_softc *, uint8_t *, uint8_t *);
133204431Srajstatic void uslcom_start_read(struct ucom_softc *);
134204431Srajstatic void uslcom_stop_read(struct ucom_softc *);
135204431Srajstatic void uslcom_start_write(struct ucom_softc *);
136204431Srajstatic void uslcom_stop_write(struct ucom_softc *);
137204431Srajstatic void uslcom_poll(struct ucom_softc *ucom);
138204431Sraj
139204431Srajstatic const struct usb_config uslcom_config[USLCOM_N_TRANSFER] = {
140204431Sraj
141204431Sraj	[USLCOM_BULK_DT_WR] = {
142204431Sraj		.type = UE_BULK,
143204431Sraj		.endpoint = UE_ADDR_ANY,
144204431Sraj		.direction = UE_DIR_OUT,
145204431Sraj		.bufsize = USLCOM_BULK_BUF_SIZE,
146204431Sraj		.flags = {.pipe_bof = 1,.force_short_xfer = 1,},
147204431Sraj		.callback = &uslcom_write_callback,
148204431Sraj	},
149204431Sraj
150204431Sraj	[USLCOM_BULK_DT_RD] = {
151204431Sraj		.type = UE_BULK,
152204431Sraj		.endpoint = UE_ADDR_ANY,
153204431Sraj		.direction = UE_DIR_IN,
154204431Sraj		.bufsize = USLCOM_BULK_BUF_SIZE,
155204431Sraj		.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
156204431Sraj		.callback = &uslcom_read_callback,
157204431Sraj	},
158204431Sraj};
159204433Sraj
160204431Srajstatic struct ucom_callback uslcom_callback = {
161204431Sraj	.ucom_cfg_open = &uslcom_open,
162204431Sraj	.ucom_cfg_close = &uslcom_close,
163204431Sraj	.ucom_cfg_get_status = &uslcom_get_status,
164204431Sraj	.ucom_cfg_set_dtr = &uslcom_set_dtr,
165204431Sraj	.ucom_cfg_set_rts = &uslcom_set_rts,
166204431Sraj	.ucom_cfg_set_break = &uslcom_set_break,
167204431Sraj	.ucom_cfg_param = &uslcom_param,
168204431Sraj	.ucom_pre_param = &uslcom_pre_param,
169204431Sraj	.ucom_start_read = &uslcom_start_read,
170204431Sraj	.ucom_stop_read = &uslcom_stop_read,
171204431Sraj	.ucom_start_write = &uslcom_start_write,
172204431Sraj	.ucom_stop_write = &uslcom_stop_write,
173204431Sraj	.ucom_poll = &uslcom_poll,
174204431Sraj};
175204431Sraj
176204431Srajstatic const STRUCT_USB_HOST_ID uslcom_devs[] = {
177204431Sraj#define	USLCOM_DEV(v,p)  { USB_VP(USB_VENDOR_##v, USB_PRODUCT_##v##_##p) }
178204431Sraj    USLCOM_DEV(BALTECH, CARDREADER),
179204431Sraj    USLCOM_DEV(CLIPSAL, 5500PCU),
180204431Sraj    USLCOM_DEV(DATAAPEX, MULTICOM),
181204431Sraj    USLCOM_DEV(DELL, DW700),
182204431Sraj    USLCOM_DEV(DIGIANSWER, ZIGBEE802154),
183204431Sraj    USLCOM_DEV(DYNASTREAM, ANTDEVBOARD),
184204431Sraj    USLCOM_DEV(DYNASTREAM, ANTDEVBOARD2),
185204431Sraj    USLCOM_DEV(DYNASTREAM, ANT2USB),
186204431Sraj    USLCOM_DEV(ELV, USBI2C),
187204431Sraj    USLCOM_DEV(FOXCONN, PIRELLI_DP_L10),
188204431Sraj    USLCOM_DEV(FOXCONN, TCOM_TC_300),
189204431Sraj    USLCOM_DEV(GEMALTO, PROXPU),
190204431Sraj    USLCOM_DEV(JABLOTRON, PC60B),
191204431Sraj    USLCOM_DEV(MEI, CASHFLOW_SC),
192204431Sraj    USLCOM_DEV(MEI, S2000),
193204431Sraj    USLCOM_DEV(JABLOTRON, PC60B),
194204431Sraj    USLCOM_DEV(OWEN, AC4),
195204431Sraj    USLCOM_DEV(PHILIPS, ACE1001),
196204431Sraj    USLCOM_DEV(PLX, CA42),
197204431Sraj    USLCOM_DEV(RENESAS, RX610),
198204431Sraj    USLCOM_DEV(SILABS, AEROCOMM),
199204431Sraj    USLCOM_DEV(SILABS, AMBER_AMB2560),
200204431Sraj    USLCOM_DEV(SILABS, ARGUSISP),
201204431Sraj    USLCOM_DEV(SILABS, ARKHAM_DS101_A),
202204431Sraj    USLCOM_DEV(SILABS, ARKHAM_DS101_M),
203204431Sraj    USLCOM_DEV(SILABS, ARYGON_MIFARE),
204204431Sraj    USLCOM_DEV(SILABS, AVIT_USB_TTL),
205204431Sraj    USLCOM_DEV(SILABS, B_G_H3000),
206204431Sraj    USLCOM_DEV(SILABS, BALLUFF_RFID),
207204431Sraj    USLCOM_DEV(SILABS, BEI_VCP),
208204431Sraj    USLCOM_DEV(SILABS, BSM7DUSB),
209204431Sraj    USLCOM_DEV(SILABS, BURNSIDE),
210204431Sraj    USLCOM_DEV(SILABS, C2_EDGE_MODEM),
211204431Sraj    USLCOM_DEV(SILABS, CP2102),
212204431Sraj    USLCOM_DEV(SILABS, CP210X_2),
213204431Sraj    USLCOM_DEV(SILABS, CRUMB128),
214204431Sraj    USLCOM_DEV(SILABS, CYGNAL),
215204431Sraj    USLCOM_DEV(SILABS, CYGNAL_DEBUG),
216204431Sraj    USLCOM_DEV(SILABS, CYGNAL_GPS),
217204431Sraj    USLCOM_DEV(SILABS, DEGREE),
218204431Sraj    USLCOM_DEV(SILABS, EMS_C1007),
219204431Sraj    USLCOM_DEV(SILABS, HELICOM),
220204431Sraj    USLCOM_DEV(SILABS, IMS_USB_RS422),
221204431Sraj    USLCOM_DEV(SILABS, INFINITY_MIC),
222204431Sraj    USLCOM_DEV(SILABS, INSYS_MODEM),
223204431Sraj    USLCOM_DEV(SILABS, KYOCERA_GPS),
224204431Sraj    USLCOM_DEV(SILABS, LIPOWSKY_HARP),
225204431Sraj    USLCOM_DEV(SILABS, LIPOWSKY_JTAG),
226204431Sraj    USLCOM_DEV(SILABS, LIPOWSKY_LIN),
227204431Sraj    USLCOM_DEV(SILABS, MC35PU),
228204431Sraj    USLCOM_DEV(SILABS, MJS_TOSLINK),
229204431Sraj    USLCOM_DEV(SILABS, MSD_DASHHAWK),
230204431Sraj    USLCOM_DEV(SILABS, POLOLU),
231204431Sraj    USLCOM_DEV(SILABS, PROCYON_AVS),
232204431Sraj    USLCOM_DEV(SILABS, SB_PARAMOUNT_ME),
233204431Sraj    USLCOM_DEV(SILABS, SUUNTO),
234204431Sraj    USLCOM_DEV(SILABS, TAMSMASTER),
235204431Sraj    USLCOM_DEV(SILABS, TELEGESYS_ETRX2),
236204431Sraj    USLCOM_DEV(SILABS, TRACIENT),
237204431Sraj    USLCOM_DEV(SILABS, TRAQMATE),
238204431Sraj    USLCOM_DEV(SILABS, USBCOUNT50),
239204431Sraj    USLCOM_DEV(SILABS, USBPULSE100),
240204431Sraj    USLCOM_DEV(SILABS, USBSCOPE50),
241204431Sraj    USLCOM_DEV(SILABS, USBWAVE12),
242204431Sraj    USLCOM_DEV(SILABS, VSTABI),
243204431Sraj    USLCOM_DEV(SILABS, WAVIT),
244204431Sraj    USLCOM_DEV(SILABS, WMRBATT),
245204431Sraj    USLCOM_DEV(SILABS, WMRRIGBLASTER),
246204431Sraj    USLCOM_DEV(SILABS, WMRRIGTALK),
247204431Sraj    USLCOM_DEV(SILABS, ZEPHYR_BIO),
248204431Sraj    USLCOM_DEV(SILABS2, DCU11CLONE),
249204431Sraj    USLCOM_DEV(SILABS3, GPRS_MODEM),
250204431Sraj    USLCOM_DEV(SILABS4, 100EU_MODEM),
251204431Sraj    USLCOM_DEV(SYNTECH, CYPHERLAB100),
252204431Sraj    USLCOM_DEV(USI, MC60),
253204431Sraj    USLCOM_DEV(VAISALA, CABLE),
254204431Sraj    USLCOM_DEV(WAGO, SERVICECABLE),
255204431Sraj    USLCOM_DEV(WAVESENSE, JAZZ),
256204431Sraj    USLCOM_DEV(WIENERPLEINBAUS, PL512),
257204431Sraj    USLCOM_DEV(WIENERPLEINBAUS, RCM),
258204431Sraj    USLCOM_DEV(WIENERPLEINBAUS, MPOD),
259204431Sraj    USLCOM_DEV(WIENERPLEINBAUS, CML),
260204431Sraj#undef USLCOM_DEV
261204431Sraj};
262204431Sraj
263204431Srajstatic device_method_t uslcom_methods[] = {
264204431Sraj	DEVMETHOD(device_probe, uslcom_probe),
265204431Sraj	DEVMETHOD(device_attach, uslcom_attach),
266204431Sraj	DEVMETHOD(device_detach, uslcom_detach),
267204431Sraj	{0, 0}
268204431Sraj};
269204431Sraj
270204431Srajstatic devclass_t uslcom_devclass;
271204431Sraj
272204431Srajstatic driver_t uslcom_driver = {
273204431Sraj	.name = "uslcom",
274204431Sraj	.methods = uslcom_methods,
275204431Sraj	.size = sizeof(struct uslcom_softc),
276204431Sraj};
277204431Sraj
278204431SrajDRIVER_MODULE(uslcom, uhub, uslcom_driver, uslcom_devclass, NULL, 0);
279204431SrajMODULE_DEPEND(uslcom, ucom, 1, 1, 1);
280204431SrajMODULE_DEPEND(uslcom, usb, 1, 1, 1);
281204431SrajMODULE_VERSION(uslcom, 1);
282204431Sraj
283204431Srajstatic int
284204431Srajuslcom_probe(device_t dev)
285204431Sraj{
286204431Sraj	struct usb_attach_arg *uaa = device_get_ivars(dev);
287204431Sraj
288204431Sraj	DPRINTFN(11, "\n");
289204431Sraj
290204431Sraj	if (uaa->usb_mode != USB_MODE_HOST) {
291204431Sraj		return (ENXIO);
292204431Sraj	}
293204431Sraj	if (uaa->info.bConfigIndex != USLCOM_CONFIG_INDEX) {
294204431Sraj		return (ENXIO);
295204431Sraj	}
296204431Sraj	if (uaa->info.bIfaceIndex != USLCOM_IFACE_INDEX) {
297204431Sraj		return (ENXIO);
298204431Sraj	}
299204431Sraj	return (usbd_lookup_id_by_uaa(uslcom_devs, sizeof(uslcom_devs), uaa));
300204431Sraj}
301204431Sraj
302204431Srajstatic int
303204431Srajuslcom_attach(device_t dev)
304204431Sraj{
305204431Sraj	struct usb_attach_arg *uaa = device_get_ivars(dev);
306204431Sraj	struct uslcom_softc *sc = device_get_softc(dev);
307204431Sraj	int error;
308204431Sraj
309204431Sraj	DPRINTFN(11, "\n");
310204431Sraj
311204431Sraj	device_set_usb_desc(dev);
312204431Sraj	mtx_init(&sc->sc_mtx, "uslcom", NULL, MTX_DEF);
313204431Sraj
314204431Sraj	sc->sc_udev = uaa->device;
315204431Sraj
316204431Sraj	error = usbd_transfer_setup(uaa->device,
317204431Sraj	    &uaa->info.bIfaceIndex, sc->sc_xfer, uslcom_config,
318204431Sraj	    USLCOM_N_TRANSFER, sc, &sc->sc_mtx);
319204431Sraj	if (error) {
320204431Sraj		DPRINTF("one or more missing USB endpoints, "
321204431Sraj		    "error=%s\n", usbd_errstr(error));
322204431Sraj		goto detach;
323204431Sraj	}
324204431Sraj	/* clear stall at first run */
325204431Sraj	mtx_lock(&sc->sc_mtx);
326204431Sraj	usbd_xfer_set_stall(sc->sc_xfer[USLCOM_BULK_DT_WR]);
327204431Sraj	usbd_xfer_set_stall(sc->sc_xfer[USLCOM_BULK_DT_RD]);
328204431Sraj	mtx_unlock(&sc->sc_mtx);
329204431Sraj
330204431Sraj	error = ucom_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc,
331204431Sraj	    &uslcom_callback, &sc->sc_mtx);
332204431Sraj	if (error) {
333204431Sraj		goto detach;
334204431Sraj	}
335204431Sraj	ucom_set_pnpinfo_usb(&sc->sc_super_ucom, dev);
336204431Sraj
337204431Sraj	return (0);
338204431Sraj
339204431Srajdetach:
340204431Sraj	uslcom_detach(dev);
341204431Sraj	return (ENXIO);
342204431Sraj}
343204431Sraj
344204431Srajstatic int
345204431Srajuslcom_detach(device_t dev)
346238742Simp{
347238742Simp	struct uslcom_softc *sc = device_get_softc(dev);
348238742Simp
349238742Simp	DPRINTF("sc=%p\n", sc);
350238742Simp
351238742Simp	ucom_detach(&sc->sc_super_ucom, &sc->sc_ucom);
352238742Simp	usbd_transfer_unsetup(sc->sc_xfer, USLCOM_N_TRANSFER);
353238742Simp	mtx_destroy(&sc->sc_mtx);
354238742Simp
355238742Simp	return (0);
356238742Simp}
357238742Simp
358238742Simpstatic void
359238742Simpuslcom_open(struct ucom_softc *ucom)
360238742Simp{
361238742Simp	struct uslcom_softc *sc = ucom->sc_parent;
362238742Simp	struct usb_device_request req;
363238742Simp
364238742Simp	req.bmRequestType = USLCOM_WRITE;
365238742Simp	req.bRequest = USLCOM_UART;
366238742Simp	USETW(req.wValue, USLCOM_UART_ENABLE);
367238742Simp	USETW(req.wIndex, USLCOM_PORT_NO);
368238742Simp	USETW(req.wLength, 0);
369238742Simp
370238742Simp        if (ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
371238742Simp	    &req, NULL, 0, 1000)) {
372238742Simp		DPRINTF("UART enable failed (ignored)\n");
373238742Simp	}
374238742Simp}
375238742Simp
376238742Simpstatic void
377238742Simpuslcom_close(struct ucom_softc *ucom)
378238742Simp{
379238742Simp	struct uslcom_softc *sc = ucom->sc_parent;
380238742Simp	struct usb_device_request req;
381238742Simp
382238742Simp	req.bmRequestType = USLCOM_WRITE;
383238742Simp	req.bRequest = USLCOM_UART;
384238742Simp	USETW(req.wValue, USLCOM_UART_DISABLE);
385238742Simp	USETW(req.wIndex, USLCOM_PORT_NO);
386238742Simp	USETW(req.wLength, 0);
387238742Simp
388238742Simp        if (ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
389238742Simp	    &req, NULL, 0, 1000)) {
390238742Simp		DPRINTF("UART disable failed (ignored)\n");
391238742Simp	}
392238742Simp}
393238742Simp
394238742Simpstatic void
395238742Simpuslcom_set_dtr(struct ucom_softc *ucom, uint8_t onoff)
396238742Simp{
397238742Simp        struct uslcom_softc *sc = ucom->sc_parent;
398238742Simp	struct usb_device_request req;
399238742Simp	uint16_t ctl;
400238742Simp
401238742Simp        DPRINTF("onoff = %d\n", onoff);
402238742Simp
403238742Simp	ctl = onoff ? USLCOM_CTRL_DTR_ON : 0;
404238742Simp	ctl |= USLCOM_CTRL_DTR_SET;
405238742Simp
406238742Simp	req.bmRequestType = USLCOM_WRITE;
407238742Simp	req.bRequest = USLCOM_CTRL;
408238742Simp	USETW(req.wValue, ctl);
409238742Simp	USETW(req.wIndex, USLCOM_PORT_NO);
410238742Simp	USETW(req.wLength, 0);
411238742Simp
412238742Simp        if (ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
413238742Simp	    &req, NULL, 0, 1000)) {
414238742Simp		DPRINTF("Setting DTR failed (ignored)\n");
415204433Sraj	}
416204433Sraj}
417204433Sraj
418204433Srajstatic void
419204433Srajuslcom_set_rts(struct ucom_softc *ucom, uint8_t onoff)
420204433Sraj{
421204433Sraj        struct uslcom_softc *sc = ucom->sc_parent;
422204433Sraj	struct usb_device_request req;
423204433Sraj	uint16_t ctl;
424204433Sraj
425204433Sraj        DPRINTF("onoff = %d\n", onoff);
426204433Sraj
427204433Sraj	ctl = onoff ? USLCOM_CTRL_RTS_ON : 0;
428204433Sraj	ctl |= USLCOM_CTRL_RTS_SET;
429204433Sraj
430204433Sraj	req.bmRequestType = USLCOM_WRITE;
431204431Sraj	req.bRequest = USLCOM_CTRL;
432204431Sraj	USETW(req.wValue, ctl);
433204431Sraj	USETW(req.wIndex, USLCOM_PORT_NO);
434204431Sraj	USETW(req.wLength, 0);
435204431Sraj
436204431Sraj        if (ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
437204431Sraj	    &req, NULL, 0, 1000)) {
438204431Sraj		DPRINTF("Setting DTR failed (ignored)\n");
439204431Sraj	}
440204431Sraj}
441204431Sraj
442204431Srajstatic int
443204431Srajuslcom_pre_param(struct ucom_softc *ucom, struct termios *t)
444204431Sraj{
445204431Sraj	if (t->c_ospeed <= 0 || t->c_ospeed > 921600)
446204431Sraj		return (EINVAL);
447204431Sraj	return (0);
448204431Sraj}
449204431Sraj
450204431Srajstatic void
451204431Srajuslcom_param(struct ucom_softc *ucom, struct termios *t)
452204431Sraj{
453204431Sraj	struct uslcom_softc *sc = ucom->sc_parent;
454204431Sraj	struct usb_device_request req;
455204431Sraj	uint16_t data;
456204431Sraj
457204431Sraj	DPRINTF("\n");
458204431Sraj
459204431Sraj	req.bmRequestType = USLCOM_WRITE;
460204431Sraj	req.bRequest = USLCOM_BAUD_RATE;
461204431Sraj	USETW(req.wValue, USLCOM_BAUD_REF / t->c_ospeed);
462204431Sraj	USETW(req.wIndex, USLCOM_PORT_NO);
463204431Sraj	USETW(req.wLength, 0);
464204431Sraj
465204431Sraj        if (ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
466204431Sraj	    &req, NULL, 0, 1000)) {
467204431Sraj		DPRINTF("Set baudrate failed (ignored)\n");
468238742Simp	}
469238742Simp
470238742Simp	if (t->c_cflag & CSTOPB)
471238742Simp		data = USLCOM_STOP_BITS_2;
472238742Simp	else
473238742Simp		data = USLCOM_STOP_BITS_1;
474238742Simp	if (t->c_cflag & PARENB) {
475238742Simp		if (t->c_cflag & PARODD)
476238742Simp			data |= USLCOM_PARITY_ODD;
477238742Simp		else
478238742Simp			data |= USLCOM_PARITY_EVEN;
479238742Simp	} else
480238742Simp		data |= USLCOM_PARITY_NONE;
481238742Simp	switch (t->c_cflag & CSIZE) {
482238742Simp	case CS5:
483238742Simp		data |= USLCOM_SET_DATA_BITS(5);
484238742Simp		break;
485238742Simp	case CS6:
486238742Simp		data |= USLCOM_SET_DATA_BITS(6);
487238742Simp		break;
488238742Simp	case CS7:
489238742Simp		data |= USLCOM_SET_DATA_BITS(7);
490238742Simp		break;
491238742Simp	case CS8:
492238742Simp		data |= USLCOM_SET_DATA_BITS(8);
493238742Simp		break;
494238742Simp	}
495238742Simp
496238742Simp	req.bmRequestType = USLCOM_WRITE;
497238742Simp	req.bRequest = USLCOM_DATA;
498238742Simp	USETW(req.wValue, data);
499238742Simp	USETW(req.wIndex, USLCOM_PORT_NO);
500238742Simp	USETW(req.wLength, 0);
501238742Simp
502204433Sraj        if (ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
503204433Sraj	    &req, NULL, 0, 1000)) {
504204433Sraj		DPRINTF("Set format failed (ignored)\n");
505204433Sraj	}
506204433Sraj	return;
507204433Sraj}
508204433Sraj
509204433Srajstatic void
510204433Srajuslcom_get_status(struct ucom_softc *ucom, uint8_t *lsr, uint8_t *msr)
511204433Sraj{
512204433Sraj	struct uslcom_softc *sc = ucom->sc_parent;
513204433Sraj
514204433Sraj	DPRINTF("\n");
515204433Sraj
516204431Sraj	*lsr = sc->sc_lsr;
517204431Sraj	*msr = sc->sc_msr;
518204431Sraj}
519204431Sraj
520204431Srajstatic void
521204431Srajuslcom_set_break(struct ucom_softc *ucom, uint8_t onoff)
522204431Sraj{
523204431Sraj        struct uslcom_softc *sc = ucom->sc_parent;
524204431Sraj	struct usb_device_request req;
525204431Sraj	uint16_t brk = onoff ? USLCOM_BREAK_ON : USLCOM_BREAK_OFF;
526204431Sraj
527204431Sraj	req.bmRequestType = USLCOM_WRITE;
528204431Sraj	req.bRequest = USLCOM_BREAK;
529204431Sraj	USETW(req.wValue, brk);
530204431Sraj	USETW(req.wIndex, USLCOM_PORT_NO);
531204431Sraj	USETW(req.wLength, 0);
532204431Sraj
533204431Sraj        if (ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
534204431Sraj	    &req, NULL, 0, 1000)) {
535204431Sraj		DPRINTF("Set BREAK failed (ignored)\n");
536204431Sraj	}
537204431Sraj}
538204431Sraj
539204431Srajstatic void
540204431Srajuslcom_write_callback(struct usb_xfer *xfer, usb_error_t error)
541204431Sraj{
542204431Sraj	struct uslcom_softc *sc = usbd_xfer_softc(xfer);
543204431Sraj	struct usb_page_cache *pc;
544204431Sraj	uint32_t actlen;
545204431Sraj
546204431Sraj	switch (USB_GET_STATE(xfer)) {
547204431Sraj	case USB_ST_SETUP:
548204431Sraj	case USB_ST_TRANSFERRED:
549204431Srajtr_setup:
550204431Sraj		pc = usbd_xfer_get_frame(xfer, 0);
551204431Sraj		if (ucom_get_data(&sc->sc_ucom, pc, 0,
552204431Sraj		    USLCOM_BULK_BUF_SIZE, &actlen)) {
553204431Sraj
554204431Sraj			DPRINTF("actlen = %d\n", actlen);
555204431Sraj
556204431Sraj			usbd_xfer_set_frame_len(xfer, 0, actlen);
557204431Sraj			usbd_transfer_submit(xfer);
558204431Sraj		}
559204431Sraj		return;
560204431Sraj
561204431Sraj	default:			/* Error */
562204431Sraj		if (error != USB_ERR_CANCELLED) {
563204431Sraj			/* try to clear stall first */
564204431Sraj			usbd_xfer_set_stall(xfer);
565204433Sraj			goto tr_setup;
566204433Sraj		}
567204433Sraj		return;
568204433Sraj	}
569204433Sraj}
570204433Sraj
571204433Srajstatic void
572204433Srajuslcom_read_callback(struct usb_xfer *xfer, usb_error_t error)
573204433Sraj{
574204433Sraj	struct uslcom_softc *sc = usbd_xfer_softc(xfer);
575204433Sraj	struct usb_page_cache *pc;
576204433Sraj	int actlen;
577204433Sraj
578204433Sraj	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
579204433Sraj
580204433Sraj	switch (USB_GET_STATE(xfer)) {
581204433Sraj	case USB_ST_TRANSFERRED:
582204433Sraj		pc = usbd_xfer_get_frame(xfer, 0);
583204433Sraj		ucom_put_data(&sc->sc_ucom, pc, 0, actlen);
584204433Sraj
585204433Sraj	case USB_ST_SETUP:
586204433Srajtr_setup:
587204433Sraj		usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
588204433Sraj		usbd_transfer_submit(xfer);
589204433Sraj		return;
590204433Sraj
591204431Sraj	default:			/* Error */
592204431Sraj		if (error != USB_ERR_CANCELLED) {
593204431Sraj			/* try to clear stall first */
594204431Sraj			usbd_xfer_set_stall(xfer);
595204431Sraj			goto tr_setup;
596204431Sraj		}
597204431Sraj		return;
598204431Sraj	}
599204431Sraj}
600204431Sraj
601204431Srajstatic void
602204431Srajuslcom_start_read(struct ucom_softc *ucom)
603204431Sraj{
604204431Sraj	struct uslcom_softc *sc = ucom->sc_parent;
605204431Sraj
606204431Sraj	/* start read endpoint */
607204431Sraj	usbd_transfer_start(sc->sc_xfer[USLCOM_BULK_DT_RD]);
608204431Sraj}
609204431Sraj
610204431Srajstatic void
611204431Srajuslcom_stop_read(struct ucom_softc *ucom)
612204431Sraj{
613204431Sraj	struct uslcom_softc *sc = ucom->sc_parent;
614204431Sraj
615204431Sraj	/* stop read endpoint */
616204431Sraj	usbd_transfer_stop(sc->sc_xfer[USLCOM_BULK_DT_RD]);
617204431Sraj}
618204431Sraj
619204431Srajstatic void
620204431Srajuslcom_start_write(struct ucom_softc *ucom)
621204431Sraj{
622204431Sraj	struct uslcom_softc *sc = ucom->sc_parent;
623204431Sraj
624204431Sraj	usbd_transfer_start(sc->sc_xfer[USLCOM_BULK_DT_WR]);
625204431Sraj}
626204431Sraj
627204431Srajstatic void
628204431Srajuslcom_stop_write(struct ucom_softc *ucom)
629204431Sraj{
630204431Sraj	struct uslcom_softc *sc = ucom->sc_parent;
631204431Sraj
632204431Sraj	usbd_transfer_stop(sc->sc_xfer[USLCOM_BULK_DT_WR]);
633204431Sraj}
634204431Sraj
635204431Srajstatic void
636204431Srajuslcom_poll(struct ucom_softc *ucom)
637204431Sraj{
638204431Sraj	struct uslcom_softc *sc = ucom->sc_parent;
639204431Sraj	usbd_transfer_poll(sc->sc_xfer, USLCOM_N_TRANSFER);
640204431Sraj}
641204431Sraj