uslcom.c revision 194228
1218885Sdim/*	$OpenBSD: uslcom.c,v 1.17 2007/11/24 10:52:12 jsg Exp $	*/
2218885Sdim
3218885Sdim#include <sys/cdefs.h>
4218885Sdim__FBSDID("$FreeBSD: head/sys/dev/usb/serial/uslcom.c 194228 2009-06-15 01:02:43Z thompsa $");
5218885Sdim
6218885Sdim/*
7218885Sdim * Copyright (c) 2006 Jonathan Gray <jsg@openbsd.org>
8218885Sdim *
9218885Sdim * Permission to use, copy, modify, and distribute this software for any
10218885Sdim * purpose with or without fee is hereby granted, provided that the above
11218885Sdim * copyright notice and this permission notice appear in all copies.
12218885Sdim *
13218885Sdim * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
14218885Sdim * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
15218885Sdim * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
16218885Sdim * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
17223017Sdim * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
18223017Sdim * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
19223017Sdim * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
20218885Sdim */
21218885Sdim
22218885Sdim#include "usbdevs.h"
23218885Sdim#include <dev/usb/usb.h>
24218885Sdim#include <dev/usb/usb_mfunc.h>
25218885Sdim#include <dev/usb/usb_error.h>
26218885Sdim
27218885Sdim#define	USB_DEBUG_VAR uslcom_debug
28218885Sdim
29218885Sdim#include <dev/usb/usb_core.h>
30218885Sdim#include <dev/usb/usb_debug.h>
31218885Sdim#include <dev/usb/usb_process.h>
32218885Sdim#include <dev/usb/usb_request.h>
33218885Sdim#include <dev/usb/usb_lookup.h>
34218885Sdim#include <dev/usb/usb_util.h>
35218885Sdim#include <dev/usb/usb_busdma.h>
36218885Sdim
37218885Sdim#include <dev/usb/serial/usb_serial.h>
38218885Sdim
39218885Sdim#if USB_DEBUG
40218885Sdimstatic int uslcom_debug = 0;
41218885Sdim
42218885SdimSYSCTL_NODE(_hw_usb, OID_AUTO, uslcom, CTLFLAG_RW, 0, "USB uslcom");
43218885SdimSYSCTL_INT(_hw_usb_uslcom, OID_AUTO, debug, CTLFLAG_RW,
44218885Sdim    &uslcom_debug, 0, "Debug level");
45218885Sdim#endif
46218885Sdim
47223017Sdim#define	USLCOM_BULK_BUF_SIZE		1024
48223017Sdim#define	USLCOM_CONFIG_INDEX	0
49223017Sdim#define	USLCOM_IFACE_INDEX	0
50223017Sdim
51223017Sdim#define	USLCOM_SET_DATA_BITS(x)	((x) << 8)
52223017Sdim
53223017Sdim#define	USLCOM_WRITE		0x41
54223017Sdim#define	USLCOM_READ		0xc1
55223017Sdim
56223017Sdim#define	USLCOM_UART		0x00
57223017Sdim#define	USLCOM_BAUD_RATE	0x01
58223017Sdim#define	USLCOM_DATA		0x03
59223017Sdim#define	USLCOM_BREAK		0x05
60223017Sdim#define	USLCOM_CTRL		0x07
61223017Sdim
62223017Sdim#define	USLCOM_UART_DISABLE	0x00
63223017Sdim#define	USLCOM_UART_ENABLE	0x01
64223017Sdim
65223017Sdim#define	USLCOM_CTRL_DTR_ON	0x0001
66223017Sdim#define	USLCOM_CTRL_DTR_SET	0x0100
67223017Sdim#define	USLCOM_CTRL_RTS_ON	0x0002
68223017Sdim#define	USLCOM_CTRL_RTS_SET	0x0200
69223017Sdim#define	USLCOM_CTRL_CTS		0x0010
70223017Sdim#define	USLCOM_CTRL_DSR		0x0020
71223017Sdim#define	USLCOM_CTRL_DCD		0x0080
72223017Sdim
73223017Sdim#define	USLCOM_BAUD_REF		0x384000
74223017Sdim
75218885Sdim#define	USLCOM_STOP_BITS_1	0x00
76218885Sdim#define	USLCOM_STOP_BITS_2	0x02
77218885Sdim
78218885Sdim#define	USLCOM_PARITY_NONE	0x00
79218885Sdim#define	USLCOM_PARITY_ODD	0x10
80218885Sdim#define	USLCOM_PARITY_EVEN	0x20
81218885Sdim
82218885Sdim#define	USLCOM_PORT_NO		0xFFFF /* XXX think this should be 0 --hps */
83218885Sdim
84218885Sdim#define	USLCOM_BREAK_OFF	0x00
85218885Sdim#define	USLCOM_BREAK_ON		0x01
86218885Sdim
87218885Sdimenum {
88218885Sdim	USLCOM_BULK_DT_WR,
89218885Sdim	USLCOM_BULK_DT_RD,
90218885Sdim	USLCOM_N_TRANSFER,
91218885Sdim};
92218885Sdim
93218885Sdimstruct uslcom_softc {
94218885Sdim	struct ucom_super_softc sc_super_ucom;
95218885Sdim	struct ucom_softc sc_ucom;
96218885Sdim
97218885Sdim	struct usb_xfer *sc_xfer[USLCOM_N_TRANSFER];
98218885Sdim	struct usb_device *sc_udev;
99218885Sdim	struct mtx sc_mtx;
100218885Sdim
101218885Sdim	uint8_t		 sc_msr;
102218885Sdim	uint8_t		 sc_lsr;
103218885Sdim};
104223017Sdim
105223017Sdimstatic device_probe_t uslcom_probe;
106223017Sdimstatic device_attach_t uslcom_attach;
107223017Sdimstatic device_detach_t uslcom_detach;
108223017Sdim
109223017Sdimstatic usb_callback_t uslcom_write_callback;
110223017Sdimstatic usb_callback_t uslcom_read_callback;
111223017Sdim
112223017Sdimstatic void uslcom_open(struct ucom_softc *);
113223017Sdimstatic void uslcom_close(struct ucom_softc *);
114223017Sdimstatic void uslcom_set_dtr(struct ucom_softc *, uint8_t);
115223017Sdimstatic void uslcom_set_rts(struct ucom_softc *, uint8_t);
116223017Sdimstatic void uslcom_set_break(struct ucom_softc *, uint8_t);
117223017Sdimstatic int uslcom_pre_param(struct ucom_softc *, struct termios *);
118223017Sdimstatic void uslcom_param(struct ucom_softc *, struct termios *);
119223017Sdimstatic void uslcom_get_status(struct ucom_softc *, uint8_t *, uint8_t *);
120223017Sdimstatic void uslcom_start_read(struct ucom_softc *);
121218885Sdimstatic void uslcom_stop_read(struct ucom_softc *);
122218885Sdimstatic void uslcom_start_write(struct ucom_softc *);
123218885Sdimstatic void uslcom_stop_write(struct ucom_softc *);
124218885Sdim
125218885Sdimstatic const struct usb_config uslcom_config[USLCOM_N_TRANSFER] = {
126218885Sdim
127218885Sdim	[USLCOM_BULK_DT_WR] = {
128218885Sdim		.type = UE_BULK,
129218885Sdim		.endpoint = UE_ADDR_ANY,
130218885Sdim		.direction = UE_DIR_OUT,
131218885Sdim		.bufsize = USLCOM_BULK_BUF_SIZE,
132218885Sdim		.flags = {.pipe_bof = 1,.force_short_xfer = 1,},
133218885Sdim		.callback = &uslcom_write_callback,
134218885Sdim	},
135218885Sdim
136218885Sdim	[USLCOM_BULK_DT_RD] = {
137218885Sdim		.type = UE_BULK,
138218885Sdim		.endpoint = UE_ADDR_ANY,
139218885Sdim		.direction = UE_DIR_IN,
140218885Sdim		.bufsize = USLCOM_BULK_BUF_SIZE,
141218885Sdim		.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
142218885Sdim		.callback = &uslcom_read_callback,
143218885Sdim	},
144218885Sdim};
145218885Sdim
146218885Sdimstatic struct ucom_callback uslcom_callback = {
147218885Sdim	.ucom_cfg_open = &uslcom_open,
148218885Sdim	.ucom_cfg_close = &uslcom_close,
149218885Sdim	.ucom_cfg_get_status = &uslcom_get_status,
150218885Sdim	.ucom_cfg_set_dtr = &uslcom_set_dtr,
151218885Sdim	.ucom_cfg_set_rts = &uslcom_set_rts,
152218885Sdim	.ucom_cfg_set_break = &uslcom_set_break,
153218885Sdim	.ucom_cfg_param = &uslcom_param,
154218885Sdim	.ucom_pre_param = &uslcom_pre_param,
155218885Sdim	.ucom_start_read = &uslcom_start_read,
156218885Sdim	.ucom_stop_read = &uslcom_stop_read,
157218885Sdim	.ucom_start_write = &uslcom_start_write,
158218885Sdim	.ucom_stop_write = &uslcom_stop_write,
159218885Sdim};
160218885Sdim
161218885Sdimstatic const struct usb_device_id uslcom_devs[] = {
162218885Sdim    { USB_VPI(USB_VENDOR_BALTECH,	USB_PRODUCT_BALTECH_CARDREADER, 0) },
163218885Sdim    { USB_VPI(USB_VENDOR_DYNASTREAM,	USB_PRODUCT_DYNASTREAM_ANTDEVBOARD, 0) },
164218885Sdim    { USB_VPI(USB_VENDOR_JABLOTRON,	USB_PRODUCT_JABLOTRON_PC60B, 0) },
165218885Sdim    { USB_VPI(USB_VENDOR_SILABS,	USB_PRODUCT_SILABS_ARGUSISP, 0) },
166218885Sdim    { USB_VPI(USB_VENDOR_SILABS,	USB_PRODUCT_SILABS_CRUMB128, 0) },
167218885Sdim    { USB_VPI(USB_VENDOR_SILABS,	USB_PRODUCT_SILABS_DEGREE, 0) },
168218885Sdim    { USB_VPI(USB_VENDOR_SILABS,	USB_PRODUCT_SILABS_BURNSIDE, 0) },
169218885Sdim    { USB_VPI(USB_VENDOR_SILABS,	USB_PRODUCT_SILABS_HELICOM, 0) },
170218885Sdim    { USB_VPI(USB_VENDOR_SILABS,	USB_PRODUCT_SILABS_LIPOWSKY_HARP, 0) },
171218885Sdim    { USB_VPI(USB_VENDOR_SILABS,	USB_PRODUCT_SILABS_LIPOWSKY_JTAG, 0) },
172218885Sdim    { USB_VPI(USB_VENDOR_SILABS,	USB_PRODUCT_SILABS_LIPOWSKY_LIN, 0) },
173218885Sdim    { USB_VPI(USB_VENDOR_SILABS,	USB_PRODUCT_SILABS_POLOLU, 0) },
174218885Sdim    { USB_VPI(USB_VENDOR_SILABS,	USB_PRODUCT_SILABS_CP2102, 0) },
175218885Sdim    { USB_VPI(USB_VENDOR_SILABS,	USB_PRODUCT_SILABS_CP210X_2, 0) },
176218885Sdim    { USB_VPI(USB_VENDOR_SILABS,	USB_PRODUCT_SILABS_SUUNTO, 0) },
177218885Sdim    { USB_VPI(USB_VENDOR_SILABS,	USB_PRODUCT_SILABS_TRAQMATE, 0) },
178218885Sdim    { USB_VPI(USB_VENDOR_SILABS2,	USB_PRODUCT_SILABS2_DCU11CLONE, 0) },
179218885Sdim    { USB_VPI(USB_VENDOR_USI,		USB_PRODUCT_USI_MC60, 0) },
180218885Sdim};
181218885Sdim
182218885Sdimstatic device_method_t uslcom_methods[] = {
183218885Sdim	DEVMETHOD(device_probe, uslcom_probe),
184218885Sdim	DEVMETHOD(device_attach, uslcom_attach),
185218885Sdim	DEVMETHOD(device_detach, uslcom_detach),
186223017Sdim	{0, 0}
187223017Sdim};
188223017Sdim
189223017Sdimstatic devclass_t uslcom_devclass;
190223017Sdim
191223017Sdimstatic driver_t uslcom_driver = {
192223017Sdim	.name = "uslcom",
193223017Sdim	.methods = uslcom_methods,
194223017Sdim	.size = sizeof(struct uslcom_softc),
195223017Sdim};
196223017Sdim
197223017SdimDRIVER_MODULE(uslcom, uhub, uslcom_driver, uslcom_devclass, NULL, 0);
198223017SdimMODULE_DEPEND(uslcom, ucom, 1, 1, 1);
199223017SdimMODULE_DEPEND(uslcom, usb, 1, 1, 1);
200223017SdimMODULE_VERSION(uslcom, 1);
201223017Sdim
202223017Sdimstatic int
203223017Sdimuslcom_probe(device_t dev)
204223017Sdim{
205223017Sdim	struct usb_attach_arg *uaa = device_get_ivars(dev);
206223017Sdim
207223017Sdim	DPRINTFN(11, "\n");
208223017Sdim
209223017Sdim	if (uaa->usb_mode != USB_MODE_HOST) {
210223017Sdim		return (ENXIO);
211223017Sdim	}
212223017Sdim	if (uaa->info.bConfigIndex != USLCOM_CONFIG_INDEX) {
213223017Sdim		return (ENXIO);
214223017Sdim	}
215223017Sdim	if (uaa->info.bIfaceIndex != USLCOM_IFACE_INDEX) {
216223017Sdim		return (ENXIO);
217223017Sdim	}
218223017Sdim	return (usbd_lookup_id_by_uaa(uslcom_devs, sizeof(uslcom_devs), uaa));
219223017Sdim}
220223017Sdim
221223017Sdimstatic int
222223017Sdimuslcom_attach(device_t dev)
223223017Sdim{
224223017Sdim	struct usb_attach_arg *uaa = device_get_ivars(dev);
225223017Sdim	struct uslcom_softc *sc = device_get_softc(dev);
226223017Sdim	int error;
227223017Sdim
228223017Sdim	DPRINTFN(11, "\n");
229223017Sdim
230223017Sdim	device_set_usb_desc(dev);
231223017Sdim	mtx_init(&sc->sc_mtx, "uslcom", NULL, MTX_DEF);
232223017Sdim
233223017Sdim	sc->sc_udev = uaa->device;
234223017Sdim
235223017Sdim	error = usbd_transfer_setup(uaa->device,
236223017Sdim	    &uaa->info.bIfaceIndex, sc->sc_xfer, uslcom_config,
237223017Sdim	    USLCOM_N_TRANSFER, sc, &sc->sc_mtx);
238223017Sdim	if (error) {
239223017Sdim		DPRINTF("one or more missing USB endpoints, "
240223017Sdim		    "error=%s\n", usbd_errstr(error));
241223017Sdim		goto detach;
242223017Sdim	}
243223017Sdim	/* clear stall at first run */
244223017Sdim	mtx_lock(&sc->sc_mtx);
245223017Sdim	usbd_transfer_set_stall(sc->sc_xfer[USLCOM_BULK_DT_WR]);
246223017Sdim	usbd_transfer_set_stall(sc->sc_xfer[USLCOM_BULK_DT_RD]);
247223017Sdim	mtx_unlock(&sc->sc_mtx);
248223017Sdim
249223017Sdim	error = ucom_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc,
250223017Sdim	    &uslcom_callback, &sc->sc_mtx);
251223017Sdim	if (error) {
252223017Sdim		goto detach;
253223017Sdim	}
254223017Sdim	return (0);
255223017Sdim
256223017Sdimdetach:
257223017Sdim	uslcom_detach(dev);
258223017Sdim	return (ENXIO);
259223017Sdim}
260223017Sdim
261223017Sdimstatic int
262223017Sdimuslcom_detach(device_t dev)
263223017Sdim{
264223017Sdim	struct uslcom_softc *sc = device_get_softc(dev);
265223017Sdim
266223017Sdim	DPRINTF("sc=%p\n", sc);
267223017Sdim
268223017Sdim	ucom_detach(&sc->sc_super_ucom, &sc->sc_ucom, 1);
269223017Sdim	usbd_transfer_unsetup(sc->sc_xfer, USLCOM_N_TRANSFER);
270223017Sdim	mtx_destroy(&sc->sc_mtx);
271223017Sdim
272223017Sdim	return (0);
273223017Sdim}
274223017Sdim
275223017Sdimstatic void
276223017Sdimuslcom_open(struct ucom_softc *ucom)
277223017Sdim{
278223017Sdim	struct uslcom_softc *sc = ucom->sc_parent;
279223017Sdim	struct usb_device_request req;
280223017Sdim
281223017Sdim	req.bmRequestType = USLCOM_WRITE;
282223017Sdim	req.bRequest = USLCOM_UART;
283223017Sdim	USETW(req.wValue, USLCOM_UART_ENABLE);
284223017Sdim	USETW(req.wIndex, USLCOM_PORT_NO);
285223017Sdim	USETW(req.wLength, 0);
286223017Sdim
287223017Sdim        if (ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
288223017Sdim	    &req, NULL, 0, 1000)) {
289223017Sdim		DPRINTF("UART enable failed (ignored)\n");
290223017Sdim	}
291223017Sdim}
292223017Sdim
293223017Sdimstatic void
294223017Sdimuslcom_close(struct ucom_softc *ucom)
295223017Sdim{
296223017Sdim	struct uslcom_softc *sc = ucom->sc_parent;
297223017Sdim	struct usb_device_request req;
298223017Sdim
299223017Sdim	req.bmRequestType = USLCOM_WRITE;
300223017Sdim	req.bRequest = USLCOM_UART;
301223017Sdim	USETW(req.wValue, USLCOM_UART_DISABLE);
302223017Sdim	USETW(req.wIndex, USLCOM_PORT_NO);
303223017Sdim	USETW(req.wLength, 0);
304223017Sdim
305223017Sdim        if (ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
306223017Sdim	    &req, NULL, 0, 1000)) {
307223017Sdim		DPRINTF("UART disable failed (ignored)\n");
308223017Sdim	}
309223017Sdim}
310223017Sdim
311223017Sdimstatic void
312223017Sdimuslcom_set_dtr(struct ucom_softc *ucom, uint8_t onoff)
313223017Sdim{
314223017Sdim        struct uslcom_softc *sc = ucom->sc_parent;
315223017Sdim	struct usb_device_request req;
316223017Sdim	uint16_t ctl;
317223017Sdim
318223017Sdim        DPRINTF("onoff = %d\n", onoff);
319223017Sdim
320223017Sdim	ctl = onoff ? USLCOM_CTRL_DTR_ON : 0;
321223017Sdim	ctl |= USLCOM_CTRL_DTR_SET;
322223017Sdim
323223017Sdim	req.bmRequestType = USLCOM_WRITE;
324223017Sdim	req.bRequest = USLCOM_CTRL;
325223017Sdim	USETW(req.wValue, ctl);
326223017Sdim	USETW(req.wIndex, USLCOM_PORT_NO);
327223017Sdim	USETW(req.wLength, 0);
328223017Sdim
329223017Sdim        if (ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
330223017Sdim	    &req, NULL, 0, 1000)) {
331223017Sdim		DPRINTF("Setting DTR failed (ignored)\n");
332223017Sdim	}
333223017Sdim}
334223017Sdim
335223017Sdimstatic void
336223017Sdimuslcom_set_rts(struct ucom_softc *ucom, uint8_t onoff)
337223017Sdim{
338223017Sdim        struct uslcom_softc *sc = ucom->sc_parent;
339223017Sdim	struct usb_device_request req;
340223017Sdim	uint16_t ctl;
341223017Sdim
342223017Sdim        DPRINTF("onoff = %d\n", onoff);
343223017Sdim
344223017Sdim	ctl = onoff ? USLCOM_CTRL_RTS_ON : 0;
345223017Sdim	ctl |= USLCOM_CTRL_RTS_SET;
346223017Sdim
347223017Sdim	req.bmRequestType = USLCOM_WRITE;
348223017Sdim	req.bRequest = USLCOM_CTRL;
349223017Sdim	USETW(req.wValue, ctl);
350223017Sdim	USETW(req.wIndex, USLCOM_PORT_NO);
351223017Sdim	USETW(req.wLength, 0);
352223017Sdim
353223017Sdim        if (ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
354223017Sdim	    &req, NULL, 0, 1000)) {
355223017Sdim		DPRINTF("Setting DTR failed (ignored)\n");
356223017Sdim	}
357223017Sdim}
358223017Sdim
359223017Sdimstatic int
360223017Sdimuslcom_pre_param(struct ucom_softc *ucom, struct termios *t)
361223017Sdim{
362223017Sdim	if (t->c_ospeed <= 0 || t->c_ospeed > 921600)
363223017Sdim		return (EINVAL);
364223017Sdim	return (0);
365223017Sdim}
366223017Sdim
367223017Sdimstatic void
368223017Sdimuslcom_param(struct ucom_softc *ucom, struct termios *t)
369223017Sdim{
370223017Sdim	struct uslcom_softc *sc = ucom->sc_parent;
371223017Sdim	struct usb_device_request req;
372223017Sdim	uint16_t data;
373223017Sdim
374223017Sdim	DPRINTF("\n");
375223017Sdim
376223017Sdim	req.bmRequestType = USLCOM_WRITE;
377223017Sdim	req.bRequest = USLCOM_BAUD_RATE;
378223017Sdim	USETW(req.wValue, USLCOM_BAUD_REF / t->c_ospeed);
379223017Sdim	USETW(req.wIndex, USLCOM_PORT_NO);
380223017Sdim	USETW(req.wLength, 0);
381223017Sdim
382223017Sdim        if (ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
383223017Sdim	    &req, NULL, 0, 1000)) {
384223017Sdim		DPRINTF("Set baudrate failed (ignored)\n");
385223017Sdim	}
386223017Sdim
387223017Sdim	if (t->c_cflag & CSTOPB)
388223017Sdim		data = USLCOM_STOP_BITS_2;
389223017Sdim	else
390223017Sdim		data = USLCOM_STOP_BITS_1;
391223017Sdim	if (t->c_cflag & PARENB) {
392223017Sdim		if (t->c_cflag & PARODD)
393223017Sdim			data |= USLCOM_PARITY_ODD;
394223017Sdim		else
395223017Sdim			data |= USLCOM_PARITY_EVEN;
396223017Sdim	} else
397223017Sdim		data |= USLCOM_PARITY_NONE;
398223017Sdim	switch (t->c_cflag & CSIZE) {
399223017Sdim	case CS5:
400223017Sdim		data |= USLCOM_SET_DATA_BITS(5);
401223017Sdim		break;
402223017Sdim	case CS6:
403223017Sdim		data |= USLCOM_SET_DATA_BITS(6);
404224145Sdim		break;
405223017Sdim	case CS7:
406223017Sdim		data |= USLCOM_SET_DATA_BITS(7);
407223017Sdim		break;
408223017Sdim	case CS8:
409223017Sdim		data |= USLCOM_SET_DATA_BITS(8);
410223017Sdim		break;
411224145Sdim	}
412223017Sdim
413223017Sdim	req.bmRequestType = USLCOM_WRITE;
414223017Sdim	req.bRequest = USLCOM_DATA;
415223017Sdim	USETW(req.wValue, data);
416223017Sdim	USETW(req.wIndex, USLCOM_PORT_NO);
417223017Sdim	USETW(req.wLength, 0);
418223017Sdim
419224145Sdim        if (ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
420223017Sdim	    &req, NULL, 0, 1000)) {
421223017Sdim		DPRINTF("Set format failed (ignored)\n");
422223017Sdim	}
423223017Sdim	return;
424223017Sdim}
425223017Sdim
426223017Sdimstatic void
427223017Sdimuslcom_get_status(struct ucom_softc *ucom, uint8_t *lsr, uint8_t *msr)
428223017Sdim{
429223017Sdim	struct uslcom_softc *sc = ucom->sc_parent;
430223017Sdim
431223017Sdim	DPRINTF("\n");
432223017Sdim
433223017Sdim	*lsr = sc->sc_lsr;
434223017Sdim	*msr = sc->sc_msr;
435223017Sdim}
436218885Sdim
437218885Sdimstatic void
438218885Sdimuslcom_set_break(struct ucom_softc *ucom, uint8_t onoff)
439218885Sdim{
440218885Sdim        struct uslcom_softc *sc = ucom->sc_parent;
441218885Sdim	struct usb_device_request req;
442218885Sdim	uint16_t brk = onoff ? USLCOM_BREAK_ON : USLCOM_BREAK_OFF;
443
444	req.bmRequestType = USLCOM_WRITE;
445	req.bRequest = USLCOM_BREAK;
446	USETW(req.wValue, brk);
447	USETW(req.wIndex, USLCOM_PORT_NO);
448	USETW(req.wLength, 0);
449
450        if (ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
451	    &req, NULL, 0, 1000)) {
452		DPRINTF("Set BREAK failed (ignored)\n");
453	}
454}
455
456static void
457uslcom_write_callback(struct usb_xfer *xfer)
458{
459	struct uslcom_softc *sc = xfer->priv_sc;
460	uint32_t actlen;
461
462	switch (USB_GET_STATE(xfer)) {
463	case USB_ST_SETUP:
464	case USB_ST_TRANSFERRED:
465tr_setup:
466		if (ucom_get_data(&sc->sc_ucom, xfer->frbuffers, 0,
467		    USLCOM_BULK_BUF_SIZE, &actlen)) {
468
469			DPRINTF("actlen = %d\n", actlen);
470
471			xfer->frlengths[0] = actlen;
472			usbd_transfer_submit(xfer);
473		}
474		return;
475
476	default:			/* Error */
477		if (xfer->error != USB_ERR_CANCELLED) {
478			/* try to clear stall first */
479			xfer->flags.stall_pipe = 1;
480			goto tr_setup;
481		}
482		return;
483	}
484}
485
486static void
487uslcom_read_callback(struct usb_xfer *xfer)
488{
489	struct uslcom_softc *sc = xfer->priv_sc;
490
491	switch (USB_GET_STATE(xfer)) {
492	case USB_ST_TRANSFERRED:
493		ucom_put_data(&sc->sc_ucom, xfer->frbuffers, 0, xfer->actlen);
494
495	case USB_ST_SETUP:
496tr_setup:
497		xfer->frlengths[0] = xfer->max_data_length;
498		usbd_transfer_submit(xfer);
499		return;
500
501	default:			/* Error */
502		if (xfer->error != USB_ERR_CANCELLED) {
503			/* try to clear stall first */
504			xfer->flags.stall_pipe = 1;
505			goto tr_setup;
506		}
507		return;
508	}
509}
510
511static void
512uslcom_start_read(struct ucom_softc *ucom)
513{
514	struct uslcom_softc *sc = ucom->sc_parent;
515
516	/* start read endpoint */
517	usbd_transfer_start(sc->sc_xfer[USLCOM_BULK_DT_RD]);
518}
519
520static void
521uslcom_stop_read(struct ucom_softc *ucom)
522{
523	struct uslcom_softc *sc = ucom->sc_parent;
524
525	/* stop read endpoint */
526	usbd_transfer_stop(sc->sc_xfer[USLCOM_BULK_DT_RD]);
527}
528
529static void
530uslcom_start_write(struct ucom_softc *ucom)
531{
532	struct uslcom_softc *sc = ucom->sc_parent;
533
534	usbd_transfer_start(sc->sc_xfer[USLCOM_BULK_DT_WR]);
535}
536
537static void
538uslcom_stop_write(struct ucom_softc *ucom)
539{
540	struct uslcom_softc *sc = ucom->sc_parent;
541
542	usbd_transfer_stop(sc->sc_xfer[USLCOM_BULK_DT_WR]);
543}
544