Line data Source code
1 : /* $OpenBSD: uchcom.c,v 1.27 2017/04/08 02:57:25 deraadt 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 CH341/340, the worst USB-serial chip in the world.
35 : */
36 :
37 : #include <sys/param.h>
38 : #include <sys/systm.h>
39 : #include <sys/kernel.h>
40 : #include <sys/malloc.h>
41 : #include <sys/tty.h>
42 : #include <sys/device.h>
43 :
44 : #include <dev/usb/usb.h>
45 : #include <dev/usb/usbdi.h>
46 : #include <dev/usb/usbdi_util.h>
47 : #include <dev/usb/usbdevs.h>
48 : #include <dev/usb/ucomvar.h>
49 :
50 : #ifdef UCHCOM_DEBUG
51 : #define DPRINTFN(n, x) do { if (uchcomdebug > (n)) printf x; } while (0)
52 : int uchcomdebug = 0;
53 : #else
54 : #define DPRINTFN(n, x)
55 : #endif
56 : #define DPRINTF(x) DPRINTFN(0, x)
57 :
58 : #define UCHCOM_IFACE_INDEX 0
59 :
60 : #define UCHCOM_REV_CH340 0x0250
61 : #define UCHCOM_INPUT_BUF_SIZE 8
62 :
63 : #define UCHCOM_REQ_GET_VERSION 0x5F
64 : #define UCHCOM_REQ_READ_REG 0x95
65 : #define UCHCOM_REQ_WRITE_REG 0x9A
66 : #define UCHCOM_REQ_RESET 0xA1
67 : #define UCHCOM_REQ_SET_DTRRTS 0xA4
68 :
69 : #define UCHCOM_REG_STAT1 0x06
70 : #define UCHCOM_REG_STAT2 0x07
71 : #define UCHCOM_REG_BPS_PRE 0x12
72 : #define UCHCOM_REG_BPS_DIV 0x13
73 : #define UCHCOM_REG_BPS_MOD 0x14
74 : #define UCHCOM_REG_BPS_PAD 0x0F
75 : #define UCHCOM_REG_BREAK1 0x05
76 : #define UCHCOM_REG_BREAK2 0x18
77 : #define UCHCOM_REG_LCR1 0x18
78 : #define UCHCOM_REG_LCR2 0x25
79 :
80 : #define UCHCOM_VER_20 0x20
81 :
82 : #define UCHCOM_BASE_UNKNOWN 0
83 : #define UCHCOM_BPS_MOD_BASE 20000000
84 : #define UCHCOM_BPS_MOD_BASE_OFS 1100
85 :
86 : #define UCHCOM_DTR_MASK 0x20
87 : #define UCHCOM_RTS_MASK 0x40
88 :
89 : #define UCHCOM_BRK1_MASK 0x01
90 : #define UCHCOM_BRK2_MASK 0x40
91 :
92 : #define UCHCOM_INTR_STAT1 0x02
93 : #define UCHCOM_INTR_STAT2 0x03
94 : #define UCHCOM_INTR_LEAST 4
95 :
96 : /*
97 : * XXX - these magic numbers come from Linux (drivers/usb/serial/ch341.c).
98 : * The manufacturer was unresponsive when asked for documentation.
99 : */
100 : #define UCHCOM_RESET_VALUE 0x501F /* line mode? */
101 : #define UCHCOM_RESET_INDEX 0xD90A /* baud rate? */
102 :
103 : #define UCHCOMIBUFSIZE 256
104 : #define UCHCOMOBUFSIZE 256
105 :
106 : struct uchcom_softc
107 : {
108 : struct device sc_dev;
109 : struct usbd_device *sc_udev;
110 : struct device *sc_subdev;
111 : struct usbd_interface *sc_iface;
112 : /* */
113 : int sc_intr_endpoint;
114 : struct usbd_pipe *sc_intr_pipe;
115 : u_char *sc_intr_buf;
116 : int sc_isize;
117 : /* */
118 : uint8_t sc_version;
119 : int sc_dtr;
120 : int sc_rts;
121 : u_char sc_lsr;
122 : u_char sc_msr;
123 : int sc_lcr1;
124 : int sc_lcr2;
125 : };
126 :
127 : struct uchcom_endpoints
128 : {
129 : int ep_bulkin;
130 : int ep_bulkout;
131 : int ep_intr;
132 : int ep_intr_size;
133 : };
134 :
135 : struct uchcom_divider
136 : {
137 : uint8_t dv_prescaler;
138 : uint8_t dv_div;
139 : uint8_t dv_mod;
140 : };
141 :
142 : struct uchcom_divider_record
143 : {
144 : uint32_t dvr_high;
145 : uint32_t dvr_low;
146 : uint32_t dvr_base_clock;
147 : struct uchcom_divider dvr_divider;
148 : };
149 :
150 : static const struct uchcom_divider_record dividers[] =
151 : {
152 : { 307200, 307200, UCHCOM_BASE_UNKNOWN, { 7, 0xD9, 0 } },
153 : { 921600, 921600, UCHCOM_BASE_UNKNOWN, { 7, 0xF3, 0 } },
154 : { 2999999, 23530, 6000000, { 3, 0, 0 } },
155 : { 23529, 2942, 750000, { 2, 0, 0 } },
156 : { 2941, 368, 93750, { 1, 0, 0 } },
157 : { 367, 1, 11719, { 0, 0, 0 } },
158 : };
159 :
160 : void uchcom_get_status(void *, int, u_char *, u_char *);
161 : void uchcom_set(void *, int, int, int);
162 : int uchcom_param(void *, int, struct termios *);
163 : int uchcom_open(void *, int);
164 : void uchcom_close(void *, int);
165 : void uchcom_intr(struct usbd_xfer *, void *, usbd_status);
166 :
167 : int uchcom_find_ifaces(struct uchcom_softc *,
168 : struct usbd_interface **);
169 : int uchcom_find_endpoints(struct uchcom_softc *,
170 : struct uchcom_endpoints *);
171 : void uchcom_close_intr_pipe(struct uchcom_softc *);
172 :
173 :
174 : usbd_status uchcom_generic_control_out(struct uchcom_softc *sc,
175 : uint8_t reqno, uint16_t value, uint16_t index);
176 : usbd_status uchcom_generic_control_in(struct uchcom_softc *, uint8_t,
177 : uint16_t, uint16_t, void *, int, int *);
178 : usbd_status uchcom_write_reg(struct uchcom_softc *, uint8_t, uint8_t,
179 : uint8_t, uint8_t);
180 : usbd_status uchcom_read_reg(struct uchcom_softc *, uint8_t, uint8_t *,
181 : uint8_t, uint8_t *);
182 : usbd_status uchcom_get_version(struct uchcom_softc *, uint8_t *);
183 : usbd_status uchcom_read_status(struct uchcom_softc *, uint8_t *);
184 : usbd_status uchcom_set_dtrrts_10(struct uchcom_softc *, uint8_t);
185 : usbd_status uchcom_set_dtrrts_20(struct uchcom_softc *, uint8_t);
186 : int uchcom_update_version(struct uchcom_softc *);
187 : void uchcom_convert_status(struct uchcom_softc *, uint8_t);
188 : int uchcom_update_status(struct uchcom_softc *);
189 : int uchcom_set_dtrrts(struct uchcom_softc *, int, int);
190 : int uchcom_set_break(struct uchcom_softc *, int);
191 : int uchcom_calc_divider_settings(struct uchcom_divider *, uint32_t);
192 : int uchcom_set_dte_rate(struct uchcom_softc *, uint32_t);
193 : int uchcom_set_line_control(struct uchcom_softc *, tcflag_t);
194 : int uchcom_clear_chip(struct uchcom_softc *);
195 : int uchcom_reset_chip(struct uchcom_softc *);
196 : int uchcom_setup_comm(struct uchcom_softc *);
197 : int uchcom_setup_intr_pipe(struct uchcom_softc *);
198 :
199 :
200 : int uchcom_match(struct device *, void *, void *);
201 : void uchcom_attach(struct device *, struct device *, void *);
202 : int uchcom_detach(struct device *, int);
203 :
204 : struct ucom_methods uchcom_methods = {
205 : uchcom_get_status,
206 : uchcom_set,
207 : uchcom_param,
208 : NULL,
209 : uchcom_open,
210 : uchcom_close,
211 : NULL,
212 : NULL,
213 : };
214 :
215 : static const struct usb_devno uchcom_devs[] = {
216 : { USB_VENDOR_WCH, USB_PRODUCT_WCH_CH341 },
217 : { USB_VENDOR_WCH2, USB_PRODUCT_WCH2_CH340 },
218 : { USB_VENDOR_WCH2, USB_PRODUCT_WCH2_CH341A }
219 : };
220 :
221 : struct cfdriver uchcom_cd = {
222 : NULL, "uchcom", DV_DULL
223 : };
224 :
225 : const struct cfattach uchcom_ca = {
226 : sizeof(struct uchcom_softc), uchcom_match, uchcom_attach, uchcom_detach
227 : };
228 :
229 : /* ----------------------------------------------------------------------
230 : * driver entry points
231 : */
232 :
233 : int
234 0 : uchcom_match(struct device *parent, void *match, void *aux)
235 : {
236 0 : struct usb_attach_arg *uaa = aux;
237 :
238 0 : if (uaa->iface == NULL)
239 0 : return UMATCH_NONE;
240 :
241 0 : return (usb_lookup(uchcom_devs, uaa->vendor, uaa->product) != NULL ?
242 : UMATCH_VENDOR_PRODUCT : UMATCH_NONE);
243 0 : }
244 :
245 : void
246 0 : uchcom_attach(struct device *parent, struct device *self, void *aux)
247 : {
248 0 : struct uchcom_softc *sc = (struct uchcom_softc *)self;
249 0 : struct usb_attach_arg *uaa = aux;
250 0 : struct ucom_attach_args uca;
251 0 : struct usbd_device *dev = uaa->device;
252 0 : struct uchcom_endpoints endpoints;
253 :
254 0 : sc->sc_udev = dev;
255 0 : sc->sc_dtr = sc->sc_rts = -1;
256 :
257 : DPRINTF(("\n\nuchcom attach: sc=%p\n", sc));
258 :
259 0 : switch (uaa->release) {
260 : case UCHCOM_REV_CH340:
261 0 : printf("%s: CH340\n", sc->sc_dev.dv_xname);
262 0 : break;
263 : default:
264 0 : printf("%s: CH341\n", sc->sc_dev.dv_xname);
265 0 : break;
266 : }
267 :
268 0 : if (uchcom_find_ifaces(sc, &sc->sc_iface))
269 : goto failed;
270 :
271 0 : if (uchcom_find_endpoints(sc, &endpoints))
272 : goto failed;
273 :
274 0 : sc->sc_intr_endpoint = endpoints.ep_intr;
275 0 : sc->sc_isize = endpoints.ep_intr_size;
276 :
277 : /* setup ucom layer */
278 0 : uca.portno = UCOM_UNK_PORTNO;
279 0 : uca.bulkin = endpoints.ep_bulkin;
280 0 : uca.bulkout = endpoints.ep_bulkout;
281 0 : uca.ibufsize = UCHCOMIBUFSIZE;
282 0 : uca.obufsize = UCHCOMOBUFSIZE;
283 0 : uca.ibufsizepad = UCHCOMIBUFSIZE;
284 0 : uca.opkthdrlen = 0;
285 0 : uca.device = dev;
286 0 : uca.iface = sc->sc_iface;
287 0 : uca.methods = &uchcom_methods;
288 0 : uca.arg = sc;
289 0 : uca.info = NULL;
290 :
291 0 : sc->sc_subdev = config_found_sm(self, &uca, ucomprint, ucomsubmatch);
292 :
293 0 : return;
294 :
295 : failed:
296 0 : usbd_deactivate(sc->sc_udev);
297 0 : }
298 :
299 : int
300 0 : uchcom_detach(struct device *self, int flags)
301 : {
302 0 : struct uchcom_softc *sc = (struct uchcom_softc *)self;
303 : int rv = 0;
304 :
305 : DPRINTF(("uchcom_detach: sc=%p flags=%d\n", sc, flags));
306 :
307 0 : uchcom_close_intr_pipe(sc);
308 :
309 0 : if (sc->sc_subdev != NULL) {
310 0 : rv = config_detach(sc->sc_subdev, flags);
311 0 : sc->sc_subdev = NULL;
312 0 : }
313 :
314 0 : return rv;
315 : }
316 :
317 : int
318 0 : uchcom_find_ifaces(struct uchcom_softc *sc, struct usbd_interface **riface)
319 : {
320 : usbd_status err;
321 :
322 0 : err = usbd_device2interface_handle(sc->sc_udev, UCHCOM_IFACE_INDEX,
323 : riface);
324 0 : if (err) {
325 0 : printf("\n%s: failed to get interface: %s\n",
326 0 : sc->sc_dev.dv_xname, usbd_errstr(err));
327 0 : return -1;
328 : }
329 :
330 0 : return 0;
331 0 : }
332 :
333 : int
334 0 : uchcom_find_endpoints(struct uchcom_softc *sc,
335 : struct uchcom_endpoints *endpoints)
336 : {
337 : int i, bin=-1, bout=-1, intr=-1, isize=0;
338 : usb_interface_descriptor_t *id;
339 : usb_endpoint_descriptor_t *ed;
340 :
341 0 : id = usbd_get_interface_descriptor(sc->sc_iface);
342 :
343 0 : for (i = 0; i < id->bNumEndpoints; i++) {
344 0 : ed = usbd_interface2endpoint_descriptor(sc->sc_iface, i);
345 0 : if (ed == NULL) {
346 0 : printf("%s: no endpoint descriptor for %d\n",
347 0 : sc->sc_dev.dv_xname, i);
348 0 : return -1;
349 : }
350 :
351 0 : if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
352 0 : UE_GET_XFERTYPE(ed->bmAttributes) == UE_INTERRUPT) {
353 : intr = ed->bEndpointAddress;
354 0 : isize = UGETW(ed->wMaxPacketSize);
355 0 : } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
356 0 : UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
357 : bin = ed->bEndpointAddress;
358 0 : } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT &&
359 0 : UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
360 : bout = ed->bEndpointAddress;
361 0 : }
362 : }
363 :
364 0 : if (intr == -1 || bin == -1 || bout == -1) {
365 0 : if (intr == -1) {
366 0 : printf("%s: no interrupt end point\n",
367 0 : sc->sc_dev.dv_xname);
368 0 : }
369 0 : if (bin == -1) {
370 0 : printf("%s: no data bulk in end point\n",
371 0 : sc->sc_dev.dv_xname);
372 0 : }
373 0 : if (bout == -1) {
374 0 : printf("%s: no data bulk out end point\n",
375 0 : sc->sc_dev.dv_xname);
376 0 : }
377 0 : return -1;
378 : }
379 0 : if (isize < UCHCOM_INTR_LEAST) {
380 0 : printf("%s: intr pipe is too short", sc->sc_dev.dv_xname);
381 0 : return -1;
382 : }
383 :
384 : DPRINTF(("%s: bulkin=%d, bulkout=%d, intr=%d, isize=%d\n",
385 : sc->sc_dev.dv_xname, bin, bout, intr, isize));
386 :
387 0 : endpoints->ep_intr = intr;
388 0 : endpoints->ep_intr_size = isize;
389 0 : endpoints->ep_bulkin = bin;
390 0 : endpoints->ep_bulkout = bout;
391 :
392 0 : return 0;
393 0 : }
394 :
395 :
396 : /* ----------------------------------------------------------------------
397 : * low level i/o
398 : */
399 :
400 : usbd_status
401 0 : uchcom_generic_control_out(struct uchcom_softc *sc, uint8_t reqno,
402 : uint16_t value, uint16_t index)
403 : {
404 0 : usb_device_request_t req;
405 :
406 0 : req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
407 0 : req.bRequest = reqno;
408 0 : USETW(req.wValue, value);
409 0 : USETW(req.wIndex, index);
410 0 : USETW(req.wLength, 0);
411 :
412 0 : return usbd_do_request(sc->sc_udev, &req, 0);
413 0 : }
414 :
415 : usbd_status
416 0 : uchcom_generic_control_in(struct uchcom_softc *sc, uint8_t reqno,
417 : uint16_t value, uint16_t index, void *buf, int buflen, int *actlen)
418 : {
419 0 : usb_device_request_t req;
420 :
421 0 : req.bmRequestType = UT_READ_VENDOR_DEVICE;
422 0 : req.bRequest = reqno;
423 0 : USETW(req.wValue, value);
424 0 : USETW(req.wIndex, index);
425 0 : USETW(req.wLength, (uint16_t)buflen);
426 :
427 0 : return usbd_do_request_flags(sc->sc_udev, &req, buf,
428 : USBD_SHORT_XFER_OK, actlen,
429 : USBD_DEFAULT_TIMEOUT);
430 0 : }
431 :
432 : usbd_status
433 0 : uchcom_write_reg(struct uchcom_softc *sc,
434 : uint8_t reg1, uint8_t val1, uint8_t reg2, uint8_t val2)
435 : {
436 : DPRINTF(("uchcom: write reg 0x%02X<-0x%02X, 0x%02X<-0x%02X\n",
437 : (unsigned)reg1, (unsigned)val1,
438 : (unsigned)reg2, (unsigned)val2));
439 0 : return uchcom_generic_control_out(
440 : sc, UCHCOM_REQ_WRITE_REG,
441 0 : reg1|((uint16_t)reg2<<8), val1|((uint16_t)val2<<8));
442 : }
443 :
444 : usbd_status
445 0 : uchcom_read_reg(struct uchcom_softc *sc,
446 : uint8_t reg1, uint8_t *rval1, uint8_t reg2, uint8_t *rval2)
447 : {
448 0 : uint8_t buf[UCHCOM_INPUT_BUF_SIZE];
449 : usbd_status err;
450 0 : int actin;
451 :
452 0 : err = uchcom_generic_control_in(
453 : sc, UCHCOM_REQ_READ_REG,
454 0 : reg1|((uint16_t)reg2<<8), 0, buf, sizeof buf, &actin);
455 0 : if (err)
456 0 : return err;
457 :
458 : DPRINTF(("uchcom: read reg 0x%02X->0x%02X, 0x%02X->0x%02X\n",
459 : (unsigned)reg1, (unsigned)buf[0],
460 : (unsigned)reg2, (unsigned)buf[1]));
461 :
462 0 : if (rval1) *rval1 = buf[0];
463 0 : if (rval2) *rval2 = buf[1];
464 :
465 0 : return USBD_NORMAL_COMPLETION;
466 0 : }
467 :
468 : usbd_status
469 0 : uchcom_get_version(struct uchcom_softc *sc, uint8_t *rver)
470 : {
471 0 : uint8_t buf[UCHCOM_INPUT_BUF_SIZE];
472 : usbd_status err;
473 0 : int actin;
474 :
475 0 : err = uchcom_generic_control_in(
476 0 : sc, UCHCOM_REQ_GET_VERSION, 0, 0, buf, sizeof buf, &actin);
477 0 : if (err)
478 0 : return err;
479 :
480 0 : if (rver) *rver = buf[0];
481 :
482 0 : return USBD_NORMAL_COMPLETION;
483 0 : }
484 :
485 : usbd_status
486 0 : uchcom_read_status(struct uchcom_softc *sc, uint8_t *rval)
487 : {
488 0 : return uchcom_read_reg(sc, UCHCOM_REG_STAT1, rval, UCHCOM_REG_STAT2,
489 : NULL);
490 : }
491 :
492 : usbd_status
493 0 : uchcom_set_dtrrts_10(struct uchcom_softc *sc, uint8_t val)
494 : {
495 0 : return uchcom_write_reg(sc, UCHCOM_REG_STAT1, val, UCHCOM_REG_STAT1,
496 : val);
497 : }
498 :
499 : usbd_status
500 0 : uchcom_set_dtrrts_20(struct uchcom_softc *sc, uint8_t val)
501 : {
502 0 : return uchcom_generic_control_out(sc, UCHCOM_REQ_SET_DTRRTS, val, 0);
503 : }
504 :
505 :
506 : /* ----------------------------------------------------------------------
507 : * middle layer
508 : */
509 :
510 : int
511 0 : uchcom_update_version(struct uchcom_softc *sc)
512 : {
513 : usbd_status err;
514 :
515 0 : err = uchcom_get_version(sc, &sc->sc_version);
516 0 : if (err) {
517 0 : printf("%s: cannot get version: %s\n",
518 0 : sc->sc_dev.dv_xname, usbd_errstr(err));
519 0 : return EIO;
520 : }
521 :
522 0 : return 0;
523 0 : }
524 :
525 : void
526 0 : uchcom_convert_status(struct uchcom_softc *sc, uint8_t cur)
527 : {
528 0 : sc->sc_dtr = !(cur & UCHCOM_DTR_MASK);
529 0 : sc->sc_rts = !(cur & UCHCOM_RTS_MASK);
530 :
531 0 : cur = ~cur & 0x0F;
532 0 : sc->sc_msr = (cur << 4) | ((sc->sc_msr >> 4) ^ cur);
533 0 : }
534 :
535 : int
536 0 : uchcom_update_status(struct uchcom_softc *sc)
537 : {
538 : usbd_status err;
539 0 : uint8_t cur;
540 :
541 0 : err = uchcom_read_status(sc, &cur);
542 0 : if (err) {
543 0 : printf("%s: cannot update status: %s\n",
544 0 : sc->sc_dev.dv_xname, usbd_errstr(err));
545 0 : return EIO;
546 : }
547 0 : uchcom_convert_status(sc, cur);
548 :
549 0 : return 0;
550 0 : }
551 :
552 :
553 : int
554 0 : uchcom_set_dtrrts(struct uchcom_softc *sc, int dtr, int rts)
555 : {
556 : usbd_status err;
557 : uint8_t val = 0;
558 :
559 0 : if (dtr) val |= UCHCOM_DTR_MASK;
560 0 : if (rts) val |= UCHCOM_RTS_MASK;
561 :
562 0 : if (sc->sc_version < UCHCOM_VER_20)
563 0 : err = uchcom_set_dtrrts_10(sc, ~val);
564 : else
565 0 : err = uchcom_set_dtrrts_20(sc, ~val);
566 :
567 0 : if (err) {
568 0 : printf("%s: cannot set DTR/RTS: %s\n",
569 0 : sc->sc_dev.dv_xname, usbd_errstr(err));
570 0 : return EIO;
571 : }
572 :
573 0 : return 0;
574 0 : }
575 :
576 : int
577 0 : uchcom_set_break(struct uchcom_softc *sc, int onoff)
578 : {
579 : usbd_status err;
580 0 : uint8_t brk1, brk2;
581 :
582 0 : err = uchcom_read_reg(sc, UCHCOM_REG_BREAK1, &brk1, UCHCOM_REG_BREAK2,
583 : &brk2);
584 0 : if (err)
585 0 : return EIO;
586 0 : if (onoff) {
587 : /* on - clear bits */
588 0 : brk1 &= ~UCHCOM_BRK1_MASK;
589 0 : brk2 &= ~UCHCOM_BRK2_MASK;
590 0 : } else {
591 : /* off - set bits */
592 0 : brk1 |= UCHCOM_BRK1_MASK;
593 0 : brk2 |= UCHCOM_BRK2_MASK;
594 : }
595 0 : err = uchcom_write_reg(sc, UCHCOM_REG_BREAK1, brk1, UCHCOM_REG_BREAK2,
596 0 : brk2);
597 0 : if (err)
598 0 : return EIO;
599 :
600 0 : return 0;
601 0 : }
602 :
603 : int
604 0 : uchcom_calc_divider_settings(struct uchcom_divider *dp, uint32_t rate)
605 : {
606 : int i;
607 : const struct uchcom_divider_record *rp;
608 : uint32_t div, rem, mod;
609 :
610 : /* find record */
611 0 : for (i=0; i<nitems(dividers); i++) {
612 0 : if (dividers[i].dvr_high >= rate &&
613 0 : dividers[i].dvr_low <= rate) {
614 : rp = ÷rs[i];
615 : goto found;
616 : }
617 : }
618 0 : return -1;
619 :
620 : found:
621 0 : dp->dv_prescaler = rp->dvr_divider.dv_prescaler;
622 0 : if (rp->dvr_base_clock == UCHCOM_BASE_UNKNOWN)
623 0 : dp->dv_div = rp->dvr_divider.dv_div;
624 : else {
625 0 : div = rp->dvr_base_clock / rate;
626 0 : rem = rp->dvr_base_clock % rate;
627 0 : if (div==0 || div>=0xFF)
628 0 : return -1;
629 0 : if ((rem<<1) >= rate)
630 0 : div += 1;
631 0 : dp->dv_div = (uint8_t)-div;
632 : }
633 :
634 0 : mod = UCHCOM_BPS_MOD_BASE/rate + UCHCOM_BPS_MOD_BASE_OFS;
635 0 : mod = mod + mod/2;
636 :
637 0 : dp->dv_mod = mod / 0x100;
638 :
639 0 : return 0;
640 0 : }
641 :
642 : int
643 0 : uchcom_set_dte_rate(struct uchcom_softc *sc, uint32_t rate)
644 : {
645 : usbd_status err;
646 0 : struct uchcom_divider dv;
647 :
648 0 : if (uchcom_calc_divider_settings(&dv, rate))
649 0 : return EINVAL;
650 :
651 0 : if ((err = uchcom_write_reg(sc,
652 0 : UCHCOM_REG_BPS_PRE, dv.dv_prescaler,
653 0 : UCHCOM_REG_BPS_DIV, dv.dv_div)) ||
654 0 : (err = uchcom_write_reg(sc,
655 0 : UCHCOM_REG_BPS_MOD, dv.dv_mod,
656 : UCHCOM_REG_BPS_PAD, 0))) {
657 0 : printf("%s: cannot set DTE rate: %s\n",
658 0 : sc->sc_dev.dv_xname, usbd_errstr(err));
659 0 : return EIO;
660 : }
661 :
662 0 : return 0;
663 0 : }
664 :
665 : int
666 0 : uchcom_set_line_control(struct uchcom_softc *sc, tcflag_t cflag)
667 : {
668 : /*
669 : * XXX: it is difficult to handle the line control appropriately:
670 : * work as chip default - CS8, no parity, !CSTOPB
671 : * other modes are not supported.
672 : */
673 :
674 0 : switch (ISSET(cflag, CSIZE)) {
675 : case CS5:
676 : case CS6:
677 : case CS7:
678 0 : return EINVAL;
679 : case CS8:
680 : break;
681 : }
682 :
683 0 : if (ISSET(cflag, PARENB) || ISSET(cflag, CSTOPB))
684 0 : return EINVAL;
685 :
686 0 : return 0;
687 0 : }
688 :
689 : int
690 0 : uchcom_clear_chip(struct uchcom_softc *sc)
691 : {
692 : usbd_status err;
693 :
694 : DPRINTF(("%s: clear\n", sc->sc_dev.dv_xname));
695 0 : err = uchcom_generic_control_out(sc, UCHCOM_REQ_RESET, 0, 0);
696 0 : if (err) {
697 0 : printf("%s: cannot clear: %s\n",
698 0 : sc->sc_dev.dv_xname, usbd_errstr(err));
699 0 : return EIO;
700 : }
701 :
702 0 : return 0;
703 0 : }
704 :
705 : int
706 0 : uchcom_reset_chip(struct uchcom_softc *sc)
707 : {
708 : usbd_status err;
709 :
710 : DPRINTF(("%s: reset\n", sc->sc_dev.dv_xname));
711 :
712 0 : err = uchcom_generic_control_out(sc, UCHCOM_REQ_RESET,
713 : UCHCOM_RESET_VALUE,
714 : UCHCOM_RESET_INDEX);
715 0 : if (err)
716 : goto failed;
717 :
718 0 : return 0;
719 :
720 : failed:
721 0 : printf("%s: cannot reset: %s\n",
722 0 : sc->sc_dev.dv_xname, usbd_errstr(err));
723 0 : return EIO;
724 0 : }
725 :
726 : int
727 0 : uchcom_setup_comm(struct uchcom_softc *sc)
728 : {
729 : int ret;
730 :
731 0 : ret = uchcom_update_version(sc);
732 0 : if (ret)
733 0 : return ret;
734 :
735 0 : ret = uchcom_clear_chip(sc);
736 0 : if (ret)
737 0 : return ret;
738 :
739 0 : ret = uchcom_set_dte_rate(sc, TTYDEF_SPEED);
740 0 : if (ret)
741 0 : return ret;
742 :
743 0 : ret = uchcom_set_line_control(sc, CS8);
744 0 : if (ret)
745 0 : return ret;
746 :
747 0 : ret = uchcom_update_status(sc);
748 0 : if (ret)
749 0 : return ret;
750 :
751 0 : ret = uchcom_reset_chip(sc);
752 0 : if (ret)
753 0 : return ret;
754 :
755 0 : ret = uchcom_set_dte_rate(sc, TTYDEF_SPEED); /* XXX */
756 0 : if (ret)
757 0 : return ret;
758 :
759 0 : sc->sc_dtr = sc->sc_rts = 1;
760 0 : ret = uchcom_set_dtrrts(sc, sc->sc_dtr, sc->sc_rts);
761 0 : if (ret)
762 0 : return ret;
763 :
764 0 : return 0;
765 0 : }
766 :
767 : int
768 0 : uchcom_setup_intr_pipe(struct uchcom_softc *sc)
769 : {
770 : usbd_status err;
771 :
772 0 : if (sc->sc_intr_endpoint != -1 && sc->sc_intr_pipe == NULL) {
773 0 : sc->sc_intr_buf = malloc(sc->sc_isize, M_USBDEV, M_WAITOK);
774 0 : err = usbd_open_pipe_intr(sc->sc_iface,
775 0 : sc->sc_intr_endpoint,
776 : USBD_SHORT_XFER_OK,
777 0 : &sc->sc_intr_pipe, sc,
778 : sc->sc_intr_buf,
779 0 : sc->sc_isize,
780 : uchcom_intr, USBD_DEFAULT_INTERVAL);
781 0 : if (err) {
782 0 : printf("%s: cannot open interrupt pipe: %s\n",
783 0 : sc->sc_dev.dv_xname,
784 0 : usbd_errstr(err));
785 0 : return EIO;
786 : }
787 : }
788 0 : return 0;
789 0 : }
790 :
791 : void
792 0 : uchcom_close_intr_pipe(struct uchcom_softc *sc)
793 : {
794 : usbd_status err;
795 :
796 0 : if (sc->sc_intr_pipe != NULL) {
797 0 : usbd_abort_pipe(sc->sc_intr_pipe);
798 0 : err = usbd_close_pipe(sc->sc_intr_pipe);
799 0 : if (err)
800 0 : printf("%s: close interrupt pipe failed: %s\n",
801 0 : sc->sc_dev.dv_xname, usbd_errstr(err));
802 0 : free(sc->sc_intr_buf, M_USBDEV, sc->sc_isize);
803 0 : sc->sc_intr_pipe = NULL;
804 0 : }
805 0 : }
806 :
807 :
808 : /* ----------------------------------------------------------------------
809 : * methods for ucom
810 : */
811 : void
812 0 : uchcom_get_status(void *arg, int portno, u_char *rlsr, u_char *rmsr)
813 : {
814 0 : struct uchcom_softc *sc = arg;
815 :
816 0 : if (usbd_is_dying(sc->sc_udev))
817 0 : return;
818 :
819 0 : *rlsr = sc->sc_lsr;
820 0 : *rmsr = sc->sc_msr;
821 0 : }
822 :
823 : void
824 0 : uchcom_set(void *arg, int portno, int reg, int onoff)
825 : {
826 0 : struct uchcom_softc *sc = arg;
827 :
828 0 : if (usbd_is_dying(sc->sc_udev))
829 0 : return;
830 :
831 0 : switch (reg) {
832 : case UCOM_SET_DTR:
833 0 : sc->sc_dtr = !!onoff;
834 0 : uchcom_set_dtrrts(sc, sc->sc_dtr, sc->sc_rts);
835 0 : break;
836 : case UCOM_SET_RTS:
837 0 : sc->sc_rts = !!onoff;
838 0 : uchcom_set_dtrrts(sc, sc->sc_dtr, sc->sc_rts);
839 0 : break;
840 : case UCOM_SET_BREAK:
841 0 : uchcom_set_break(sc, onoff);
842 0 : break;
843 : }
844 0 : }
845 :
846 : int
847 0 : uchcom_param(void *arg, int portno, struct termios *t)
848 : {
849 0 : struct uchcom_softc *sc = arg;
850 : int ret;
851 :
852 0 : if (usbd_is_dying(sc->sc_udev))
853 0 : return 0;
854 :
855 0 : ret = uchcom_set_line_control(sc, t->c_cflag);
856 0 : if (ret)
857 0 : return ret;
858 :
859 0 : ret = uchcom_set_dte_rate(sc, t->c_ospeed);
860 0 : if (ret)
861 0 : return ret;
862 :
863 0 : return 0;
864 0 : }
865 :
866 : int
867 0 : uchcom_open(void *arg, int portno)
868 : {
869 : int ret;
870 0 : struct uchcom_softc *sc = arg;
871 :
872 0 : if (usbd_is_dying(sc->sc_udev))
873 0 : return EIO;
874 :
875 0 : ret = uchcom_setup_intr_pipe(sc);
876 0 : if (ret)
877 0 : return ret;
878 :
879 0 : ret = uchcom_setup_comm(sc);
880 0 : if (ret)
881 0 : return ret;
882 :
883 0 : return 0;
884 0 : }
885 :
886 : void
887 0 : uchcom_close(void *arg, int portno)
888 : {
889 0 : struct uchcom_softc *sc = arg;
890 :
891 0 : uchcom_close_intr_pipe(sc);
892 0 : }
893 :
894 :
895 : /* ----------------------------------------------------------------------
896 : * callback when the modem status is changed.
897 : */
898 : void
899 0 : uchcom_intr(struct usbd_xfer *xfer, void *priv, usbd_status status)
900 : {
901 0 : struct uchcom_softc *sc = priv;
902 0 : u_char *buf = sc->sc_intr_buf;
903 :
904 0 : if (usbd_is_dying(sc->sc_udev))
905 0 : return;
906 :
907 0 : if (status != USBD_NORMAL_COMPLETION) {
908 0 : if (status == USBD_NOT_STARTED || status == USBD_CANCELLED)
909 0 : return;
910 :
911 : DPRINTF(("%s: abnormal status: %s\n",
912 : sc->sc_dev.dv_xname, usbd_errstr(status)));
913 0 : usbd_clear_endpoint_stall_async(sc->sc_intr_pipe);
914 0 : return;
915 : }
916 : DPRINTF(("%s: intr: 0x%02X 0x%02X 0x%02X 0x%02X "
917 : "0x%02X 0x%02X 0x%02X 0x%02X\n",
918 : sc->sc_dev.dv_xname,
919 : (unsigned)buf[0], (unsigned)buf[1],
920 : (unsigned)buf[2], (unsigned)buf[3],
921 : (unsigned)buf[4], (unsigned)buf[5],
922 : (unsigned)buf[6], (unsigned)buf[7]));
923 :
924 0 : uchcom_convert_status(sc, buf[UCHCOM_INTR_STAT1]);
925 0 : ucom_status_change((struct ucom_softc *) sc->sc_subdev);
926 0 : }
|