xref: /openbsd/sys/dev/usb/uchcom.c (revision 81508fe3)
1 /*	$OpenBSD: uchcom.c,v 1.36 2024/05/23 03:21:09 jsg Exp $	*/
2 /*	$NetBSD: uchcom.c,v 1.1 2007/09/03 17:57:37 tshiozak Exp $	*/
3 
4 /*
5  * Copyright (c) 2007 The NetBSD Foundation, Inc.
6  * All rights reserved.
7  *
8  * This code is derived from software contributed to The NetBSD Foundation
9  * by Takuya SHIOZAKI (tshiozak@netbsd.org).
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
21  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
24  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30  * POSSIBILITY OF SUCH DAMAGE.
31  */
32 
33 /*
34  * driver for WinChipHead CH9102/343/341/340.
35  */
36 
37 #include <sys/param.h>
38 #include <sys/systm.h>
39 #include <sys/malloc.h>
40 #include <sys/tty.h>
41 #include <sys/device.h>
42 
43 #include <machine/bus.h>
44 
45 #include <dev/usb/usb.h>
46 #include <dev/usb/usbdi.h>
47 #include <dev/usb/usbdivar.h>
48 #include <dev/usb/usbdevs.h>
49 #include <dev/usb/ucomvar.h>
50 
51 #ifdef UCHCOM_DEBUG
52 #define DPRINTFN(n, x)  do { if (uchcomdebug > (n)) printf x; } while (0)
53 int	uchcomdebug = 0;
54 #else
55 #define DPRINTFN(n, x)
56 #endif
57 #define DPRINTF(x) DPRINTFN(0, x)
58 
59 #define	UCHCOM_IFACE_INDEX		0
60 #define UCHCOM_SECOND_IFACE_INDEX	1
61 
62 #define UCHCOM_REV_CH340	0x0250
63 #define UCHCOM_REV_CH343	0x0440
64 #define UCHCOM_INPUT_BUF_SIZE	8
65 
66 #define UCHCOM_REQ_GET_VERSION		0x5F
67 #define UCHCOM_REQ_READ_REG		0x95
68 #define UCHCOM_REQ_WRITE_REG		0x9A
69 #define UCHCOM_REQ_RESET		0xA1
70 #define UCHCOM_REQ_SET_DTRRTS		0xA4
71 #define UCHCOM_REQ_CH343_WRITE_REG	0xA8
72 #define UCHCOM_REQ_SET_BAUDRATE		UCHCOM_REQ_RESET
73 
74 #define UCHCOM_REG_STAT1	0x06
75 #define UCHCOM_REG_STAT2	0x07
76 #define UCHCOM_REG_BPS_PRE	0x12
77 #define UCHCOM_REG_BPS_DIV	0x13
78 #define UCHCOM_REG_BPS_MOD	0x14
79 #define UCHCOM_REG_BPS_PAD	0x0F
80 #define UCHCOM_REG_BREAK	0x05
81 #define UCHCOM_REG_LCR		0x18
82 #define UCHCOM_REG_LCR2		0x25
83 
84 #define UCHCOM_VER_20		0x20
85 
86 #define UCHCOM_BASE_UNKNOWN	0
87 #define UCHCOM_BPS_MOD_BASE	20000000
88 #define UCHCOM_BPS_MOD_BASE_OFS	1100
89 
90 #define UCHCOM_BPS_PRE_IMM	0x80	/* CH341: immediate RX forwarding */
91 
92 #define UCHCOM_DTR_MASK		0x20
93 #define UCHCOM_RTS_MASK		0x40
94 
95 #define UCHCOM_BREAK_MASK	0x01
96 #define UCHCOM_ABREAK_MASK	0x10
97 #define UCHCOM_CH343_BREAK_MASK	0x80
98 
99 #define UCHCOM_LCR_CS5		0x00
100 #define UCHCOM_LCR_CS6		0x01
101 #define UCHCOM_LCR_CS7		0x02
102 #define UCHCOM_LCR_CS8		0x03
103 #define UCHCOM_LCR_STOPB	0x04
104 #define UCHCOM_LCR_PARENB	0x08
105 #define UCHCOM_LCR_PARODD	0x00
106 #define UCHCOM_LCR_PAREVEN	0x10
107 #define UCHCOM_LCR_PARMARK	0x20
108 #define UCHCOM_LCR_PARSPACE	0x30
109 #define UCHCOM_LCR_TXE		0x40
110 #define UCHCOM_LCR_RXE		0x80
111 
112 #define UCHCOM_INTR_STAT1	0x02
113 #define UCHCOM_INTR_STAT2	0x03
114 #define UCHCOM_INTR_LEAST	4
115 
116 #define UCHCOM_T		0x08
117 #define UCHCOM_CL		0x04
118 #define UCHCOM_CT		0x80
119 /*
120  * XXX - these magic numbers come from Linux (drivers/usb/serial/ch341.c).
121  * The manufacturer was unresponsive when asked for documentation.
122  */
123 #define UCHCOM_RESET_VALUE	0x501F	/* line mode? */
124 #define UCHCOM_RESET_INDEX	0xD90A	/* baud rate? */
125 
126 #define UCHCOMOBUFSIZE 256
127 
128 #define UCHCOM_TYPE_CH343	1
129 
130 struct uchcom_softc
131 {
132 	struct device		 sc_dev;
133 	struct usbd_device	*sc_udev;
134 	struct device		*sc_subdev;
135 	struct usbd_interface	*sc_intr_iface;
136 	struct usbd_interface	*sc_data_iface;
137 	/* */
138 	int			 sc_intr_endpoint;
139 	struct usbd_pipe	*sc_intr_pipe;
140 	u_char			*sc_intr_buf;
141 	int			 sc_isize;
142 	/* */
143 	int			 sc_release;
144 	uint8_t			 sc_version;
145 	int			 sc_type;
146 	int			 sc_dtr;
147 	int			 sc_rts;
148 	u_char			 sc_lsr;
149 	u_char			 sc_msr;
150 	int			 sc_lcr1;
151 	int			 sc_lcr2;
152 };
153 
154 struct uchcom_endpoints
155 {
156 	int		ep_bulkin;
157 	int		ep_bulkin_size;
158 	int		ep_bulkout;
159 	int		ep_intr;
160 	int		ep_intr_size;
161 };
162 
163 struct uchcom_divider
164 {
165 	uint8_t		dv_prescaler;
166 	uint8_t		dv_div;
167 	uint8_t		dv_mod;
168 };
169 
170 struct uchcom_divider_record
171 {
172 	uint32_t		dvr_high;
173 	uint32_t		dvr_low;
174 	uint32_t		dvr_base_clock;
175 	struct uchcom_divider	dvr_divider;
176 };
177 
178 static const struct uchcom_divider_record dividers[] =
179 {
180 	{  307200, 307200, UCHCOM_BASE_UNKNOWN, { 7, 0xD9, 0 } },
181 	{  921600, 921600, UCHCOM_BASE_UNKNOWN, { 7, 0xF3, 0 } },
182 	{ 2999999,  23530,             6000000, { 3,    0, 0 } },
183 	{   23529,   2942,              750000, { 2,    0, 0 } },
184 	{    2941,    368,               93750, { 1,    0, 0 } },
185 	{     367,      1,               11719, { 0,    0, 0 } },
186 };
187 
188 void		uchcom_get_status(void *, int, u_char *, u_char *);
189 void		uchcom_set(void *, int, int, int);
190 int		uchcom_param(void *, int, struct termios *);
191 int		uchcom_open(void *, int);
192 void		uchcom_close(void *, int);
193 void		uchcom_intr(struct usbd_xfer *, void *, usbd_status);
194 
195 int		uchcom_find_endpoints(struct uchcom_softc *,
196 		    struct uchcom_endpoints *);
197 void		uchcom_close_intr_pipe(struct uchcom_softc *);
198 
199 
200 usbd_status	uchcom_generic_control_out(struct uchcom_softc *sc,
201 		    uint8_t reqno, uint16_t value, uint16_t index);
202 usbd_status	uchcom_generic_control_in(struct uchcom_softc *, uint8_t,
203 		    uint16_t, uint16_t, void *, int, int *);
204 usbd_status	uchcom_write_reg(struct uchcom_softc *, uint8_t, uint8_t,
205 		    uint8_t, uint8_t);
206 usbd_status	uchcom_read_reg(struct uchcom_softc *, uint8_t, uint8_t *,
207 		    uint8_t, uint8_t *);
208 usbd_status	uchcom_get_version(struct uchcom_softc *, uint8_t *);
209 usbd_status	uchcom_read_status(struct uchcom_softc *, uint8_t *);
210 usbd_status	uchcom_set_dtrrts_10(struct uchcom_softc *, uint8_t);
211 usbd_status	uchcom_set_dtrrts_20(struct uchcom_softc *, uint8_t);
212 int		uchcom_update_version(struct uchcom_softc *);
213 void		uchcom_convert_status(struct uchcom_softc *, uint8_t);
214 int		uchcom_update_status(struct uchcom_softc *);
215 int		uchcom_set_dtrrts(struct uchcom_softc *, int, int);
216 int		uchcom_set_break(struct uchcom_softc *, int);
217 int		uchcom_set_break_ch343(struct uchcom_softc *, int);
218 void		uchcom_calc_baudrate_ch343(uint32_t, uint8_t *, uint8_t *);
219 int		uchcom_calc_divider_settings(struct uchcom_divider *, uint32_t);
220 int		uchcom_set_dte_rate_ch343(struct uchcom_softc *, uint32_t,
221 		    uint16_t);
222 int		uchcom_set_dte_rate(struct uchcom_softc *, uint32_t);
223 uint16_t	uchcom_set_line_control(struct uchcom_softc *, tcflag_t,
224 		    uint16_t *);
225 int		uchcom_clear_chip(struct uchcom_softc *);
226 int		uchcom_reset_chip(struct uchcom_softc *);
227 int		uchcom_setup_comm(struct uchcom_softc *);
228 int		uchcom_setup_intr_pipe(struct uchcom_softc *);
229 
230 
231 int		uchcom_match(struct device *, void *, void *);
232 void		uchcom_attach(struct device *, struct device *, void *);
233 int		uchcom_detach(struct device *, int);
234 
235 const struct ucom_methods uchcom_methods = {
236 	uchcom_get_status,
237 	uchcom_set,
238 	uchcom_param,
239 	NULL,
240 	uchcom_open,
241 	uchcom_close,
242 	NULL,
243 	NULL,
244 };
245 
246 static const struct usb_devno uchcom_devs[] = {
247 	{ USB_VENDOR_WCH, USB_PRODUCT_WCH_CH341 },
248 	{ USB_VENDOR_WCH2, USB_PRODUCT_WCH2_CH340 },
249 	{ USB_VENDOR_WCH2, USB_PRODUCT_WCH2_CH341A },
250 	{ USB_VENDOR_WCH2, USB_PRODUCT_WCH2_CH343 },
251 	{ USB_VENDOR_WCH2, USB_PRODUCT_WCH2_CH9102 }
252 };
253 
254 struct cfdriver uchcom_cd = {
255 	NULL, "uchcom", DV_DULL
256 };
257 
258 const struct cfattach uchcom_ca = {
259 	sizeof(struct uchcom_softc), uchcom_match, uchcom_attach, uchcom_detach
260 };
261 
262 /* ----------------------------------------------------------------------
263  * driver entry points
264  */
265 
266 int
uchcom_match(struct device * parent,void * match,void * aux)267 uchcom_match(struct device *parent, void *match, void *aux)
268 {
269 	struct usb_attach_arg *uaa = aux;
270 
271 	if (uaa->iface == NULL)
272 		return UMATCH_NONE;
273 
274 	return (usb_lookup(uchcom_devs, uaa->vendor, uaa->product) != NULL ?
275 	    UMATCH_VENDOR_PRODUCT : UMATCH_NONE);
276 }
277 
278 void
uchcom_attach(struct device * parent,struct device * self,void * aux)279 uchcom_attach(struct device *parent, struct device *self, void *aux)
280 {
281 	struct uchcom_softc *sc = (struct uchcom_softc *)self;
282 	struct usb_attach_arg *uaa = aux;
283 	struct ucom_attach_args uca;
284 	struct uchcom_endpoints endpoints;
285 
286 	sc->sc_udev = uaa->device;
287 	sc->sc_intr_iface = uaa->iface;
288 	sc->sc_dtr = sc->sc_rts = -1;
289 	sc->sc_release = uaa->release;
290 
291 	DPRINTF(("\n\nuchcom attach: sc=%p\n", sc));
292 
293 	if (sc->sc_release >= UCHCOM_REV_CH343) {
294 		printf("%s: CH343\n", sc->sc_dev.dv_xname);
295 		sc->sc_type = UCHCOM_TYPE_CH343;
296 	} else if (sc->sc_release == UCHCOM_REV_CH340)
297 		printf("%s: CH340\n", sc->sc_dev.dv_xname);
298 	else
299 		printf("%s: CH341\n", sc->sc_dev.dv_xname);
300 
301 	if (uchcom_find_endpoints(sc, &endpoints))
302 		goto failed;
303 
304 	sc->sc_intr_endpoint = endpoints.ep_intr;
305 	sc->sc_isize = endpoints.ep_intr_size;
306 
307 	/* setup ucom layer */
308 	uca.portno = UCOM_UNK_PORTNO;
309 	uca.bulkin = endpoints.ep_bulkin;
310 	uca.bulkout = endpoints.ep_bulkout;
311 	uca.ibufsize = endpoints.ep_bulkin_size;
312 	uca.obufsize = UCHCOMOBUFSIZE;
313 	uca.ibufsizepad = endpoints.ep_bulkin_size;
314 	uca.opkthdrlen = 0;
315 	uca.device = sc->sc_udev;
316 	uca.iface = sc->sc_data_iface;
317 	uca.methods = &uchcom_methods;
318 	uca.arg = sc;
319 	uca.info = NULL;
320 
321 	sc->sc_subdev = config_found_sm(self, &uca, ucomprint, ucomsubmatch);
322 
323 	return;
324 
325 failed:
326 	usbd_deactivate(sc->sc_udev);
327 }
328 
329 int
uchcom_detach(struct device * self,int flags)330 uchcom_detach(struct device *self, int flags)
331 {
332 	struct uchcom_softc *sc = (struct uchcom_softc *)self;
333 	int rv = 0;
334 
335 	DPRINTF(("uchcom_detach: sc=%p flags=%d\n", sc, flags));
336 
337 	uchcom_close_intr_pipe(sc);
338 
339 	if (sc->sc_subdev != NULL) {
340 		rv = config_detach(sc->sc_subdev, flags);
341 		sc->sc_subdev = NULL;
342 	}
343 
344 	return rv;
345 }
346 
347 int
uchcom_find_endpoints(struct uchcom_softc * sc,struct uchcom_endpoints * endpoints)348 uchcom_find_endpoints(struct uchcom_softc *sc,
349     struct uchcom_endpoints *endpoints)
350 {
351 	int i, bin=-1, bout=-1, intr=-1, binsize=0, isize=0;
352 	usb_config_descriptor_t *cdesc;
353 	usb_interface_descriptor_t *id;
354 	usb_endpoint_descriptor_t *ed;
355 	usbd_status err;
356 	uint8_t ifaceno;
357 
358 	/* Get the config descriptor. */
359 	cdesc = usbd_get_config_descriptor(sc->sc_udev);
360 
361 	if (cdesc == NULL) {
362 		printf("%s: failed to get configuration descriptor\n",
363 		    sc->sc_dev.dv_xname);
364 		return -1;
365         }
366 
367 	id = usbd_get_interface_descriptor(sc->sc_intr_iface);
368 
369 	for (i = 0; i < id->bNumEndpoints; i++) {
370 		ed = usbd_interface2endpoint_descriptor(sc->sc_intr_iface, i);
371 		if (ed == NULL) {
372 			printf("%s: no endpoint descriptor for %d\n",
373 				sc->sc_dev.dv_xname, i);
374 			return -1;
375 		}
376 
377 		if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
378 		    UE_GET_XFERTYPE(ed->bmAttributes) == UE_INTERRUPT) {
379 			intr = ed->bEndpointAddress;
380 			isize = UGETW(ed->wMaxPacketSize);
381 		}
382 	}
383 
384 	ifaceno = (cdesc->bNumInterfaces == 2) ?
385 	    UCHCOM_SECOND_IFACE_INDEX : UCHCOM_IFACE_INDEX;
386 
387 	err = usbd_device2interface_handle(sc->sc_udev, ifaceno,
388 	    &sc->sc_data_iface);
389 	if (err) {
390 		printf("\n%s: failed to get second interface, err=%s\n",
391 		    sc->sc_dev.dv_xname, usbd_errstr(err));
392 		return -1;
393 	}
394 
395 	id = usbd_get_interface_descriptor(sc->sc_data_iface);
396 
397 	for (i = 0; i < id->bNumEndpoints; i++) {
398 		ed = usbd_interface2endpoint_descriptor(sc->sc_data_iface, i);
399 		if (ed == NULL) {
400 			printf("%s: no endpoint descriptor for %d\n",
401 				sc->sc_dev.dv_xname, i);
402 			return -1;
403 		}
404 
405 		if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
406 		    UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
407 			bin = ed->bEndpointAddress;
408 			binsize = UGETW(ed->wMaxPacketSize);
409 		} else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT &&
410 		    UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
411 			bout = ed->bEndpointAddress;
412 		}
413 	}
414 
415 	if (intr == -1 || bin == -1 || bout == -1) {
416 		if (intr == -1) {
417 			printf("%s: no interrupt end point\n",
418 			       sc->sc_dev.dv_xname);
419 		}
420 		if (bin == -1) {
421 			printf("%s: no data bulk in end point\n",
422 			       sc->sc_dev.dv_xname);
423 		}
424 		if (bout == -1) {
425 			printf("%s: no data bulk out end point\n",
426 			       sc->sc_dev.dv_xname);
427 		}
428 		return -1;
429 	}
430 	if (isize < UCHCOM_INTR_LEAST) {
431 		printf("%s: intr pipe is too short", sc->sc_dev.dv_xname);
432 		return -1;
433 	}
434 
435 	DPRINTF(("%s: bulkin=%d, bulkout=%d, intr=%d, isize=%d\n",
436 		 sc->sc_dev.dv_xname, bin, bout, intr, isize));
437 
438 	usbd_claim_iface(sc->sc_udev, ifaceno);
439 
440 	endpoints->ep_intr = intr;
441 	endpoints->ep_intr_size = isize;
442 	endpoints->ep_bulkin = bin;
443 	endpoints->ep_bulkin_size = binsize;
444 	endpoints->ep_bulkout = bout;
445 
446 	return 0;
447 }
448 
449 
450 /* ----------------------------------------------------------------------
451  * low level i/o
452  */
453 
454 usbd_status
uchcom_generic_control_out(struct uchcom_softc * sc,uint8_t reqno,uint16_t value,uint16_t index)455 uchcom_generic_control_out(struct uchcom_softc *sc, uint8_t reqno,
456     uint16_t value, uint16_t index)
457 {
458 	usb_device_request_t req;
459 
460 	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
461 	req.bRequest = reqno;
462 	USETW(req.wValue, value);
463 	USETW(req.wIndex, index);
464 	USETW(req.wLength, 0);
465 
466 	return usbd_do_request(sc->sc_udev, &req, 0);
467 }
468 
469 usbd_status
uchcom_generic_control_in(struct uchcom_softc * sc,uint8_t reqno,uint16_t value,uint16_t index,void * buf,int buflen,int * actlen)470 uchcom_generic_control_in(struct uchcom_softc *sc, uint8_t reqno,
471     uint16_t value, uint16_t index, void *buf, int buflen, int *actlen)
472 {
473 	usb_device_request_t req;
474 
475 	req.bmRequestType = UT_READ_VENDOR_DEVICE;
476 	req.bRequest = reqno;
477 	USETW(req.wValue, value);
478 	USETW(req.wIndex, index);
479 	USETW(req.wLength, (uint16_t)buflen);
480 
481 	return usbd_do_request_flags(sc->sc_udev, &req, buf,
482 				     USBD_SHORT_XFER_OK, actlen,
483 				     USBD_DEFAULT_TIMEOUT);
484 }
485 
486 usbd_status
uchcom_write_reg(struct uchcom_softc * sc,uint8_t reg1,uint8_t val1,uint8_t reg2,uint8_t val2)487 uchcom_write_reg(struct uchcom_softc *sc,
488     uint8_t reg1, uint8_t val1, uint8_t reg2, uint8_t val2)
489 {
490 	DPRINTF(("uchcom: write reg 0x%02X<-0x%02X, 0x%02X<-0x%02X\n",
491 		 (unsigned)reg1, (unsigned)val1,
492 		 (unsigned)reg2, (unsigned)val2));
493 	return uchcom_generic_control_out(sc,
494 	    	(sc->sc_type != UCHCOM_TYPE_CH343) ?
495 		UCHCOM_REQ_WRITE_REG : UCHCOM_REQ_CH343_WRITE_REG,
496 		reg1|((uint16_t)reg2<<8), val1|((uint16_t)val2<<8));
497 }
498 
499 usbd_status
uchcom_read_reg(struct uchcom_softc * sc,uint8_t reg1,uint8_t * rval1,uint8_t reg2,uint8_t * rval2)500 uchcom_read_reg(struct uchcom_softc *sc,
501     uint8_t reg1, uint8_t *rval1, uint8_t reg2, uint8_t *rval2)
502 {
503 	uint8_t buf[UCHCOM_INPUT_BUF_SIZE];
504 	usbd_status err;
505 	int actin;
506 
507 	err = uchcom_generic_control_in(
508 		sc, UCHCOM_REQ_READ_REG,
509 		reg1|((uint16_t)reg2<<8), 0, buf, sizeof buf, &actin);
510 	if (err)
511 		return err;
512 
513 	DPRINTF(("uchcom: read reg 0x%02X->0x%02X, 0x%02X->0x%02X\n",
514 		 (unsigned)reg1, (unsigned)buf[0],
515 		 (unsigned)reg2, (unsigned)buf[1]));
516 
517 	if (rval1) *rval1 = buf[0];
518 	if (rval2) *rval2 = buf[1];
519 
520 	return USBD_NORMAL_COMPLETION;
521 }
522 
523 usbd_status
uchcom_get_version(struct uchcom_softc * sc,uint8_t * rver)524 uchcom_get_version(struct uchcom_softc *sc, uint8_t *rver)
525 {
526 	uint8_t buf[UCHCOM_INPUT_BUF_SIZE];
527 	usbd_status err;
528 	int actin;
529 
530 	err = uchcom_generic_control_in(
531 		sc, UCHCOM_REQ_GET_VERSION, 0, 0, buf, sizeof buf, &actin);
532 	if (err)
533 		return err;
534 
535 	if (rver) *rver = buf[0];
536 
537 	return USBD_NORMAL_COMPLETION;
538 }
539 
540 usbd_status
uchcom_read_status(struct uchcom_softc * sc,uint8_t * rval)541 uchcom_read_status(struct uchcom_softc *sc, uint8_t *rval)
542 {
543 	return uchcom_read_reg(sc, UCHCOM_REG_STAT1, rval, UCHCOM_REG_STAT2,
544 	    NULL);
545 }
546 
547 usbd_status
uchcom_set_dtrrts_10(struct uchcom_softc * sc,uint8_t val)548 uchcom_set_dtrrts_10(struct uchcom_softc *sc, uint8_t val)
549 {
550 	return uchcom_write_reg(sc, UCHCOM_REG_STAT1, val, UCHCOM_REG_STAT1,
551 	    val);
552 }
553 
554 usbd_status
uchcom_set_dtrrts_20(struct uchcom_softc * sc,uint8_t val)555 uchcom_set_dtrrts_20(struct uchcom_softc *sc, uint8_t val)
556 {
557 	return uchcom_generic_control_out(sc, UCHCOM_REQ_SET_DTRRTS, val, 0);
558 }
559 
560 
561 /* ----------------------------------------------------------------------
562  * middle layer
563  */
564 
565 int
uchcom_update_version(struct uchcom_softc * sc)566 uchcom_update_version(struct uchcom_softc *sc)
567 {
568 	usbd_status err;
569 
570 	err = uchcom_get_version(sc, &sc->sc_version);
571 	if (err) {
572 		printf("%s: cannot get version: %s\n",
573 		       sc->sc_dev.dv_xname, usbd_errstr(err));
574 		return EIO;
575 	}
576 
577 	return 0;
578 }
579 
580 void
uchcom_convert_status(struct uchcom_softc * sc,uint8_t cur)581 uchcom_convert_status(struct uchcom_softc *sc, uint8_t cur)
582 {
583 	sc->sc_dtr = !(cur & UCHCOM_DTR_MASK);
584 	sc->sc_rts = !(cur & UCHCOM_RTS_MASK);
585 
586 	cur = ~cur & 0x0F;
587 	sc->sc_msr = (cur << 4) | ((sc->sc_msr >> 4) ^ cur);
588 }
589 
590 int
uchcom_update_status(struct uchcom_softc * sc)591 uchcom_update_status(struct uchcom_softc *sc)
592 {
593 	usbd_status err;
594 	uint8_t cur;
595 
596 	err = uchcom_read_status(sc, &cur);
597 	if (err) {
598 		printf("%s: cannot update status: %s\n",
599 		       sc->sc_dev.dv_xname, usbd_errstr(err));
600 		return EIO;
601 	}
602 	uchcom_convert_status(sc, cur);
603 
604 	return 0;
605 }
606 
607 
608 int
uchcom_set_dtrrts(struct uchcom_softc * sc,int dtr,int rts)609 uchcom_set_dtrrts(struct uchcom_softc *sc, int dtr, int rts)
610 {
611 	usbd_status err;
612 	uint8_t val = 0;
613 
614 	if (dtr) val |= UCHCOM_DTR_MASK;
615 	if (rts) val |= UCHCOM_RTS_MASK;
616 
617 	if (sc->sc_version < UCHCOM_VER_20)
618 		err = uchcom_set_dtrrts_10(sc, ~val);
619 	else
620 		err = uchcom_set_dtrrts_20(sc, ~val);
621 
622 	if (err) {
623 		printf("%s: cannot set DTR/RTS: %s\n",
624 		       sc->sc_dev.dv_xname, usbd_errstr(err));
625 		return EIO;
626 	}
627 
628 	return 0;
629 }
630 
631 int
uchcom_set_break(struct uchcom_softc * sc,int onoff)632 uchcom_set_break(struct uchcom_softc *sc, int onoff)
633 {
634 	usbd_status err;
635 	uint8_t brk, lcr;
636 
637 	err = uchcom_read_reg(sc, UCHCOM_REG_BREAK, &brk, UCHCOM_REG_LCR, &lcr);
638 	if (err)
639 		return EIO;
640 	if (onoff) {
641 		/* on - clear bits */
642 		brk &= ~UCHCOM_BREAK_MASK;
643 		lcr &= ~UCHCOM_LCR_TXE;
644 	} else {
645 		/* off - set bits */
646 		brk |= UCHCOM_BREAK_MASK;
647 		lcr |= UCHCOM_LCR_TXE;
648 	}
649 	err = uchcom_write_reg(sc, UCHCOM_REG_BREAK, brk, UCHCOM_REG_LCR, lcr);
650 	if (err)
651 		return EIO;
652 
653 	return 0;
654 }
655 
656 int
uchcom_set_break_ch343(struct uchcom_softc * sc,int onoff)657 uchcom_set_break_ch343(struct uchcom_softc *sc, int onoff)
658 {
659 	usbd_status err;
660 	uint8_t brk = UCHCOM_CH343_BREAK_MASK;
661 
662 	if (!onoff)
663 		brk |= UCHCOM_ABREAK_MASK;
664 
665 	err = uchcom_write_reg(sc, brk, 0, 0, 0);
666 	if (err)
667 		return EIO;
668 
669 	return 0;
670 }
671 
672 void
uchcom_calc_baudrate_ch343(uint32_t rate,uint8_t * divisor,uint8_t * factor)673 uchcom_calc_baudrate_ch343(uint32_t rate, uint8_t *divisor, uint8_t *factor)
674 {
675 	uint32_t clk = 12000000;
676 
677 	if (rate >= 256000)
678 		*divisor = 7;
679 	else if (rate > 23529) {
680 		clk /= 2;
681 		*divisor = 3;
682 	} else if (rate > 2941) {
683 		clk /=  16;
684 		*divisor = 2;
685 	} else if (rate > 367) {
686 		clk /= 128;
687 		*divisor = 1;
688 	} else {
689 		clk = 11719;
690 		*divisor = 0;
691 	}
692 
693 	*factor = 256 - clk / rate;
694 }
695 
696 int
uchcom_calc_divider_settings(struct uchcom_divider * dp,uint32_t rate)697 uchcom_calc_divider_settings(struct uchcom_divider *dp, uint32_t rate)
698 {
699 	int i;
700 	const struct uchcom_divider_record *rp;
701 	uint32_t div, rem, mod;
702 
703 	/* find record */
704 	for (i=0; i<nitems(dividers); i++) {
705 		if (dividers[i].dvr_high >= rate &&
706 		    dividers[i].dvr_low <= rate) {
707 			rp = &dividers[i];
708 			goto found;
709 		}
710 	}
711 	return -1;
712 
713 found:
714 	dp->dv_prescaler = rp->dvr_divider.dv_prescaler;
715 	if (rp->dvr_base_clock == UCHCOM_BASE_UNKNOWN)
716 		dp->dv_div = rp->dvr_divider.dv_div;
717 	else {
718 		div = rp->dvr_base_clock / rate;
719 		rem = rp->dvr_base_clock % rate;
720 		if (div==0 || div>=0xFF)
721 			return -1;
722 		if ((rem<<1) >= rate)
723 			div += 1;
724 		dp->dv_div = (uint8_t)-div;
725 	}
726 
727 	mod = UCHCOM_BPS_MOD_BASE/rate + UCHCOM_BPS_MOD_BASE_OFS;
728 	mod = mod + mod/2;
729 
730 	dp->dv_mod = mod / 0x100;
731 
732 	return 0;
733 }
734 
735 int
uchcom_set_dte_rate_ch343(struct uchcom_softc * sc,uint32_t rate,uint16_t val)736 uchcom_set_dte_rate_ch343(struct uchcom_softc *sc, uint32_t rate, uint16_t val)
737 {
738 	usbd_status err;
739 	uint16_t idx;
740 	uint8_t factor, div;
741 
742 	uchcom_calc_baudrate_ch343(rate, &div, &factor);
743 	idx = (factor << 8) | div;
744 
745 	err = uchcom_generic_control_out(sc, UCHCOM_REQ_SET_BAUDRATE, val, idx);
746 	if (err) {
747 		printf("%s: cannot set DTE rate: %s\n",
748 		    sc->sc_dev.dv_xname, usbd_errstr(err));
749 		return EIO;
750 	}
751 
752 	return 0;
753 }
754 
755 int
uchcom_set_dte_rate(struct uchcom_softc * sc,uint32_t rate)756 uchcom_set_dte_rate(struct uchcom_softc *sc, uint32_t rate)
757 {
758 	usbd_status err;
759 	struct uchcom_divider dv;
760 
761 	if (uchcom_calc_divider_settings(&dv, rate))
762 		return EINVAL;
763 
764 	if ((err = uchcom_write_reg(sc,
765 			     UCHCOM_REG_BPS_PRE, dv.dv_prescaler,
766 			     UCHCOM_REG_BPS_DIV, dv.dv_div)) ||
767 	    (err = uchcom_write_reg(sc,
768 			     UCHCOM_REG_BPS_MOD, dv.dv_mod,
769 			     UCHCOM_REG_BPS_PAD, 0))) {
770 		printf("%s: cannot set DTE rate: %s\n",
771 		       sc->sc_dev.dv_xname, usbd_errstr(err));
772 		return EIO;
773 	}
774 
775 	return 0;
776 }
777 
778 uint16_t
uchcom_set_line_control(struct uchcom_softc * sc,tcflag_t cflag,uint16_t * val)779 uchcom_set_line_control(struct uchcom_softc *sc, tcflag_t cflag, uint16_t *val)
780 {
781 	usbd_status err;
782 	uint8_t lcr = 0, lcr2 = 0;
783 
784 	if (sc->sc_release == UCHCOM_REV_CH340) {
785 		/*
786 		 * XXX: it is difficult to handle the line control
787 		 * appropriately on CH340:
788 		 *   work as chip default - CS8, no parity, !CSTOPB
789 		 *   other modes are not supported.
790 		 */
791 		switch (ISSET(cflag, CSIZE)) {
792 		case CS5:
793 		case CS6:
794 		case CS7:
795 			return EINVAL;
796 		case CS8:
797 			break;
798 		}
799 		if (ISSET(cflag, PARENB) || ISSET(cflag, CSTOPB))
800 			return EINVAL;
801 		return 0;
802 	}
803 
804 	if (sc->sc_type != UCHCOM_TYPE_CH343) {
805 		err = uchcom_read_reg(sc, UCHCOM_REG_LCR, &lcr, UCHCOM_REG_LCR2,
806 		    &lcr2);
807 		if (err) {
808 			printf("%s: cannot get LCR: %s\n",
809 			    sc->sc_dev.dv_xname, usbd_errstr(err));
810 			return EIO;
811 		}
812 	}
813 
814 	lcr = UCHCOM_LCR_RXE | UCHCOM_LCR_TXE;
815 
816 	switch (ISSET(cflag, CSIZE)) {
817 	case CS5:
818 		lcr |= UCHCOM_LCR_CS5;
819 		break;
820 	case CS6:
821 		lcr |= UCHCOM_LCR_CS6;
822 		break;
823 	case CS7:
824 		lcr |= UCHCOM_LCR_CS7;
825 		break;
826 	case CS8:
827 		lcr |= UCHCOM_LCR_CS8;
828 		break;
829 	}
830 
831 	if (ISSET(cflag, PARENB)) {
832 		lcr |= UCHCOM_LCR_PARENB;
833 		if (!ISSET(cflag, PARODD))
834 			lcr |= UCHCOM_LCR_PAREVEN;
835 	}
836 
837 	if (ISSET(cflag, CSTOPB)) {
838 		lcr |= UCHCOM_LCR_STOPB;
839 	}
840 
841 	if (sc->sc_type != UCHCOM_TYPE_CH343) {
842 		err = uchcom_write_reg(sc, UCHCOM_REG_LCR, lcr, UCHCOM_REG_LCR2,
843 		    lcr2);
844 		if (err) {
845 			printf("%s: cannot set LCR: %s\n",
846 			    sc->sc_dev.dv_xname, usbd_errstr(err));
847 			return EIO;
848 		}
849 	} else
850 		*val = UCHCOM_T | UCHCOM_CL | UCHCOM_CT | lcr << 8;
851 
852 	return 0;
853 }
854 
855 int
uchcom_clear_chip(struct uchcom_softc * sc)856 uchcom_clear_chip(struct uchcom_softc *sc)
857 {
858 	usbd_status err;
859 
860 	DPRINTF(("%s: clear\n", sc->sc_dev.dv_xname));
861 	err = uchcom_generic_control_out(sc, UCHCOM_REQ_RESET, 0, 0);
862 	if (err) {
863 		printf("%s: cannot clear: %s\n",
864 		       sc->sc_dev.dv_xname, usbd_errstr(err));
865 		return EIO;
866 	}
867 
868 	return 0;
869 }
870 
871 int
uchcom_reset_chip(struct uchcom_softc * sc)872 uchcom_reset_chip(struct uchcom_softc *sc)
873 {
874 	usbd_status err;
875 
876 	DPRINTF(("%s: reset\n", sc->sc_dev.dv_xname));
877 
878 	err = uchcom_generic_control_out(sc, UCHCOM_REQ_RESET,
879 					 UCHCOM_RESET_VALUE,
880 					 UCHCOM_RESET_INDEX);
881 	if (err)
882 		goto failed;
883 
884 	return 0;
885 
886 failed:
887 	printf("%s: cannot reset: %s\n",
888 	       sc->sc_dev.dv_xname, usbd_errstr(err));
889 	return EIO;
890 }
891 
892 int
uchcom_setup_comm(struct uchcom_softc * sc)893 uchcom_setup_comm(struct uchcom_softc *sc)
894 {
895 	int ret;
896 
897 	ret = uchcom_clear_chip(sc);
898 	if (ret)
899 		return ret;
900 
901 	ret = uchcom_set_dte_rate(sc, TTYDEF_SPEED);
902 	if (ret)
903 		return ret;
904 
905 	ret = uchcom_set_line_control(sc, CS8, 0);
906 	if (ret)
907 		return ret;
908 
909 	ret = uchcom_update_status(sc);
910 	if (ret)
911 		return ret;
912 
913 	ret = uchcom_reset_chip(sc);
914 	if (ret)
915 		return ret;
916 
917 	ret = uchcom_set_dte_rate(sc, TTYDEF_SPEED); /* XXX */
918 	if (ret)
919 		return ret;
920 
921 	return 0;
922 }
923 
924 int
uchcom_setup_intr_pipe(struct uchcom_softc * sc)925 uchcom_setup_intr_pipe(struct uchcom_softc *sc)
926 {
927 	usbd_status err;
928 
929 	if (sc->sc_intr_endpoint != -1 && sc->sc_intr_pipe == NULL) {
930 		sc->sc_intr_buf = malloc(sc->sc_isize, M_USBDEV, M_WAITOK);
931 		err = usbd_open_pipe_intr(sc->sc_intr_iface,
932 					  sc->sc_intr_endpoint,
933 					  USBD_SHORT_XFER_OK,
934 					  &sc->sc_intr_pipe, sc,
935 					  sc->sc_intr_buf,
936 					  sc->sc_isize,
937 					  uchcom_intr, USBD_DEFAULT_INTERVAL);
938 		if (err) {
939 			printf("%s: cannot open interrupt pipe: %s\n",
940 			       sc->sc_dev.dv_xname,
941 			       usbd_errstr(err));
942 			return EIO;
943 		}
944 	}
945 	return 0;
946 }
947 
948 void
uchcom_close_intr_pipe(struct uchcom_softc * sc)949 uchcom_close_intr_pipe(struct uchcom_softc *sc)
950 {
951 	usbd_status err;
952 
953 	if (sc->sc_intr_pipe != NULL) {
954 		err = usbd_close_pipe(sc->sc_intr_pipe);
955 		if (err)
956 			printf("%s: close interrupt pipe failed: %s\n",
957 			       sc->sc_dev.dv_xname, usbd_errstr(err));
958 		free(sc->sc_intr_buf, M_USBDEV, sc->sc_isize);
959 		sc->sc_intr_pipe = NULL;
960 	}
961 }
962 
963 
964 /* ----------------------------------------------------------------------
965  * methods for ucom
966  */
967 void
uchcom_get_status(void * arg,int portno,u_char * rlsr,u_char * rmsr)968 uchcom_get_status(void *arg, int portno, u_char *rlsr, u_char *rmsr)
969 {
970 	struct uchcom_softc *sc = arg;
971 
972 	if (usbd_is_dying(sc->sc_udev))
973 		return;
974 
975 	*rlsr = sc->sc_lsr;
976 	*rmsr = sc->sc_msr;
977 }
978 
979 void
uchcom_set(void * arg,int portno,int reg,int onoff)980 uchcom_set(void *arg, int portno, int reg, int onoff)
981 {
982 	struct uchcom_softc *sc = arg;
983 
984 	if (usbd_is_dying(sc->sc_udev))
985 		return;
986 
987 	switch (reg) {
988 	case UCOM_SET_DTR:
989 		sc->sc_dtr = !!onoff;
990 		uchcom_set_dtrrts(sc, sc->sc_dtr, sc->sc_rts);
991 		break;
992 	case UCOM_SET_RTS:
993 		sc->sc_rts = !!onoff;
994 		uchcom_set_dtrrts(sc, sc->sc_dtr, sc->sc_rts);
995 		break;
996 	case UCOM_SET_BREAK:
997 		if (sc->sc_type == UCHCOM_TYPE_CH343)
998 			uchcom_set_break_ch343(sc, onoff);
999 		else
1000 			uchcom_set_break(sc, onoff);
1001 		break;
1002 	}
1003 }
1004 
1005 int
uchcom_param(void * arg,int portno,struct termios * t)1006 uchcom_param(void *arg, int portno, struct termios *t)
1007 {
1008 	struct uchcom_softc *sc = arg;
1009 	uint16_t val = 0;
1010 	int ret;
1011 
1012 	if (usbd_is_dying(sc->sc_udev))
1013 		return 0;
1014 
1015 	ret = uchcom_set_line_control(sc, t->c_cflag, &val);
1016 	if (ret)
1017 		return ret;
1018 
1019 	if (sc->sc_type == UCHCOM_TYPE_CH343)
1020 		ret = uchcom_set_dte_rate_ch343(sc, t->c_ospeed, val);
1021 	else
1022 		ret = uchcom_set_dte_rate(sc, t->c_ospeed);
1023 	if (ret)
1024 		return ret;
1025 
1026 	return 0;
1027 }
1028 
1029 int
uchcom_open(void * arg,int portno)1030 uchcom_open(void *arg, int portno)
1031 {
1032 	int ret;
1033 	struct uchcom_softc *sc = arg;
1034 
1035 	if (usbd_is_dying(sc->sc_udev))
1036 		return EIO;
1037 
1038 	ret = uchcom_setup_intr_pipe(sc);
1039 	if (ret)
1040 		return ret;
1041 
1042 	ret = uchcom_update_version(sc);
1043 	if (ret)
1044 		return ret;
1045 
1046 	if (sc->sc_type == UCHCOM_TYPE_CH343)
1047 		ret = uchcom_update_status(sc);
1048 	else
1049 		ret = uchcom_setup_comm(sc);
1050 	if (ret)
1051 		return ret;
1052 
1053 	sc->sc_dtr = sc->sc_rts = 1;
1054 	ret = uchcom_set_dtrrts(sc, sc->sc_dtr, sc->sc_rts);
1055 	if (ret)
1056 		return ret;
1057 
1058 	return 0;
1059 }
1060 
1061 void
uchcom_close(void * arg,int portno)1062 uchcom_close(void *arg, int portno)
1063 {
1064 	struct uchcom_softc *sc = arg;
1065 
1066 	uchcom_close_intr_pipe(sc);
1067 }
1068 
1069 
1070 /* ----------------------------------------------------------------------
1071  * callback when the modem status is changed.
1072  */
1073 void
uchcom_intr(struct usbd_xfer * xfer,void * priv,usbd_status status)1074 uchcom_intr(struct usbd_xfer *xfer, void *priv, usbd_status status)
1075 {
1076 	struct uchcom_softc *sc = priv;
1077 	u_char *buf = sc->sc_intr_buf;
1078 	uint32_t intrstat;
1079 
1080 	if (usbd_is_dying(sc->sc_udev))
1081 		return;
1082 
1083 	if (status != USBD_NORMAL_COMPLETION) {
1084 		if (status == USBD_NOT_STARTED || status == USBD_CANCELLED)
1085 			return;
1086 
1087 		DPRINTF(("%s: abnormal status: %s\n",
1088 			 sc->sc_dev.dv_xname, usbd_errstr(status)));
1089 		usbd_clear_endpoint_stall_async(sc->sc_intr_pipe);
1090 		return;
1091 	}
1092 	DPRINTF(("%s: intr: 0x%02X 0x%02X 0x%02X 0x%02X "
1093 		 "0x%02X 0x%02X 0x%02X 0x%02X\n",
1094 		 sc->sc_dev.dv_xname,
1095 		 (unsigned)buf[0], (unsigned)buf[1],
1096 		 (unsigned)buf[2], (unsigned)buf[3],
1097 		 (unsigned)buf[4], (unsigned)buf[5],
1098 		 (unsigned)buf[6], (unsigned)buf[7]));
1099 
1100 	intrstat = (sc->sc_type == UCHCOM_TYPE_CH343) ?
1101 	    xfer->actlen - 1 : UCHCOM_INTR_STAT1;
1102 
1103 	uchcom_convert_status(sc, buf[intrstat]);
1104 	ucom_status_change((struct ucom_softc *) sc->sc_subdev);
1105 }
1106