1184610Salfred/* $FreeBSD: releng/11.0/sys/dev/usb/serial/umoscom.c 292080 2015-12-11 05:28:00Z imp $ */
2184610Salfred/*	$OpenBSD: umoscom.c,v 1.2 2006/10/26 06:02:43 jsg Exp $	*/
3184610Salfred
4184610Salfred/*
5184610Salfred * Copyright (c) 2006 Jonathan Gray <jsg@openbsd.org>
6184610Salfred *
7184610Salfred * Permission to use, copy, modify, and distribute this software for any
8184610Salfred * purpose with or without fee is hereby granted, provided that the above
9184610Salfred * copyright notice and this permission notice appear in all copies.
10184610Salfred *
11184610Salfred * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12184610Salfred * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13184610Salfred * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14184610Salfred * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15184610Salfred * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16184610Salfred * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17184610Salfred * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18184610Salfred */
19184610Salfred
20194677Sthompsa#include <sys/stdint.h>
21194677Sthompsa#include <sys/stddef.h>
22194677Sthompsa#include <sys/param.h>
23194677Sthompsa#include <sys/queue.h>
24194677Sthompsa#include <sys/types.h>
25194677Sthompsa#include <sys/systm.h>
26194677Sthompsa#include <sys/kernel.h>
27194677Sthompsa#include <sys/bus.h>
28194677Sthompsa#include <sys/module.h>
29194677Sthompsa#include <sys/lock.h>
30194677Sthompsa#include <sys/mutex.h>
31194677Sthompsa#include <sys/condvar.h>
32194677Sthompsa#include <sys/sysctl.h>
33194677Sthompsa#include <sys/sx.h>
34194677Sthompsa#include <sys/unistd.h>
35194677Sthompsa#include <sys/callout.h>
36194677Sthompsa#include <sys/malloc.h>
37194677Sthompsa#include <sys/priv.h>
38194677Sthompsa
39194677Sthompsa#include <dev/usb/usb.h>
40194677Sthompsa#include <dev/usb/usbdi.h>
41194677Sthompsa#include <dev/usb/usbdi_util.h>
42188746Sthompsa#include "usbdevs.h"
43184610Salfred
44184610Salfred#define	USB_DEBUG_VAR umoscom_debug
45188942Sthompsa#include <dev/usb/usb_debug.h>
46188942Sthompsa#include <dev/usb/usb_process.h>
47184610Salfred
48188942Sthompsa#include <dev/usb/serial/usb_serial.h>
49184610Salfred
50207077Sthompsa#ifdef USB_DEBUG
51184610Salfredstatic int umoscom_debug = 0;
52184610Salfred
53227309Sedstatic SYSCTL_NODE(_hw_usb, OID_AUTO, umoscom, CTLFLAG_RW, 0, "USB umoscom");
54276701ShselaskySYSCTL_INT(_hw_usb_umoscom, OID_AUTO, debug, CTLFLAG_RWTUN,
55184610Salfred    &umoscom_debug, 0, "Debug level");
56184610Salfred#endif
57184610Salfred
58184610Salfred#define	UMOSCOM_BUFSIZE	       1024	/* bytes */
59184610Salfred
60184610Salfred#define	UMOSCOM_CONFIG_INDEX	0
61184610Salfred#define	UMOSCOM_IFACE_INDEX	0
62184610Salfred
63184610Salfred/* interrupt packet */
64184610Salfred#define	UMOSCOM_IIR_RLS		0x06
65184610Salfred#define	UMOSCOM_IIR_RDA		0x04
66184610Salfred#define	UMOSCOM_IIR_CTI		0x0c
67184610Salfred#define	UMOSCOM_IIR_THR		0x02
68184610Salfred#define	UMOSCOM_IIR_MS		0x00
69184610Salfred
70184610Salfred/* registers */
71184610Salfred#define	UMOSCOM_READ		0x0d
72184610Salfred#define	UMOSCOM_WRITE		0x0e
73184610Salfred#define	UMOSCOM_UART_REG	0x0300
74184610Salfred#define	UMOSCOM_VEND_REG	0x0000
75184610Salfred
76184610Salfred#define	UMOSCOM_TXBUF		0x00	/* Write */
77184610Salfred#define	UMOSCOM_RXBUF		0x00	/* Read */
78184610Salfred#define	UMOSCOM_INT		0x01
79184610Salfred#define	UMOSCOM_FIFO		0x02	/* Write */
80184610Salfred#define	UMOSCOM_ISR		0x02	/* Read */
81184610Salfred#define	UMOSCOM_LCR		0x03
82184610Salfred#define	UMOSCOM_MCR		0x04
83184610Salfred#define	UMOSCOM_LSR		0x05
84184610Salfred#define	UMOSCOM_MSR		0x06
85184610Salfred#define	UMOSCOM_SCRATCH		0x07
86184610Salfred#define	UMOSCOM_DIV_LO		0x08
87184610Salfred#define	UMOSCOM_DIV_HI		0x09
88184610Salfred#define	UMOSCOM_EFR		0x0a
89184610Salfred#define	UMOSCOM_XON1		0x0b
90184610Salfred#define	UMOSCOM_XON2		0x0c
91184610Salfred#define	UMOSCOM_XOFF1		0x0d
92184610Salfred#define	UMOSCOM_XOFF2		0x0e
93184610Salfred
94184610Salfred#define	UMOSCOM_BAUDLO		0x00
95184610Salfred#define	UMOSCOM_BAUDHI		0x01
96184610Salfred
97184610Salfred#define	UMOSCOM_INT_RXEN	0x01
98184610Salfred#define	UMOSCOM_INT_TXEN	0x02
99184610Salfred#define	UMOSCOM_INT_RSEN	0x04
100184610Salfred#define	UMOSCOM_INT_MDMEM	0x08
101184610Salfred#define	UMOSCOM_INT_SLEEP	0x10
102184610Salfred#define	UMOSCOM_INT_XOFF	0x20
103184610Salfred#define	UMOSCOM_INT_RTS		0x40
104184610Salfred
105184610Salfred#define	UMOSCOM_FIFO_EN		0x01
106184610Salfred#define	UMOSCOM_FIFO_RXCLR	0x02
107184610Salfred#define	UMOSCOM_FIFO_TXCLR	0x04
108184610Salfred#define	UMOSCOM_FIFO_DMA_BLK	0x08
109184610Salfred#define	UMOSCOM_FIFO_TXLVL_MASK	0x30
110184610Salfred#define	UMOSCOM_FIFO_TXLVL_8	0x00
111184610Salfred#define	UMOSCOM_FIFO_TXLVL_16	0x10
112184610Salfred#define	UMOSCOM_FIFO_TXLVL_32	0x20
113184610Salfred#define	UMOSCOM_FIFO_TXLVL_56	0x30
114184610Salfred#define	UMOSCOM_FIFO_RXLVL_MASK	0xc0
115184610Salfred#define	UMOSCOM_FIFO_RXLVL_8	0x00
116184610Salfred#define	UMOSCOM_FIFO_RXLVL_16	0x40
117184610Salfred#define	UMOSCOM_FIFO_RXLVL_56	0x80
118184610Salfred#define	UMOSCOM_FIFO_RXLVL_80	0xc0
119184610Salfred
120184610Salfred#define	UMOSCOM_ISR_MDM		0x00
121184610Salfred#define	UMOSCOM_ISR_NONE	0x01
122184610Salfred#define	UMOSCOM_ISR_TX		0x02
123184610Salfred#define	UMOSCOM_ISR_RX		0x04
124184610Salfred#define	UMOSCOM_ISR_LINE	0x06
125184610Salfred#define	UMOSCOM_ISR_RXTIMEOUT	0x0c
126184610Salfred#define	UMOSCOM_ISR_RX_XOFF	0x10
127184610Salfred#define	UMOSCOM_ISR_RTSCTS	0x20
128184610Salfred#define	UMOSCOM_ISR_FIFOEN	0xc0
129184610Salfred
130184610Salfred#define	UMOSCOM_LCR_DBITS(x)	((x) - 5)
131184610Salfred#define	UMOSCOM_LCR_STOP_BITS_1	0x00
132184610Salfred#define	UMOSCOM_LCR_STOP_BITS_2	0x04	/* 2 if 6-8 bits/char or 1.5 if 5 */
133184610Salfred#define	UMOSCOM_LCR_PARITY_NONE	0x00
134184610Salfred#define	UMOSCOM_LCR_PARITY_ODD	0x08
135184610Salfred#define	UMOSCOM_LCR_PARITY_EVEN	0x18
136184610Salfred#define	UMOSCOM_LCR_BREAK	0x40
137184610Salfred#define	UMOSCOM_LCR_DIVLATCH_EN	0x80
138184610Salfred
139184610Salfred#define	UMOSCOM_MCR_DTR		0x01
140184610Salfred#define	UMOSCOM_MCR_RTS		0x02
141184610Salfred#define	UMOSCOM_MCR_LOOP	0x04
142184610Salfred#define	UMOSCOM_MCR_INTEN	0x08
143184610Salfred#define	UMOSCOM_MCR_LOOPBACK	0x10
144184610Salfred#define	UMOSCOM_MCR_XONANY	0x20
145184610Salfred#define	UMOSCOM_MCR_IRDA_EN	0x40
146184610Salfred#define	UMOSCOM_MCR_BAUD_DIV4	0x80
147184610Salfred
148184610Salfred#define	UMOSCOM_LSR_RXDATA	0x01
149184610Salfred#define	UMOSCOM_LSR_RXOVER	0x02
150184610Salfred#define	UMOSCOM_LSR_RXPAR_ERR	0x04
151184610Salfred#define	UMOSCOM_LSR_RXFRM_ERR	0x08
152184610Salfred#define	UMOSCOM_LSR_RXBREAK	0x10
153184610Salfred#define	UMOSCOM_LSR_TXEMPTY	0x20
154184610Salfred#define	UMOSCOM_LSR_TXALLEMPTY	0x40
155184610Salfred#define	UMOSCOM_LSR_TXFIFO_ERR	0x80
156184610Salfred
157184610Salfred#define	UMOSCOM_MSR_CTS_CHG	0x01
158184610Salfred#define	UMOSCOM_MSR_DSR_CHG	0x02
159184610Salfred#define	UMOSCOM_MSR_RI_CHG	0x04
160184610Salfred#define	UMOSCOM_MSR_CD_CHG	0x08
161184610Salfred#define	UMOSCOM_MSR_CTS		0x10
162184610Salfred#define	UMOSCOM_MSR_RTS		0x20
163184610Salfred#define	UMOSCOM_MSR_RI		0x40
164184610Salfred#define	UMOSCOM_MSR_CD		0x80
165184610Salfred
166184610Salfred#define	UMOSCOM_BAUD_REF	115200
167184610Salfred
168187259Sthompsaenum {
169187259Sthompsa	UMOSCOM_BULK_DT_WR,
170187259Sthompsa	UMOSCOM_BULK_DT_RD,
171187259Sthompsa	UMOSCOM_INTR_DT_RD,
172188413Sthompsa	UMOSCOM_N_TRANSFER,
173187259Sthompsa};
174187259Sthompsa
175184610Salfredstruct umoscom_softc {
176192984Sthompsa	struct ucom_super_softc sc_super_ucom;
177192984Sthompsa	struct ucom_softc sc_ucom;
178184610Salfred
179192984Sthompsa	struct usb_xfer *sc_xfer[UMOSCOM_N_TRANSFER];
180192984Sthompsa	struct usb_device *sc_udev;
181189265Sthompsa	struct mtx sc_mtx;
182184610Salfred
183184610Salfred	uint8_t	sc_mcr;
184184610Salfred	uint8_t	sc_lcr;
185184610Salfred};
186184610Salfred
187184610Salfred/* prototypes */
188184610Salfred
189184610Salfredstatic device_probe_t umoscom_probe;
190184610Salfredstatic device_attach_t umoscom_attach;
191184610Salfredstatic device_detach_t umoscom_detach;
192239299Shselaskystatic void umoscom_free_softc(struct umoscom_softc *);
193184610Salfred
194193045Sthompsastatic usb_callback_t umoscom_write_callback;
195193045Sthompsastatic usb_callback_t umoscom_read_callback;
196193045Sthompsastatic usb_callback_t umoscom_intr_callback;
197184610Salfred
198239180Shselaskystatic void	umoscom_free(struct ucom_softc *);
199192984Sthompsastatic void	umoscom_cfg_open(struct ucom_softc *);
200192984Sthompsastatic void	umoscom_cfg_close(struct ucom_softc *);
201192984Sthompsastatic void	umoscom_cfg_set_break(struct ucom_softc *, uint8_t);
202192984Sthompsastatic void	umoscom_cfg_set_dtr(struct ucom_softc *, uint8_t);
203192984Sthompsastatic void	umoscom_cfg_set_rts(struct ucom_softc *, uint8_t);
204192984Sthompsastatic int	umoscom_pre_param(struct ucom_softc *, struct termios *);
205192984Sthompsastatic void	umoscom_cfg_param(struct ucom_softc *, struct termios *);
206192984Sthompsastatic void	umoscom_cfg_get_status(struct ucom_softc *, uint8_t *,
207185948Sthompsa		    uint8_t *);
208185948Sthompsastatic void	umoscom_cfg_write(struct umoscom_softc *, uint16_t, uint16_t);
209185948Sthompsastatic uint8_t	umoscom_cfg_read(struct umoscom_softc *, uint16_t);
210192984Sthompsastatic void	umoscom_start_read(struct ucom_softc *);
211192984Sthompsastatic void	umoscom_stop_read(struct ucom_softc *);
212192984Sthompsastatic void	umoscom_start_write(struct ucom_softc *);
213192984Sthompsastatic void	umoscom_stop_write(struct ucom_softc *);
214197570Sthompsastatic void	umoscom_poll(struct ucom_softc *ucom);
215184610Salfred
216192984Sthompsastatic const struct usb_config umoscom_config_data[UMOSCOM_N_TRANSFER] = {
217184610Salfred
218187259Sthompsa	[UMOSCOM_BULK_DT_WR] = {
219184610Salfred		.type = UE_BULK,
220184610Salfred		.endpoint = UE_ADDR_ANY,
221184610Salfred		.direction = UE_DIR_OUT,
222190734Sthompsa		.bufsize = UMOSCOM_BUFSIZE,
223190734Sthompsa		.flags = {.pipe_bof = 1,.force_short_xfer = 1,},
224190734Sthompsa		.callback = &umoscom_write_callback,
225184610Salfred	},
226184610Salfred
227187259Sthompsa	[UMOSCOM_BULK_DT_RD] = {
228184610Salfred		.type = UE_BULK,
229184610Salfred		.endpoint = UE_ADDR_ANY,
230184610Salfred		.direction = UE_DIR_IN,
231190734Sthompsa		.bufsize = UMOSCOM_BUFSIZE,
232190734Sthompsa		.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
233190734Sthompsa		.callback = &umoscom_read_callback,
234184610Salfred	},
235184610Salfred
236187259Sthompsa	[UMOSCOM_INTR_DT_RD] = {
237184610Salfred		.type = UE_INTERRUPT,
238184610Salfred		.endpoint = UE_ADDR_ANY,
239184610Salfred		.direction = UE_DIR_IN,
240190734Sthompsa		.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
241190734Sthompsa		.bufsize = 0,	/* use wMaxPacketSize */
242190734Sthompsa		.callback = &umoscom_intr_callback,
243184610Salfred	},
244184610Salfred};
245184610Salfred
246192984Sthompsastatic const struct ucom_callback umoscom_callback = {
247184610Salfred	/* configuration callbacks */
248194228Sthompsa	.ucom_cfg_get_status = &umoscom_cfg_get_status,
249194228Sthompsa	.ucom_cfg_set_dtr = &umoscom_cfg_set_dtr,
250194228Sthompsa	.ucom_cfg_set_rts = &umoscom_cfg_set_rts,
251194228Sthompsa	.ucom_cfg_set_break = &umoscom_cfg_set_break,
252194228Sthompsa	.ucom_cfg_param = &umoscom_cfg_param,
253194228Sthompsa	.ucom_cfg_open = &umoscom_cfg_open,
254194228Sthompsa	.ucom_cfg_close = &umoscom_cfg_close,
255184610Salfred
256184610Salfred	/* other callbacks */
257194228Sthompsa	.ucom_pre_param = &umoscom_pre_param,
258194228Sthompsa	.ucom_start_read = &umoscom_start_read,
259194228Sthompsa	.ucom_stop_read = &umoscom_stop_read,
260194228Sthompsa	.ucom_start_write = &umoscom_start_write,
261194228Sthompsa	.ucom_stop_write = &umoscom_stop_write,
262197570Sthompsa	.ucom_poll = &umoscom_poll,
263239180Shselasky	.ucom_free = &umoscom_free,
264184610Salfred};
265184610Salfred
266184610Salfredstatic device_method_t umoscom_methods[] = {
267184610Salfred	DEVMETHOD(device_probe, umoscom_probe),
268184610Salfred	DEVMETHOD(device_attach, umoscom_attach),
269184610Salfred	DEVMETHOD(device_detach, umoscom_detach),
270239180Shselasky	DEVMETHOD_END
271184610Salfred};
272184610Salfred
273184610Salfredstatic devclass_t umoscom_devclass;
274184610Salfred
275184610Salfredstatic driver_t umoscom_driver = {
276184610Salfred	.name = "umoscom",
277184610Salfred	.methods = umoscom_methods,
278184610Salfred	.size = sizeof(struct umoscom_softc),
279184610Salfred};
280184610Salfred
281292080Simpstatic const STRUCT_USB_HOST_ID umoscom_devs[] = {
282292080Simp	{USB_VPI(USB_VENDOR_MOSCHIP, USB_PRODUCT_MOSCHIP_MCS7703, 0)}
283292080Simp};
284292080Simp
285189275SthompsaDRIVER_MODULE(umoscom, uhub, umoscom_driver, umoscom_devclass, NULL, 0);
286188942SthompsaMODULE_DEPEND(umoscom, ucom, 1, 1, 1);
287188942SthompsaMODULE_DEPEND(umoscom, usb, 1, 1, 1);
288212122SthompsaMODULE_VERSION(umoscom, 1);
289292080SimpUSB_PNP_HOST_INFO(umoscom_devs);
290184610Salfred
291184610Salfredstatic int
292184610Salfredumoscom_probe(device_t dev)
293184610Salfred{
294192984Sthompsa	struct usb_attach_arg *uaa = device_get_ivars(dev);
295184610Salfred
296192499Sthompsa	if (uaa->usb_mode != USB_MODE_HOST) {
297184610Salfred		return (ENXIO);
298184610Salfred	}
299184610Salfred	if (uaa->info.bConfigIndex != UMOSCOM_CONFIG_INDEX) {
300184610Salfred		return (ENXIO);
301184610Salfred	}
302184610Salfred	if (uaa->info.bIfaceIndex != UMOSCOM_IFACE_INDEX) {
303184610Salfred		return (ENXIO);
304184610Salfred	}
305194228Sthompsa	return (usbd_lookup_id_by_uaa(umoscom_devs, sizeof(umoscom_devs), uaa));
306184610Salfred}
307184610Salfred
308184610Salfredstatic int
309184610Salfredumoscom_attach(device_t dev)
310184610Salfred{
311192984Sthompsa	struct usb_attach_arg *uaa = device_get_ivars(dev);
312184610Salfred	struct umoscom_softc *sc = device_get_softc(dev);
313184610Salfred	int error;
314184610Salfred	uint8_t iface_index;
315184610Salfred
316184610Salfred	sc->sc_udev = uaa->device;
317184610Salfred	sc->sc_mcr = 0x08;		/* enable interrupts */
318184610Salfred
319184610Salfred	/* XXX the device doesn't provide any ID string, so set a static one */
320184610Salfred	device_set_desc(dev, "MOSCHIP USB Serial Port Adapter");
321184610Salfred	device_printf(dev, "<MOSCHIP USB Serial Port Adapter>\n");
322184610Salfred
323189265Sthompsa	mtx_init(&sc->sc_mtx, "umoscom", NULL, MTX_DEF);
324239180Shselasky	ucom_ref(&sc->sc_super_ucom);
325189265Sthompsa
326184610Salfred	iface_index = UMOSCOM_IFACE_INDEX;
327194228Sthompsa	error = usbd_transfer_setup(uaa->device, &iface_index,
328187259Sthompsa	    sc->sc_xfer, umoscom_config_data,
329189265Sthompsa	    UMOSCOM_N_TRANSFER, sc, &sc->sc_mtx);
330184610Salfred
331184610Salfred	if (error) {
332184610Salfred		goto detach;
333184610Salfred	}
334184610Salfred	/* clear stall at first run */
335189265Sthompsa	mtx_lock(&sc->sc_mtx);
336194677Sthompsa	usbd_xfer_set_stall(sc->sc_xfer[UMOSCOM_BULK_DT_WR]);
337194677Sthompsa	usbd_xfer_set_stall(sc->sc_xfer[UMOSCOM_BULK_DT_RD]);
338189265Sthompsa	mtx_unlock(&sc->sc_mtx);
339184610Salfred
340194228Sthompsa	error = ucom_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc,
341189265Sthompsa	    &umoscom_callback, &sc->sc_mtx);
342184610Salfred	if (error) {
343184610Salfred		goto detach;
344184610Salfred	}
345214843Sn_hibma	ucom_set_pnpinfo_usb(&sc->sc_super_ucom, dev);
346214843Sn_hibma
347184610Salfred	return (0);
348184610Salfred
349184610Salfreddetach:
350194228Sthompsa	device_printf(dev, "attach error: %s\n", usbd_errstr(error));
351184610Salfred	umoscom_detach(dev);
352184610Salfred	return (ENXIO);
353184610Salfred}
354184610Salfred
355184610Salfredstatic int
356184610Salfredumoscom_detach(device_t dev)
357184610Salfred{
358184610Salfred	struct umoscom_softc *sc = device_get_softc(dev);
359184610Salfred
360214761Sn_hibma	ucom_detach(&sc->sc_super_ucom, &sc->sc_ucom);
361194228Sthompsa	usbd_transfer_unsetup(sc->sc_xfer, UMOSCOM_N_TRANSFER);
362184610Salfred
363239299Shselasky	device_claim_softc(dev);
364239299Shselasky
365239299Shselasky	umoscom_free_softc(sc);
366239299Shselasky
367184610Salfred	return (0);
368184610Salfred}
369184610Salfred
370239180ShselaskyUCOM_UNLOAD_DRAIN(umoscom);
371239180Shselasky
372184610Salfredstatic void
373239299Shselaskyumoscom_free_softc(struct umoscom_softc *sc)
374239180Shselasky{
375239180Shselasky	if (ucom_unref(&sc->sc_super_ucom)) {
376239299Shselasky		mtx_destroy(&sc->sc_mtx);
377239299Shselasky		device_free_softc(sc);
378239180Shselasky	}
379239180Shselasky}
380239180Shselasky
381239180Shselaskystatic void
382239180Shselaskyumoscom_free(struct ucom_softc *ucom)
383239180Shselasky{
384239299Shselasky	umoscom_free_softc(ucom->sc_parent);
385239180Shselasky}
386239180Shselasky
387239180Shselaskystatic void
388192984Sthompsaumoscom_cfg_open(struct ucom_softc *ucom)
389184610Salfred{
390184610Salfred	struct umoscom_softc *sc = ucom->sc_parent;
391184610Salfred
392184610Salfred	DPRINTF("\n");
393184610Salfred
394184610Salfred	/* Purge FIFOs or odd things happen */
395184610Salfred	umoscom_cfg_write(sc, UMOSCOM_FIFO, 0x00 | UMOSCOM_UART_REG);
396184610Salfred
397184610Salfred	/* Enable FIFO */
398184610Salfred	umoscom_cfg_write(sc, UMOSCOM_FIFO, UMOSCOM_FIFO_EN |
399184610Salfred	    UMOSCOM_FIFO_RXCLR | UMOSCOM_FIFO_TXCLR |
400184610Salfred	    UMOSCOM_FIFO_DMA_BLK | UMOSCOM_FIFO_RXLVL_MASK |
401184610Salfred	    UMOSCOM_UART_REG);
402184610Salfred
403184610Salfred	/* Enable Interrupt Registers */
404184610Salfred	umoscom_cfg_write(sc, UMOSCOM_INT, 0x0C | UMOSCOM_UART_REG);
405184610Salfred
406184610Salfred	/* Magic */
407184610Salfred	umoscom_cfg_write(sc, 0x01, 0x08);
408184610Salfred
409184610Salfred	/* Magic */
410184610Salfred	umoscom_cfg_write(sc, 0x00, 0x02);
411184610Salfred}
412184610Salfred
413184610Salfredstatic void
414192984Sthompsaumoscom_cfg_close(struct ucom_softc *ucom)
415184610Salfred{
416184610Salfred	return;
417184610Salfred}
418184610Salfred
419184610Salfredstatic void
420192984Sthompsaumoscom_cfg_set_break(struct ucom_softc *ucom, uint8_t onoff)
421184610Salfred{
422184610Salfred	struct umoscom_softc *sc = ucom->sc_parent;
423184610Salfred	uint16_t val;
424184610Salfred
425184610Salfred	val = sc->sc_lcr;
426184610Salfred	if (onoff)
427184610Salfred		val |= UMOSCOM_LCR_BREAK;
428184610Salfred
429184610Salfred	umoscom_cfg_write(sc, UMOSCOM_LCR, val | UMOSCOM_UART_REG);
430184610Salfred}
431184610Salfred
432184610Salfredstatic void
433192984Sthompsaumoscom_cfg_set_dtr(struct ucom_softc *ucom, uint8_t onoff)
434184610Salfred{
435184610Salfred	struct umoscom_softc *sc = ucom->sc_parent;
436184610Salfred
437184610Salfred	if (onoff)
438184610Salfred		sc->sc_mcr |= UMOSCOM_MCR_DTR;
439184610Salfred	else
440184610Salfred		sc->sc_mcr &= ~UMOSCOM_MCR_DTR;
441184610Salfred
442184610Salfred	umoscom_cfg_write(sc, UMOSCOM_MCR, sc->sc_mcr | UMOSCOM_UART_REG);
443184610Salfred}
444184610Salfred
445184610Salfredstatic void
446192984Sthompsaumoscom_cfg_set_rts(struct ucom_softc *ucom, uint8_t onoff)
447184610Salfred{
448184610Salfred	struct umoscom_softc *sc = ucom->sc_parent;
449184610Salfred
450184610Salfred	if (onoff)
451184610Salfred		sc->sc_mcr |= UMOSCOM_MCR_RTS;
452184610Salfred	else
453184610Salfred		sc->sc_mcr &= ~UMOSCOM_MCR_RTS;
454184610Salfred
455184610Salfred	umoscom_cfg_write(sc, UMOSCOM_MCR, sc->sc_mcr | UMOSCOM_UART_REG);
456184610Salfred}
457184610Salfred
458184610Salfredstatic int
459192984Sthompsaumoscom_pre_param(struct ucom_softc *ucom, struct termios *t)
460184610Salfred{
461184610Salfred	if ((t->c_ospeed <= 1) || (t->c_ospeed > 115200))
462184610Salfred		return (EINVAL);
463184610Salfred
464184610Salfred	return (0);
465184610Salfred}
466184610Salfred
467184610Salfredstatic void
468192984Sthompsaumoscom_cfg_param(struct ucom_softc *ucom, struct termios *t)
469184610Salfred{
470184610Salfred	struct umoscom_softc *sc = ucom->sc_parent;
471184610Salfred	uint16_t data;
472184610Salfred
473184610Salfred	DPRINTF("speed=%d\n", t->c_ospeed);
474184610Salfred
475184610Salfred	data = ((uint32_t)UMOSCOM_BAUD_REF) / ((uint32_t)t->c_ospeed);
476184610Salfred
477184610Salfred	if (data == 0) {
478184610Salfred		DPRINTF("invalid baud rate!\n");
479184610Salfred		return;
480184610Salfred	}
481184610Salfred	umoscom_cfg_write(sc, UMOSCOM_LCR,
482184610Salfred	    UMOSCOM_LCR_DIVLATCH_EN | UMOSCOM_UART_REG);
483184610Salfred
484184610Salfred	umoscom_cfg_write(sc, UMOSCOM_BAUDLO,
485184610Salfred	    (data & 0xFF) | UMOSCOM_UART_REG);
486184610Salfred
487184610Salfred	umoscom_cfg_write(sc, UMOSCOM_BAUDHI,
488184610Salfred	    ((data >> 8) & 0xFF) | UMOSCOM_UART_REG);
489184610Salfred
490184610Salfred	if (t->c_cflag & CSTOPB)
491184610Salfred		data = UMOSCOM_LCR_STOP_BITS_2;
492184610Salfred	else
493184610Salfred		data = UMOSCOM_LCR_STOP_BITS_1;
494184610Salfred
495184610Salfred	if (t->c_cflag & PARENB) {
496184610Salfred		if (t->c_cflag & PARODD)
497184610Salfred			data |= UMOSCOM_LCR_PARITY_ODD;
498184610Salfred		else
499184610Salfred			data |= UMOSCOM_LCR_PARITY_EVEN;
500184610Salfred	} else
501184610Salfred		data |= UMOSCOM_LCR_PARITY_NONE;
502184610Salfred
503184610Salfred	switch (t->c_cflag & CSIZE) {
504184610Salfred	case CS5:
505184610Salfred		data |= UMOSCOM_LCR_DBITS(5);
506184610Salfred		break;
507184610Salfred	case CS6:
508184610Salfred		data |= UMOSCOM_LCR_DBITS(6);
509184610Salfred		break;
510184610Salfred	case CS7:
511184610Salfred		data |= UMOSCOM_LCR_DBITS(7);
512184610Salfred		break;
513184610Salfred	case CS8:
514184610Salfred		data |= UMOSCOM_LCR_DBITS(8);
515184610Salfred		break;
516184610Salfred	}
517184610Salfred
518184610Salfred	sc->sc_lcr = data;
519184610Salfred	umoscom_cfg_write(sc, UMOSCOM_LCR, data | UMOSCOM_UART_REG);
520184610Salfred}
521184610Salfred
522184610Salfredstatic void
523192984Sthompsaumoscom_cfg_get_status(struct ucom_softc *ucom, uint8_t *p_lsr, uint8_t *p_msr)
524184610Salfred{
525184610Salfred	struct umoscom_softc *sc = ucom->sc_parent;
526184610Salfred	uint8_t lsr;
527184610Salfred	uint8_t msr;
528184610Salfred
529184610Salfred	DPRINTFN(5, "\n");
530184610Salfred
531184610Salfred	/* read status registers */
532184610Salfred
533184610Salfred	lsr = umoscom_cfg_read(sc, UMOSCOM_LSR);
534184610Salfred	msr = umoscom_cfg_read(sc, UMOSCOM_MSR);
535184610Salfred
536184610Salfred	/* translate bits */
537184610Salfred
538184610Salfred	if (msr & UMOSCOM_MSR_CTS)
539184610Salfred		*p_msr |= SER_CTS;
540184610Salfred
541184610Salfred	if (msr & UMOSCOM_MSR_CD)
542184610Salfred		*p_msr |= SER_DCD;
543184610Salfred
544184610Salfred	if (msr & UMOSCOM_MSR_RI)
545184610Salfred		*p_msr |= SER_RI;
546184610Salfred
547184610Salfred	if (msr & UMOSCOM_MSR_RTS)
548184610Salfred		*p_msr |= SER_DSR;
549184610Salfred}
550184610Salfred
551184610Salfredstatic void
552184610Salfredumoscom_cfg_write(struct umoscom_softc *sc, uint16_t reg, uint16_t val)
553184610Salfred{
554192984Sthompsa	struct usb_device_request req;
555184610Salfred
556184610Salfred	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
557184610Salfred	req.bRequest = UMOSCOM_WRITE;
558184610Salfred	USETW(req.wValue, val);
559184610Salfred	USETW(req.wIndex, reg);
560184610Salfred	USETW(req.wLength, 0);
561184610Salfred
562194228Sthompsa	ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
563188413Sthompsa	    &req, NULL, 0, 1000);
564184610Salfred}
565184610Salfred
566184610Salfredstatic uint8_t
567184610Salfredumoscom_cfg_read(struct umoscom_softc *sc, uint16_t reg)
568184610Salfred{
569192984Sthompsa	struct usb_device_request req;
570184610Salfred	uint8_t val;
571184610Salfred
572184610Salfred	req.bmRequestType = UT_READ_VENDOR_DEVICE;
573184610Salfred	req.bRequest = UMOSCOM_READ;
574184610Salfred	USETW(req.wValue, 0);
575184610Salfred	USETW(req.wIndex, reg);
576184610Salfred	USETW(req.wLength, 1);
577184610Salfred
578194228Sthompsa	ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
579188413Sthompsa	    &req, &val, 0, 1000);
580184610Salfred
581184610Salfred	DPRINTF("reg=0x%04x, val=0x%02x\n", reg, val);
582184610Salfred
583184610Salfred	return (val);
584184610Salfred}
585184610Salfred
586184610Salfredstatic void
587192984Sthompsaumoscom_start_read(struct ucom_softc *ucom)
588184610Salfred{
589184610Salfred	struct umoscom_softc *sc = ucom->sc_parent;
590184610Salfred
591184610Salfred#if 0
592184610Salfred	/* start interrupt endpoint */
593194228Sthompsa	usbd_transfer_start(sc->sc_xfer[UMOSCOM_INTR_DT_RD]);
594184610Salfred#endif
595184610Salfred	/* start read endpoint */
596194228Sthompsa	usbd_transfer_start(sc->sc_xfer[UMOSCOM_BULK_DT_RD]);
597184610Salfred}
598184610Salfred
599184610Salfredstatic void
600192984Sthompsaumoscom_stop_read(struct ucom_softc *ucom)
601184610Salfred{
602184610Salfred	struct umoscom_softc *sc = ucom->sc_parent;
603184610Salfred
604184610Salfred	/* stop interrupt transfer */
605194228Sthompsa	usbd_transfer_stop(sc->sc_xfer[UMOSCOM_INTR_DT_RD]);
606184610Salfred
607184610Salfred	/* stop read endpoint */
608194228Sthompsa	usbd_transfer_stop(sc->sc_xfer[UMOSCOM_BULK_DT_RD]);
609184610Salfred}
610184610Salfred
611184610Salfredstatic void
612192984Sthompsaumoscom_start_write(struct ucom_softc *ucom)
613184610Salfred{
614184610Salfred	struct umoscom_softc *sc = ucom->sc_parent;
615184610Salfred
616194228Sthompsa	usbd_transfer_start(sc->sc_xfer[UMOSCOM_BULK_DT_WR]);
617184610Salfred}
618184610Salfred
619184610Salfredstatic void
620192984Sthompsaumoscom_stop_write(struct ucom_softc *ucom)
621184610Salfred{
622184610Salfred	struct umoscom_softc *sc = ucom->sc_parent;
623184610Salfred
624194228Sthompsa	usbd_transfer_stop(sc->sc_xfer[UMOSCOM_BULK_DT_WR]);
625184610Salfred}
626184610Salfred
627184610Salfredstatic void
628194677Sthompsaumoscom_write_callback(struct usb_xfer *xfer, usb_error_t error)
629184610Salfred{
630194677Sthompsa	struct umoscom_softc *sc = usbd_xfer_softc(xfer);
631194677Sthompsa	struct usb_page_cache *pc;
632184610Salfred	uint32_t actlen;
633184610Salfred
634184610Salfred	switch (USB_GET_STATE(xfer)) {
635184610Salfred	case USB_ST_SETUP:
636184610Salfred	case USB_ST_TRANSFERRED:
637188413Sthompsatr_setup:
638184610Salfred		DPRINTF("\n");
639184610Salfred
640194677Sthompsa		pc = usbd_xfer_get_frame(xfer, 0);
641194677Sthompsa		if (ucom_get_data(&sc->sc_ucom, pc, 0,
642184610Salfred		    UMOSCOM_BUFSIZE, &actlen)) {
643184610Salfred
644194677Sthompsa			usbd_xfer_set_frame_len(xfer, 0, actlen);
645194228Sthompsa			usbd_transfer_submit(xfer);
646184610Salfred		}
647184610Salfred		return;
648184610Salfred
649184610Salfred	default:			/* Error */
650194677Sthompsa		if (error != USB_ERR_CANCELLED) {
651184610Salfred			DPRINTFN(0, "transfer failed\n");
652188413Sthompsa			/* try to clear stall first */
653194677Sthompsa			usbd_xfer_set_stall(xfer);
654188413Sthompsa			goto tr_setup;
655184610Salfred		}
656184610Salfred		return;
657184610Salfred	}
658184610Salfred}
659184610Salfred
660184610Salfredstatic void
661194677Sthompsaumoscom_read_callback(struct usb_xfer *xfer, usb_error_t error)
662184610Salfred{
663194677Sthompsa	struct umoscom_softc *sc = usbd_xfer_softc(xfer);
664194677Sthompsa	struct usb_page_cache *pc;
665194677Sthompsa	int actlen;
666184610Salfred
667194677Sthompsa	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
668194677Sthompsa
669184610Salfred	switch (USB_GET_STATE(xfer)) {
670184610Salfred	case USB_ST_TRANSFERRED:
671194677Sthompsa		DPRINTF("got %d bytes\n", actlen);
672194677Sthompsa		pc = usbd_xfer_get_frame(xfer, 0);
673194677Sthompsa		ucom_put_data(&sc->sc_ucom, pc, 0, actlen);
674184610Salfred
675184610Salfred	case USB_ST_SETUP:
676188413Sthompsatr_setup:
677184610Salfred		DPRINTF("\n");
678184610Salfred
679194677Sthompsa		usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
680194228Sthompsa		usbd_transfer_submit(xfer);
681184610Salfred		return;
682184610Salfred
683184610Salfred	default:			/* Error */
684194677Sthompsa		if (error != USB_ERR_CANCELLED) {
685184610Salfred			DPRINTFN(0, "transfer failed\n");
686188413Sthompsa			/* try to clear stall first */
687194677Sthompsa			usbd_xfer_set_stall(xfer);
688188413Sthompsa			goto tr_setup;
689184610Salfred		}
690184610Salfred		return;
691184610Salfred	}
692184610Salfred}
693184610Salfred
694184610Salfredstatic void
695194677Sthompsaumoscom_intr_callback(struct usb_xfer *xfer, usb_error_t error)
696184610Salfred{
697194677Sthompsa	struct umoscom_softc *sc = usbd_xfer_softc(xfer);
698194677Sthompsa	int actlen;
699184610Salfred
700194677Sthompsa	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
701194677Sthompsa
702184610Salfred	switch (USB_GET_STATE(xfer)) {
703184610Salfred	case USB_ST_TRANSFERRED:
704194677Sthompsa		if (actlen < 2) {
705184610Salfred			DPRINTF("too short message\n");
706184610Salfred			goto tr_setup;
707184610Salfred		}
708194228Sthompsa		ucom_status_change(&sc->sc_ucom);
709184610Salfred
710184610Salfred	case USB_ST_SETUP:
711184610Salfredtr_setup:
712194677Sthompsa		usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
713194228Sthompsa		usbd_transfer_submit(xfer);
714184610Salfred		return;
715184610Salfred
716184610Salfred	default:			/* Error */
717194677Sthompsa		if (error != USB_ERR_CANCELLED) {
718184610Salfred			DPRINTFN(0, "transfer failed\n");
719188413Sthompsa			/* try to clear stall first */
720194677Sthompsa			usbd_xfer_set_stall(xfer);
721188413Sthompsa			goto tr_setup;
722184610Salfred		}
723184610Salfred		return;
724184610Salfred	}
725184610Salfred}
726197570Sthompsa
727197570Sthompsastatic void
728197570Sthompsaumoscom_poll(struct ucom_softc *ucom)
729197570Sthompsa{
730197570Sthompsa	struct umoscom_softc *sc = ucom->sc_parent;
731197570Sthompsa	usbd_transfer_poll(sc->sc_xfer, UMOSCOM_N_TRANSFER);
732197570Sthompsa}
733