umoscom.c revision 227309
165977Salex/* $FreeBSD: head/sys/dev/usb/serial/umoscom.c 227309 2011-11-07 15:43:11Z ed $ */
265977Salex/*	$OpenBSD: umoscom.c,v 1.2 2006/10/26 06:02:43 jsg Exp $	*/
365977Salex
465977Salex/*
565977Salex * Copyright (c) 2006 Jonathan Gray <jsg@openbsd.org>
665977Salex *
765977Salex * Permission to use, copy, modify, and distribute this software for any
865977Salex * purpose with or without fee is hereby granted, provided that the above
965977Salex * copyright notice and this permission notice appear in all copies.
1065977Salex *
1165977Salex * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1265977Salex * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1365977Salex * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1465977Salex * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1565977Salex * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1665977Salex * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1765977Salex * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1865977Salex */
1965977Salex
2065977Salex#include <sys/stdint.h>
2165977Salex#include <sys/stddef.h>
2265977Salex#include <sys/param.h>
2365977Salex#include <sys/queue.h>
2465977Salex#include <sys/types.h>
2565977Salex#include <sys/systm.h>
2665977Salex#include <sys/kernel.h>
2765977Salex#include <sys/bus.h>
2865977Salex#include <sys/module.h>
2965977Salex#include <sys/lock.h>
3065977Salex#include <sys/mutex.h>
31239510Sjhb#include <sys/condvar.h>
3265977Salex#include <sys/sysctl.h>
3365977Salex#include <sys/sx.h>
3465977Salex#include <sys/unistd.h>
35239510Sjhb#include <sys/callout.h>
36239510Sjhb#include <sys/malloc.h>
37239510Sjhb#include <sys/priv.h>
38239510Sjhb
3965977Salex#include <dev/usb/usb.h>
4065977Salex#include <dev/usb/usbdi.h>
4188509Sdavidc#include <dev/usb/usbdi_util.h>
4288509Sdavidc#include "usbdevs.h"
4384306Sru
4484306Sru#define	USB_DEBUG_VAR umoscom_debug
4574984Sbde#include <dev/usb/usb_debug.h>
46239510Sjhb#include <dev/usb/usb_process.h>
47239510Sjhb
48239510Sjhb#include <dev/usb/serial/usb_serial.h>
4965977Salex
5065977Salex#ifdef USB_DEBUG
5165977Salexstatic int umoscom_debug = 0;
5265977Salex
5365977Salexstatic SYSCTL_NODE(_hw_usb, OID_AUTO, umoscom, CTLFLAG_RW, 0, "USB umoscom");
5465977SalexSYSCTL_INT(_hw_usb_umoscom, OID_AUTO, debug, CTLFLAG_RW,
5565977Salex    &umoscom_debug, 0, "Debug level");
5665977Salex#endif
5779676Salex
5865977Salex#define	UMOSCOM_BUFSIZE	       1024	/* bytes */
5965977Salex
6065977Salex#define	UMOSCOM_CONFIG_INDEX	0
6165977Salex#define	UMOSCOM_IFACE_INDEX	0
6265977Salex
6381449Sru/* interrupt packet */
6465977Salex#define	UMOSCOM_IIR_RLS		0x06
6565977Salex#define	UMOSCOM_IIR_RDA		0x04
6665977Salex#define	UMOSCOM_IIR_CTI		0x0c
6765977Salex#define	UMOSCOM_IIR_THR		0x02
6865977Salex#define	UMOSCOM_IIR_MS		0x00
6965977Salex
7065977Salex/* registers */
7165977Salex#define	UMOSCOM_READ		0x0d
7265977Salex#define	UMOSCOM_WRITE		0x0e
7365977Salex#define	UMOSCOM_UART_REG	0x0300
7465977Salex#define	UMOSCOM_VEND_REG	0x0000
7565977Salex
7665977Salex#define	UMOSCOM_TXBUF		0x00	/* Write */
7779676Salex#define	UMOSCOM_RXBUF		0x00	/* Read */
7865977Salex#define	UMOSCOM_INT		0x01
7979676Salex#define	UMOSCOM_FIFO		0x02	/* Write */
8065977Salex#define	UMOSCOM_ISR		0x02	/* Read */
8165977Salex#define	UMOSCOM_LCR		0x03
8265977Salex#define	UMOSCOM_MCR		0x04
8365977Salex#define	UMOSCOM_LSR		0x05
8465977Salex#define	UMOSCOM_MSR		0x06
8565977Salex#define	UMOSCOM_SCRATCH		0x07
8665977Salex#define	UMOSCOM_DIV_LO		0x08
8765977Salex#define	UMOSCOM_DIV_HI		0x09
8865977Salex#define	UMOSCOM_EFR		0x0a
8965977Salex#define	UMOSCOM_XON1		0x0b
9065977Salex#define	UMOSCOM_XON2		0x0c
9179676Salex#define	UMOSCOM_XOFF1		0x0d
9265977Salex#define	UMOSCOM_XOFF2		0x0e
9365977Salex
9465977Salex#define	UMOSCOM_BAUDLO		0x00
9565977Salex#define	UMOSCOM_BAUDHI		0x01
9674109Salex
9774109Salex#define	UMOSCOM_INT_RXEN	0x01
9865977Salex#define	UMOSCOM_INT_TXEN	0x02
9965977Salex#define	UMOSCOM_INT_RSEN	0x04
10065977Salex#define	UMOSCOM_INT_MDMEM	0x08
10165977Salex#define	UMOSCOM_INT_SLEEP	0x10
10265977Salex#define	UMOSCOM_INT_XOFF	0x20
10365977Salex#define	UMOSCOM_INT_RTS		0x40
104239510Sjhb
105239510Sjhb#define	UMOSCOM_FIFO_EN		0x01
106239510Sjhb#define	UMOSCOM_FIFO_RXCLR	0x02
107239510Sjhb#define	UMOSCOM_FIFO_TXCLR	0x04
108239510Sjhb#define	UMOSCOM_FIFO_DMA_BLK	0x08
109239510Sjhb#define	UMOSCOM_FIFO_TXLVL_MASK	0x30
110239510Sjhb#define	UMOSCOM_FIFO_TXLVL_8	0x00
111239510Sjhb#define	UMOSCOM_FIFO_TXLVL_16	0x10
112239510Sjhb#define	UMOSCOM_FIFO_TXLVL_32	0x20
113239510Sjhb#define	UMOSCOM_FIFO_TXLVL_56	0x30
114239510Sjhb#define	UMOSCOM_FIFO_RXLVL_MASK	0xc0
115239510Sjhb#define	UMOSCOM_FIFO_RXLVL_8	0x00
116239510Sjhb#define	UMOSCOM_FIFO_RXLVL_16	0x40
117239510Sjhb#define	UMOSCOM_FIFO_RXLVL_56	0x80
118239510Sjhb#define	UMOSCOM_FIFO_RXLVL_80	0xc0
119239510Sjhb
120239510Sjhb#define	UMOSCOM_ISR_MDM		0x00
121239510Sjhb#define	UMOSCOM_ISR_NONE	0x01
122239510Sjhb#define	UMOSCOM_ISR_TX		0x02
123239510Sjhb#define	UMOSCOM_ISR_RX		0x04
124239510Sjhb#define	UMOSCOM_ISR_LINE	0x06
125239510Sjhb#define	UMOSCOM_ISR_RXTIMEOUT	0x0c
126239510Sjhb#define	UMOSCOM_ISR_RX_XOFF	0x10
127239510Sjhb#define	UMOSCOM_ISR_RTSCTS	0x20
128239510Sjhb#define	UMOSCOM_ISR_FIFOEN	0xc0
129239510Sjhb
130239510Sjhb#define	UMOSCOM_LCR_DBITS(x)	((x) - 5)
131239510Sjhb#define	UMOSCOM_LCR_STOP_BITS_1	0x00
132239510Sjhb#define	UMOSCOM_LCR_STOP_BITS_2	0x04	/* 2 if 6-8 bits/char or 1.5 if 5 */
133239510Sjhb#define	UMOSCOM_LCR_PARITY_NONE	0x00
134239510Sjhb#define	UMOSCOM_LCR_PARITY_ODD	0x08
135239510Sjhb#define	UMOSCOM_LCR_PARITY_EVEN	0x18
136239510Sjhb#define	UMOSCOM_LCR_BREAK	0x40
137239510Sjhb#define	UMOSCOM_LCR_DIVLATCH_EN	0x80
138239510Sjhb
139239510Sjhb#define	UMOSCOM_MCR_DTR		0x01
140239510Sjhb#define	UMOSCOM_MCR_RTS		0x02
141239510Sjhb#define	UMOSCOM_MCR_LOOP	0x04
142239510Sjhb#define	UMOSCOM_MCR_INTEN	0x08
14365977Salex#define	UMOSCOM_MCR_LOOPBACK	0x10
14479366Sru#define	UMOSCOM_MCR_XONANY	0x20
14565977Salex#define	UMOSCOM_MCR_IRDA_EN	0x40
146239510Sjhb#define	UMOSCOM_MCR_BAUD_DIV4	0x80
147239510Sjhb
14865977Salex#define	UMOSCOM_LSR_RXDATA	0x01
14965977Salex#define	UMOSCOM_LSR_RXOVER	0x02
15065977Salex#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;
192
193static usb_callback_t umoscom_write_callback;
194static usb_callback_t umoscom_read_callback;
195static usb_callback_t umoscom_intr_callback;
196
197static void	umoscom_cfg_open(struct ucom_softc *);
198static void	umoscom_cfg_close(struct ucom_softc *);
199static void	umoscom_cfg_set_break(struct ucom_softc *, uint8_t);
200static void	umoscom_cfg_set_dtr(struct ucom_softc *, uint8_t);
201static void	umoscom_cfg_set_rts(struct ucom_softc *, uint8_t);
202static int	umoscom_pre_param(struct ucom_softc *, struct termios *);
203static void	umoscom_cfg_param(struct ucom_softc *, struct termios *);
204static void	umoscom_cfg_get_status(struct ucom_softc *, uint8_t *,
205		    uint8_t *);
206static void	umoscom_cfg_write(struct umoscom_softc *, uint16_t, uint16_t);
207static uint8_t	umoscom_cfg_read(struct umoscom_softc *, uint16_t);
208static void	umoscom_start_read(struct ucom_softc *);
209static void	umoscom_stop_read(struct ucom_softc *);
210static void	umoscom_start_write(struct ucom_softc *);
211static void	umoscom_stop_write(struct ucom_softc *);
212static void	umoscom_poll(struct ucom_softc *ucom);
213
214static const struct usb_config umoscom_config_data[UMOSCOM_N_TRANSFER] = {
215
216	[UMOSCOM_BULK_DT_WR] = {
217		.type = UE_BULK,
218		.endpoint = UE_ADDR_ANY,
219		.direction = UE_DIR_OUT,
220		.bufsize = UMOSCOM_BUFSIZE,
221		.flags = {.pipe_bof = 1,.force_short_xfer = 1,},
222		.callback = &umoscom_write_callback,
223	},
224
225	[UMOSCOM_BULK_DT_RD] = {
226		.type = UE_BULK,
227		.endpoint = UE_ADDR_ANY,
228		.direction = UE_DIR_IN,
229		.bufsize = UMOSCOM_BUFSIZE,
230		.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
231		.callback = &umoscom_read_callback,
232	},
233
234	[UMOSCOM_INTR_DT_RD] = {
235		.type = UE_INTERRUPT,
236		.endpoint = UE_ADDR_ANY,
237		.direction = UE_DIR_IN,
238		.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
239		.bufsize = 0,	/* use wMaxPacketSize */
240		.callback = &umoscom_intr_callback,
241	},
242};
243
244static const struct ucom_callback umoscom_callback = {
245	/* configuration callbacks */
246	.ucom_cfg_get_status = &umoscom_cfg_get_status,
247	.ucom_cfg_set_dtr = &umoscom_cfg_set_dtr,
248	.ucom_cfg_set_rts = &umoscom_cfg_set_rts,
249	.ucom_cfg_set_break = &umoscom_cfg_set_break,
250	.ucom_cfg_param = &umoscom_cfg_param,
251	.ucom_cfg_open = &umoscom_cfg_open,
252	.ucom_cfg_close = &umoscom_cfg_close,
253
254	/* other callbacks */
255	.ucom_pre_param = &umoscom_pre_param,
256	.ucom_start_read = &umoscom_start_read,
257	.ucom_stop_read = &umoscom_stop_read,
258	.ucom_start_write = &umoscom_start_write,
259	.ucom_stop_write = &umoscom_stop_write,
260	.ucom_poll = &umoscom_poll,
261};
262
263static device_method_t umoscom_methods[] = {
264	DEVMETHOD(device_probe, umoscom_probe),
265	DEVMETHOD(device_attach, umoscom_attach),
266	DEVMETHOD(device_detach, umoscom_detach),
267	{0, 0}
268};
269
270static devclass_t umoscom_devclass;
271
272static driver_t umoscom_driver = {
273	.name = "umoscom",
274	.methods = umoscom_methods,
275	.size = sizeof(struct umoscom_softc),
276};
277
278DRIVER_MODULE(umoscom, uhub, umoscom_driver, umoscom_devclass, NULL, 0);
279MODULE_DEPEND(umoscom, ucom, 1, 1, 1);
280MODULE_DEPEND(umoscom, usb, 1, 1, 1);
281MODULE_VERSION(umoscom, 1);
282
283static const STRUCT_USB_HOST_ID umoscom_devs[] = {
284	{USB_VPI(USB_VENDOR_MOSCHIP, USB_PRODUCT_MOSCHIP_MCS7703, 0)}
285};
286
287static int
288umoscom_probe(device_t dev)
289{
290	struct usb_attach_arg *uaa = device_get_ivars(dev);
291
292	if (uaa->usb_mode != USB_MODE_HOST) {
293		return (ENXIO);
294	}
295	if (uaa->info.bConfigIndex != UMOSCOM_CONFIG_INDEX) {
296		return (ENXIO);
297	}
298	if (uaa->info.bIfaceIndex != UMOSCOM_IFACE_INDEX) {
299		return (ENXIO);
300	}
301	return (usbd_lookup_id_by_uaa(umoscom_devs, sizeof(umoscom_devs), uaa));
302}
303
304static int
305umoscom_attach(device_t dev)
306{
307	struct usb_attach_arg *uaa = device_get_ivars(dev);
308	struct umoscom_softc *sc = device_get_softc(dev);
309	int error;
310	uint8_t iface_index;
311
312	sc->sc_udev = uaa->device;
313	sc->sc_mcr = 0x08;		/* enable interrupts */
314
315	/* XXX the device doesn't provide any ID string, so set a static one */
316	device_set_desc(dev, "MOSCHIP USB Serial Port Adapter");
317	device_printf(dev, "<MOSCHIP USB Serial Port Adapter>\n");
318
319	mtx_init(&sc->sc_mtx, "umoscom", NULL, MTX_DEF);
320
321	iface_index = UMOSCOM_IFACE_INDEX;
322	error = usbd_transfer_setup(uaa->device, &iface_index,
323	    sc->sc_xfer, umoscom_config_data,
324	    UMOSCOM_N_TRANSFER, sc, &sc->sc_mtx);
325
326	if (error) {
327		goto detach;
328	}
329	/* clear stall at first run */
330	mtx_lock(&sc->sc_mtx);
331	usbd_xfer_set_stall(sc->sc_xfer[UMOSCOM_BULK_DT_WR]);
332	usbd_xfer_set_stall(sc->sc_xfer[UMOSCOM_BULK_DT_RD]);
333	mtx_unlock(&sc->sc_mtx);
334
335	error = ucom_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc,
336	    &umoscom_callback, &sc->sc_mtx);
337	if (error) {
338		goto detach;
339	}
340	ucom_set_pnpinfo_usb(&sc->sc_super_ucom, dev);
341
342	return (0);
343
344detach:
345	device_printf(dev, "attach error: %s\n", usbd_errstr(error));
346	umoscom_detach(dev);
347	return (ENXIO);
348}
349
350static int
351umoscom_detach(device_t dev)
352{
353	struct umoscom_softc *sc = device_get_softc(dev);
354
355	ucom_detach(&sc->sc_super_ucom, &sc->sc_ucom);
356	usbd_transfer_unsetup(sc->sc_xfer, UMOSCOM_N_TRANSFER);
357	mtx_destroy(&sc->sc_mtx);
358
359	return (0);
360}
361
362static void
363umoscom_cfg_open(struct ucom_softc *ucom)
364{
365	struct umoscom_softc *sc = ucom->sc_parent;
366
367	DPRINTF("\n");
368
369	/* Purge FIFOs or odd things happen */
370	umoscom_cfg_write(sc, UMOSCOM_FIFO, 0x00 | UMOSCOM_UART_REG);
371
372	/* Enable FIFO */
373	umoscom_cfg_write(sc, UMOSCOM_FIFO, UMOSCOM_FIFO_EN |
374	    UMOSCOM_FIFO_RXCLR | UMOSCOM_FIFO_TXCLR |
375	    UMOSCOM_FIFO_DMA_BLK | UMOSCOM_FIFO_RXLVL_MASK |
376	    UMOSCOM_UART_REG);
377
378	/* Enable Interrupt Registers */
379	umoscom_cfg_write(sc, UMOSCOM_INT, 0x0C | UMOSCOM_UART_REG);
380
381	/* Magic */
382	umoscom_cfg_write(sc, 0x01, 0x08);
383
384	/* Magic */
385	umoscom_cfg_write(sc, 0x00, 0x02);
386}
387
388static void
389umoscom_cfg_close(struct ucom_softc *ucom)
390{
391	return;
392}
393
394static void
395umoscom_cfg_set_break(struct ucom_softc *ucom, uint8_t onoff)
396{
397	struct umoscom_softc *sc = ucom->sc_parent;
398	uint16_t val;
399
400	val = sc->sc_lcr;
401	if (onoff)
402		val |= UMOSCOM_LCR_BREAK;
403
404	umoscom_cfg_write(sc, UMOSCOM_LCR, val | UMOSCOM_UART_REG);
405}
406
407static void
408umoscom_cfg_set_dtr(struct ucom_softc *ucom, uint8_t onoff)
409{
410	struct umoscom_softc *sc = ucom->sc_parent;
411
412	if (onoff)
413		sc->sc_mcr |= UMOSCOM_MCR_DTR;
414	else
415		sc->sc_mcr &= ~UMOSCOM_MCR_DTR;
416
417	umoscom_cfg_write(sc, UMOSCOM_MCR, sc->sc_mcr | UMOSCOM_UART_REG);
418}
419
420static void
421umoscom_cfg_set_rts(struct ucom_softc *ucom, uint8_t onoff)
422{
423	struct umoscom_softc *sc = ucom->sc_parent;
424
425	if (onoff)
426		sc->sc_mcr |= UMOSCOM_MCR_RTS;
427	else
428		sc->sc_mcr &= ~UMOSCOM_MCR_RTS;
429
430	umoscom_cfg_write(sc, UMOSCOM_MCR, sc->sc_mcr | UMOSCOM_UART_REG);
431}
432
433static int
434umoscom_pre_param(struct ucom_softc *ucom, struct termios *t)
435{
436	if ((t->c_ospeed <= 1) || (t->c_ospeed > 115200))
437		return (EINVAL);
438
439	return (0);
440}
441
442static void
443umoscom_cfg_param(struct ucom_softc *ucom, struct termios *t)
444{
445	struct umoscom_softc *sc = ucom->sc_parent;
446	uint16_t data;
447
448	DPRINTF("speed=%d\n", t->c_ospeed);
449
450	data = ((uint32_t)UMOSCOM_BAUD_REF) / ((uint32_t)t->c_ospeed);
451
452	if (data == 0) {
453		DPRINTF("invalid baud rate!\n");
454		return;
455	}
456	umoscom_cfg_write(sc, UMOSCOM_LCR,
457	    UMOSCOM_LCR_DIVLATCH_EN | UMOSCOM_UART_REG);
458
459	umoscom_cfg_write(sc, UMOSCOM_BAUDLO,
460	    (data & 0xFF) | UMOSCOM_UART_REG);
461
462	umoscom_cfg_write(sc, UMOSCOM_BAUDHI,
463	    ((data >> 8) & 0xFF) | UMOSCOM_UART_REG);
464
465	if (t->c_cflag & CSTOPB)
466		data = UMOSCOM_LCR_STOP_BITS_2;
467	else
468		data = UMOSCOM_LCR_STOP_BITS_1;
469
470	if (t->c_cflag & PARENB) {
471		if (t->c_cflag & PARODD)
472			data |= UMOSCOM_LCR_PARITY_ODD;
473		else
474			data |= UMOSCOM_LCR_PARITY_EVEN;
475	} else
476		data |= UMOSCOM_LCR_PARITY_NONE;
477
478	switch (t->c_cflag & CSIZE) {
479	case CS5:
480		data |= UMOSCOM_LCR_DBITS(5);
481		break;
482	case CS6:
483		data |= UMOSCOM_LCR_DBITS(6);
484		break;
485	case CS7:
486		data |= UMOSCOM_LCR_DBITS(7);
487		break;
488	case CS8:
489		data |= UMOSCOM_LCR_DBITS(8);
490		break;
491	}
492
493	sc->sc_lcr = data;
494	umoscom_cfg_write(sc, UMOSCOM_LCR, data | UMOSCOM_UART_REG);
495}
496
497static void
498umoscom_cfg_get_status(struct ucom_softc *ucom, uint8_t *p_lsr, uint8_t *p_msr)
499{
500	struct umoscom_softc *sc = ucom->sc_parent;
501	uint8_t lsr;
502	uint8_t msr;
503
504	DPRINTFN(5, "\n");
505
506	/* read status registers */
507
508	lsr = umoscom_cfg_read(sc, UMOSCOM_LSR);
509	msr = umoscom_cfg_read(sc, UMOSCOM_MSR);
510
511	/* translate bits */
512
513	if (msr & UMOSCOM_MSR_CTS)
514		*p_msr |= SER_CTS;
515
516	if (msr & UMOSCOM_MSR_CD)
517		*p_msr |= SER_DCD;
518
519	if (msr & UMOSCOM_MSR_RI)
520		*p_msr |= SER_RI;
521
522	if (msr & UMOSCOM_MSR_RTS)
523		*p_msr |= SER_DSR;
524}
525
526static void
527umoscom_cfg_write(struct umoscom_softc *sc, uint16_t reg, uint16_t val)
528{
529	struct usb_device_request req;
530
531	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
532	req.bRequest = UMOSCOM_WRITE;
533	USETW(req.wValue, val);
534	USETW(req.wIndex, reg);
535	USETW(req.wLength, 0);
536
537	ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
538	    &req, NULL, 0, 1000);
539}
540
541static uint8_t
542umoscom_cfg_read(struct umoscom_softc *sc, uint16_t reg)
543{
544	struct usb_device_request req;
545	uint8_t val;
546
547	req.bmRequestType = UT_READ_VENDOR_DEVICE;
548	req.bRequest = UMOSCOM_READ;
549	USETW(req.wValue, 0);
550	USETW(req.wIndex, reg);
551	USETW(req.wLength, 1);
552
553	ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
554	    &req, &val, 0, 1000);
555
556	DPRINTF("reg=0x%04x, val=0x%02x\n", reg, val);
557
558	return (val);
559}
560
561static void
562umoscom_start_read(struct ucom_softc *ucom)
563{
564	struct umoscom_softc *sc = ucom->sc_parent;
565
566#if 0
567	/* start interrupt endpoint */
568	usbd_transfer_start(sc->sc_xfer[UMOSCOM_INTR_DT_RD]);
569#endif
570	/* start read endpoint */
571	usbd_transfer_start(sc->sc_xfer[UMOSCOM_BULK_DT_RD]);
572}
573
574static void
575umoscom_stop_read(struct ucom_softc *ucom)
576{
577	struct umoscom_softc *sc = ucom->sc_parent;
578
579	/* stop interrupt transfer */
580	usbd_transfer_stop(sc->sc_xfer[UMOSCOM_INTR_DT_RD]);
581
582	/* stop read endpoint */
583	usbd_transfer_stop(sc->sc_xfer[UMOSCOM_BULK_DT_RD]);
584}
585
586static void
587umoscom_start_write(struct ucom_softc *ucom)
588{
589	struct umoscom_softc *sc = ucom->sc_parent;
590
591	usbd_transfer_start(sc->sc_xfer[UMOSCOM_BULK_DT_WR]);
592}
593
594static void
595umoscom_stop_write(struct ucom_softc *ucom)
596{
597	struct umoscom_softc *sc = ucom->sc_parent;
598
599	usbd_transfer_stop(sc->sc_xfer[UMOSCOM_BULK_DT_WR]);
600}
601
602static void
603umoscom_write_callback(struct usb_xfer *xfer, usb_error_t error)
604{
605	struct umoscom_softc *sc = usbd_xfer_softc(xfer);
606	struct usb_page_cache *pc;
607	uint32_t actlen;
608
609	switch (USB_GET_STATE(xfer)) {
610	case USB_ST_SETUP:
611	case USB_ST_TRANSFERRED:
612tr_setup:
613		DPRINTF("\n");
614
615		pc = usbd_xfer_get_frame(xfer, 0);
616		if (ucom_get_data(&sc->sc_ucom, pc, 0,
617		    UMOSCOM_BUFSIZE, &actlen)) {
618
619			usbd_xfer_set_frame_len(xfer, 0, actlen);
620			usbd_transfer_submit(xfer);
621		}
622		return;
623
624	default:			/* Error */
625		if (error != USB_ERR_CANCELLED) {
626			DPRINTFN(0, "transfer failed\n");
627			/* try to clear stall first */
628			usbd_xfer_set_stall(xfer);
629			goto tr_setup;
630		}
631		return;
632	}
633}
634
635static void
636umoscom_read_callback(struct usb_xfer *xfer, usb_error_t error)
637{
638	struct umoscom_softc *sc = usbd_xfer_softc(xfer);
639	struct usb_page_cache *pc;
640	int actlen;
641
642	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
643
644	switch (USB_GET_STATE(xfer)) {
645	case USB_ST_TRANSFERRED:
646		DPRINTF("got %d bytes\n", actlen);
647		pc = usbd_xfer_get_frame(xfer, 0);
648		ucom_put_data(&sc->sc_ucom, pc, 0, actlen);
649
650	case USB_ST_SETUP:
651tr_setup:
652		DPRINTF("\n");
653
654		usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
655		usbd_transfer_submit(xfer);
656		return;
657
658	default:			/* Error */
659		if (error != USB_ERR_CANCELLED) {
660			DPRINTFN(0, "transfer failed\n");
661			/* try to clear stall first */
662			usbd_xfer_set_stall(xfer);
663			goto tr_setup;
664		}
665		return;
666	}
667}
668
669static void
670umoscom_intr_callback(struct usb_xfer *xfer, usb_error_t error)
671{
672	struct umoscom_softc *sc = usbd_xfer_softc(xfer);
673	int actlen;
674
675	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
676
677	switch (USB_GET_STATE(xfer)) {
678	case USB_ST_TRANSFERRED:
679		if (actlen < 2) {
680			DPRINTF("too short message\n");
681			goto tr_setup;
682		}
683		ucom_status_change(&sc->sc_ucom);
684
685	case USB_ST_SETUP:
686tr_setup:
687		usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
688		usbd_transfer_submit(xfer);
689		return;
690
691	default:			/* Error */
692		if (error != USB_ERR_CANCELLED) {
693			DPRINTFN(0, "transfer failed\n");
694			/* try to clear stall first */
695			usbd_xfer_set_stall(xfer);
696			goto tr_setup;
697		}
698		return;
699	}
700}
701
702static void
703umoscom_poll(struct ucom_softc *ucom)
704{
705	struct umoscom_softc *sc = ucom->sc_parent;
706	usbd_transfer_poll(sc->sc_xfer, UMOSCOM_N_TRANSFER);
707}
708