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