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 = ÷rs[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