uslcom.c revision 189275
138494Sobrien/*	$OpenBSD: uslcom.c,v 1.17 2007/11/24 10:52:12 jsg Exp $	*/
282794Sobrien
338494Sobrien#include <sys/cdefs.h>
438494Sobrien__FBSDID("$FreeBSD: head/sys/dev/usb/serial/uslcom.c 189275 2009-03-02 05:37:05Z thompsa $");
538494Sobrien
638494Sobrien/*
738494Sobrien * Copyright (c) 2006 Jonathan Gray <jsg@openbsd.org>
838494Sobrien *
938494Sobrien * Permission to use, copy, modify, and distribute this software for any
1038494Sobrien * purpose with or without fee is hereby granted, provided that the above
1138494Sobrien * copyright notice and this permission notice appear in all copies.
1238494Sobrien *
1338494Sobrien * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1438494Sobrien * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1538494Sobrien * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1638494Sobrien * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1738494Sobrien * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1838494Sobrien * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1938494Sobrien * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
2042629Sobrien */
2138494Sobrien
2238494Sobrien#include "usbdevs.h"
2338494Sobrien#include <dev/usb/usb.h>
2438494Sobrien#include <dev/usb/usb_mfunc.h>
2538494Sobrien#include <dev/usb/usb_error.h>
2638494Sobrien
2738494Sobrien#define	USB_DEBUG_VAR uslcom_debug
2838494Sobrien
2938494Sobrien#include <dev/usb/usb_core.h>
3038494Sobrien#include <dev/usb/usb_debug.h>
3138494Sobrien#include <dev/usb/usb_process.h>
3238494Sobrien#include <dev/usb/usb_request.h>
3338494Sobrien#include <dev/usb/usb_lookup.h>
3438494Sobrien#include <dev/usb/usb_util.h>
3538494Sobrien#include <dev/usb/usb_busdma.h>
3638494Sobrien
3738494Sobrien#include <dev/usb/serial/usb_serial.h>
3838494Sobrien
3938494Sobrien#if USB_DEBUG
4038494Sobrienstatic int uslcom_debug = 0;
4182794Sobrien
4238494SobrienSYSCTL_NODE(_hw_usb2, OID_AUTO, uslcom, CTLFLAG_RW, 0, "USB uslcom");
4338494SobrienSYSCTL_INT(_hw_usb2_uslcom, OID_AUTO, debug, CTLFLAG_RW,
4438494Sobrien    &uslcom_debug, 0, "Debug level");
4538494Sobrien#endif
4638494Sobrien
4738494Sobrien#define	USLCOM_BULK_BUF_SIZE		1024
4838494Sobrien#define	USLCOM_CONFIG_INDEX	0
4938494Sobrien#define	USLCOM_IFACE_INDEX	0
5038494Sobrien
5138494Sobrien#define	USLCOM_SET_DATA_BITS(x)	((x) << 8)
5238494Sobrien
5338494Sobrien#define	USLCOM_WRITE		0x41
5438494Sobrien#define	USLCOM_READ		0xc1
5538494Sobrien
5638494Sobrien#define	USLCOM_UART		0x00
5738494Sobrien#define	USLCOM_BAUD_RATE	0x01
5838494Sobrien#define	USLCOM_DATA		0x03
5938494Sobrien#define	USLCOM_BREAK		0x05
6038494Sobrien#define	USLCOM_CTRL		0x07
6138494Sobrien
6238494Sobrien#define	USLCOM_UART_DISABLE	0x00
6338494Sobrien#define	USLCOM_UART_ENABLE	0x01
6438494Sobrien
6538494Sobrien#define	USLCOM_CTRL_DTR_ON	0x0001
6638494Sobrien#define	USLCOM_CTRL_DTR_SET	0x0100
6738494Sobrien#define	USLCOM_CTRL_RTS_ON	0x0002
6842629Sobrien#define	USLCOM_CTRL_RTS_SET	0x0200
6938494Sobrien#define	USLCOM_CTRL_CTS		0x0010
7038494Sobrien#define	USLCOM_CTRL_DSR		0x0020
7138494Sobrien#define	USLCOM_CTRL_DCD		0x0080
7238494Sobrien
7338494Sobrien#define	USLCOM_BAUD_REF		0x384000
7438494Sobrien
7538494Sobrien#define	USLCOM_STOP_BITS_1	0x00
7638494Sobrien#define	USLCOM_STOP_BITS_2	0x02
7738494Sobrien
7838494Sobrien#define	USLCOM_PARITY_NONE	0x00
7938494Sobrien#define	USLCOM_PARITY_ODD	0x10
8038494Sobrien#define	USLCOM_PARITY_EVEN	0x20
8138494Sobrien
8238494Sobrien#define	USLCOM_PORT_NO		0xFFFF /* XXX think this should be 0 --hps */
8338494Sobrien
8438494Sobrien#define	USLCOM_BREAK_OFF	0x00
8538494Sobrien#define	USLCOM_BREAK_ON		0x01
8638494Sobrien
8738494Sobrienenum {
8838494Sobrien	USLCOM_BULK_DT_WR,
8938494Sobrien	USLCOM_BULK_DT_RD,
9038494Sobrien	USLCOM_N_TRANSFER,
9138494Sobrien};
9238494Sobrien
9338494Sobrienstruct uslcom_softc {
9438494Sobrien	struct usb2_com_super_softc sc_super_ucom;
9538494Sobrien	struct usb2_com_softc sc_ucom;
9638494Sobrien
9738494Sobrien	struct usb2_xfer *sc_xfer[USLCOM_N_TRANSFER];
9838494Sobrien	struct usb2_device *sc_udev;
9938494Sobrien	struct mtx sc_mtx;
10038494Sobrien
10138494Sobrien	uint8_t		 sc_msr;
10238494Sobrien	uint8_t		 sc_lsr;
10338494Sobrien};
10438494Sobrien
10538494Sobrienstatic device_probe_t uslcom_probe;
10638494Sobrienstatic device_attach_t uslcom_attach;
10738494Sobrienstatic device_detach_t uslcom_detach;
10838494Sobrien
10938494Sobrienstatic usb2_callback_t uslcom_write_callback;
11038494Sobrienstatic usb2_callback_t uslcom_read_callback;
11138494Sobrien
11238494Sobrienstatic void uslcom_open(struct usb2_com_softc *);
11338494Sobrienstatic void uslcom_close(struct usb2_com_softc *);
11438494Sobrienstatic void uslcom_set_dtr(struct usb2_com_softc *, uint8_t);
11538494Sobrienstatic void uslcom_set_rts(struct usb2_com_softc *, uint8_t);
11638494Sobrienstatic void uslcom_set_break(struct usb2_com_softc *, uint8_t);
11738494Sobrienstatic int uslcom_pre_param(struct usb2_com_softc *, struct termios *);
11838494Sobrienstatic void uslcom_param(struct usb2_com_softc *, struct termios *);
11938494Sobrienstatic void uslcom_get_status(struct usb2_com_softc *, uint8_t *, uint8_t *);
12038494Sobrienstatic void uslcom_start_read(struct usb2_com_softc *);
12138494Sobrienstatic void uslcom_stop_read(struct usb2_com_softc *);
12238494Sobrienstatic void uslcom_start_write(struct usb2_com_softc *);
12338494Sobrienstatic void uslcom_stop_write(struct usb2_com_softc *);
12438494Sobrien
12538494Sobrienstatic const struct usb2_config uslcom_config[USLCOM_N_TRANSFER] = {
12638494Sobrien
12738494Sobrien	[USLCOM_BULK_DT_WR] = {
12838494Sobrien		.type = UE_BULK,
12938494Sobrien		.endpoint = UE_ADDR_ANY,
13038494Sobrien		.direction = UE_DIR_OUT,
13138494Sobrien		.mh.bufsize = USLCOM_BULK_BUF_SIZE,
13238494Sobrien		.mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,},
13338494Sobrien		.mh.callback = &uslcom_write_callback,
13438494Sobrien	},
13538494Sobrien
13638494Sobrien	[USLCOM_BULK_DT_RD] = {
13738494Sobrien		.type = UE_BULK,
13838494Sobrien		.endpoint = UE_ADDR_ANY,
13938494Sobrien		.direction = UE_DIR_IN,
14038494Sobrien		.mh.bufsize = USLCOM_BULK_BUF_SIZE,
14138494Sobrien		.mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
14238494Sobrien		.mh.callback = &uslcom_read_callback,
14338494Sobrien	},
14438494Sobrien};
14538494Sobrien
14638494Sobrienstruct usb2_com_callback uslcom_callback = {
14738494Sobrien	.usb2_com_cfg_open = &uslcom_open,
14838494Sobrien	.usb2_com_cfg_close = &uslcom_close,
14938494Sobrien	.usb2_com_cfg_get_status = &uslcom_get_status,
15038494Sobrien	.usb2_com_cfg_set_dtr = &uslcom_set_dtr,
15138494Sobrien	.usb2_com_cfg_set_rts = &uslcom_set_rts,
15238494Sobrien	.usb2_com_cfg_set_break = &uslcom_set_break,
15338494Sobrien	.usb2_com_cfg_param = &uslcom_param,
15438494Sobrien	.usb2_com_pre_param = &uslcom_pre_param,
15538494Sobrien	.usb2_com_start_read = &uslcom_start_read,
15638494Sobrien	.usb2_com_stop_read = &uslcom_stop_read,
15738494Sobrien	.usb2_com_start_write = &uslcom_start_write,
15838494Sobrien	.usb2_com_stop_write = &uslcom_stop_write,
15938494Sobrien};
16038494Sobrien
16138494Sobrienstatic const struct usb2_device_id uslcom_devs[] = {
16238494Sobrien    { USB_VPI(USB_VENDOR_BALTECH,	USB_PRODUCT_BALTECH_CARDREADER, 0) },
16338494Sobrien    { USB_VPI(USB_VENDOR_DYNASTREAM,	USB_PRODUCT_DYNASTREAM_ANTDEVBOARD, 0) },
16438494Sobrien    { USB_VPI(USB_VENDOR_JABLOTRON,	USB_PRODUCT_JABLOTRON_PC60B, 0) },
16538494Sobrien    { USB_VPI(USB_VENDOR_SILABS,	USB_PRODUCT_SILABS_ARGUSISP, 0) },
16638494Sobrien    { USB_VPI(USB_VENDOR_SILABS,	USB_PRODUCT_SILABS_CRUMB128, 0) },
16738494Sobrien    { USB_VPI(USB_VENDOR_SILABS,	USB_PRODUCT_SILABS_DEGREE, 0) },
16838494Sobrien    { USB_VPI(USB_VENDOR_SILABS,	USB_PRODUCT_SILABS_BURNSIDE, 0) },
16982794Sobrien    { USB_VPI(USB_VENDOR_SILABS,	USB_PRODUCT_SILABS_HELICOM, 0) },
17082794Sobrien    { USB_VPI(USB_VENDOR_SILABS,	USB_PRODUCT_SILABS_LIPOWSKY_HARP, 0) },
17182794Sobrien    { USB_VPI(USB_VENDOR_SILABS,	USB_PRODUCT_SILABS_LIPOWSKY_JTAG, 0) },
17282794Sobrien    { USB_VPI(USB_VENDOR_SILABS,	USB_PRODUCT_SILABS_LIPOWSKY_LIN, 0) },
17382794Sobrien    { USB_VPI(USB_VENDOR_SILABS,	USB_PRODUCT_SILABS_POLOLU, 0) },
17482794Sobrien    { USB_VPI(USB_VENDOR_SILABS,	USB_PRODUCT_SILABS_CP2102, 0) },
17582794Sobrien    { USB_VPI(USB_VENDOR_SILABS,	USB_PRODUCT_SILABS_CP210X_2, 0) },
17682794Sobrien    { USB_VPI(USB_VENDOR_SILABS,	USB_PRODUCT_SILABS_SUUNTO, 0) },
17782794Sobrien    { USB_VPI(USB_VENDOR_SILABS,	USB_PRODUCT_SILABS_TRAQMATE, 0) },
17882794Sobrien    { USB_VPI(USB_VENDOR_SILABS2,	USB_PRODUCT_SILABS2_DCU11CLONE, 0) },
17982794Sobrien    { USB_VPI(USB_VENDOR_USI,		USB_PRODUCT_USI_MC60, 0) },
18082794Sobrien};
18182794Sobrien
18282794Sobrienstatic device_method_t uslcom_methods[] = {
18382794Sobrien	DEVMETHOD(device_probe, uslcom_probe),
18482794Sobrien	DEVMETHOD(device_attach, uslcom_attach),
18582794Sobrien	DEVMETHOD(device_detach, uslcom_detach),
18682794Sobrien	{0, 0}
18782794Sobrien};
18882794Sobrien
18982794Sobrienstatic devclass_t uslcom_devclass;
19082794Sobrien
19182794Sobrienstatic driver_t uslcom_driver = {
19282794Sobrien	.name = "uslcom",
19382794Sobrien	.methods = uslcom_methods,
19482794Sobrien	.size = sizeof(struct uslcom_softc),
19582794Sobrien};
19682794Sobrien
19782794SobrienDRIVER_MODULE(uslcom, uhub, uslcom_driver, uslcom_devclass, NULL, 0);
19882794SobrienMODULE_DEPEND(uslcom, ucom, 1, 1, 1);
19982794SobrienMODULE_DEPEND(uslcom, usb, 1, 1, 1);
20082794SobrienMODULE_VERSION(uslcom, 1);
20182794Sobrien
20282794Sobrienstatic int
20382794Sobrienuslcom_probe(device_t dev)
20482794Sobrien{
205	struct usb2_attach_arg *uaa = device_get_ivars(dev);
206
207	DPRINTFN(11, "\n");
208
209	if (uaa->usb2_mode != USB_MODE_HOST) {
210		return (ENXIO);
211	}
212	if (uaa->info.bConfigIndex != USLCOM_CONFIG_INDEX) {
213		return (ENXIO);
214	}
215	if (uaa->info.bIfaceIndex != USLCOM_IFACE_INDEX) {
216		return (ENXIO);
217	}
218	return (usb2_lookup_id_by_uaa(uslcom_devs, sizeof(uslcom_devs), uaa));
219}
220
221static int
222uslcom_attach(device_t dev)
223{
224	struct usb2_attach_arg *uaa = device_get_ivars(dev);
225	struct uslcom_softc *sc = device_get_softc(dev);
226	int error;
227
228	DPRINTFN(11, "\n");
229
230	device_set_usb2_desc(dev);
231	mtx_init(&sc->sc_mtx, "uslcom", NULL, MTX_DEF);
232
233	sc->sc_udev = uaa->device;
234
235	error = usb2_transfer_setup(uaa->device,
236	    &uaa->info.bIfaceIndex, sc->sc_xfer, uslcom_config,
237	    USLCOM_N_TRANSFER, sc, &sc->sc_mtx);
238	if (error) {
239		DPRINTF("one or more missing USB endpoints, "
240		    "error=%s\n", usb2_errstr(error));
241		goto detach;
242	}
243	/* clear stall at first run */
244	mtx_lock(&sc->sc_mtx);
245	usb2_transfer_set_stall(sc->sc_xfer[USLCOM_BULK_DT_WR]);
246	usb2_transfer_set_stall(sc->sc_xfer[USLCOM_BULK_DT_RD]);
247	mtx_unlock(&sc->sc_mtx);
248
249	error = usb2_com_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc,
250	    &uslcom_callback, &sc->sc_mtx);
251	if (error) {
252		goto detach;
253	}
254	return (0);
255
256detach:
257	uslcom_detach(dev);
258	return (ENXIO);
259}
260
261static int
262uslcom_detach(device_t dev)
263{
264	struct uslcom_softc *sc = device_get_softc(dev);
265
266	DPRINTF("sc=%p\n", sc);
267
268	usb2_com_detach(&sc->sc_super_ucom, &sc->sc_ucom, 1);
269	usb2_transfer_unsetup(sc->sc_xfer, USLCOM_N_TRANSFER);
270	mtx_destroy(&sc->sc_mtx);
271
272	return (0);
273}
274
275static void
276uslcom_open(struct usb2_com_softc *ucom)
277{
278	struct uslcom_softc *sc = ucom->sc_parent;
279	struct usb2_device_request req;
280
281	req.bmRequestType = USLCOM_WRITE;
282	req.bRequest = USLCOM_UART;
283	USETW(req.wValue, USLCOM_UART_ENABLE);
284	USETW(req.wIndex, USLCOM_PORT_NO);
285	USETW(req.wLength, 0);
286
287        if (usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
288	    &req, NULL, 0, 1000)) {
289		DPRINTF("UART enable failed (ignored)\n");
290	}
291}
292
293static void
294uslcom_close(struct usb2_com_softc *ucom)
295{
296	struct uslcom_softc *sc = ucom->sc_parent;
297	struct usb2_device_request req;
298
299	req.bmRequestType = USLCOM_WRITE;
300	req.bRequest = USLCOM_UART;
301	USETW(req.wValue, USLCOM_UART_DISABLE);
302	USETW(req.wIndex, USLCOM_PORT_NO);
303	USETW(req.wLength, 0);
304
305        if (usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
306	    &req, NULL, 0, 1000)) {
307		DPRINTF("UART disable failed (ignored)\n");
308	}
309}
310
311static void
312uslcom_set_dtr(struct usb2_com_softc *ucom, uint8_t onoff)
313{
314        struct uslcom_softc *sc = ucom->sc_parent;
315	struct usb2_device_request req;
316	uint16_t ctl;
317
318        DPRINTF("onoff = %d\n", onoff);
319
320	ctl = onoff ? USLCOM_CTRL_DTR_ON : 0;
321	ctl |= USLCOM_CTRL_DTR_SET;
322
323	req.bmRequestType = USLCOM_WRITE;
324	req.bRequest = USLCOM_CTRL;
325	USETW(req.wValue, ctl);
326	USETW(req.wIndex, USLCOM_PORT_NO);
327	USETW(req.wLength, 0);
328
329        if (usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
330	    &req, NULL, 0, 1000)) {
331		DPRINTF("Setting DTR failed (ignored)\n");
332	}
333}
334
335static void
336uslcom_set_rts(struct usb2_com_softc *ucom, uint8_t onoff)
337{
338        struct uslcom_softc *sc = ucom->sc_parent;
339	struct usb2_device_request req;
340	uint16_t ctl;
341
342        DPRINTF("onoff = %d\n", onoff);
343
344	ctl = onoff ? USLCOM_CTRL_RTS_ON : 0;
345	ctl |= USLCOM_CTRL_RTS_SET;
346
347	req.bmRequestType = USLCOM_WRITE;
348	req.bRequest = USLCOM_CTRL;
349	USETW(req.wValue, ctl);
350	USETW(req.wIndex, USLCOM_PORT_NO);
351	USETW(req.wLength, 0);
352
353        if (usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
354	    &req, NULL, 0, 1000)) {
355		DPRINTF("Setting DTR failed (ignored)\n");
356	}
357}
358
359static int
360uslcom_pre_param(struct usb2_com_softc *ucom, struct termios *t)
361{
362	if (t->c_ospeed <= 0 || t->c_ospeed > 921600)
363		return (EINVAL);
364	return (0);
365}
366
367static void
368uslcom_param(struct usb2_com_softc *ucom, struct termios *t)
369{
370	struct uslcom_softc *sc = ucom->sc_parent;
371	struct usb2_device_request req;
372	uint16_t data;
373
374	DPRINTF("\n");
375
376	req.bmRequestType = USLCOM_WRITE;
377	req.bRequest = USLCOM_BAUD_RATE;
378	USETW(req.wValue, USLCOM_BAUD_REF / t->c_ospeed);
379	USETW(req.wIndex, USLCOM_PORT_NO);
380	USETW(req.wLength, 0);
381
382        if (usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
383	    &req, NULL, 0, 1000)) {
384		DPRINTF("Set baudrate failed (ignored)\n");
385	}
386
387	if (t->c_cflag & CSTOPB)
388		data = USLCOM_STOP_BITS_2;
389	else
390		data = USLCOM_STOP_BITS_1;
391	if (t->c_cflag & PARENB) {
392		if (t->c_cflag & PARODD)
393			data |= USLCOM_PARITY_ODD;
394		else
395			data |= USLCOM_PARITY_EVEN;
396	} else
397		data |= USLCOM_PARITY_NONE;
398	switch (t->c_cflag & CSIZE) {
399	case CS5:
400		data |= USLCOM_SET_DATA_BITS(5);
401		break;
402	case CS6:
403		data |= USLCOM_SET_DATA_BITS(6);
404		break;
405	case CS7:
406		data |= USLCOM_SET_DATA_BITS(7);
407		break;
408	case CS8:
409		data |= USLCOM_SET_DATA_BITS(8);
410		break;
411	}
412
413	req.bmRequestType = USLCOM_WRITE;
414	req.bRequest = USLCOM_DATA;
415	USETW(req.wValue, data);
416	USETW(req.wIndex, USLCOM_PORT_NO);
417	USETW(req.wLength, 0);
418
419        if (usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
420	    &req, NULL, 0, 1000)) {
421		DPRINTF("Set format failed (ignored)\n");
422	}
423	return;
424}
425
426static void
427uslcom_get_status(struct usb2_com_softc *ucom, uint8_t *lsr, uint8_t *msr)
428{
429	struct uslcom_softc *sc = ucom->sc_parent;
430
431	DPRINTF("\n");
432
433	*lsr = sc->sc_lsr;
434	*msr = sc->sc_msr;
435}
436
437static void
438uslcom_set_break(struct usb2_com_softc *ucom, uint8_t onoff)
439{
440        struct uslcom_softc *sc = ucom->sc_parent;
441	struct usb2_device_request req;
442	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 (usb2_com_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 usb2_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 (usb2_com_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			usb2_start_hardware(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 usb2_xfer *xfer)
488{
489	struct uslcom_softc *sc = xfer->priv_sc;
490
491	switch (USB_GET_STATE(xfer)) {
492	case USB_ST_TRANSFERRED:
493		usb2_com_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		usb2_start_hardware(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 usb2_com_softc *ucom)
513{
514	struct uslcom_softc *sc = ucom->sc_parent;
515
516	/* start read endpoint */
517	usb2_transfer_start(sc->sc_xfer[USLCOM_BULK_DT_RD]);
518}
519
520static void
521uslcom_stop_read(struct usb2_com_softc *ucom)
522{
523	struct uslcom_softc *sc = ucom->sc_parent;
524
525	/* stop read endpoint */
526	usb2_transfer_stop(sc->sc_xfer[USLCOM_BULK_DT_RD]);
527}
528
529static void
530uslcom_start_write(struct usb2_com_softc *ucom)
531{
532	struct uslcom_softc *sc = ucom->sc_parent;
533
534	usb2_transfer_start(sc->sc_xfer[USLCOM_BULK_DT_WR]);
535}
536
537static void
538uslcom_stop_write(struct usb2_com_softc *ucom)
539{
540	struct uslcom_softc *sc = ucom->sc_parent;
541
542	usb2_transfer_stop(sc->sc_xfer[USLCOM_BULK_DT_WR]);
543}
544