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