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