1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2025 Diane Bruce
5 * All rights reserved
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer
12 * in this position and unchanged.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 /*
30 * Device driver for Fintek F81232 USB to UART bridge.
31 */
32
33 #include <sys/stdint.h>
34 #include <sys/stddef.h>
35 #include <sys/param.h>
36 #include <sys/queue.h>
37 #include <sys/types.h>
38 #include <sys/systm.h>
39 #include <sys/kernel.h>
40 #include <sys/bus.h>
41 #include <sys/module.h>
42 #include <sys/lock.h>
43 #include <sys/mutex.h>
44 #include <sys/condvar.h>
45 #include <sys/sysctl.h>
46 #include <sys/sx.h>
47 #include <sys/unistd.h>
48 #include <sys/callout.h>
49 #include <sys/malloc.h>
50 #include <sys/priv.h>
51
52 #include <dev/usb/usb.h>
53 #include <dev/usb/usbdi.h>
54 #include <dev/usb/usbdi_util.h>
55 #include "usbdevs.h"
56
57 #define USB_DEBUG_VAR ufintek_debug
58 #include <dev/usb/usb_debug.h>
59 #include <dev/usb/usb_process.h>
60
61 #include <dev/usb/serial/usb_serial.h>
62
63 #ifdef USB_DEBUG
64 static int ufintek_debug = 0;
65
66 static SYSCTL_NODE(_hw_usb, OID_AUTO, ufintek, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
67 "USB ufintek");
68 SYSCTL_INT(_hw_usb_ufintek, OID_AUTO, debug, CTLFLAG_RWTUN,
69 &ufintek_debug, 1, "Debug level");
70 #endif
71
72 #define UFINTEK_BUFSIZE 256
73 #define UFINTEK_CONFIG_INDEX 0
74
75 #define UFINTEK_REGISTER_REQUEST 0xA0 /* uint8_t request */
76 #define UFINTEK_READ 0xC0 /* uint8_t requesttype */
77 #define UFINTEK_WRITE 0x40 /* uint8_t requesttype */
78
79 #define UFINTEK_IFACE_INDEX 0
80
81 #define UFINTEK_UART_REG 0x120
82
83 /*
84 * It appears the FINTEK serial port looks much like a 16550
85 */
86
87 #define UFINTEK_RXBUF 0x00 /* Receiver Buffer Read only */
88 #define UFINTEK_TXBUF 0x00 /* Transmitter Holding Register Write only*/
89 #define UFINTEK_IER 0x01 /* Interrupt Enable Read/Write */
90 #define UFINTEK_IIR 0x02 /* Interrupt Identification Read only */
91 #define UFINTEK_FCR 0x02 /* FIFO control Write only */
92 #define UFINTEK_LCR 0x03 /* Line Control Register Read/Write */
93 #define UFINTEK_MCR 0x04 /* Modem Control Write only */
94 #define UFINTEK_LSR 0x05 /* Line Status Read only */
95 #define UFINTEK_MSR 0x06 /* Modem Status Read only */
96
97 #define UFINTEK_BAUDLO 0x00 /* Same as UFINTEK_TXBUF and UFINTEK_RXBUF */
98 #define UFINTEK_BAUDHI 0x01 /* Same as UFINTEK_IER */
99
100 /* From uart16550.readthedocs.io */
101 #define UFINTEK_IER_RXEN 0x01 /* Received data available interrupt */
102 #define UFINTEK_IER_TXEN 0x02
103 #define UFINTEK_IER_RSEN 0x04 /* Receiver line status interrupt */
104 #define UFINTEK_IER_MSI 0x08
105 #define UFINTEK_IER_SLEEP 0x10
106 #define UFINTEK_IER_XOFF 0x20
107 #define UFINTEK_IER_RTS 0x40
108
109 #define UFINTEK_FCR_EN 0x01
110 #define UFINTEK_FCR_RXCLR 0x02
111 #define UFINTEK_FCR_TXCLR 0x04
112 #define UFINTEK_FCR_DMA_BLK 0x08
113 #define UFINTEK_FCR_TXLVL_MASK 0x30
114 #define UFINTEK_FCR_TRIGGER_8 0x80
115
116 #define UFINTEK_ISR_NONE 0x01
117 #define UFINTEK_ISR_TX 0x02
118 #define UFINTEK_ISR_RX 0x04
119 #define UFINTEK_ISR_LINE 0x06
120 #define UFINTEK_ISR_MDM 0x08
121 #define UFINTEK_ISR_RXTIMEOUT 0x0C
122 #define UFINTEK_ISR_RX_XOFF 0x10
123 #define UFINTEK_ISR_RTSCTS 0x20
124 #define UFINTEK_ISR_FIFOEN 0xC0
125
126 #define UFINTEK_LCR_WORDLEN_5 0x00
127 #define UFINTEK_LCR_WORDLEN_6 0x01
128 #define UFINTEK_LCR_WORDLEN_7 0x02
129 #define UFINTEK_LCR_WORDLEN_8 0x03
130
131 /* As with ns16550 if word length is 5 then 2 stops is 1.5 stop bits.
132 * This mimics the ancient TTY15/19 (Murray code/Baudot) printers that
133 * needed an extra half stop bit to stop the mechanical clutch!
134 */
135 #define UFINTEK_LCR_STOP_BITS_1 0x00
136 #define UFINTEK_LCR_STOP_BITS_2 0x04
137
138 #define UFINTEK_LCR_PARITY_NONE 0x00
139 #define UFINTEK_LCR_PARITY_ODD 0x08
140 #define UFINTEK_LCR_PARITY_EVEN 0x18
141 #define UFINTEK_LCR_BREAK 0x40
142 #define UFINTEK_LCR_DLAB 0x80
143
144 #define UFINTEK_MCR_DTR 0x01
145 #define UFINTEK_MCR_RTS 0x02
146 #define UFINTEK_MCR_LOOP 0x04
147 #define UFINTEK_MCR_INTEN 0x08
148 #define UFINTEK_MCR_LOOPBACK 0x10
149 #define UFINTEK_MCR_XONANY 0x20
150 #define UFINTEK_MCR_IRDA_EN 0x40
151 #define UFINTEK_MCR_BAUD_DIV4 0x80
152
153 #define UFINTEK_LSR_RXDATA 0x01
154 #define UFINTEK_LSR_RXOVER 0x02
155 #define UFINTEK_LSR_RXPAR_ERR 0x04
156 #define UFINTEK_LSR_RXFRM_ERR 0x08
157 #define UFINTEK_LSR_RXBREAK 0x10
158 #define UFINTEK_LSR_TXEMPTY 0x20
159 #define UFINTEK_LSR_TXALLEMPTY 0x40
160 #define UFINTEK_LSR_TXFIFO_ERR 0x80
161
162 #define UFINTEK_MSR_CTS_CHG 0x01
163 #define UFINTEK_MSR_DSR_CHG 0x02
164 #define UFINTEK_MSR_RI_CHG 0x04
165 #define UFINTEK_MSR_CD_CHG 0x08
166 #define UFINTEK_MSR_CTS 0x10
167 #define UFINTEK_MSR_RTS 0x20
168 #define UFINTEK_MSR_RI 0x40
169 #define UFINTEK_MSR_CD 0x80
170
171 #define UFINTEK_BAUD_REF 115200
172 #define UFINTEK_DEF_SPEED 115200
173
174 /*
175 * XXX Future use
176 * Baud rate multiplier clock
177 * On other similar FINTEK chips the divider clock can be modified
178 * via another port. This will be easy to add later.
179 *
180 * 0x00 1.846MHz
181 * 0x01 18.46MHz
182 * 0x10 24.00MHz
183 * 0x11 14.77MHz
184 */
185 #define UFINTEK_CLK_REG 0x106
186 #define UFINTEK_CLK_MSK 0x3
187 #define UFINTEK_CLK_1_846 0x00
188 #define UFINTEK_CLK_18_46 0x01
189 #define UFINTEK_CLK_24_00 0x10
190 #define UFINTEK_CLK_14_77 0x11
191
192 enum {
193 UFINTEK_BULK_DT_WR,
194 UFINTEK_BULK_DT_RD,
195 UFINTEK_N_TRANSFER,
196 };
197
198 struct ufintek_softc {
199 struct ucom_super_softc sc_super_ucom;
200 struct ucom_softc sc_ucom;
201
202 struct usb_xfer *sc_xfer[UFINTEK_N_TRANSFER];
203 struct usb_device *sc_udev;
204 struct mtx sc_mtx;
205
206 uint32_t sc_model;
207 uint8_t sc_lcr;
208 uint8_t sc_mcr;
209 };
210
211 /* prototypes */
212 static device_probe_t ufintek_probe;
213 static device_attach_t ufintek_attach;
214 static device_detach_t ufintek_detach;
215 static void ufintek_free_softc(struct ufintek_softc *);
216
217 static usb_callback_t ufintek_write_callback;
218 static usb_callback_t ufintek_read_callback;
219 static void ufintek_free(struct ucom_softc *);
220 static void ufintek_cfg_open(struct ucom_softc *);
221 static void ufintek_cfg_close(struct ucom_softc *);
222 static void ufintek_cfg_set_dtr(struct ucom_softc *, uint8_t);
223 static void ufintek_cfg_set_rts(struct ucom_softc *, uint8_t);
224 static void ufintek_cfg_set_break(struct ucom_softc *, uint8_t);
225 static void ufintek_cfg_param(struct ucom_softc *, struct termios *);
226 static void ufintek_cfg_get_status(struct ucom_softc *, uint8_t *, uint8_t *);
227 static int ufintek_pre_param(struct ucom_softc *, struct termios *);
228 static void ufintek_cfg_write(struct ufintek_softc *, uint16_t, uint8_t);
229 static uint8_t ufintek_cfg_read(struct ufintek_softc *, uint16_t);
230 static void ufintek_start_read(struct ucom_softc *);
231 static void ufintek_stop_read(struct ucom_softc *);
232 static void ufintek_start_write(struct ucom_softc *);
233 static void ufintek_stop_write(struct ucom_softc *);
234 static void ufintek_poll(struct ucom_softc *ucom);
235
236 static const struct usb_config ufintek_config[UFINTEK_N_TRANSFER] = {
237 [UFINTEK_BULK_DT_WR] = {
238 .type = UE_BULK,
239 .endpoint = UE_ADDR_ANY,
240 .direction = UE_DIR_OUT,
241 .bufsize = UFINTEK_BUFSIZE,
242 .flags = {.pipe_bof = 1,.force_short_xfer = 1,},
243 .callback = &ufintek_write_callback,
244 },
245 [UFINTEK_BULK_DT_RD] = {
246 .type = UE_BULK,
247 .endpoint = UE_ADDR_ANY,
248 .direction = UE_DIR_IN,
249 .bufsize = UFINTEK_BUFSIZE,
250 .flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
251 .callback = &ufintek_read_callback,
252 },
253 };
254
255 static const struct ucom_callback ufintek_callback = {
256 /* configuration callbacks */
257 .ucom_cfg_get_status = &ufintek_cfg_get_status,
258 .ucom_cfg_set_dtr = &ufintek_cfg_set_dtr,
259 .ucom_cfg_set_rts = &ufintek_cfg_set_rts,
260 .ucom_cfg_set_break = &ufintek_cfg_set_break,
261 .ucom_cfg_open = &ufintek_cfg_open,
262 .ucom_cfg_close = &ufintek_cfg_close,
263 .ucom_cfg_param = &ufintek_cfg_param,
264 .ucom_pre_param = &ufintek_pre_param,
265 .ucom_start_read = &ufintek_start_read,
266 .ucom_stop_read = &ufintek_stop_read,
267 .ucom_start_write = &ufintek_start_write,
268 .ucom_stop_write = &ufintek_stop_write,
269 .ucom_poll = &ufintek_poll,
270 .ucom_free = &ufintek_free,
271 };
272
273 static device_method_t ufintek_methods[] = {
274 DEVMETHOD(device_probe, ufintek_probe),
275 DEVMETHOD(device_attach, ufintek_attach),
276 DEVMETHOD(device_detach, ufintek_detach),
277 DEVMETHOD_END
278 };
279
280 static driver_t ufintek_driver = {
281 .name = "ufintek",
282 .methods = ufintek_methods,
283 .size = sizeof(struct ufintek_softc),
284 };
285
286
287 /*
288 * The following VENDOR PRODUCT definitions should be moved to usbdevs
289 *
290 * vendor FINTEK 0x1934
291 * product FINTEK SERIAL 0x0706
292 *
293 */
294 #ifndef USB_VENDOR_FINTEK
295 #define USB_VENDOR_FINTEK 0x1934
296 #endif
297 #ifndef USB_PRODUCT_FINTEK_SERIAL
298 #define USB_PRODUCT_FINTEK_SERIAL 0x0706
299 #endif
300 #ifndef FINTEK_MODEL_F81232
301 #define FINTEK_MODEL_F81232 0x02
302 #endif
303
304 static const STRUCT_USB_HOST_ID ufintek_devs[] = {
305 {USB_VPI(USB_VENDOR_FINTEK, USB_PRODUCT_FINTEK_SERIAL, FINTEK_MODEL_F81232)}
306 };
307
308 DRIVER_MODULE(ufintek, uhub, ufintek_driver, NULL, NULL);
309 MODULE_DEPEND(ufintek, ucom, 1, 1, 1);
310 MODULE_DEPEND(ufintek, usb, 1, 1, 1);
311 MODULE_VERSION(ufintek, 1);
312 USB_PNP_HOST_INFO(ufintek_devs);
313
314 #define UFINTEK_DEFAULT_BAUD 115200
315 #define UFINTEK_DEFAULT_LCR UFINTEK_LCR_WORDLEN_8|UFINTEK_LCR_STOP_BITS_1| \
316 UFINTEK_LCR_PARITY_NONE
317
318 static int
ufintek_probe(device_t dev)319 ufintek_probe(device_t dev)
320 {
321 struct usb_attach_arg *uaa = device_get_ivars(dev);
322
323 if (uaa->usb_mode != USB_MODE_HOST) {
324 return (ENXIO);
325 }
326 if (uaa->info.bConfigIndex != UFINTEK_CONFIG_INDEX) {
327 return (ENXIO);
328 }
329 if (uaa->info.bIfaceIndex != UFINTEK_IFACE_INDEX) {
330 return (ENXIO);
331 }
332 return (usbd_lookup_id_by_uaa(ufintek_devs, sizeof(ufintek_devs), uaa));
333 }
334
335 static int
ufintek_attach(device_t dev)336 ufintek_attach(device_t dev)
337 {
338 struct usb_attach_arg *uaa = device_get_ivars(dev);
339 struct ufintek_softc *sc = device_get_softc(dev);
340 int error;
341 uint8_t iface_index;
342
343 sc->sc_udev = uaa->device;
344
345 device_set_usb_desc(dev);
346
347 device_printf(dev, "<FINTEK USB Serial Port Adapter>\n");
348
349 mtx_init(&sc->sc_mtx, "ufintek", NULL, MTX_DEF);
350 ucom_ref(&sc->sc_super_ucom);
351
352 DPRINTF("\n");
353
354 /* get chip model */
355 sc->sc_model = USB_GET_DRIVER_INFO(uaa);
356 if (sc->sc_model == 0) {
357 device_printf(dev, "unsupported device\n");
358 goto detach;
359 }
360 device_printf(dev, "FINTEK F8123%X USB to RS232 bridge\n", sc->sc_model);
361 iface_index = UFINTEK_IFACE_INDEX;
362 error = usbd_transfer_setup(uaa->device, &iface_index,
363 sc->sc_xfer, ufintek_config,
364 UFINTEK_N_TRANSFER, sc, &sc->sc_mtx);
365
366 if (error) {
367 device_printf(dev, "allocating USB transfers failed\n");
368 goto detach;
369 }
370
371 /* clear stall at first run */
372 mtx_lock(&sc->sc_mtx);
373 usbd_xfer_set_stall(sc->sc_xfer[UFINTEK_BULK_DT_WR]);
374 usbd_xfer_set_stall(sc->sc_xfer[UFINTEK_BULK_DT_RD]);
375 mtx_unlock(&sc->sc_mtx);
376
377 error = ucom_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc,
378 &ufintek_callback, &sc->sc_mtx);
379 if (error) {
380 goto detach;
381 }
382 ucom_set_pnpinfo_usb(&sc->sc_super_ucom, dev);
383
384 return (0); /* success */
385
386 detach:
387 ufintek_detach(dev);
388 return (ENXIO);
389 }
390
391 static int
ufintek_detach(device_t dev)392 ufintek_detach(device_t dev)
393 {
394 struct ufintek_softc *sc = device_get_softc(dev);
395
396 ucom_detach(&sc->sc_super_ucom, &sc->sc_ucom);
397 usbd_transfer_unsetup(sc->sc_xfer, UFINTEK_N_TRANSFER);
398
399 device_claim_softc(dev);
400
401 ufintek_free_softc(sc);
402
403 return (0);
404 }
405
406 UCOM_UNLOAD_DRAIN(ufintek);
407
408 static void
ufintek_free_softc(struct ufintek_softc * sc)409 ufintek_free_softc(struct ufintek_softc *sc)
410 {
411 if (ucom_unref(&sc->sc_super_ucom)) {
412 mtx_destroy(&sc->sc_mtx);
413 device_free_softc(sc);
414 }
415 }
416
417 static void
ufintek_free(struct ucom_softc * ucom)418 ufintek_free(struct ucom_softc *ucom)
419 {
420 ufintek_free_softc(ucom->sc_parent);
421 }
422
423 static void
ufintek_cfg_open(struct ucom_softc * ucom)424 ufintek_cfg_open(struct ucom_softc *ucom)
425 {
426 struct ufintek_softc *sc = ucom->sc_parent;
427
428 /* Enable FIFO, trigger8 */
429 ufintek_cfg_write(sc, UFINTEK_UART_REG | UFINTEK_FCR,
430 UFINTEK_FCR_EN | UFINTEK_FCR_TRIGGER_8);
431
432 sc->sc_mcr = UFINTEK_MCR_DTR|UFINTEK_MCR_RTS;
433 ufintek_cfg_write(sc, UFINTEK_UART_REG | UFINTEK_MCR, sc->sc_mcr);
434
435 /* Enable interrupts */
436 ufintek_cfg_write(sc, UFINTEK_UART_REG | UFINTEK_IER,
437 UFINTEK_IER_MSI );
438 }
439
440 static void
ufintek_cfg_close(struct ucom_softc * ucom)441 ufintek_cfg_close(struct ucom_softc *ucom)
442 {
443 return;
444 }
445
446 static void
ufintek_cfg_set_break(struct ucom_softc * ucom,uint8_t onoff)447 ufintek_cfg_set_break(struct ucom_softc *ucom, uint8_t onoff)
448 {
449 struct ufintek_softc *sc = ucom->sc_parent;
450
451 if (onoff)
452 sc->sc_lcr |= UFINTEK_LCR_BREAK;
453 else
454 sc->sc_lcr &= ~UFINTEK_LCR_BREAK;
455
456 ufintek_cfg_write(sc, UFINTEK_UART_REG | UFINTEK_LCR, sc->sc_lcr);
457 }
458
459 static void
ufintek_cfg_set_dtr(struct ucom_softc * ucom,uint8_t onoff)460 ufintek_cfg_set_dtr(struct ucom_softc *ucom, uint8_t onoff)
461 {
462 struct ufintek_softc *sc = ucom->sc_parent;
463
464 if (onoff)
465 sc->sc_mcr |= UFINTEK_MCR_DTR;
466 else
467 sc->sc_mcr &= ~UFINTEK_MCR_DTR;
468
469 ufintek_cfg_write(sc, UFINTEK_UART_REG | UFINTEK_MCR, sc->sc_mcr);
470 }
471
472 static void
ufintek_cfg_set_rts(struct ucom_softc * ucom,uint8_t onoff)473 ufintek_cfg_set_rts(struct ucom_softc *ucom, uint8_t onoff)
474 {
475 struct ufintek_softc *sc = ucom->sc_parent;
476
477 if (onoff)
478 sc->sc_mcr |= UFINTEK_MCR_RTS;
479 else
480 sc->sc_mcr &= ~UFINTEK_MCR_RTS;
481
482 ufintek_cfg_write(sc, UFINTEK_UART_REG | UFINTEK_MCR, sc->sc_mcr);
483 }
484
485 static int
ufintek_pre_param(struct ucom_softc * ucom,struct termios * t)486 ufintek_pre_param(struct ucom_softc *ucom, struct termios *t)
487 {
488 struct ufintek_softc *sc = ucom->sc_parent;
489 uint16_t divisor;
490
491 if ((t->c_ospeed <= 1) || (t->c_ospeed > 115200))
492 return (EINVAL);
493
494 sc->sc_mcr = UFINTEK_MCR_DTR|UFINTEK_MCR_RTS;
495 sc->sc_lcr = UFINTEK_DEFAULT_LCR;
496 ufintek_cfg_write(sc, UFINTEK_UART_REG | UFINTEK_LCR, sc->sc_lcr);
497
498 DPRINTF("baud=%d\n", t->c_ospeed);
499
500 divisor = ((uint32_t)UFINTEK_BAUD_REF) / ((uint32_t)t->c_ospeed);
501
502 if (divisor == 0) {
503 DPRINTF("invalid baud rate!\n");
504 return (1);
505 }
506
507 /* Flip RXBUF and TXBUF to BAUDLO and BAUDHI */
508 ufintek_cfg_write(sc, UFINTEK_UART_REG | UFINTEK_LCR, UFINTEK_LCR_DLAB);
509 ufintek_cfg_write(sc, UFINTEK_UART_REG | UFINTEK_BAUDLO,
510 (divisor & 0xFF));
511 ufintek_cfg_write(sc, UFINTEK_UART_REG | UFINTEK_BAUDHI,
512 ((divisor >> 8) & 0xFF));
513 ufintek_cfg_write(sc, UFINTEK_UART_REG | UFINTEK_LCR, sc->sc_lcr);
514
515 return (0);
516 }
517
518 static void
ufintek_cfg_param(struct ucom_softc * ucom,struct termios * t)519 ufintek_cfg_param(struct ucom_softc *ucom, struct termios *t)
520 {
521 struct ufintek_softc *sc = ucom->sc_parent;
522 uint8_t lcr=0;
523 uint16_t divisor;
524
525 DPRINTF("baud=%d\n", t->c_ospeed);
526
527 divisor = ((uint32_t)UFINTEK_BAUD_REF) / ((uint32_t)t->c_ospeed);
528
529 if (divisor == 0) {
530 DPRINTF("invalid baud rate!\n");
531 return;
532 }
533
534 /* Flip RXBUF and TXBUF to BAUDLO and BAUDHI */
535 ufintek_cfg_write(sc, UFINTEK_UART_REG | UFINTEK_LCR, UFINTEK_LCR_DLAB);
536 ufintek_cfg_write(sc, UFINTEK_UART_REG | UFINTEK_BAUDLO,
537 (divisor & 0xFF));
538 ufintek_cfg_write(sc, UFINTEK_UART_REG | UFINTEK_BAUDHI,
539 ((divisor >> 8) & 0xFF));
540
541 if (!(t->c_cflag & CIGNORE)) {
542 lcr = 0;
543 switch (t->c_cflag & CSIZE) {
544 case CS8:
545 lcr |= UFINTEK_LCR_WORDLEN_8;
546 break;
547 case CS7:
548 lcr |= UFINTEK_LCR_WORDLEN_7;
549 break;
550 case CS6:
551 lcr |= UFINTEK_LCR_WORDLEN_6;
552 break;
553 case CS5:
554 break;
555 default:
556 break;
557 }
558
559 if (t->c_cflag & CSTOPB)
560 lcr |= UFINTEK_LCR_STOP_BITS_1;
561 if (t->c_cflag & PARODD)
562 lcr |= UFINTEK_LCR_PARITY_ODD;
563 else if (t->c_cflag & PARENB)
564 lcr |= UFINTEK_LCR_PARITY_EVEN;
565 }
566 sc->sc_lcr = lcr;
567 ufintek_cfg_write(sc, UFINTEK_UART_REG | UFINTEK_LCR, sc->sc_lcr);
568 }
569
570 static void
ufintek_cfg_get_status(struct ucom_softc * ucom,uint8_t * p_lsr,uint8_t * p_msr)571 ufintek_cfg_get_status(struct ucom_softc *ucom, uint8_t *p_lsr, uint8_t *p_msr)
572 {
573 struct ufintek_softc *sc = ucom->sc_parent;
574 uint8_t lsr;
575 uint8_t ufintek_msr;
576
577 lsr = ufintek_cfg_read(sc, UFINTEK_UART_REG | UFINTEK_LSR);
578 lsr &= 7; /* Only need bottom bits */
579 *p_lsr = lsr;
580
581 ufintek_msr = ufintek_cfg_read(sc, UFINTEK_UART_REG | UFINTEK_MSR);
582
583 /* translate bits */
584
585 *p_msr = 0;
586 if (ufintek_msr & UFINTEK_MSR_CTS)
587 *p_msr |= SER_CTS;
588
589 if (ufintek_msr & UFINTEK_MSR_CD)
590 *p_msr |= SER_DCD;
591
592 if (ufintek_msr & UFINTEK_MSR_RI)
593 *p_msr |= SER_RI;
594
595 if (ufintek_msr & UFINTEK_MSR_RTS)
596 *p_msr |= SER_RTS;
597 }
598
599 static void
ufintek_start_read(struct ucom_softc * ucom)600 ufintek_start_read(struct ucom_softc *ucom)
601 {
602 struct ufintek_softc *sc = ucom->sc_parent;
603
604 usbd_transfer_start(sc->sc_xfer[UFINTEK_BULK_DT_RD]);
605 }
606
607 static void
ufintek_stop_read(struct ucom_softc * ucom)608 ufintek_stop_read(struct ucom_softc *ucom)
609 {
610 struct ufintek_softc *sc = ucom->sc_parent;
611
612 usbd_transfer_stop(sc->sc_xfer[UFINTEK_BULK_DT_RD]);
613 }
614
615 static void
ufintek_start_write(struct ucom_softc * ucom)616 ufintek_start_write(struct ucom_softc *ucom)
617 {
618 struct ufintek_softc *sc = ucom->sc_parent;
619
620 usbd_transfer_start(sc->sc_xfer[UFINTEK_BULK_DT_WR]);
621 }
622
623 static void
ufintek_stop_write(struct ucom_softc * ucom)624 ufintek_stop_write(struct ucom_softc *ucom)
625 {
626 struct ufintek_softc *sc = ucom->sc_parent;
627
628 usbd_transfer_stop(sc->sc_xfer[UFINTEK_BULK_DT_WR]);
629 }
630
631 static void
ufintek_cfg_write(struct ufintek_softc * sc,uint16_t reg,uint8_t val)632 ufintek_cfg_write(struct ufintek_softc *sc, uint16_t reg, uint8_t val)
633 {
634 struct usb_device_request req;
635 usb_error_t uerr;
636 uint8_t data;
637
638 req.bmRequestType = UFINTEK_WRITE;
639 req.bRequest = UFINTEK_REGISTER_REQUEST;
640 USETW(req.wValue, reg);
641 USETW(req.wIndex, 0);
642 USETW(req.wLength, 1);
643
644 data = val;
645
646 uerr = ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
647 &req, &data, 0, 1000);
648 if (uerr != 0)
649 DPRINTF("failed to set ctrl %s\n", usbd_errstr(uerr));
650 }
651
652 static uint8_t
ufintek_cfg_read(struct ufintek_softc * sc,uint16_t reg)653 ufintek_cfg_read(struct ufintek_softc *sc, uint16_t reg)
654 {
655 struct usb_device_request req;
656 uint8_t val;
657
658 req.bmRequestType = UFINTEK_READ;
659 req.bRequest = UFINTEK_REGISTER_REQUEST;
660 USETW(req.wValue, reg);
661 USETW(req.wIndex, 0);
662 USETW(req.wLength, 1);
663
664 ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
665 &req, &val, 0, 1000);
666
667 DPRINTF("reg=0x%04x, val=0x%02x\n", reg, val);
668 return (val);
669 }
670
671
672 static void
ufintek_write_callback(struct usb_xfer * xfer,usb_error_t error)673 ufintek_write_callback(struct usb_xfer *xfer, usb_error_t error)
674 {
675 struct ufintek_softc *sc = usbd_xfer_softc(xfer);
676 struct usb_page_cache *pc;
677 uint32_t actlen;
678
679 switch (USB_GET_STATE(xfer)) {
680 case USB_ST_SETUP:
681 case USB_ST_TRANSFERRED:
682 tr_setup:
683
684 pc = usbd_xfer_get_frame(xfer, 0);
685 if (ucom_get_data(&sc->sc_ucom, pc, 0,
686 UFINTEK_BUFSIZE, &actlen)) {
687 usbd_xfer_set_frame_len(xfer, 0, actlen);
688 usbd_transfer_submit(xfer);
689 }
690 return;
691
692 default: /* Error */
693 if (error != USB_ERR_CANCELLED) {
694 /* try to clear stall first */
695 usbd_xfer_set_stall(xfer);
696 goto tr_setup;
697 }
698 return;
699 }
700 }
701
702 static void
ufintek_read_callback(struct usb_xfer * xfer,usb_error_t error)703 ufintek_read_callback(struct usb_xfer *xfer, usb_error_t error)
704 {
705 struct ufintek_softc *sc = usbd_xfer_softc(xfer);
706 struct usb_page_cache *pc;
707 int actlen;
708 uint8_t buf[UFINTEK_BUFSIZE];
709 int i;
710
711 usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
712 switch (USB_GET_STATE(xfer)) {
713 case USB_ST_TRANSFERRED:
714 DPRINTF("got %d bytes\n", actlen);
715 if ((actlen < 2) || (actlen % 2))
716 goto tr_setup;
717 pc = usbd_xfer_get_frame(xfer, 0);
718 usbd_copy_out(pc, 0, &buf, sizeof(buf));
719 /* XXX From Linux driver the very first byte after open
720 * is supposed to be a status which we should ignore.
721 * Why it is 0xFF I don't know TBH.
722 */
723 if (buf[0] == 0xFF)
724 buf[0] = ufintek_cfg_read(sc, UFINTEK_UART_REG);
725 else {
726 /*
727 * Annoyingly this device presents data as
728 * <LSR><DATA><LSR><DATA> ...
729 */
730 for (i = 0; i < actlen; i += 2) {
731 ucom_put_data(&sc->sc_ucom, pc, i+1, 1);
732 }
733
734 /* FALLTHROUGH */
735 }
736 case USB_ST_SETUP:
737 tr_setup:
738 usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
739 usbd_transfer_submit(xfer);
740 return;
741
742 default: /* Error */
743 if (error != USB_ERR_CANCELLED) {
744 /* try to clear stall first */
745 usbd_xfer_set_stall(xfer);
746 goto tr_setup;
747 }
748 return;
749 }
750 }
751
752
753
754 static void
ufintek_poll(struct ucom_softc * ucom)755 ufintek_poll(struct ucom_softc *ucom)
756 {
757 struct ufintek_softc *sc = ucom->sc_parent;
758 usbd_transfer_poll(sc->sc_xfer, UFINTEK_N_TRANSFER);
759 }
760