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 | CTLFLAG_MPSAFE, 0,
54    "USB umoscom");
55SYSCTL_INT(_hw_usb_umoscom, OID_AUTO, debug, CTLFLAG_RWTUN,
56    &umoscom_debug, 0, "Debug level");
57#endif
58
59#define	UMOSCOM_BUFSIZE	       1024	/* bytes */
60
61#define	UMOSCOM_CONFIG_INDEX	0
62#define	UMOSCOM_IFACE_INDEX	0
63
64/* interrupt packet */
65#define	UMOSCOM_IIR_RLS		0x06
66#define	UMOSCOM_IIR_RDA		0x04
67#define	UMOSCOM_IIR_CTI		0x0c
68#define	UMOSCOM_IIR_THR		0x02
69#define	UMOSCOM_IIR_MS		0x00
70
71/* registers */
72#define	UMOSCOM_READ		0x0d
73#define	UMOSCOM_WRITE		0x0e
74#define	UMOSCOM_UART_REG	0x0300
75#define	UMOSCOM_VEND_REG	0x0000
76
77#define	UMOSCOM_TXBUF		0x00	/* Write */
78#define	UMOSCOM_RXBUF		0x00	/* Read */
79#define	UMOSCOM_INT		0x01
80#define	UMOSCOM_FIFO		0x02	/* Write */
81#define	UMOSCOM_ISR		0x02	/* Read */
82#define	UMOSCOM_LCR		0x03
83#define	UMOSCOM_MCR		0x04
84#define	UMOSCOM_LSR		0x05
85#define	UMOSCOM_MSR		0x06
86#define	UMOSCOM_SCRATCH		0x07
87#define	UMOSCOM_DIV_LO		0x08
88#define	UMOSCOM_DIV_HI		0x09
89#define	UMOSCOM_EFR		0x0a
90#define	UMOSCOM_XON1		0x0b
91#define	UMOSCOM_XON2		0x0c
92#define	UMOSCOM_XOFF1		0x0d
93#define	UMOSCOM_XOFF2		0x0e
94
95#define	UMOSCOM_BAUDLO		0x00
96#define	UMOSCOM_BAUDHI		0x01
97
98#define	UMOSCOM_INT_RXEN	0x01
99#define	UMOSCOM_INT_TXEN	0x02
100#define	UMOSCOM_INT_RSEN	0x04
101#define	UMOSCOM_INT_MDMEM	0x08
102#define	UMOSCOM_INT_SLEEP	0x10
103#define	UMOSCOM_INT_XOFF	0x20
104#define	UMOSCOM_INT_RTS		0x40
105
106#define	UMOSCOM_FIFO_EN		0x01
107#define	UMOSCOM_FIFO_RXCLR	0x02
108#define	UMOSCOM_FIFO_TXCLR	0x04
109#define	UMOSCOM_FIFO_DMA_BLK	0x08
110#define	UMOSCOM_FIFO_TXLVL_MASK	0x30
111#define	UMOSCOM_FIFO_TXLVL_8	0x00
112#define	UMOSCOM_FIFO_TXLVL_16	0x10
113#define	UMOSCOM_FIFO_TXLVL_32	0x20
114#define	UMOSCOM_FIFO_TXLVL_56	0x30
115#define	UMOSCOM_FIFO_RXLVL_MASK	0xc0
116#define	UMOSCOM_FIFO_RXLVL_8	0x00
117#define	UMOSCOM_FIFO_RXLVL_16	0x40
118#define	UMOSCOM_FIFO_RXLVL_56	0x80
119#define	UMOSCOM_FIFO_RXLVL_80	0xc0
120
121#define	UMOSCOM_ISR_MDM		0x00
122#define	UMOSCOM_ISR_NONE	0x01
123#define	UMOSCOM_ISR_TX		0x02
124#define	UMOSCOM_ISR_RX		0x04
125#define	UMOSCOM_ISR_LINE	0x06
126#define	UMOSCOM_ISR_RXTIMEOUT	0x0c
127#define	UMOSCOM_ISR_RX_XOFF	0x10
128#define	UMOSCOM_ISR_RTSCTS	0x20
129#define	UMOSCOM_ISR_FIFOEN	0xc0
130
131#define	UMOSCOM_LCR_DBITS(x)	((x) - 5)
132#define	UMOSCOM_LCR_STOP_BITS_1	0x00
133#define	UMOSCOM_LCR_STOP_BITS_2	0x04	/* 2 if 6-8 bits/char or 1.5 if 5 */
134#define	UMOSCOM_LCR_PARITY_NONE	0x00
135#define	UMOSCOM_LCR_PARITY_ODD	0x08
136#define	UMOSCOM_LCR_PARITY_EVEN	0x18
137#define	UMOSCOM_LCR_BREAK	0x40
138#define	UMOSCOM_LCR_DIVLATCH_EN	0x80
139
140#define	UMOSCOM_MCR_DTR		0x01
141#define	UMOSCOM_MCR_RTS		0x02
142#define	UMOSCOM_MCR_LOOP	0x04
143#define	UMOSCOM_MCR_INTEN	0x08
144#define	UMOSCOM_MCR_LOOPBACK	0x10
145#define	UMOSCOM_MCR_XONANY	0x20
146#define	UMOSCOM_MCR_IRDA_EN	0x40
147#define	UMOSCOM_MCR_BAUD_DIV4	0x80
148
149#define	UMOSCOM_LSR_RXDATA	0x01
150#define	UMOSCOM_LSR_RXOVER	0x02
151#define	UMOSCOM_LSR_RXPAR_ERR	0x04
152#define	UMOSCOM_LSR_RXFRM_ERR	0x08
153#define	UMOSCOM_LSR_RXBREAK	0x10
154#define	UMOSCOM_LSR_TXEMPTY	0x20
155#define	UMOSCOM_LSR_TXALLEMPTY	0x40
156#define	UMOSCOM_LSR_TXFIFO_ERR	0x80
157
158#define	UMOSCOM_MSR_CTS_CHG	0x01
159#define	UMOSCOM_MSR_DSR_CHG	0x02
160#define	UMOSCOM_MSR_RI_CHG	0x04
161#define	UMOSCOM_MSR_CD_CHG	0x08
162#define	UMOSCOM_MSR_CTS		0x10
163#define	UMOSCOM_MSR_RTS		0x20
164#define	UMOSCOM_MSR_RI		0x40
165#define	UMOSCOM_MSR_CD		0x80
166
167#define	UMOSCOM_BAUD_REF	115200
168
169enum {
170	UMOSCOM_BULK_DT_WR,
171	UMOSCOM_BULK_DT_RD,
172	UMOSCOM_INTR_DT_RD,
173	UMOSCOM_N_TRANSFER,
174};
175
176struct umoscom_softc {
177	struct ucom_super_softc sc_super_ucom;
178	struct ucom_softc sc_ucom;
179
180	struct usb_xfer *sc_xfer[UMOSCOM_N_TRANSFER];
181	struct usb_device *sc_udev;
182	struct mtx sc_mtx;
183
184	uint8_t	sc_mcr;
185	uint8_t	sc_lcr;
186};
187
188/* prototypes */
189
190static device_probe_t umoscom_probe;
191static device_attach_t umoscom_attach;
192static device_detach_t umoscom_detach;
193static void umoscom_free_softc(struct umoscom_softc *);
194
195static usb_callback_t umoscom_write_callback;
196static usb_callback_t umoscom_read_callback;
197static usb_callback_t umoscom_intr_callback;
198
199static void	umoscom_free(struct ucom_softc *);
200static void	umoscom_cfg_open(struct ucom_softc *);
201static void	umoscom_cfg_close(struct ucom_softc *);
202static void	umoscom_cfg_set_break(struct ucom_softc *, uint8_t);
203static void	umoscom_cfg_set_dtr(struct ucom_softc *, uint8_t);
204static void	umoscom_cfg_set_rts(struct ucom_softc *, uint8_t);
205static int	umoscom_pre_param(struct ucom_softc *, struct termios *);
206static void	umoscom_cfg_param(struct ucom_softc *, struct termios *);
207static void	umoscom_cfg_get_status(struct ucom_softc *, uint8_t *,
208		    uint8_t *);
209static void	umoscom_cfg_write(struct umoscom_softc *, uint16_t, uint16_t);
210static uint8_t	umoscom_cfg_read(struct umoscom_softc *, uint16_t);
211static void	umoscom_start_read(struct ucom_softc *);
212static void	umoscom_stop_read(struct ucom_softc *);
213static void	umoscom_start_write(struct ucom_softc *);
214static void	umoscom_stop_write(struct ucom_softc *);
215static void	umoscom_poll(struct ucom_softc *ucom);
216
217static const struct usb_config umoscom_config_data[UMOSCOM_N_TRANSFER] = {
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
281static const STRUCT_USB_HOST_ID umoscom_devs[] = {
282	{USB_VPI(USB_VENDOR_MOSCHIP, USB_PRODUCT_MOSCHIP_MCS7703, 0)}
283};
284
285DRIVER_MODULE(umoscom, uhub, umoscom_driver, umoscom_devclass, NULL, 0);
286MODULE_DEPEND(umoscom, ucom, 1, 1, 1);
287MODULE_DEPEND(umoscom, usb, 1, 1, 1);
288MODULE_VERSION(umoscom, 1);
289USB_PNP_HOST_INFO(umoscom_devs);
290
291static int
292umoscom_probe(device_t dev)
293{
294	struct usb_attach_arg *uaa = device_get_ivars(dev);
295
296	if (uaa->usb_mode != USB_MODE_HOST) {
297		return (ENXIO);
298	}
299	if (uaa->info.bConfigIndex != UMOSCOM_CONFIG_INDEX) {
300		return (ENXIO);
301	}
302	if (uaa->info.bIfaceIndex != UMOSCOM_IFACE_INDEX) {
303		return (ENXIO);
304	}
305	return (usbd_lookup_id_by_uaa(umoscom_devs, sizeof(umoscom_devs), uaa));
306}
307
308static int
309umoscom_attach(device_t dev)
310{
311	struct usb_attach_arg *uaa = device_get_ivars(dev);
312	struct umoscom_softc *sc = device_get_softc(dev);
313	int error;
314	uint8_t iface_index;
315
316	sc->sc_udev = uaa->device;
317	sc->sc_mcr = 0x08;		/* enable interrupts */
318
319	/* XXX the device doesn't provide any ID string, so set a static one */
320	device_set_desc(dev, "MOSCHIP USB Serial Port Adapter");
321	device_printf(dev, "<MOSCHIP USB Serial Port Adapter>\n");
322
323	mtx_init(&sc->sc_mtx, "umoscom", NULL, MTX_DEF);
324	ucom_ref(&sc->sc_super_ucom);
325
326	iface_index = UMOSCOM_IFACE_INDEX;
327	error = usbd_transfer_setup(uaa->device, &iface_index,
328	    sc->sc_xfer, umoscom_config_data,
329	    UMOSCOM_N_TRANSFER, sc, &sc->sc_mtx);
330
331	if (error) {
332		goto detach;
333	}
334	/* clear stall at first run */
335	mtx_lock(&sc->sc_mtx);
336	usbd_xfer_set_stall(sc->sc_xfer[UMOSCOM_BULK_DT_WR]);
337	usbd_xfer_set_stall(sc->sc_xfer[UMOSCOM_BULK_DT_RD]);
338	mtx_unlock(&sc->sc_mtx);
339
340	error = ucom_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc,
341	    &umoscom_callback, &sc->sc_mtx);
342	if (error) {
343		goto detach;
344	}
345	ucom_set_pnpinfo_usb(&sc->sc_super_ucom, dev);
346
347	return (0);
348
349detach:
350	device_printf(dev, "attach error: %s\n", usbd_errstr(error));
351	umoscom_detach(dev);
352	return (ENXIO);
353}
354
355static int
356umoscom_detach(device_t dev)
357{
358	struct umoscom_softc *sc = device_get_softc(dev);
359
360	ucom_detach(&sc->sc_super_ucom, &sc->sc_ucom);
361	usbd_transfer_unsetup(sc->sc_xfer, UMOSCOM_N_TRANSFER);
362
363	device_claim_softc(dev);
364
365	umoscom_free_softc(sc);
366
367	return (0);
368}
369
370UCOM_UNLOAD_DRAIN(umoscom);
371
372static void
373umoscom_free_softc(struct umoscom_softc *sc)
374{
375	if (ucom_unref(&sc->sc_super_ucom)) {
376		mtx_destroy(&sc->sc_mtx);
377		device_free_softc(sc);
378	}
379}
380
381static void
382umoscom_free(struct ucom_softc *ucom)
383{
384	umoscom_free_softc(ucom->sc_parent);
385}
386
387static void
388umoscom_cfg_open(struct ucom_softc *ucom)
389{
390	struct umoscom_softc *sc = ucom->sc_parent;
391
392	DPRINTF("\n");
393
394	/* Purge FIFOs or odd things happen */
395	umoscom_cfg_write(sc, UMOSCOM_FIFO, 0x00 | UMOSCOM_UART_REG);
396
397	/* Enable FIFO */
398	umoscom_cfg_write(sc, UMOSCOM_FIFO, UMOSCOM_FIFO_EN |
399	    UMOSCOM_FIFO_RXCLR | UMOSCOM_FIFO_TXCLR |
400	    UMOSCOM_FIFO_DMA_BLK | UMOSCOM_FIFO_RXLVL_MASK |
401	    UMOSCOM_UART_REG);
402
403	/* Enable Interrupt Registers */
404	umoscom_cfg_write(sc, UMOSCOM_INT, 0x0C | UMOSCOM_UART_REG);
405
406	/* Magic */
407	umoscom_cfg_write(sc, 0x01, 0x08);
408
409	/* Magic */
410	umoscom_cfg_write(sc, 0x00, 0x02);
411}
412
413static void
414umoscom_cfg_close(struct ucom_softc *ucom)
415{
416	return;
417}
418
419static void
420umoscom_cfg_set_break(struct ucom_softc *ucom, uint8_t onoff)
421{
422	struct umoscom_softc *sc = ucom->sc_parent;
423	uint16_t val;
424
425	val = sc->sc_lcr;
426	if (onoff)
427		val |= UMOSCOM_LCR_BREAK;
428
429	umoscom_cfg_write(sc, UMOSCOM_LCR, val | UMOSCOM_UART_REG);
430}
431
432static void
433umoscom_cfg_set_dtr(struct ucom_softc *ucom, uint8_t onoff)
434{
435	struct umoscom_softc *sc = ucom->sc_parent;
436
437	if (onoff)
438		sc->sc_mcr |= UMOSCOM_MCR_DTR;
439	else
440		sc->sc_mcr &= ~UMOSCOM_MCR_DTR;
441
442	umoscom_cfg_write(sc, UMOSCOM_MCR, sc->sc_mcr | UMOSCOM_UART_REG);
443}
444
445static void
446umoscom_cfg_set_rts(struct ucom_softc *ucom, uint8_t onoff)
447{
448	struct umoscom_softc *sc = ucom->sc_parent;
449
450	if (onoff)
451		sc->sc_mcr |= UMOSCOM_MCR_RTS;
452	else
453		sc->sc_mcr &= ~UMOSCOM_MCR_RTS;
454
455	umoscom_cfg_write(sc, UMOSCOM_MCR, sc->sc_mcr | UMOSCOM_UART_REG);
456}
457
458static int
459umoscom_pre_param(struct ucom_softc *ucom, struct termios *t)
460{
461	if ((t->c_ospeed <= 1) || (t->c_ospeed > 115200))
462		return (EINVAL);
463
464	return (0);
465}
466
467static void
468umoscom_cfg_param(struct ucom_softc *ucom, struct termios *t)
469{
470	struct umoscom_softc *sc = ucom->sc_parent;
471	uint16_t data;
472
473	DPRINTF("speed=%d\n", t->c_ospeed);
474
475	data = ((uint32_t)UMOSCOM_BAUD_REF) / ((uint32_t)t->c_ospeed);
476
477	if (data == 0) {
478		DPRINTF("invalid baud rate!\n");
479		return;
480	}
481	umoscom_cfg_write(sc, UMOSCOM_LCR,
482	    UMOSCOM_LCR_DIVLATCH_EN | UMOSCOM_UART_REG);
483
484	umoscom_cfg_write(sc, UMOSCOM_BAUDLO,
485	    (data & 0xFF) | UMOSCOM_UART_REG);
486
487	umoscom_cfg_write(sc, UMOSCOM_BAUDHI,
488	    ((data >> 8) & 0xFF) | UMOSCOM_UART_REG);
489
490	if (t->c_cflag & CSTOPB)
491		data = UMOSCOM_LCR_STOP_BITS_2;
492	else
493		data = UMOSCOM_LCR_STOP_BITS_1;
494
495	if (t->c_cflag & PARENB) {
496		if (t->c_cflag & PARODD)
497			data |= UMOSCOM_LCR_PARITY_ODD;
498		else
499			data |= UMOSCOM_LCR_PARITY_EVEN;
500	} else
501		data |= UMOSCOM_LCR_PARITY_NONE;
502
503	switch (t->c_cflag & CSIZE) {
504	case CS5:
505		data |= UMOSCOM_LCR_DBITS(5);
506		break;
507	case CS6:
508		data |= UMOSCOM_LCR_DBITS(6);
509		break;
510	case CS7:
511		data |= UMOSCOM_LCR_DBITS(7);
512		break;
513	case CS8:
514		data |= UMOSCOM_LCR_DBITS(8);
515		break;
516	}
517
518	sc->sc_lcr = data;
519	umoscom_cfg_write(sc, UMOSCOM_LCR, data | UMOSCOM_UART_REG);
520}
521
522static void
523umoscom_cfg_get_status(struct ucom_softc *ucom, uint8_t *p_lsr, uint8_t *p_msr)
524{
525	struct umoscom_softc *sc = ucom->sc_parent;
526	uint8_t msr;
527
528	DPRINTFN(5, "\n");
529
530	/*
531	 * Read status registers.  MSR bits need translation from ns16550 to
532	 * SER_* values.  LSR bits are ns16550 in hardware and ucom.
533	 */
534
535	*p_lsr = umoscom_cfg_read(sc, UMOSCOM_LSR);
536	msr = umoscom_cfg_read(sc, UMOSCOM_MSR);
537
538	/* translate bits */
539
540	if (msr & UMOSCOM_MSR_CTS)
541		*p_msr |= SER_CTS;
542
543	if (msr & UMOSCOM_MSR_CD)
544		*p_msr |= SER_DCD;
545
546	if (msr & UMOSCOM_MSR_RI)
547		*p_msr |= SER_RI;
548
549	if (msr & UMOSCOM_MSR_RTS)
550		*p_msr |= SER_DSR;
551}
552
553static void
554umoscom_cfg_write(struct umoscom_softc *sc, uint16_t reg, uint16_t val)
555{
556	struct usb_device_request req;
557
558	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
559	req.bRequest = UMOSCOM_WRITE;
560	USETW(req.wValue, val);
561	USETW(req.wIndex, reg);
562	USETW(req.wLength, 0);
563
564	ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
565	    &req, NULL, 0, 1000);
566}
567
568static uint8_t
569umoscom_cfg_read(struct umoscom_softc *sc, uint16_t reg)
570{
571	struct usb_device_request req;
572	uint8_t val;
573
574	req.bmRequestType = UT_READ_VENDOR_DEVICE;
575	req.bRequest = UMOSCOM_READ;
576	USETW(req.wValue, 0);
577	USETW(req.wIndex, reg);
578	USETW(req.wLength, 1);
579
580	ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
581	    &req, &val, 0, 1000);
582
583	DPRINTF("reg=0x%04x, val=0x%02x\n", reg, val);
584
585	return (val);
586}
587
588static void
589umoscom_start_read(struct ucom_softc *ucom)
590{
591	struct umoscom_softc *sc = ucom->sc_parent;
592
593#if 0
594	/* start interrupt endpoint */
595	usbd_transfer_start(sc->sc_xfer[UMOSCOM_INTR_DT_RD]);
596#endif
597	/* start read endpoint */
598	usbd_transfer_start(sc->sc_xfer[UMOSCOM_BULK_DT_RD]);
599}
600
601static void
602umoscom_stop_read(struct ucom_softc *ucom)
603{
604	struct umoscom_softc *sc = ucom->sc_parent;
605
606	/* stop interrupt transfer */
607	usbd_transfer_stop(sc->sc_xfer[UMOSCOM_INTR_DT_RD]);
608
609	/* stop read endpoint */
610	usbd_transfer_stop(sc->sc_xfer[UMOSCOM_BULK_DT_RD]);
611}
612
613static void
614umoscom_start_write(struct ucom_softc *ucom)
615{
616	struct umoscom_softc *sc = ucom->sc_parent;
617
618	usbd_transfer_start(sc->sc_xfer[UMOSCOM_BULK_DT_WR]);
619}
620
621static void
622umoscom_stop_write(struct ucom_softc *ucom)
623{
624	struct umoscom_softc *sc = ucom->sc_parent;
625
626	usbd_transfer_stop(sc->sc_xfer[UMOSCOM_BULK_DT_WR]);
627}
628
629static void
630umoscom_write_callback(struct usb_xfer *xfer, usb_error_t error)
631{
632	struct umoscom_softc *sc = usbd_xfer_softc(xfer);
633	struct usb_page_cache *pc;
634	uint32_t actlen;
635
636	switch (USB_GET_STATE(xfer)) {
637	case USB_ST_SETUP:
638	case USB_ST_TRANSFERRED:
639tr_setup:
640		DPRINTF("\n");
641
642		pc = usbd_xfer_get_frame(xfer, 0);
643		if (ucom_get_data(&sc->sc_ucom, pc, 0,
644		    UMOSCOM_BUFSIZE, &actlen)) {
645			usbd_xfer_set_frame_len(xfer, 0, actlen);
646			usbd_transfer_submit(xfer);
647		}
648		return;
649
650	default:			/* Error */
651		if (error != USB_ERR_CANCELLED) {
652			DPRINTFN(0, "transfer failed\n");
653			/* try to clear stall first */
654			usbd_xfer_set_stall(xfer);
655			goto tr_setup;
656		}
657		return;
658	}
659}
660
661static void
662umoscom_read_callback(struct usb_xfer *xfer, usb_error_t error)
663{
664	struct umoscom_softc *sc = usbd_xfer_softc(xfer);
665	struct usb_page_cache *pc;
666	int actlen;
667
668	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
669
670	switch (USB_GET_STATE(xfer)) {
671	case USB_ST_TRANSFERRED:
672		DPRINTF("got %d bytes\n", actlen);
673		pc = usbd_xfer_get_frame(xfer, 0);
674		ucom_put_data(&sc->sc_ucom, pc, 0, actlen);
675
676	case USB_ST_SETUP:
677tr_setup:
678		DPRINTF("\n");
679
680		usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
681		usbd_transfer_submit(xfer);
682		return;
683
684	default:			/* Error */
685		if (error != USB_ERR_CANCELLED) {
686			DPRINTFN(0, "transfer failed\n");
687			/* try to clear stall first */
688			usbd_xfer_set_stall(xfer);
689			goto tr_setup;
690		}
691		return;
692	}
693}
694
695static void
696umoscom_intr_callback(struct usb_xfer *xfer, usb_error_t error)
697{
698	struct umoscom_softc *sc = usbd_xfer_softc(xfer);
699	int actlen;
700
701	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
702
703	switch (USB_GET_STATE(xfer)) {
704	case USB_ST_TRANSFERRED:
705		if (actlen < 2) {
706			DPRINTF("too short message\n");
707			goto tr_setup;
708		}
709		ucom_status_change(&sc->sc_ucom);
710
711	case USB_ST_SETUP:
712tr_setup:
713		usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
714		usbd_transfer_submit(xfer);
715		return;
716
717	default:			/* Error */
718		if (error != USB_ERR_CANCELLED) {
719			DPRINTFN(0, "transfer failed\n");
720			/* try to clear stall first */
721			usbd_xfer_set_stall(xfer);
722			goto tr_setup;
723		}
724		return;
725	}
726}
727
728static void
729umoscom_poll(struct ucom_softc *ucom)
730{
731	struct umoscom_softc *sc = ucom->sc_parent;
732	usbd_transfer_poll(sc->sc_xfer, UMOSCOM_N_TRANSFER);
733}
734