1/*	$OpenBSD: umoscom.c,v 1.2 2006/10/26 06:02:43 jsg Exp $	*/
2
3/*
4 * Copyright (c) 2006 Jonathan Gray <jsg@openbsd.org>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19#include <sys/stdint.h>
20#include <sys/stddef.h>
21#include <sys/param.h>
22#include <sys/queue.h>
23#include <sys/types.h>
24#include <sys/systm.h>
25#include <sys/kernel.h>
26#include <sys/bus.h>
27#include <sys/module.h>
28#include <sys/lock.h>
29#include <sys/mutex.h>
30#include <sys/condvar.h>
31#include <sys/sysctl.h>
32#include <sys/sx.h>
33#include <sys/unistd.h>
34#include <sys/callout.h>
35#include <sys/malloc.h>
36#include <sys/priv.h>
37
38#include <dev/usb/usb.h>
39#include <dev/usb/usbdi.h>
40#include <dev/usb/usbdi_util.h>
41#include "usbdevs.h"
42
43#define	USB_DEBUG_VAR umoscom_debug
44#include <dev/usb/usb_debug.h>
45#include <dev/usb/usb_process.h>
46
47#include <dev/usb/serial/usb_serial.h>
48
49#ifdef USB_DEBUG
50static int umoscom_debug = 0;
51
52static SYSCTL_NODE(_hw_usb, OID_AUTO, umoscom, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
53    "USB umoscom");
54SYSCTL_INT(_hw_usb_umoscom, OID_AUTO, debug, CTLFLAG_RWTUN,
55    &umoscom_debug, 0, "Debug level");
56#endif
57
58#define	UMOSCOM_BUFSIZE	       1024	/* bytes */
59
60#define	UMOSCOM_CONFIG_INDEX	0
61#define	UMOSCOM_IFACE_INDEX	0
62
63/* interrupt packet */
64#define	UMOSCOM_IIR_RLS		0x06
65#define	UMOSCOM_IIR_RDA		0x04
66#define	UMOSCOM_IIR_CTI		0x0c
67#define	UMOSCOM_IIR_THR		0x02
68#define	UMOSCOM_IIR_MS		0x00
69
70/* registers */
71#define	UMOSCOM_READ		0x0d
72#define	UMOSCOM_WRITE		0x0e
73#define	UMOSCOM_UART_REG	0x0300
74#define	UMOSCOM_VEND_REG	0x0000
75
76#define	UMOSCOM_TXBUF		0x00	/* Write */
77#define	UMOSCOM_RXBUF		0x00	/* Read */
78#define	UMOSCOM_INT		0x01
79#define	UMOSCOM_FIFO		0x02	/* Write */
80#define	UMOSCOM_ISR		0x02	/* Read */
81#define	UMOSCOM_LCR		0x03
82#define	UMOSCOM_MCR		0x04
83#define	UMOSCOM_LSR		0x05
84#define	UMOSCOM_MSR		0x06
85#define	UMOSCOM_SCRATCH		0x07
86#define	UMOSCOM_DIV_LO		0x08
87#define	UMOSCOM_DIV_HI		0x09
88#define	UMOSCOM_EFR		0x0a
89#define	UMOSCOM_XON1		0x0b
90#define	UMOSCOM_XON2		0x0c
91#define	UMOSCOM_XOFF1		0x0d
92#define	UMOSCOM_XOFF2		0x0e
93
94#define	UMOSCOM_BAUDLO		0x00
95#define	UMOSCOM_BAUDHI		0x01
96
97#define	UMOSCOM_INT_RXEN	0x01
98#define	UMOSCOM_INT_TXEN	0x02
99#define	UMOSCOM_INT_RSEN	0x04
100#define	UMOSCOM_INT_MDMEM	0x08
101#define	UMOSCOM_INT_SLEEP	0x10
102#define	UMOSCOM_INT_XOFF	0x20
103#define	UMOSCOM_INT_RTS		0x40
104
105#define	UMOSCOM_FIFO_EN		0x01
106#define	UMOSCOM_FIFO_RXCLR	0x02
107#define	UMOSCOM_FIFO_TXCLR	0x04
108#define	UMOSCOM_FIFO_DMA_BLK	0x08
109#define	UMOSCOM_FIFO_TXLVL_MASK	0x30
110#define	UMOSCOM_FIFO_TXLVL_8	0x00
111#define	UMOSCOM_FIFO_TXLVL_16	0x10
112#define	UMOSCOM_FIFO_TXLVL_32	0x20
113#define	UMOSCOM_FIFO_TXLVL_56	0x30
114#define	UMOSCOM_FIFO_RXLVL_MASK	0xc0
115#define	UMOSCOM_FIFO_RXLVL_8	0x00
116#define	UMOSCOM_FIFO_RXLVL_16	0x40
117#define	UMOSCOM_FIFO_RXLVL_56	0x80
118#define	UMOSCOM_FIFO_RXLVL_80	0xc0
119
120#define	UMOSCOM_ISR_MDM		0x00
121#define	UMOSCOM_ISR_NONE	0x01
122#define	UMOSCOM_ISR_TX		0x02
123#define	UMOSCOM_ISR_RX		0x04
124#define	UMOSCOM_ISR_LINE	0x06
125#define	UMOSCOM_ISR_RXTIMEOUT	0x0c
126#define	UMOSCOM_ISR_RX_XOFF	0x10
127#define	UMOSCOM_ISR_RTSCTS	0x20
128#define	UMOSCOM_ISR_FIFOEN	0xc0
129
130#define	UMOSCOM_LCR_DBITS(x)	((x) - 5)
131#define	UMOSCOM_LCR_STOP_BITS_1	0x00
132#define	UMOSCOM_LCR_STOP_BITS_2	0x04	/* 2 if 6-8 bits/char or 1.5 if 5 */
133#define	UMOSCOM_LCR_PARITY_NONE	0x00
134#define	UMOSCOM_LCR_PARITY_ODD	0x08
135#define	UMOSCOM_LCR_PARITY_EVEN	0x18
136#define	UMOSCOM_LCR_BREAK	0x40
137#define	UMOSCOM_LCR_DIVLATCH_EN	0x80
138
139#define	UMOSCOM_MCR_DTR		0x01
140#define	UMOSCOM_MCR_RTS		0x02
141#define	UMOSCOM_MCR_LOOP	0x04
142#define	UMOSCOM_MCR_INTEN	0x08
143#define	UMOSCOM_MCR_LOOPBACK	0x10
144#define	UMOSCOM_MCR_XONANY	0x20
145#define	UMOSCOM_MCR_IRDA_EN	0x40
146#define	UMOSCOM_MCR_BAUD_DIV4	0x80
147
148#define	UMOSCOM_LSR_RXDATA	0x01
149#define	UMOSCOM_LSR_RXOVER	0x02
150#define	UMOSCOM_LSR_RXPAR_ERR	0x04
151#define	UMOSCOM_LSR_RXFRM_ERR	0x08
152#define	UMOSCOM_LSR_RXBREAK	0x10
153#define	UMOSCOM_LSR_TXEMPTY	0x20
154#define	UMOSCOM_LSR_TXALLEMPTY	0x40
155#define	UMOSCOM_LSR_TXFIFO_ERR	0x80
156
157#define	UMOSCOM_MSR_CTS_CHG	0x01
158#define	UMOSCOM_MSR_DSR_CHG	0x02
159#define	UMOSCOM_MSR_RI_CHG	0x04
160#define	UMOSCOM_MSR_CD_CHG	0x08
161#define	UMOSCOM_MSR_CTS		0x10
162#define	UMOSCOM_MSR_RTS		0x20
163#define	UMOSCOM_MSR_RI		0x40
164#define	UMOSCOM_MSR_CD		0x80
165
166#define	UMOSCOM_BAUD_REF	115200
167
168enum {
169	UMOSCOM_BULK_DT_WR,
170	UMOSCOM_BULK_DT_RD,
171	UMOSCOM_INTR_DT_RD,
172	UMOSCOM_N_TRANSFER,
173};
174
175struct umoscom_softc {
176	struct ucom_super_softc sc_super_ucom;
177	struct ucom_softc sc_ucom;
178
179	struct usb_xfer *sc_xfer[UMOSCOM_N_TRANSFER];
180	struct usb_device *sc_udev;
181	struct mtx sc_mtx;
182
183	uint8_t	sc_mcr;
184	uint8_t	sc_lcr;
185};
186
187/* prototypes */
188
189static device_probe_t umoscom_probe;
190static device_attach_t umoscom_attach;
191static device_detach_t umoscom_detach;
192static void umoscom_free_softc(struct umoscom_softc *);
193
194static usb_callback_t umoscom_write_callback;
195static usb_callback_t umoscom_read_callback;
196static usb_callback_t umoscom_intr_callback;
197
198static void	umoscom_free(struct ucom_softc *);
199static void	umoscom_cfg_open(struct ucom_softc *);
200static void	umoscom_cfg_close(struct ucom_softc *);
201static void	umoscom_cfg_set_break(struct ucom_softc *, uint8_t);
202static void	umoscom_cfg_set_dtr(struct ucom_softc *, uint8_t);
203static void	umoscom_cfg_set_rts(struct ucom_softc *, uint8_t);
204static int	umoscom_pre_param(struct ucom_softc *, struct termios *);
205static void	umoscom_cfg_param(struct ucom_softc *, struct termios *);
206static void	umoscom_cfg_get_status(struct ucom_softc *, uint8_t *,
207		    uint8_t *);
208static void	umoscom_cfg_write(struct umoscom_softc *, uint16_t, uint16_t);
209static uint8_t	umoscom_cfg_read(struct umoscom_softc *, uint16_t);
210static void	umoscom_start_read(struct ucom_softc *);
211static void	umoscom_stop_read(struct ucom_softc *);
212static void	umoscom_start_write(struct ucom_softc *);
213static void	umoscom_stop_write(struct ucom_softc *);
214static void	umoscom_poll(struct ucom_softc *ucom);
215
216static const struct usb_config umoscom_config_data[UMOSCOM_N_TRANSFER] = {
217	[UMOSCOM_BULK_DT_WR] = {
218		.type = UE_BULK,
219		.endpoint = UE_ADDR_ANY,
220		.direction = UE_DIR_OUT,
221		.bufsize = UMOSCOM_BUFSIZE,
222		.flags = {.pipe_bof = 1,.force_short_xfer = 1,},
223		.callback = &umoscom_write_callback,
224	},
225
226	[UMOSCOM_BULK_DT_RD] = {
227		.type = UE_BULK,
228		.endpoint = UE_ADDR_ANY,
229		.direction = UE_DIR_IN,
230		.bufsize = UMOSCOM_BUFSIZE,
231		.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
232		.callback = &umoscom_read_callback,
233	},
234
235	[UMOSCOM_INTR_DT_RD] = {
236		.type = UE_INTERRUPT,
237		.endpoint = UE_ADDR_ANY,
238		.direction = UE_DIR_IN,
239		.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
240		.bufsize = 0,	/* use wMaxPacketSize */
241		.callback = &umoscom_intr_callback,
242	},
243};
244
245static const struct ucom_callback umoscom_callback = {
246	/* configuration callbacks */
247	.ucom_cfg_get_status = &umoscom_cfg_get_status,
248	.ucom_cfg_set_dtr = &umoscom_cfg_set_dtr,
249	.ucom_cfg_set_rts = &umoscom_cfg_set_rts,
250	.ucom_cfg_set_break = &umoscom_cfg_set_break,
251	.ucom_cfg_param = &umoscom_cfg_param,
252	.ucom_cfg_open = &umoscom_cfg_open,
253	.ucom_cfg_close = &umoscom_cfg_close,
254
255	/* other callbacks */
256	.ucom_pre_param = &umoscom_pre_param,
257	.ucom_start_read = &umoscom_start_read,
258	.ucom_stop_read = &umoscom_stop_read,
259	.ucom_start_write = &umoscom_start_write,
260	.ucom_stop_write = &umoscom_stop_write,
261	.ucom_poll = &umoscom_poll,
262	.ucom_free = &umoscom_free,
263};
264
265static device_method_t umoscom_methods[] = {
266	DEVMETHOD(device_probe, umoscom_probe),
267	DEVMETHOD(device_attach, umoscom_attach),
268	DEVMETHOD(device_detach, umoscom_detach),
269	DEVMETHOD_END
270};
271
272static driver_t umoscom_driver = {
273	.name = "umoscom",
274	.methods = umoscom_methods,
275	.size = sizeof(struct umoscom_softc),
276};
277
278static const STRUCT_USB_HOST_ID umoscom_devs[] = {
279	{USB_VPI(USB_VENDOR_MOSCHIP, USB_PRODUCT_MOSCHIP_MCS7703, 0)}
280};
281
282DRIVER_MODULE(umoscom, uhub, umoscom_driver, NULL, NULL);
283MODULE_DEPEND(umoscom, ucom, 1, 1, 1);
284MODULE_DEPEND(umoscom, usb, 1, 1, 1);
285MODULE_VERSION(umoscom, 1);
286USB_PNP_HOST_INFO(umoscom_devs);
287
288static int
289umoscom_probe(device_t dev)
290{
291	struct usb_attach_arg *uaa = device_get_ivars(dev);
292
293	if (uaa->usb_mode != USB_MODE_HOST) {
294		return (ENXIO);
295	}
296	if (uaa->info.bConfigIndex != UMOSCOM_CONFIG_INDEX) {
297		return (ENXIO);
298	}
299	if (uaa->info.bIfaceIndex != UMOSCOM_IFACE_INDEX) {
300		return (ENXIO);
301	}
302	return (usbd_lookup_id_by_uaa(umoscom_devs, sizeof(umoscom_devs), uaa));
303}
304
305static int
306umoscom_attach(device_t dev)
307{
308	struct usb_attach_arg *uaa = device_get_ivars(dev);
309	struct umoscom_softc *sc = device_get_softc(dev);
310	int error;
311	uint8_t iface_index;
312
313	sc->sc_udev = uaa->device;
314	sc->sc_mcr = 0x08;		/* enable interrupts */
315
316	/* XXX the device doesn't provide any ID string, so set a static one */
317	device_set_desc(dev, "MOSCHIP USB Serial Port Adapter");
318	device_printf(dev, "<MOSCHIP USB Serial Port Adapter>\n");
319
320	mtx_init(&sc->sc_mtx, "umoscom", NULL, MTX_DEF);
321	ucom_ref(&sc->sc_super_ucom);
322
323	iface_index = UMOSCOM_IFACE_INDEX;
324	error = usbd_transfer_setup(uaa->device, &iface_index,
325	    sc->sc_xfer, umoscom_config_data,
326	    UMOSCOM_N_TRANSFER, sc, &sc->sc_mtx);
327
328	if (error) {
329		goto detach;
330	}
331	/* clear stall at first run */
332	mtx_lock(&sc->sc_mtx);
333	usbd_xfer_set_stall(sc->sc_xfer[UMOSCOM_BULK_DT_WR]);
334	usbd_xfer_set_stall(sc->sc_xfer[UMOSCOM_BULK_DT_RD]);
335	mtx_unlock(&sc->sc_mtx);
336
337	error = ucom_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc,
338	    &umoscom_callback, &sc->sc_mtx);
339	if (error) {
340		goto detach;
341	}
342	ucom_set_pnpinfo_usb(&sc->sc_super_ucom, dev);
343
344	return (0);
345
346detach:
347	device_printf(dev, "attach error: %s\n", usbd_errstr(error));
348	umoscom_detach(dev);
349	return (ENXIO);
350}
351
352static int
353umoscom_detach(device_t dev)
354{
355	struct umoscom_softc *sc = device_get_softc(dev);
356
357	ucom_detach(&sc->sc_super_ucom, &sc->sc_ucom);
358	usbd_transfer_unsetup(sc->sc_xfer, UMOSCOM_N_TRANSFER);
359
360	device_claim_softc(dev);
361
362	umoscom_free_softc(sc);
363
364	return (0);
365}
366
367UCOM_UNLOAD_DRAIN(umoscom);
368
369static void
370umoscom_free_softc(struct umoscom_softc *sc)
371{
372	if (ucom_unref(&sc->sc_super_ucom)) {
373		mtx_destroy(&sc->sc_mtx);
374		device_free_softc(sc);
375	}
376}
377
378static void
379umoscom_free(struct ucom_softc *ucom)
380{
381	umoscom_free_softc(ucom->sc_parent);
382}
383
384static void
385umoscom_cfg_open(struct ucom_softc *ucom)
386{
387	struct umoscom_softc *sc = ucom->sc_parent;
388
389	DPRINTF("\n");
390
391	/* Purge FIFOs or odd things happen */
392	umoscom_cfg_write(sc, UMOSCOM_FIFO, 0x00 | UMOSCOM_UART_REG);
393
394	/* Enable FIFO */
395	umoscom_cfg_write(sc, UMOSCOM_FIFO, UMOSCOM_FIFO_EN |
396	    UMOSCOM_FIFO_RXCLR | UMOSCOM_FIFO_TXCLR |
397	    UMOSCOM_FIFO_DMA_BLK | UMOSCOM_FIFO_RXLVL_MASK |
398	    UMOSCOM_UART_REG);
399
400	/* Enable Interrupt Registers */
401	umoscom_cfg_write(sc, UMOSCOM_INT, 0x0C | UMOSCOM_UART_REG);
402
403	/* Magic */
404	umoscom_cfg_write(sc, 0x01, 0x08);
405
406	/* Magic */
407	umoscom_cfg_write(sc, 0x00, 0x02);
408}
409
410static void
411umoscom_cfg_close(struct ucom_softc *ucom)
412{
413	return;
414}
415
416static void
417umoscom_cfg_set_break(struct ucom_softc *ucom, uint8_t onoff)
418{
419	struct umoscom_softc *sc = ucom->sc_parent;
420	uint16_t val;
421
422	val = sc->sc_lcr;
423	if (onoff)
424		val |= UMOSCOM_LCR_BREAK;
425
426	umoscom_cfg_write(sc, UMOSCOM_LCR, val | UMOSCOM_UART_REG);
427}
428
429static void
430umoscom_cfg_set_dtr(struct ucom_softc *ucom, uint8_t onoff)
431{
432	struct umoscom_softc *sc = ucom->sc_parent;
433
434	if (onoff)
435		sc->sc_mcr |= UMOSCOM_MCR_DTR;
436	else
437		sc->sc_mcr &= ~UMOSCOM_MCR_DTR;
438
439	umoscom_cfg_write(sc, UMOSCOM_MCR, sc->sc_mcr | UMOSCOM_UART_REG);
440}
441
442static void
443umoscom_cfg_set_rts(struct ucom_softc *ucom, uint8_t onoff)
444{
445	struct umoscom_softc *sc = ucom->sc_parent;
446
447	if (onoff)
448		sc->sc_mcr |= UMOSCOM_MCR_RTS;
449	else
450		sc->sc_mcr &= ~UMOSCOM_MCR_RTS;
451
452	umoscom_cfg_write(sc, UMOSCOM_MCR, sc->sc_mcr | UMOSCOM_UART_REG);
453}
454
455static int
456umoscom_pre_param(struct ucom_softc *ucom, struct termios *t)
457{
458	if ((t->c_ospeed <= 1) || (t->c_ospeed > 115200))
459		return (EINVAL);
460
461	return (0);
462}
463
464static void
465umoscom_cfg_param(struct ucom_softc *ucom, struct termios *t)
466{
467	struct umoscom_softc *sc = ucom->sc_parent;
468	uint16_t data;
469
470	DPRINTF("speed=%d\n", t->c_ospeed);
471
472	data = ((uint32_t)UMOSCOM_BAUD_REF) / ((uint32_t)t->c_ospeed);
473
474	if (data == 0) {
475		DPRINTF("invalid baud rate!\n");
476		return;
477	}
478	umoscom_cfg_write(sc, UMOSCOM_LCR,
479	    UMOSCOM_LCR_DIVLATCH_EN | UMOSCOM_UART_REG);
480
481	umoscom_cfg_write(sc, UMOSCOM_BAUDLO,
482	    (data & 0xFF) | UMOSCOM_UART_REG);
483
484	umoscom_cfg_write(sc, UMOSCOM_BAUDHI,
485	    ((data >> 8) & 0xFF) | UMOSCOM_UART_REG);
486
487	if (t->c_cflag & CSTOPB)
488		data = UMOSCOM_LCR_STOP_BITS_2;
489	else
490		data = UMOSCOM_LCR_STOP_BITS_1;
491
492	if (t->c_cflag & PARENB) {
493		if (t->c_cflag & PARODD)
494			data |= UMOSCOM_LCR_PARITY_ODD;
495		else
496			data |= UMOSCOM_LCR_PARITY_EVEN;
497	} else
498		data |= UMOSCOM_LCR_PARITY_NONE;
499
500	switch (t->c_cflag & CSIZE) {
501	case CS5:
502		data |= UMOSCOM_LCR_DBITS(5);
503		break;
504	case CS6:
505		data |= UMOSCOM_LCR_DBITS(6);
506		break;
507	case CS7:
508		data |= UMOSCOM_LCR_DBITS(7);
509		break;
510	case CS8:
511		data |= UMOSCOM_LCR_DBITS(8);
512		break;
513	}
514
515	sc->sc_lcr = data;
516	umoscom_cfg_write(sc, UMOSCOM_LCR, data | UMOSCOM_UART_REG);
517}
518
519static void
520umoscom_cfg_get_status(struct ucom_softc *ucom, uint8_t *p_lsr, uint8_t *p_msr)
521{
522	struct umoscom_softc *sc = ucom->sc_parent;
523	uint8_t msr;
524
525	DPRINTFN(5, "\n");
526
527	/*
528	 * Read status registers.  MSR bits need translation from ns16550 to
529	 * SER_* values.  LSR bits are ns16550 in hardware and ucom.
530	 */
531
532	*p_lsr = umoscom_cfg_read(sc, UMOSCOM_LSR);
533	msr = umoscom_cfg_read(sc, UMOSCOM_MSR);
534
535	/* translate bits */
536
537	if (msr & UMOSCOM_MSR_CTS)
538		*p_msr |= SER_CTS;
539
540	if (msr & UMOSCOM_MSR_CD)
541		*p_msr |= SER_DCD;
542
543	if (msr & UMOSCOM_MSR_RI)
544		*p_msr |= SER_RI;
545
546	if (msr & UMOSCOM_MSR_RTS)
547		*p_msr |= SER_DSR;
548}
549
550static void
551umoscom_cfg_write(struct umoscom_softc *sc, uint16_t reg, uint16_t val)
552{
553	struct usb_device_request req;
554
555	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
556	req.bRequest = UMOSCOM_WRITE;
557	USETW(req.wValue, val);
558	USETW(req.wIndex, reg);
559	USETW(req.wLength, 0);
560
561	ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
562	    &req, NULL, 0, 1000);
563}
564
565static uint8_t
566umoscom_cfg_read(struct umoscom_softc *sc, uint16_t reg)
567{
568	struct usb_device_request req;
569	uint8_t val;
570
571	req.bmRequestType = UT_READ_VENDOR_DEVICE;
572	req.bRequest = UMOSCOM_READ;
573	USETW(req.wValue, 0);
574	USETW(req.wIndex, reg);
575	USETW(req.wLength, 1);
576
577	ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
578	    &req, &val, 0, 1000);
579
580	DPRINTF("reg=0x%04x, val=0x%02x\n", reg, val);
581
582	return (val);
583}
584
585static void
586umoscom_start_read(struct ucom_softc *ucom)
587{
588	struct umoscom_softc *sc = ucom->sc_parent;
589
590#if 0
591	/* start interrupt endpoint */
592	usbd_transfer_start(sc->sc_xfer[UMOSCOM_INTR_DT_RD]);
593#endif
594	/* start read endpoint */
595	usbd_transfer_start(sc->sc_xfer[UMOSCOM_BULK_DT_RD]);
596}
597
598static void
599umoscom_stop_read(struct ucom_softc *ucom)
600{
601	struct umoscom_softc *sc = ucom->sc_parent;
602
603	/* stop interrupt transfer */
604	usbd_transfer_stop(sc->sc_xfer[UMOSCOM_INTR_DT_RD]);
605
606	/* stop read endpoint */
607	usbd_transfer_stop(sc->sc_xfer[UMOSCOM_BULK_DT_RD]);
608}
609
610static void
611umoscom_start_write(struct ucom_softc *ucom)
612{
613	struct umoscom_softc *sc = ucom->sc_parent;
614
615	usbd_transfer_start(sc->sc_xfer[UMOSCOM_BULK_DT_WR]);
616}
617
618static void
619umoscom_stop_write(struct ucom_softc *ucom)
620{
621	struct umoscom_softc *sc = ucom->sc_parent;
622
623	usbd_transfer_stop(sc->sc_xfer[UMOSCOM_BULK_DT_WR]);
624}
625
626static void
627umoscom_write_callback(struct usb_xfer *xfer, usb_error_t error)
628{
629	struct umoscom_softc *sc = usbd_xfer_softc(xfer);
630	struct usb_page_cache *pc;
631	uint32_t actlen;
632
633	switch (USB_GET_STATE(xfer)) {
634	case USB_ST_SETUP:
635	case USB_ST_TRANSFERRED:
636tr_setup:
637		DPRINTF("\n");
638
639		pc = usbd_xfer_get_frame(xfer, 0);
640		if (ucom_get_data(&sc->sc_ucom, pc, 0,
641		    UMOSCOM_BUFSIZE, &actlen)) {
642			usbd_xfer_set_frame_len(xfer, 0, actlen);
643			usbd_transfer_submit(xfer);
644		}
645		return;
646
647	default:			/* Error */
648		if (error != USB_ERR_CANCELLED) {
649			DPRINTFN(0, "transfer failed\n");
650			/* try to clear stall first */
651			usbd_xfer_set_stall(xfer);
652			goto tr_setup;
653		}
654		return;
655	}
656}
657
658static void
659umoscom_read_callback(struct usb_xfer *xfer, usb_error_t error)
660{
661	struct umoscom_softc *sc = usbd_xfer_softc(xfer);
662	struct usb_page_cache *pc;
663	int actlen;
664
665	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
666
667	switch (USB_GET_STATE(xfer)) {
668	case USB_ST_TRANSFERRED:
669		DPRINTF("got %d bytes\n", actlen);
670		pc = usbd_xfer_get_frame(xfer, 0);
671		ucom_put_data(&sc->sc_ucom, pc, 0, actlen);
672
673	case USB_ST_SETUP:
674tr_setup:
675		DPRINTF("\n");
676
677		usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
678		usbd_transfer_submit(xfer);
679		return;
680
681	default:			/* Error */
682		if (error != USB_ERR_CANCELLED) {
683			DPRINTFN(0, "transfer failed\n");
684			/* try to clear stall first */
685			usbd_xfer_set_stall(xfer);
686			goto tr_setup;
687		}
688		return;
689	}
690}
691
692static void
693umoscom_intr_callback(struct usb_xfer *xfer, usb_error_t error)
694{
695	struct umoscom_softc *sc = usbd_xfer_softc(xfer);
696	int actlen;
697
698	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
699
700	switch (USB_GET_STATE(xfer)) {
701	case USB_ST_TRANSFERRED:
702		if (actlen < 2) {
703			DPRINTF("too short message\n");
704			goto tr_setup;
705		}
706		ucom_status_change(&sc->sc_ucom);
707
708	case USB_ST_SETUP:
709tr_setup:
710		usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
711		usbd_transfer_submit(xfer);
712		return;
713
714	default:			/* Error */
715		if (error != USB_ERR_CANCELLED) {
716			DPRINTFN(0, "transfer failed\n");
717			/* try to clear stall first */
718			usbd_xfer_set_stall(xfer);
719			goto tr_setup;
720		}
721		return;
722	}
723}
724
725static void
726umoscom_poll(struct ucom_softc *ucom)
727{
728	struct umoscom_softc *sc = ucom->sc_parent;
729	usbd_transfer_poll(sc->sc_xfer, UMOSCOM_N_TRANSFER);
730}
731