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