xref: /openbsd/sys/dev/usb/moscom.c (revision 81508fe3)
1 /*	$OpenBSD: moscom.c,v 1.26 2024/05/23 03:21:09 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/param.h>
20 #include <sys/systm.h>
21 #include <sys/tty.h>
22 #include <sys/device.h>
23 
24 #include <dev/usb/usb.h>
25 #include <dev/usb/usbdi.h>
26 #include <dev/usb/usbdevs.h>
27 
28 #include <dev/usb/ucomvar.h>
29 
30 #define MOSCOMBUFSZ		256
31 #define MOSCOM_IFACE_NO		0
32 
33 #define MOSCOM_READ		0x0d
34 #define MOSCOM_WRITE		0x0e
35 #define MOSCOM_UART_REG		0x0300
36 #define MOSCOM_VEND_REG		0x0000
37 
38 #define MOSCOM_TXBUF		0x00	/* Write */
39 #define MOSCOM_RXBUF		0x00	/* Read */
40 #define MOSCOM_INT		0x01
41 #define MOSCOM_FIFO		0x02	/* Write */
42 #define MOSCOM_ISR		0x02	/* Read */
43 #define MOSCOM_LCR		0x03
44 #define MOSCOM_MCR		0x04
45 #define MOSCOM_LSR		0x05
46 #define MOSCOM_MSR		0x06
47 #define MOSCOM_SCRATCH		0x07
48 #define MOSCOM_DIV_LO		0x08
49 #define MOSCOM_DIV_HI		0x09
50 #define MOSCOM_EFR		0x0a
51 #define	MOSCOM_XON1		0x0b
52 #define MOSCOM_XON2		0x0c
53 #define MOSCOM_XOFF1		0x0d
54 #define MOSCOM_XOFF2		0x0e
55 
56 #define MOSCOM_BAUDLO		0x00
57 #define MOSCOM_BAUDHI		0x01
58 
59 #define MOSCOM_INT_RXEN		0x01
60 #define MOSCOM_INT_TXEN		0x02
61 #define MOSCOM_INT_RSEN		0x04
62 #define MOSCOM_INT_MDMEM	0x08
63 #define MOSCOM_INT_SLEEP	0x10
64 #define MOSCOM_INT_XOFF		0x20
65 #define MOSCOM_INT_RTS		0x40
66 
67 #define MOSCOM_FIFO_EN		0x01
68 #define MOSCOM_FIFO_RXCLR	0x02
69 #define MOSCOM_FIFO_TXCLR	0x04
70 #define MOSCOM_FIFO_DMA_BLK	0x08
71 #define MOSCOM_FIFO_TXLVL_MASK	0x30
72 #define MOSCOM_FIFO_TXLVL_8	0x00
73 #define MOSCOM_FIFO_TXLVL_16	0x10
74 #define MOSCOM_FIFO_TXLVL_32	0x20
75 #define MOSCOM_FIFO_TXLVL_56	0x30
76 #define MOSCOM_FIFO_RXLVL_MASK	0xc0
77 #define MOSCOM_FIFO_RXLVL_8	0x00
78 #define MOSCOM_FIFO_RXLVL_16	0x40
79 #define MOSCOM_FIFO_RXLVL_56	0x80
80 #define MOSCOM_FIFO_RXLVL_80	0xc0
81 
82 #define MOSCOM_ISR_MDM		0x00
83 #define MOSCOM_ISR_NONE		0x01
84 #define MOSCOM_ISR_TX		0x02
85 #define MOSCOM_ISR_RX		0x04
86 #define MOSCOM_ISR_LINE		0x06
87 #define MOSCOM_ISR_RXTIMEOUT	0x0c
88 #define MOSCOM_ISR_RX_XOFF	0x10
89 #define MOSCOM_ISR_RTSCTS	0x20
90 #define MOSCOM_ISR_FIFOEN	0xc0
91 
92 #define MOSCOM_LCR_DBITS(x)	(x - 5)
93 #define MOSCOM_LCR_STOP_BITS_1	0x00
94 #define MOSCOM_LCR_STOP_BITS_2	0x04	/* 2 if 6-8 bits/char or 1.5 if 5 */
95 #define MOSCOM_LCR_PARITY_NONE	0x00
96 #define MOSCOM_LCR_PARITY_ODD	0x08
97 #define MOSCOM_LCR_PARITY_EVEN	0x18
98 #define MOSCOM_LCR_BREAK	0x40
99 #define MOSCOM_LCR_DIVLATCH_EN	0x80
100 
101 #define MOSCOM_MCR_DTR		0x01
102 #define MOSCOM_MCR_RTS		0x02
103 #define MOSCOM_MCR_LOOP		0x04
104 #define MOSCOM_MCR_INTEN	0x08
105 #define MOSCOM_MCR_LOOPBACK	0x10
106 #define MOSCOM_MCR_XONANY	0x20
107 #define MOSCOM_MCR_IRDA_EN	0x40
108 #define MOSCOM_MCR_BAUD_DIV4	0x80
109 
110 #define MOSCOM_LSR_RXDATA	0x01
111 #define MOSCOM_LSR_RXOVER	0x02
112 #define MOSCOM_LSR_RXPAR_ERR	0x04
113 #define MOSCOM_LSR_RXFRM_ERR	0x08
114 #define MOSCOM_LSR_RXBREAK	0x10
115 #define MOSCOM_LSR_TXEMPTY	0x20
116 #define MOSCOM_LSR_TXALLEMPTY	0x40
117 #define MOSCOM_LSR_TXFIFO_ERR	0x80
118 
119 #define MOSCOM_MSR_CTS_CHG	0x01
120 #define MOSCOM_MSR_DSR_CHG	0x02
121 #define MOSCOM_MSR_RI_CHG	0x04
122 #define MOSCOM_MSR_CD_CHG	0x08
123 #define MOSCOM_MSR_CTS		0x10
124 #define MOSCOM_MSR_RTS		0x20
125 #define MOSCOM_MSR_RI		0x40
126 #define MOSCOM_MSR_CD		0x80
127 
128 #define MOSCOM_BAUD_REF		115200
129 
130 struct moscom_softc {
131 	struct device		 sc_dev;
132 	struct usbd_device	*sc_udev;
133 	struct usbd_interface	*sc_iface;
134 	struct device		*sc_subdev;
135 
136 	u_char			 sc_msr;
137 	u_char			 sc_lsr;
138 	u_char			 sc_lcr;
139 };
140 
141 void	moscom_set(void *, int, int, int);
142 int	moscom_param(void *, int, struct termios *);
143 int	moscom_open(void *, int);
144 int	moscom_cmd(struct moscom_softc *, int, int);
145 
146 const struct ucom_methods moscom_methods = {
147 	NULL,
148 	moscom_set,
149 	moscom_param,
150 	NULL,
151 	moscom_open,
152 	NULL,
153 	NULL,
154 	NULL,
155 };
156 
157 static const struct usb_devno moscom_devs[] = {
158 	{ USB_VENDOR_MOSCHIP,		USB_PRODUCT_MOSCHIP_MCS7703 }
159 };
160 
161 int moscom_match(struct device *, void *, void *);
162 void moscom_attach(struct device *, struct device *, void *);
163 int moscom_detach(struct device *, int);
164 
165 struct cfdriver moscom_cd = {
166 	NULL, "moscom", DV_DULL
167 };
168 
169 const struct cfattach moscom_ca = {
170 	sizeof(struct moscom_softc), moscom_match, moscom_attach, moscom_detach
171 };
172 
173 int
moscom_match(struct device * parent,void * match,void * aux)174 moscom_match(struct device *parent, void *match, void *aux)
175 {
176 	struct usb_attach_arg *uaa = aux;
177 
178 	if (uaa->iface == NULL)
179 		return UMATCH_NONE;
180 
181 	return (usb_lookup(moscom_devs, uaa->vendor, uaa->product) != NULL) ?
182 	    UMATCH_VENDOR_PRODUCT : UMATCH_NONE;
183 }
184 
185 void
moscom_attach(struct device * parent,struct device * self,void * aux)186 moscom_attach(struct device *parent, struct device *self, void *aux)
187 {
188 	struct moscom_softc *sc = (struct moscom_softc *)self;
189 	struct usb_attach_arg *uaa = aux;
190 	struct ucom_attach_args uca;
191 	usb_interface_descriptor_t *id;
192 	usb_endpoint_descriptor_t *ed;
193 	usbd_status error;
194 	int i;
195 
196 	bzero(&uca, sizeof(uca));
197 	sc->sc_udev = uaa->device;
198 
199 	/* get the first interface handle */
200 	error = usbd_device2interface_handle(sc->sc_udev, MOSCOM_IFACE_NO,
201 	    &sc->sc_iface);
202 	if (error != 0) {
203 		printf("%s: could not get interface handle\n",
204 		    sc->sc_dev.dv_xname);
205 		usbd_deactivate(sc->sc_udev);
206 		return;
207 	}
208 
209 	id = usbd_get_interface_descriptor(sc->sc_iface);
210 
211 	uca.bulkin = uca.bulkout = -1;
212 	for (i = 0; i < id->bNumEndpoints; i++) {
213 		ed = usbd_interface2endpoint_descriptor(sc->sc_iface, i);
214 		if (ed == NULL) {
215 			printf("%s: no endpoint descriptor found for %d\n",
216 			    sc->sc_dev.dv_xname, i);
217 			usbd_deactivate(sc->sc_udev);
218 			return;
219 		}
220 
221 		if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
222 		    UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK)
223 			uca.bulkin = ed->bEndpointAddress;
224 		else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT &&
225 		    UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK)
226 			uca.bulkout = ed->bEndpointAddress;
227 	}
228 
229 	if (uca.bulkin == -1 || uca.bulkout == -1) {
230 		printf("%s: missing endpoint\n", sc->sc_dev.dv_xname);
231 		usbd_deactivate(sc->sc_udev);
232 		return;
233 	}
234 
235 	uca.ibufsize = MOSCOMBUFSZ;
236 	uca.obufsize = MOSCOMBUFSZ;
237 	uca.ibufsizepad = MOSCOMBUFSZ;
238 	uca.opkthdrlen = 0;
239 	uca.device = sc->sc_udev;
240 	uca.iface = sc->sc_iface;
241 	uca.methods = &moscom_methods;
242 	uca.arg = sc;
243 	uca.info = NULL;
244 
245 	sc->sc_subdev = config_found_sm(self, &uca, ucomprint, ucomsubmatch);
246 }
247 
248 int
moscom_detach(struct device * self,int flags)249 moscom_detach(struct device *self, int flags)
250 {
251 	struct moscom_softc *sc = (struct moscom_softc *)self;
252 	int rv = 0;
253 
254 	if (sc->sc_subdev != NULL) {
255 		rv = config_detach(sc->sc_subdev, flags);
256 		sc->sc_subdev = NULL;
257 	}
258 
259 	return (rv);
260 }
261 
262 int
moscom_open(void * vsc,int portno)263 moscom_open(void *vsc, int portno)
264 {
265 	struct moscom_softc *sc = vsc;
266 	usb_device_request_t req;
267 
268 	if (usbd_is_dying(sc->sc_udev))
269 		return (EIO);
270 
271 	/* Purge FIFOs or odd things happen */
272 	if (moscom_cmd(sc, MOSCOM_FIFO, 0x00) != 0)
273 		return (EIO);
274 
275 	if (moscom_cmd(sc, MOSCOM_FIFO, MOSCOM_FIFO_EN |
276 	    MOSCOM_FIFO_RXCLR | MOSCOM_FIFO_TXCLR |
277 	    MOSCOM_FIFO_DMA_BLK | MOSCOM_FIFO_RXLVL_MASK) != 0)
278 		return (EIO);
279 
280 	/* Magic tell device we're ready for data command */
281 	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
282 	req.bRequest = MOSCOM_WRITE;
283 	USETW(req.wValue, 0x08);
284 	USETW(req.wIndex, MOSCOM_INT);
285 	USETW(req.wLength, 0);
286 	if (usbd_do_request(sc->sc_udev, &req, NULL) != 0)
287 		return (EIO);
288 
289 	return (0);
290 }
291 
292 void
moscom_set(void * vsc,int portno,int reg,int onoff)293 moscom_set(void *vsc, int portno, int reg, int onoff)
294 {
295 	struct moscom_softc *sc = vsc;
296 	int val;
297 
298 	switch (reg) {
299 	case UCOM_SET_DTR:
300 		val = onoff ? MOSCOM_MCR_DTR : 0;
301 		break;
302 	case UCOM_SET_RTS:
303 		val = onoff ? MOSCOM_MCR_RTS : 0;
304 		break;
305 	case UCOM_SET_BREAK:
306 		val = sc->sc_lcr;
307 		if (onoff)
308 			val |= MOSCOM_LCR_BREAK;
309 		moscom_cmd(sc, MOSCOM_LCR, val);
310 		return;
311 	default:
312 		return;
313 	}
314 
315 	moscom_cmd(sc, MOSCOM_MCR, val);
316 }
317 
318 int
moscom_param(void * vsc,int portno,struct termios * t)319 moscom_param(void *vsc, int portno, struct termios *t)
320 {
321 	struct moscom_softc *sc = (struct moscom_softc *)vsc;
322 	int data;
323 
324 	if (t->c_ospeed <= 0 || t->c_ospeed > 115200)
325 		return (EINVAL);
326 
327 	data = MOSCOM_BAUD_REF / t->c_ospeed;
328 
329 	if (data == 0 || data > 0xffff)
330 		return (EINVAL);
331 
332 	moscom_cmd(sc, MOSCOM_LCR, MOSCOM_LCR_DIVLATCH_EN);
333 	moscom_cmd(sc, MOSCOM_BAUDLO, data & 0xFF);
334 	moscom_cmd(sc, MOSCOM_BAUDHI, (data >> 8) & 0xFF);
335 
336 	if (ISSET(t->c_cflag, CSTOPB))
337 		data = MOSCOM_LCR_STOP_BITS_2;
338 	else
339 		data = MOSCOM_LCR_STOP_BITS_1;
340 	if (ISSET(t->c_cflag, PARENB)) {
341 		if (ISSET(t->c_cflag, PARODD))
342 			data |= MOSCOM_LCR_PARITY_ODD;
343 		else
344 			data |= MOSCOM_LCR_PARITY_EVEN;
345 	} else
346 		data |= MOSCOM_LCR_PARITY_NONE;
347 	switch (ISSET(t->c_cflag, CSIZE)) {
348 	case CS5:
349 		data |= MOSCOM_LCR_DBITS(5);
350 		break;
351 	case CS6:
352 		data |= MOSCOM_LCR_DBITS(6);
353 		break;
354 	case CS7:
355 		data |= MOSCOM_LCR_DBITS(7);
356 		break;
357 	case CS8:
358 		data |= MOSCOM_LCR_DBITS(8);
359 		break;
360 	}
361 
362 	sc->sc_lcr = data;
363 	moscom_cmd(sc, MOSCOM_LCR, sc->sc_lcr);
364 
365 #if 0
366 	/* XXX flow control */
367 	if (ISSET(t->c_cflag, CRTSCTS))
368 		/*  rts/cts flow ctl */
369 	} else if (ISSET(t->c_iflag, IXON|IXOFF)) {
370 		/*  xon/xoff flow ctl */
371 	} else {
372 		/* disable flow ctl */
373 	}
374 #endif
375 
376 	return (0);
377 }
378 
379 int
moscom_cmd(struct moscom_softc * sc,int reg,int val)380 moscom_cmd(struct moscom_softc *sc, int reg, int val)
381 {
382 	usb_device_request_t req;
383 	usbd_status err;
384 
385 	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
386 	req.bRequest = MOSCOM_WRITE;
387 	USETW(req.wValue, val + MOSCOM_UART_REG);
388 	USETW(req.wIndex, reg);
389 	USETW(req.wLength, 0);
390 	err = usbd_do_request(sc->sc_udev, &req, NULL);
391 	if (err)
392 		return (EIO);
393 	else
394 		return (0);
395 }
396