uchcom.c revision 1.35 1 /* $NetBSD: uchcom.c,v 1.35 2020/03/13 18:17:40 christos Exp $ */
2
3 /*
4 * Copyright (c) 2007 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Takuya SHIOZAKI (tshiozak (at) netbsd.org).
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32 #include <sys/cdefs.h>
33 __KERNEL_RCSID(0, "$NetBSD: uchcom.c,v 1.35 2020/03/13 18:17:40 christos Exp $");
34
35 #ifdef _KERNEL_OPT
36 #include "opt_usb.h"
37 #endif
38
39 /*
40 * driver for WinChipHead CH341/340, the worst USB-serial chip in the world.
41 */
42
43 #include <sys/param.h>
44 #include <sys/systm.h>
45 #include <sys/kernel.h>
46 #include <sys/kmem.h>
47 #include <sys/ioctl.h>
48 #include <sys/conf.h>
49 #include <sys/tty.h>
50 #include <sys/file.h>
51 #include <sys/select.h>
52 #include <sys/proc.h>
53 #include <sys/device.h>
54 #include <sys/poll.h>
55
56 #include <dev/usb/usb.h>
57
58 #include <dev/usb/usbdi.h>
59 #include <dev/usb/usbdi_util.h>
60 #include <dev/usb/usbdevs.h>
61
62 #include <dev/usb/ucomvar.h>
63
64 #ifdef UCHCOM_DEBUG
65 #define DPRINTFN(n, x) if (uchcomdebug > (n)) printf x
66 int uchcomdebug = 0;
67 #else
68 #define DPRINTFN(n, x)
69 #endif
70 #define DPRINTF(x) DPRINTFN(0, x)
71
72 #define UCHCOM_IFACE_INDEX 0
73 #define UCHCOM_CONFIG_INDEX 0
74
75 #define UCHCOM_INPUT_BUF_SIZE 8
76
77 #define UCHCOM_REQ_GET_VERSION 0x5F
78 #define UCHCOM_REQ_READ_REG 0x95
79 #define UCHCOM_REQ_WRITE_REG 0x9A
80 #define UCHCOM_REQ_RESET 0xA1
81 #define UCHCOM_REQ_SET_DTRRTS 0xA4
82
83 #define UCHCOM_REG_STAT1 0x06
84 #define UCHCOM_REG_STAT2 0x07
85 #define UCHCOM_REG_BPS_PRE 0x12
86 #define UCHCOM_REG_BPS_DIV 0x13
87 #define UCHCOM_REG_BREAK 0x05
88 #define UCHCOM_REG_LCR 0x18
89 #define UCHCOM_REG_LCR2 0x25
90
91 #define UCHCOM_VER_20 0x20
92 #define UCHCOM_VER_30 0x30
93
94 #define UCHCOM_BPS_PRE_IMM 0x80 /* CH341: immediate RX forwarding */
95
96 #define UCHCOM_DTR_MASK 0x20
97 #define UCHCOM_RTS_MASK 0x40
98
99 #define UCHCOM_BREAK_MASK 0x01
100
101 #define UCHCOM_LCR_CS5 0x00
102 #define UCHCOM_LCR_CS6 0x01
103 #define UCHCOM_LCR_CS7 0x02
104 #define UCHCOM_LCR_CS8 0x03
105 #define UCHCOM_LCR_STOPB 0x04
106 #define UCHCOM_LCR_PARENB 0x08
107 #define UCHCOM_LCR_PARODD 0x00
108 #define UCHCOM_LCR_PAREVEN 0x10
109 #define UCHCOM_LCR_PARMARK 0x20
110 #define UCHCOM_LCR_PARSPACE 0x30
111 #define UCHCOM_LCR_TXE 0x40
112 #define UCHCOM_LCR_RXE 0x80
113
114 #define UCHCOM_INTR_STAT1 0x02
115 #define UCHCOM_INTR_STAT2 0x03
116 #define UCHCOM_INTR_LEAST 4
117
118 #define UCHCOMIBUFSIZE 256
119 #define UCHCOMOBUFSIZE 256
120
121 #define UCHCOM_RESET_VALUE 0x501F
122 #define UCHCOM_RESET_INDEX 0xD90A
123
124 struct uchcom_softc
125 {
126 device_t sc_dev;
127 struct usbd_device * sc_udev;
128 device_t sc_subdev;
129 struct usbd_interface * sc_iface;
130 bool sc_dying;
131 /* */
132 int sc_intr_endpoint;
133 int sc_intr_size;
134 struct usbd_pipe * sc_intr_pipe;
135 u_char *sc_intr_buf;
136 /* */
137 uint8_t sc_version;
138 int sc_dtr;
139 int sc_rts;
140 u_char sc_lsr;
141 u_char sc_msr;
142 };
143
144 struct uchcom_endpoints
145 {
146 int ep_bulkin;
147 int ep_bulkout;
148 int ep_intr;
149 int ep_intr_size;
150 };
151
152 struct uchcom_divider
153 {
154 uint8_t dv_prescaler;
155 uint8_t dv_div;
156 };
157
158 static const uint32_t rates4x[8] = {
159 [0] = 4 * 12000000 / 1024,
160 [1] = 4 * 12000000 / 128,
161 [2] = 4 * 12000000 / 16,
162 [3] = 4 * 12000000 / 2,
163 [7] = 4 * 12000000,
164 };
165
166 static const struct usb_devno uchcom_devs[] = {
167 { USB_VENDOR_QINHENG2, USB_PRODUCT_QINHENG2_CH341SER },
168 { USB_VENDOR_QINHENG, USB_PRODUCT_QINHENG_CH340 },
169 { USB_VENDOR_QINHENG, USB_PRODUCT_QINHENG_CH341_ASP },
170 };
171 #define uchcom_lookup(v, p) usb_lookup(uchcom_devs, v, p)
172
173 static void uchcom_get_status(void *, int, u_char *, u_char *);
174 static void uchcom_set(void *, int, int, int);
175 static int uchcom_param(void *, int, struct termios *);
176 static int uchcom_open(void *, int);
177 static void uchcom_close(void *, int);
178 static void uchcom_intr(struct usbd_xfer *, void *,
179 usbd_status);
180
181 static int set_config(struct uchcom_softc *);
182 static int find_ifaces(struct uchcom_softc *, struct usbd_interface **);
183 static int find_endpoints(struct uchcom_softc *,
184 struct uchcom_endpoints *);
185 static void close_intr_pipe(struct uchcom_softc *);
186
187
188 static const struct ucom_methods uchcom_methods = {
189 .ucom_get_status = uchcom_get_status,
190 .ucom_set = uchcom_set,
191 .ucom_param = uchcom_param,
192 .ucom_open = uchcom_open,
193 .ucom_close = uchcom_close,
194 };
195
196 static int uchcom_match(device_t, cfdata_t, void *);
197 static void uchcom_attach(device_t, device_t, void *);
198 static void uchcom_childdet(device_t, device_t);
199 static int uchcom_detach(device_t, int);
200
201 CFATTACH_DECL2_NEW(uchcom,
202 sizeof(struct uchcom_softc),
203 uchcom_match,
204 uchcom_attach,
205 uchcom_detach,
206 NULL,
207 NULL,
208 uchcom_childdet);
209
210 /* ----------------------------------------------------------------------
211 * driver entry points
212 */
213
214 static int
215 uchcom_match(device_t parent, cfdata_t match, void *aux)
216 {
217 struct usb_attach_arg *uaa = aux;
218
219 return (uchcom_lookup(uaa->uaa_vendor, uaa->uaa_product) != NULL ?
220 UMATCH_VENDOR_PRODUCT : UMATCH_NONE);
221 }
222
223 static void
224 uchcom_attach(device_t parent, device_t self, void *aux)
225 {
226 struct uchcom_softc *sc = device_private(self);
227 struct usb_attach_arg *uaa = aux;
228 struct usbd_device *dev = uaa->uaa_device;
229 char *devinfop;
230 struct uchcom_endpoints endpoints;
231 struct ucom_attach_args ucaa;
232
233 aprint_naive("\n");
234 aprint_normal("\n");
235
236 devinfop = usbd_devinfo_alloc(dev, 0);
237 aprint_normal_dev(self, "%s\n", devinfop);
238 usbd_devinfo_free(devinfop);
239
240 sc->sc_dev = self;
241 sc->sc_udev = dev;
242 sc->sc_dying = false;
243 sc->sc_dtr = sc->sc_rts = -1;
244 sc->sc_lsr = sc->sc_msr = 0;
245
246 DPRINTF(("\n\nuchcom attach: sc=%p\n", sc));
247
248 if (set_config(sc))
249 goto failed;
250
251 if (find_ifaces(sc, &sc->sc_iface))
252 goto failed;
253
254 if (find_endpoints(sc, &endpoints))
255 goto failed;
256
257 sc->sc_intr_endpoint = endpoints.ep_intr;
258 sc->sc_intr_size = endpoints.ep_intr_size;
259
260 /* setup ucom layer */
261 ucaa.ucaa_portno = UCOM_UNK_PORTNO;
262 ucaa.ucaa_bulkin = endpoints.ep_bulkin;
263 ucaa.ucaa_bulkout = endpoints.ep_bulkout;
264 ucaa.ucaa_ibufsize = UCHCOMIBUFSIZE;
265 ucaa.ucaa_obufsize = UCHCOMOBUFSIZE;
266 ucaa.ucaa_ibufsizepad = UCHCOMIBUFSIZE;
267 ucaa.ucaa_opkthdrlen = 0;
268 ucaa.ucaa_device = dev;
269 ucaa.ucaa_iface = sc->sc_iface;
270 ucaa.ucaa_methods = &uchcom_methods;
271 ucaa.ucaa_arg = sc;
272 ucaa.ucaa_info = NULL;
273
274 usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, sc->sc_udev, sc->sc_dev);
275
276 sc->sc_subdev = config_found_sm_loc(self, "ucombus", NULL, &ucaa,
277 ucomprint, ucomsubmatch);
278
279 return;
280
281 failed:
282 sc->sc_dying = true;
283 return;
284 }
285
286 static void
287 uchcom_childdet(device_t self, device_t child)
288 {
289 struct uchcom_softc *sc = device_private(self);
290
291 KASSERT(sc->sc_subdev == child);
292 sc->sc_subdev = NULL;
293 }
294
295 static int
296 uchcom_detach(device_t self, int flags)
297 {
298 struct uchcom_softc *sc = device_private(self);
299 int rv = 0;
300
301 DPRINTF(("uchcom_detach: sc=%p flags=%d\n", sc, flags));
302
303 close_intr_pipe(sc);
304
305 sc->sc_dying = true;
306
307 if (sc->sc_subdev != NULL) {
308 rv = config_detach(sc->sc_subdev, flags);
309 sc->sc_subdev = NULL;
310 }
311
312 usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_udev, sc->sc_dev);
313
314 return rv;
315 }
316
317 static int
318 set_config(struct uchcom_softc *sc)
319 {
320 usbd_status err;
321
322 err = usbd_set_config_index(sc->sc_udev, UCHCOM_CONFIG_INDEX, 1);
323 if (err) {
324 aprint_error_dev(sc->sc_dev,
325 "failed to set configuration: %s\n", usbd_errstr(err));
326 return -1;
327 }
328
329 return 0;
330 }
331
332 static int
333 find_ifaces(struct uchcom_softc *sc, struct usbd_interface **riface)
334 {
335 usbd_status err;
336
337 err = usbd_device2interface_handle(sc->sc_udev, UCHCOM_IFACE_INDEX,
338 riface);
339 if (err) {
340 aprint_error("\n%s: failed to get interface: %s\n",
341 device_xname(sc->sc_dev), usbd_errstr(err));
342 return -1;
343 }
344
345 return 0;
346 }
347
348 static int
349 find_endpoints(struct uchcom_softc *sc, struct uchcom_endpoints *endpoints)
350 {
351 int i, bin=-1, bout=-1, intr=-1, isize=0;
352 usb_interface_descriptor_t *id;
353 usb_endpoint_descriptor_t *ed;
354
355 id = usbd_get_interface_descriptor(sc->sc_iface);
356
357 for (i = 0; i < id->bNumEndpoints; i++) {
358 ed = usbd_interface2endpoint_descriptor(sc->sc_iface, i);
359 if (ed == NULL) {
360 aprint_error_dev(sc->sc_dev,
361 "no endpoint descriptor for %d\n", i);
362 return -1;
363 }
364
365 if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
366 UE_GET_XFERTYPE(ed->bmAttributes) == UE_INTERRUPT) {
367 intr = ed->bEndpointAddress;
368 isize = UGETW(ed->wMaxPacketSize);
369 } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
370 UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
371 bin = ed->bEndpointAddress;
372 } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT &&
373 UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
374 bout = ed->bEndpointAddress;
375 }
376 }
377
378 if (intr == -1 || bin == -1 || bout == -1) {
379 if (intr == -1) {
380 aprint_error_dev(sc->sc_dev,
381 "no interrupt end point\n");
382 }
383 if (bin == -1) {
384 aprint_error_dev(sc->sc_dev,
385 "no data bulk in end point\n");
386 }
387 if (bout == -1) {
388 aprint_error_dev(sc->sc_dev,
389 "no data bulk out end point\n");
390 }
391 return -1;
392 }
393 if (isize < UCHCOM_INTR_LEAST) {
394 aprint_error_dev(sc->sc_dev, "intr pipe is too short\n");
395 return -1;
396 }
397
398 DPRINTF(("%s: bulkin=%d, bulkout=%d, intr=%d, isize=%d\n",
399 device_xname(sc->sc_dev), bin, bout, intr, isize));
400
401 endpoints->ep_intr = intr;
402 endpoints->ep_intr_size = isize;
403 endpoints->ep_bulkin = bin;
404 endpoints->ep_bulkout = bout;
405
406 return 0;
407 }
408
409
410 /* ----------------------------------------------------------------------
411 * low level i/o
412 */
413
414 static __inline usbd_status
415 generic_control_out(struct uchcom_softc *sc, uint8_t reqno,
416 uint16_t value, uint16_t index)
417 {
418 usb_device_request_t req;
419
420 req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
421 req.bRequest = reqno;
422 USETW(req.wValue, value);
423 USETW(req.wIndex, index);
424 USETW(req.wLength, 0);
425
426 return usbd_do_request(sc->sc_udev, &req, 0);
427 }
428
429 static __inline usbd_status
430 generic_control_in(struct uchcom_softc *sc, uint8_t reqno,
431 uint16_t value, uint16_t index, void *buf, int buflen,
432 int *actlen)
433 {
434 usb_device_request_t req;
435
436 req.bmRequestType = UT_READ_VENDOR_DEVICE;
437 req.bRequest = reqno;
438 USETW(req.wValue, value);
439 USETW(req.wIndex, index);
440 USETW(req.wLength, (uint16_t)buflen);
441
442 return usbd_do_request_flags(sc->sc_udev, &req, buf,
443 USBD_SHORT_XFER_OK, actlen,
444 USBD_DEFAULT_TIMEOUT);
445 }
446
447 static __inline usbd_status
448 write_reg(struct uchcom_softc *sc,
449 uint8_t reg1, uint8_t val1, uint8_t reg2, uint8_t val2)
450 {
451 DPRINTF(("%s: write reg %#02X<-%#02X, %#02X<-%#02X\n",
452 device_xname(sc->sc_dev),
453 (unsigned)reg1, (unsigned)val1,
454 (unsigned)reg2, (unsigned)val2));
455 return generic_control_out(
456 sc, UCHCOM_REQ_WRITE_REG,
457 reg1|((uint16_t)reg2<<8), val1|((uint16_t)val2<<8));
458 }
459
460 static __inline usbd_status
461 read_reg(struct uchcom_softc *sc,
462 uint8_t reg1, uint8_t *rval1, uint8_t reg2, uint8_t *rval2)
463 {
464 uint8_t buf[UCHCOM_INPUT_BUF_SIZE];
465 usbd_status err;
466 int actin;
467
468 err = generic_control_in(
469 sc, UCHCOM_REQ_READ_REG,
470 reg1|((uint16_t)reg2<<8), 0, buf, sizeof(buf), &actin);
471 if (err)
472 return err;
473
474 DPRINTF(("%s: read reg %#02X->%#02X, %#02X->%#02X\n",
475 device_xname(sc->sc_dev),
476 (unsigned)reg1, (unsigned)buf[0],
477 (unsigned)reg2, (unsigned)buf[1]));
478
479 if (rval1) *rval1 = buf[0];
480 if (rval2) *rval2 = buf[1];
481
482 return USBD_NORMAL_COMPLETION;
483 }
484
485 static __inline usbd_status
486 get_version(struct uchcom_softc *sc, uint8_t *rver)
487 {
488 uint8_t buf[UCHCOM_INPUT_BUF_SIZE];
489 usbd_status err;
490 int actin;
491
492 err = generic_control_in(
493 sc, UCHCOM_REQ_GET_VERSION, 0, 0, buf, sizeof(buf), &actin);
494 if (err)
495 return err;
496
497 if (rver) *rver = buf[0];
498
499 return USBD_NORMAL_COMPLETION;
500 }
501
502 static __inline usbd_status
503 get_status(struct uchcom_softc *sc, uint8_t *rval)
504 {
505 return read_reg(sc, UCHCOM_REG_STAT1, rval, UCHCOM_REG_STAT2, NULL);
506 }
507
508 static __inline usbd_status
509 set_dtrrts_10(struct uchcom_softc *sc, uint8_t val)
510 {
511 return write_reg(sc, UCHCOM_REG_STAT1, val, UCHCOM_REG_STAT1, val);
512 }
513
514 static __inline usbd_status
515 set_dtrrts_20(struct uchcom_softc *sc, uint8_t val)
516 {
517 return generic_control_out(sc, UCHCOM_REQ_SET_DTRRTS, val, 0);
518 }
519
520
521 /* ----------------------------------------------------------------------
522 * middle layer
523 */
524
525 static int
526 update_version(struct uchcom_softc *sc)
527 {
528 usbd_status err;
529
530 err = get_version(sc, &sc->sc_version);
531 if (err) {
532 device_printf(sc->sc_dev, "cannot get version: %s\n",
533 usbd_errstr(err));
534 return EIO;
535 }
536 DPRINTF(("%s: update_version %d\n", device_xname(sc->sc_dev), sc->sc_version));
537
538 return 0;
539 }
540
541 static void
542 convert_status(struct uchcom_softc *sc, uint8_t cur)
543 {
544 sc->sc_dtr = !(cur & UCHCOM_DTR_MASK);
545 sc->sc_rts = !(cur & UCHCOM_RTS_MASK);
546
547 cur = ~cur & 0x0F;
548 sc->sc_msr = (cur << 4) | ((sc->sc_msr >> 4) ^ cur);
549 }
550
551 static int
552 update_status(struct uchcom_softc *sc)
553 {
554 usbd_status err;
555 uint8_t cur;
556
557 err = get_status(sc, &cur);
558 if (err) {
559 device_printf(sc->sc_dev,
560 "cannot update status: %s\n", usbd_errstr(err));
561 return EIO;
562 }
563 convert_status(sc, cur);
564
565 return 0;
566 }
567
568
569 static int
570 set_dtrrts(struct uchcom_softc *sc, int dtr, int rts)
571 {
572 usbd_status err;
573 uint8_t val = 0;
574
575 if (dtr) val |= UCHCOM_DTR_MASK;
576 if (rts) val |= UCHCOM_RTS_MASK;
577
578 if (sc->sc_version < UCHCOM_VER_20)
579 err = set_dtrrts_10(sc, ~val);
580 else
581 err = set_dtrrts_20(sc, ~val);
582
583 if (err) {
584 device_printf(sc->sc_dev, "cannot set DTR/RTS: %s\n",
585 usbd_errstr(err));
586 return EIO;
587 }
588
589 return 0;
590 }
591
592 static int
593 set_break(struct uchcom_softc *sc, int onoff)
594 {
595 usbd_status err;
596 uint8_t brk, lcr;
597
598 err = read_reg(sc, UCHCOM_REG_BREAK, &brk, UCHCOM_REG_LCR, &lcr);
599 if (err)
600 return EIO;
601 if (onoff) {
602 /* on - clear bits */
603 brk &= ~UCHCOM_BREAK_MASK;
604 lcr &= ~UCHCOM_LCR_TXE;
605 } else {
606 /* off - set bits */
607 brk |= UCHCOM_BREAK_MASK;
608 lcr |= UCHCOM_LCR_TXE;
609 }
610 err = write_reg(sc, UCHCOM_REG_BREAK, brk, UCHCOM_REG_LCR, lcr);
611 if (err)
612 return EIO;
613
614 return 0;
615 }
616
617 static int
618 calc_divider_settings(struct uchcom_divider *dp, uint32_t rate)
619 {
620 size_t i;
621 uint32_t best, div, pre;
622 const uint32_t rate4x = rate * 4U;
623
624 if (rate == 0 || rate > 3000000)
625 return -1;
626
627 pre = __arraycount(rates4x);
628 best = UINT32_MAX;
629
630 for (i = 0; i < __arraycount(rates4x); i++) {
631 uint32_t score, try;
632 try = rates4x[i] * 2 / rate4x;
633 try = (try / 2) + (try & 1);
634 if (try < 2)
635 continue;
636 if (try > 255)
637 try = 255;
638 score = abs((int)rate4x - rates4x[i] / try);
639 if (score < best) {
640 best = score;
641 pre = i;
642 div = try;
643 }
644 }
645
646 if (pre >= __arraycount(rates4x))
647 return -1;
648 if ((rates4x[pre] / div / 4) < (rate * 99 / 100))
649 return -1;
650 if ((rates4x[pre] / div / 4) > (rate * 101 / 100))
651 return -1;
652
653 dp->dv_prescaler = pre;
654 dp->dv_div = (uint8_t)-div;
655
656 return 0;
657 }
658
659 static int
660 set_dte_rate(struct uchcom_softc *sc, uint32_t rate)
661 {
662 usbd_status err;
663 struct uchcom_divider dv;
664
665 if (calc_divider_settings(&dv, rate))
666 return EINVAL;
667
668 if ((err = write_reg(sc,
669 UCHCOM_REG_BPS_PRE,
670 dv.dv_prescaler | UCHCOM_BPS_PRE_IMM,
671 UCHCOM_REG_BPS_DIV, dv.dv_div))) {
672 device_printf(sc->sc_dev, "cannot set DTE rate: %s\n",
673 usbd_errstr(err));
674 return EIO;
675 }
676
677 return 0;
678 }
679
680 static int
681 set_line_control(struct uchcom_softc *sc, tcflag_t cflag)
682 {
683 usbd_status err;
684 uint8_t lcr = 0, lcr2 = 0;
685
686 err = read_reg(sc, UCHCOM_REG_LCR, &lcr, UCHCOM_REG_LCR2, &lcr2);
687 if (err) {
688 device_printf(sc->sc_dev, "cannot get LCR: %s\n",
689 usbd_errstr(err));
690 return EIO;
691 }
692
693 lcr = UCHCOM_LCR_RXE | UCHCOM_LCR_TXE;
694
695 switch (ISSET(cflag, CSIZE)) {
696 case CS5:
697 lcr |= UCHCOM_LCR_CS5;
698 break;
699 case CS6:
700 lcr |= UCHCOM_LCR_CS6;
701 break;
702 case CS7:
703 lcr |= UCHCOM_LCR_CS7;
704 break;
705 case CS8:
706 lcr |= UCHCOM_LCR_CS8;
707 break;
708 }
709
710 if (ISSET(cflag, PARENB)) {
711 lcr |= UCHCOM_LCR_PARENB;
712 if (!ISSET(cflag, PARODD))
713 lcr |= UCHCOM_LCR_PAREVEN;
714 }
715
716 if (ISSET(cflag, CSTOPB)) {
717 lcr |= UCHCOM_LCR_STOPB;
718 }
719
720 err = write_reg(sc, UCHCOM_REG_LCR, lcr, UCHCOM_REG_LCR2, lcr2);
721 if (err) {
722 device_printf(sc->sc_dev, "cannot set LCR: %s\n",
723 usbd_errstr(err));
724 return EIO;
725 }
726
727 return 0;
728 }
729
730 static int
731 clear_chip(struct uchcom_softc *sc)
732 {
733 usbd_status err;
734
735 DPRINTF(("%s: clear\n", device_xname(sc->sc_dev)));
736 err = generic_control_out(sc, UCHCOM_REQ_RESET, 0, 0);
737 if (err) {
738 device_printf(sc->sc_dev, "cannot clear: %s\n",
739 usbd_errstr(err));
740 return EIO;
741 }
742
743 return 0;
744 }
745
746 static int
747 reset_chip(struct uchcom_softc *sc)
748 {
749 usbd_status err;
750
751 err = generic_control_out(sc, UCHCOM_REQ_RESET,
752 UCHCOM_RESET_VALUE, UCHCOM_RESET_INDEX);
753 if (err)
754 goto failed;
755
756 return 0;
757
758 failed:
759 printf("%s: cannot reset: %s\n",
760 device_xname(sc->sc_dev), usbd_errstr(err));
761 return EIO;
762 }
763
764 static int
765 setup_comm(struct uchcom_softc *sc)
766 {
767 int ret;
768
769 ret = update_version(sc);
770 if (ret)
771 return ret;
772
773 ret = clear_chip(sc);
774 if (ret)
775 return ret;
776
777 ret = set_dte_rate(sc, TTYDEF_SPEED);
778 if (ret)
779 return ret;
780
781 ret = set_line_control(sc, CS8);
782 if (ret)
783 return ret;
784
785 ret = update_status(sc);
786 if (ret)
787 return ret;
788
789 ret = reset_chip(sc);
790 if (ret)
791 return ret;
792
793 ret = set_dte_rate(sc, TTYDEF_SPEED); /* XXX */
794 if (ret)
795 return ret;
796
797 sc->sc_dtr = sc->sc_rts = 1;
798 ret = set_dtrrts(sc, sc->sc_dtr, sc->sc_rts);
799 if (ret)
800 return ret;
801
802 return 0;
803 }
804
805 static int
806 setup_intr_pipe(struct uchcom_softc *sc)
807 {
808 usbd_status err;
809
810 if (sc->sc_intr_endpoint != -1 && sc->sc_intr_pipe == NULL) {
811 sc->sc_intr_buf = kmem_alloc(sc->sc_intr_size, KM_SLEEP);
812 err = usbd_open_pipe_intr(sc->sc_iface,
813 sc->sc_intr_endpoint,
814 USBD_SHORT_XFER_OK,
815 &sc->sc_intr_pipe, sc,
816 sc->sc_intr_buf,
817 sc->sc_intr_size,
818 uchcom_intr, USBD_DEFAULT_INTERVAL);
819 if (err) {
820 device_printf(sc->sc_dev,
821 "cannot open interrupt pipe: %s\n",
822 usbd_errstr(err));
823 return EIO;
824 }
825 }
826 return 0;
827 }
828
829 static void
830 close_intr_pipe(struct uchcom_softc *sc)
831 {
832
833 if (sc->sc_intr_pipe != NULL) {
834 usbd_abort_pipe(sc->sc_intr_pipe);
835 usbd_close_pipe(sc->sc_intr_pipe);
836 sc->sc_intr_pipe = NULL;
837 }
838 if (sc->sc_intr_buf != NULL) {
839 kmem_free(sc->sc_intr_buf, sc->sc_intr_size);
840 sc->sc_intr_buf = NULL;
841 }
842 }
843
844
845 /* ----------------------------------------------------------------------
846 * methods for ucom
847 */
848 static void
849 uchcom_get_status(void *arg, int portno, u_char *rlsr, u_char *rmsr)
850 {
851 struct uchcom_softc *sc = arg;
852
853 if (sc->sc_dying)
854 return;
855
856 *rlsr = sc->sc_lsr;
857 *rmsr = sc->sc_msr;
858 }
859
860 static void
861 uchcom_set(void *arg, int portno, int reg, int onoff)
862 {
863 struct uchcom_softc *sc = arg;
864
865 if (sc->sc_dying)
866 return;
867
868 switch (reg) {
869 case UCOM_SET_DTR:
870 sc->sc_dtr = !!onoff;
871 set_dtrrts(sc, sc->sc_dtr, sc->sc_rts);
872 break;
873 case UCOM_SET_RTS:
874 sc->sc_rts = !!onoff;
875 set_dtrrts(sc, sc->sc_dtr, sc->sc_rts);
876 break;
877 case UCOM_SET_BREAK:
878 set_break(sc, onoff);
879 break;
880 }
881 }
882
883 static int
884 uchcom_param(void *arg, int portno, struct termios *t)
885 {
886 struct uchcom_softc *sc = arg;
887 int ret;
888
889 if (sc->sc_dying)
890 return EIO;
891
892 ret = set_line_control(sc, t->c_cflag);
893 if (ret)
894 return ret;
895
896 ret = set_dte_rate(sc, t->c_ospeed);
897 if (ret)
898 return ret;
899
900 return 0;
901 }
902
903 static int
904 uchcom_open(void *arg, int portno)
905 {
906 int ret;
907 struct uchcom_softc *sc = arg;
908
909 if (sc->sc_dying)
910 return EIO;
911
912 ret = setup_intr_pipe(sc);
913 if (ret)
914 return ret;
915
916 ret = setup_comm(sc);
917 if (ret)
918 return ret;
919
920 return 0;
921 }
922
923 static void
924 uchcom_close(void *arg, int portno)
925 {
926 struct uchcom_softc *sc = arg;
927
928 if (sc->sc_dying)
929 return;
930
931 close_intr_pipe(sc);
932 }
933
934
935 /* ----------------------------------------------------------------------
936 * callback when the modem status is changed.
937 */
938 static void
939 uchcom_intr(struct usbd_xfer *xfer, void * priv,
940 usbd_status status)
941 {
942 struct uchcom_softc *sc = priv;
943 u_char *buf = sc->sc_intr_buf;
944
945 if (sc->sc_dying)
946 return;
947
948 if (status != USBD_NORMAL_COMPLETION) {
949 if (status == USBD_NOT_STARTED || status == USBD_CANCELLED)
950 return;
951
952 DPRINTF(("%s: abnormal status: %s\n",
953 device_xname(sc->sc_dev), usbd_errstr(status)));
954 usbd_clear_endpoint_stall_async(sc->sc_intr_pipe);
955 return;
956 }
957 DPRINTF(("%s: intr: %#02X %#02X %#02X %#02X "
958 "%#02X %#02X %#02X %#02X\n",
959 device_xname(sc->sc_dev),
960 (unsigned)buf[0], (unsigned)buf[1],
961 (unsigned)buf[2], (unsigned)buf[3],
962 (unsigned)buf[4], (unsigned)buf[5],
963 (unsigned)buf[6], (unsigned)buf[7]));
964
965 convert_status(sc, buf[UCHCOM_INTR_STAT1]);
966 ucom_status_change(device_private(sc->sc_subdev));
967 }
968