1/* $FreeBSD$ */
2/*	$OpenBSD: umoscom.c,v 1.2 2006/10/26 06:02:43 jsg Exp $	*/
3
4/*
5 * Copyright (c) 2006 Jonathan Gray <jsg@openbsd.org>
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 */
19
20#include <sys/stdint.h>
21#include <sys/stddef.h>
22#include <sys/param.h>
23#include <sys/queue.h>
24#include <sys/types.h>
25#include <sys/systm.h>
26#include <sys/kernel.h>
27#include <sys/bus.h>
28#include <sys/module.h>
29#include <sys/lock.h>
30#include <sys/mutex.h>
31#include <sys/condvar.h>
32#include <sys/sysctl.h>
33#include <sys/sx.h>
34#include <sys/unistd.h>
35#include <sys/callout.h>
36#include <sys/malloc.h>
37#include <sys/priv.h>
38
39#include <dev/usb/usb.h>
40#include <dev/usb/usbdi.h>
41#include <dev/usb/usbdi_util.h>
42#include "usbdevs.h"
43
44#define	USB_DEBUG_VAR umoscom_debug
45#include <dev/usb/usb_debug.h>
46#include <dev/usb/usb_process.h>
47
48#include <dev/usb/serial/usb_serial.h>
49
50#ifdef USB_DEBUG
51static int umoscom_debug = 0;
52
53static SYSCTL_NODE(_hw_usb, OID_AUTO, umoscom, CTLFLAG_RW, 0, "USB umoscom");
54SYSCTL_INT(_hw_usb_umoscom, OID_AUTO, debug, CTLFLAG_RW,
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
218	[UMOSCOM_BULK_DT_WR] = {
219		.type = UE_BULK,
220		.endpoint = UE_ADDR_ANY,
221		.direction = UE_DIR_OUT,
222		.bufsize = UMOSCOM_BUFSIZE,
223		.flags = {.pipe_bof = 1,.force_short_xfer = 1,},
224		.callback = &umoscom_write_callback,
225	},
226
227	[UMOSCOM_BULK_DT_RD] = {
228		.type = UE_BULK,
229		.endpoint = UE_ADDR_ANY,
230		.direction = UE_DIR_IN,
231		.bufsize = UMOSCOM_BUFSIZE,
232		.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
233		.callback = &umoscom_read_callback,
234	},
235
236	[UMOSCOM_INTR_DT_RD] = {
237		.type = UE_INTERRUPT,
238		.endpoint = UE_ADDR_ANY,
239		.direction = UE_DIR_IN,
240		.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
241		.bufsize = 0,	/* use wMaxPacketSize */
242		.callback = &umoscom_intr_callback,
243	},
244};
245
246static const struct ucom_callback umoscom_callback = {
247	/* configuration callbacks */
248	.ucom_cfg_get_status = &umoscom_cfg_get_status,
249	.ucom_cfg_set_dtr = &umoscom_cfg_set_dtr,
250	.ucom_cfg_set_rts = &umoscom_cfg_set_rts,
251	.ucom_cfg_set_break = &umoscom_cfg_set_break,
252	.ucom_cfg_param = &umoscom_cfg_param,
253	.ucom_cfg_open = &umoscom_cfg_open,
254	.ucom_cfg_close = &umoscom_cfg_close,
255
256	/* other callbacks */
257	.ucom_pre_param = &umoscom_pre_param,
258	.ucom_start_read = &umoscom_start_read,
259	.ucom_stop_read = &umoscom_stop_read,
260	.ucom_start_write = &umoscom_start_write,
261	.ucom_stop_write = &umoscom_stop_write,
262	.ucom_poll = &umoscom_poll,
263	.ucom_free = &umoscom_free,
264};
265
266static device_method_t umoscom_methods[] = {
267	DEVMETHOD(device_probe, umoscom_probe),
268	DEVMETHOD(device_attach, umoscom_attach),
269	DEVMETHOD(device_detach, umoscom_detach),
270	DEVMETHOD_END
271};
272
273static devclass_t umoscom_devclass;
274
275static driver_t umoscom_driver = {
276	.name = "umoscom",
277	.methods = umoscom_methods,
278	.size = sizeof(struct umoscom_softc),
279};
280
281DRIVER_MODULE(umoscom, uhub, umoscom_driver, umoscom_devclass, NULL, 0);
282MODULE_DEPEND(umoscom, ucom, 1, 1, 1);
283MODULE_DEPEND(umoscom, usb, 1, 1, 1);
284MODULE_VERSION(umoscom, 1);
285
286static const STRUCT_USB_HOST_ID umoscom_devs[] = {
287	{USB_VPI(USB_VENDOR_MOSCHIP, USB_PRODUCT_MOSCHIP_MCS7703, 0)}
288};
289
290static int
291umoscom_probe(device_t dev)
292{
293	struct usb_attach_arg *uaa = device_get_ivars(dev);
294
295	if (uaa->usb_mode != USB_MODE_HOST) {
296		return (ENXIO);
297	}
298	if (uaa->info.bConfigIndex != UMOSCOM_CONFIG_INDEX) {
299		return (ENXIO);
300	}
301	if (uaa->info.bIfaceIndex != UMOSCOM_IFACE_INDEX) {
302		return (ENXIO);
303	}
304	return (usbd_lookup_id_by_uaa(umoscom_devs, sizeof(umoscom_devs), uaa));
305}
306
307static int
308umoscom_attach(device_t dev)
309{
310	struct usb_attach_arg *uaa = device_get_ivars(dev);
311	struct umoscom_softc *sc = device_get_softc(dev);
312	int error;
313	uint8_t iface_index;
314
315	sc->sc_udev = uaa->device;
316	sc->sc_mcr = 0x08;		/* enable interrupts */
317
318	/* XXX the device doesn't provide any ID string, so set a static one */
319	device_set_desc(dev, "MOSCHIP USB Serial Port Adapter");
320	device_printf(dev, "<MOSCHIP USB Serial Port Adapter>\n");
321
322	mtx_init(&sc->sc_mtx, "umoscom", NULL, MTX_DEF);
323	ucom_ref(&sc->sc_super_ucom);
324
325	iface_index = UMOSCOM_IFACE_INDEX;
326	error = usbd_transfer_setup(uaa->device, &iface_index,
327	    sc->sc_xfer, umoscom_config_data,
328	    UMOSCOM_N_TRANSFER, sc, &sc->sc_mtx);
329
330	if (error) {
331		goto detach;
332	}
333	/* clear stall at first run */
334	mtx_lock(&sc->sc_mtx);
335	usbd_xfer_set_stall(sc->sc_xfer[UMOSCOM_BULK_DT_WR]);
336	usbd_xfer_set_stall(sc->sc_xfer[UMOSCOM_BULK_DT_RD]);
337	mtx_unlock(&sc->sc_mtx);
338
339	error = ucom_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc,
340	    &umoscom_callback, &sc->sc_mtx);
341	if (error) {
342		goto detach;
343	}
344	ucom_set_pnpinfo_usb(&sc->sc_super_ucom, dev);
345
346	return (0);
347
348detach:
349	device_printf(dev, "attach error: %s\n", usbd_errstr(error));
350	umoscom_detach(dev);
351	return (ENXIO);
352}
353
354static int
355umoscom_detach(device_t dev)
356{
357	struct umoscom_softc *sc = device_get_softc(dev);
358
359	ucom_detach(&sc->sc_super_ucom, &sc->sc_ucom);
360	usbd_transfer_unsetup(sc->sc_xfer, UMOSCOM_N_TRANSFER);
361
362	device_claim_softc(dev);
363
364	umoscom_free_softc(sc);
365
366	return (0);
367}
368
369UCOM_UNLOAD_DRAIN(umoscom);
370
371static void
372umoscom_free_softc(struct umoscom_softc *sc)
373{
374	if (ucom_unref(&sc->sc_super_ucom)) {
375		mtx_destroy(&sc->sc_mtx);
376		device_free_softc(sc);
377	}
378}
379
380static void
381umoscom_free(struct ucom_softc *ucom)
382{
383	umoscom_free_softc(ucom->sc_parent);
384}
385
386static void
387umoscom_cfg_open(struct ucom_softc *ucom)
388{
389	struct umoscom_softc *sc = ucom->sc_parent;
390
391	DPRINTF("\n");
392
393	/* Purge FIFOs or odd things happen */
394	umoscom_cfg_write(sc, UMOSCOM_FIFO, 0x00 | UMOSCOM_UART_REG);
395
396	/* Enable FIFO */
397	umoscom_cfg_write(sc, UMOSCOM_FIFO, UMOSCOM_FIFO_EN |
398	    UMOSCOM_FIFO_RXCLR | UMOSCOM_FIFO_TXCLR |
399	    UMOSCOM_FIFO_DMA_BLK | UMOSCOM_FIFO_RXLVL_MASK |
400	    UMOSCOM_UART_REG);
401
402	/* Enable Interrupt Registers */
403	umoscom_cfg_write(sc, UMOSCOM_INT, 0x0C | UMOSCOM_UART_REG);
404
405	/* Magic */
406	umoscom_cfg_write(sc, 0x01, 0x08);
407
408	/* Magic */
409	umoscom_cfg_write(sc, 0x00, 0x02);
410}
411
412static void
413umoscom_cfg_close(struct ucom_softc *ucom)
414{
415	return;
416}
417
418static void
419umoscom_cfg_set_break(struct ucom_softc *ucom, uint8_t onoff)
420{
421	struct umoscom_softc *sc = ucom->sc_parent;
422	uint16_t val;
423
424	val = sc->sc_lcr;
425	if (onoff)
426		val |= UMOSCOM_LCR_BREAK;
427
428	umoscom_cfg_write(sc, UMOSCOM_LCR, val | UMOSCOM_UART_REG);
429}
430
431static void
432umoscom_cfg_set_dtr(struct ucom_softc *ucom, uint8_t onoff)
433{
434	struct umoscom_softc *sc = ucom->sc_parent;
435
436	if (onoff)
437		sc->sc_mcr |= UMOSCOM_MCR_DTR;
438	else
439		sc->sc_mcr &= ~UMOSCOM_MCR_DTR;
440
441	umoscom_cfg_write(sc, UMOSCOM_MCR, sc->sc_mcr | UMOSCOM_UART_REG);
442}
443
444static void
445umoscom_cfg_set_rts(struct ucom_softc *ucom, uint8_t onoff)
446{
447	struct umoscom_softc *sc = ucom->sc_parent;
448
449	if (onoff)
450		sc->sc_mcr |= UMOSCOM_MCR_RTS;
451	else
452		sc->sc_mcr &= ~UMOSCOM_MCR_RTS;
453
454	umoscom_cfg_write(sc, UMOSCOM_MCR, sc->sc_mcr | UMOSCOM_UART_REG);
455}
456
457static int
458umoscom_pre_param(struct ucom_softc *ucom, struct termios *t)
459{
460	if ((t->c_ospeed <= 1) || (t->c_ospeed > 115200))
461		return (EINVAL);
462
463	return (0);
464}
465
466static void
467umoscom_cfg_param(struct ucom_softc *ucom, struct termios *t)
468{
469	struct umoscom_softc *sc = ucom->sc_parent;
470	uint16_t data;
471
472	DPRINTF("speed=%d\n", t->c_ospeed);
473
474	data = ((uint32_t)UMOSCOM_BAUD_REF) / ((uint32_t)t->c_ospeed);
475
476	if (data == 0) {
477		DPRINTF("invalid baud rate!\n");
478		return;
479	}
480	umoscom_cfg_write(sc, UMOSCOM_LCR,
481	    UMOSCOM_LCR_DIVLATCH_EN | UMOSCOM_UART_REG);
482
483	umoscom_cfg_write(sc, UMOSCOM_BAUDLO,
484	    (data & 0xFF) | UMOSCOM_UART_REG);
485
486	umoscom_cfg_write(sc, UMOSCOM_BAUDHI,
487	    ((data >> 8) & 0xFF) | UMOSCOM_UART_REG);
488
489	if (t->c_cflag & CSTOPB)
490		data = UMOSCOM_LCR_STOP_BITS_2;
491	else
492		data = UMOSCOM_LCR_STOP_BITS_1;
493
494	if (t->c_cflag & PARENB) {
495		if (t->c_cflag & PARODD)
496			data |= UMOSCOM_LCR_PARITY_ODD;
497		else
498			data |= UMOSCOM_LCR_PARITY_EVEN;
499	} else
500		data |= UMOSCOM_LCR_PARITY_NONE;
501
502	switch (t->c_cflag & CSIZE) {
503	case CS5:
504		data |= UMOSCOM_LCR_DBITS(5);
505		break;
506	case CS6:
507		data |= UMOSCOM_LCR_DBITS(6);
508		break;
509	case CS7:
510		data |= UMOSCOM_LCR_DBITS(7);
511		break;
512	case CS8:
513		data |= UMOSCOM_LCR_DBITS(8);
514		break;
515	}
516
517	sc->sc_lcr = data;
518	umoscom_cfg_write(sc, UMOSCOM_LCR, data | UMOSCOM_UART_REG);
519}
520
521static void
522umoscom_cfg_get_status(struct ucom_softc *ucom, uint8_t *p_lsr, uint8_t *p_msr)
523{
524	struct umoscom_softc *sc = ucom->sc_parent;
525	uint8_t lsr;
526	uint8_t msr;
527
528	DPRINTFN(5, "\n");
529
530	/* read status registers */
531
532	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
643			usbd_xfer_set_frame_len(xfer, 0, actlen);
644			usbd_transfer_submit(xfer);
645		}
646		return;
647
648	default:			/* Error */
649		if (error != USB_ERR_CANCELLED) {
650			DPRINTFN(0, "transfer failed\n");
651			/* try to clear stall first */
652			usbd_xfer_set_stall(xfer);
653			goto tr_setup;
654		}
655		return;
656	}
657}
658
659static void
660umoscom_read_callback(struct usb_xfer *xfer, usb_error_t error)
661{
662	struct umoscom_softc *sc = usbd_xfer_softc(xfer);
663	struct usb_page_cache *pc;
664	int actlen;
665
666	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
667
668	switch (USB_GET_STATE(xfer)) {
669	case USB_ST_TRANSFERRED:
670		DPRINTF("got %d bytes\n", actlen);
671		pc = usbd_xfer_get_frame(xfer, 0);
672		ucom_put_data(&sc->sc_ucom, pc, 0, actlen);
673
674	case USB_ST_SETUP:
675tr_setup:
676		DPRINTF("\n");
677
678		usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
679		usbd_transfer_submit(xfer);
680		return;
681
682	default:			/* Error */
683		if (error != USB_ERR_CANCELLED) {
684			DPRINTFN(0, "transfer failed\n");
685			/* try to clear stall first */
686			usbd_xfer_set_stall(xfer);
687			goto tr_setup;
688		}
689		return;
690	}
691}
692
693static void
694umoscom_intr_callback(struct usb_xfer *xfer, usb_error_t error)
695{
696	struct umoscom_softc *sc = usbd_xfer_softc(xfer);
697	int actlen;
698
699	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
700
701	switch (USB_GET_STATE(xfer)) {
702	case USB_ST_TRANSFERRED:
703		if (actlen < 2) {
704			DPRINTF("too short message\n");
705			goto tr_setup;
706		}
707		ucom_status_change(&sc->sc_ucom);
708
709	case USB_ST_SETUP:
710tr_setup:
711		usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
712		usbd_transfer_submit(xfer);
713		return;
714
715	default:			/* Error */
716		if (error != USB_ERR_CANCELLED) {
717			DPRINTFN(0, "transfer failed\n");
718			/* try to clear stall first */
719			usbd_xfer_set_stall(xfer);
720			goto tr_setup;
721		}
722		return;
723	}
724}
725
726static void
727umoscom_poll(struct ucom_softc *ucom)
728{
729	struct umoscom_softc *sc = ucom->sc_parent;
730	usbd_transfer_poll(sc->sc_xfer, UMOSCOM_N_TRANSFER);
731}
732