xref: /openbsd-src/sys/dev/usb/uchcom.c (revision f2da64fbbbf1b03f09f390ab01267c93dfd77c4c)
1 /*	$OpenBSD: uchcom.c,v 1.25 2016/09/02 09:14:59 mpi 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 	int			 sc_intr_size;
115 	struct usbd_pipe	*sc_intr_pipe;
116 	u_char			*sc_intr_buf;
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 uchcom_match(struct device *parent, void *match, void *aux)
235 {
236 	struct usb_attach_arg *uaa = aux;
237 
238 	if (uaa->iface == NULL)
239 		return UMATCH_NONE;
240 
241 	return (usb_lookup(uchcom_devs, uaa->vendor, uaa->product) != NULL ?
242 	    UMATCH_VENDOR_PRODUCT : UMATCH_NONE);
243 }
244 
245 void
246 uchcom_attach(struct device *parent, struct device *self, void *aux)
247 {
248 	struct uchcom_softc *sc = (struct uchcom_softc *)self;
249 	struct usb_attach_arg *uaa = aux;
250 	struct ucom_attach_args uca;
251 	struct usbd_device *dev = uaa->device;
252 	struct uchcom_endpoints endpoints;
253 
254         sc->sc_udev = dev;
255 	sc->sc_dtr = sc->sc_rts = -1;
256 	sc->sc_lsr = sc->sc_msr = 0;
257 
258 	DPRINTF(("\n\nuchcom attach: sc=%p\n", sc));
259 
260 	switch (uaa->release) {
261 	case UCHCOM_REV_CH340:
262 		printf("%s: CH340\n", sc->sc_dev.dv_xname);
263 		break;
264 	default:
265 		printf("%s: CH341\n", sc->sc_dev.dv_xname);
266 		break;
267 	}
268 
269 	if (uchcom_find_ifaces(sc, &sc->sc_iface))
270 		goto failed;
271 
272 	if (uchcom_find_endpoints(sc, &endpoints))
273 		goto failed;
274 
275 	sc->sc_intr_endpoint = endpoints.ep_intr;
276 	sc->sc_intr_size = endpoints.ep_intr_size;
277 
278 	/* setup ucom layer */
279 	uca.portno = UCOM_UNK_PORTNO;
280 	uca.bulkin = endpoints.ep_bulkin;
281 	uca.bulkout = endpoints.ep_bulkout;
282 	uca.ibufsize = UCHCOMIBUFSIZE;
283 	uca.obufsize = UCHCOMOBUFSIZE;
284 	uca.ibufsizepad = UCHCOMIBUFSIZE;
285 	uca.opkthdrlen = 0;
286 	uca.device = dev;
287 	uca.iface = sc->sc_iface;
288 	uca.methods = &uchcom_methods;
289 	uca.arg = sc;
290 	uca.info = NULL;
291 
292 	sc->sc_subdev = config_found_sm(self, &uca, ucomprint, ucomsubmatch);
293 
294 	return;
295 
296 failed:
297 	usbd_deactivate(sc->sc_udev);
298 }
299 
300 int
301 uchcom_detach(struct device *self, int flags)
302 {
303 	struct uchcom_softc *sc = (struct uchcom_softc *)self;
304 	int rv = 0;
305 
306 	DPRINTF(("uchcom_detach: sc=%p flags=%d\n", sc, flags));
307 
308 	uchcom_close_intr_pipe(sc);
309 
310 	if (sc->sc_subdev != NULL) {
311 		rv = config_detach(sc->sc_subdev, flags);
312 		sc->sc_subdev = NULL;
313 	}
314 
315 	return rv;
316 }
317 
318 int
319 uchcom_find_ifaces(struct uchcom_softc *sc, struct usbd_interface **riface)
320 {
321 	usbd_status err;
322 
323 	err = usbd_device2interface_handle(sc->sc_udev, UCHCOM_IFACE_INDEX,
324 					   riface);
325 	if (err) {
326 		printf("\n%s: failed to get interface: %s\n",
327 			sc->sc_dev.dv_xname, usbd_errstr(err));
328 		return -1;
329 	}
330 
331 	return 0;
332 }
333 
334 int
335 uchcom_find_endpoints(struct uchcom_softc *sc,
336     struct uchcom_endpoints *endpoints)
337 {
338 	int i, bin=-1, bout=-1, intr=-1, isize=0;
339 	usb_interface_descriptor_t *id;
340 	usb_endpoint_descriptor_t *ed;
341 
342 	id = usbd_get_interface_descriptor(sc->sc_iface);
343 
344 	for (i = 0; i < id->bNumEndpoints; i++) {
345 		ed = usbd_interface2endpoint_descriptor(sc->sc_iface, i);
346 		if (ed == NULL) {
347 			printf("%s: no endpoint descriptor for %d\n",
348 				sc->sc_dev.dv_xname, i);
349 			return -1;
350 		}
351 
352 		if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
353 		    UE_GET_XFERTYPE(ed->bmAttributes) == UE_INTERRUPT) {
354 			intr = ed->bEndpointAddress;
355 			isize = UGETW(ed->wMaxPacketSize);
356 		} else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
357 			   UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
358 			bin = ed->bEndpointAddress;
359 		} else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT &&
360 			   UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
361 			bout = ed->bEndpointAddress;
362 		}
363 	}
364 
365 	if (intr == -1 || bin == -1 || bout == -1) {
366 		if (intr == -1) {
367 			printf("%s: no interrupt end point\n",
368 			       sc->sc_dev.dv_xname);
369 		}
370 		if (bin == -1) {
371 			printf("%s: no data bulk in end point\n",
372 			       sc->sc_dev.dv_xname);
373 		}
374 		if (bout == -1) {
375 			printf("%s: no data bulk out end point\n",
376 			       sc->sc_dev.dv_xname);
377 		}
378 		return -1;
379 	}
380 	if (isize < UCHCOM_INTR_LEAST) {
381 		printf("%s: intr pipe is too short", sc->sc_dev.dv_xname);
382 		return -1;
383 	}
384 
385 	DPRINTF(("%s: bulkin=%d, bulkout=%d, intr=%d, isize=%d\n",
386 		 sc->sc_dev.dv_xname, bin, bout, intr, isize));
387 
388 	endpoints->ep_intr = intr;
389 	endpoints->ep_intr_size = isize;
390 	endpoints->ep_bulkin = bin;
391 	endpoints->ep_bulkout = bout;
392 
393 	return 0;
394 }
395 
396 
397 /* ----------------------------------------------------------------------
398  * low level i/o
399  */
400 
401 usbd_status
402 uchcom_generic_control_out(struct uchcom_softc *sc, uint8_t reqno,
403     uint16_t value, uint16_t index)
404 {
405 	usb_device_request_t req;
406 
407 	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
408 	req.bRequest = reqno;
409 	USETW(req.wValue, value);
410 	USETW(req.wIndex, index);
411 	USETW(req.wLength, 0);
412 
413 	return usbd_do_request(sc->sc_udev, &req, 0);
414 }
415 
416 usbd_status
417 uchcom_generic_control_in(struct uchcom_softc *sc, uint8_t reqno,
418     uint16_t value, uint16_t index, void *buf, int buflen, int *actlen)
419 {
420 	usb_device_request_t req;
421 
422 	req.bmRequestType = UT_READ_VENDOR_DEVICE;
423 	req.bRequest = reqno;
424 	USETW(req.wValue, value);
425 	USETW(req.wIndex, index);
426 	USETW(req.wLength, (uint16_t)buflen);
427 
428 	return usbd_do_request_flags(sc->sc_udev, &req, buf,
429 				     USBD_SHORT_XFER_OK, actlen,
430 				     USBD_DEFAULT_TIMEOUT);
431 }
432 
433 usbd_status
434 uchcom_write_reg(struct uchcom_softc *sc,
435     uint8_t reg1, uint8_t val1, uint8_t reg2, uint8_t val2)
436 {
437 	DPRINTF(("uchcom: write reg 0x%02X<-0x%02X, 0x%02X<-0x%02X\n",
438 		 (unsigned)reg1, (unsigned)val1,
439 		 (unsigned)reg2, (unsigned)val2));
440 	return uchcom_generic_control_out(
441 		sc, UCHCOM_REQ_WRITE_REG,
442 		reg1|((uint16_t)reg2<<8), val1|((uint16_t)val2<<8));
443 }
444 
445 usbd_status
446 uchcom_read_reg(struct uchcom_softc *sc,
447     uint8_t reg1, uint8_t *rval1, uint8_t reg2, uint8_t *rval2)
448 {
449 	uint8_t buf[UCHCOM_INPUT_BUF_SIZE];
450 	usbd_status err;
451 	int actin;
452 
453 	err = uchcom_generic_control_in(
454 		sc, UCHCOM_REQ_READ_REG,
455 		reg1|((uint16_t)reg2<<8), 0, buf, sizeof buf, &actin);
456 	if (err)
457 		return err;
458 
459 	DPRINTF(("uchcom: read reg 0x%02X->0x%02X, 0x%02X->0x%02X\n",
460 		 (unsigned)reg1, (unsigned)buf[0],
461 		 (unsigned)reg2, (unsigned)buf[1]));
462 
463 	if (rval1) *rval1 = buf[0];
464 	if (rval2) *rval2 = buf[1];
465 
466 	return USBD_NORMAL_COMPLETION;
467 }
468 
469 usbd_status
470 uchcom_get_version(struct uchcom_softc *sc, uint8_t *rver)
471 {
472 	uint8_t buf[UCHCOM_INPUT_BUF_SIZE];
473 	usbd_status err;
474 	int actin;
475 
476 	err = uchcom_generic_control_in(
477 		sc, UCHCOM_REQ_GET_VERSION, 0, 0, buf, sizeof buf, &actin);
478 	if (err)
479 		return err;
480 
481 	if (rver) *rver = buf[0];
482 
483 	return USBD_NORMAL_COMPLETION;
484 }
485 
486 usbd_status
487 uchcom_read_status(struct uchcom_softc *sc, uint8_t *rval)
488 {
489 	return uchcom_read_reg(sc, UCHCOM_REG_STAT1, rval, UCHCOM_REG_STAT2,
490 	    NULL);
491 }
492 
493 usbd_status
494 uchcom_set_dtrrts_10(struct uchcom_softc *sc, uint8_t val)
495 {
496 	return uchcom_write_reg(sc, UCHCOM_REG_STAT1, val, UCHCOM_REG_STAT1,
497 	    val);
498 }
499 
500 usbd_status
501 uchcom_set_dtrrts_20(struct uchcom_softc *sc, uint8_t val)
502 {
503 	return uchcom_generic_control_out(sc, UCHCOM_REQ_SET_DTRRTS, val, 0);
504 }
505 
506 
507 /* ----------------------------------------------------------------------
508  * middle layer
509  */
510 
511 int
512 uchcom_update_version(struct uchcom_softc *sc)
513 {
514 	usbd_status err;
515 
516 	err = uchcom_get_version(sc, &sc->sc_version);
517 	if (err) {
518 		printf("%s: cannot get version: %s\n",
519 		       sc->sc_dev.dv_xname, usbd_errstr(err));
520 		return EIO;
521 	}
522 
523 	return 0;
524 }
525 
526 void
527 uchcom_convert_status(struct uchcom_softc *sc, uint8_t cur)
528 {
529 	sc->sc_dtr = !(cur & UCHCOM_DTR_MASK);
530 	sc->sc_rts = !(cur & UCHCOM_RTS_MASK);
531 
532 	cur = ~cur & 0x0F;
533 	sc->sc_msr = (cur << 4) | ((sc->sc_msr >> 4) ^ cur);
534 }
535 
536 int
537 uchcom_update_status(struct uchcom_softc *sc)
538 {
539 	usbd_status err;
540 	uint8_t cur;
541 
542 	err = uchcom_read_status(sc, &cur);
543 	if (err) {
544 		printf("%s: cannot update status: %s\n",
545 		       sc->sc_dev.dv_xname, usbd_errstr(err));
546 		return EIO;
547 	}
548 	uchcom_convert_status(sc, cur);
549 
550 	return 0;
551 }
552 
553 
554 int
555 uchcom_set_dtrrts(struct uchcom_softc *sc, int dtr, int rts)
556 {
557 	usbd_status err;
558 	uint8_t val = 0;
559 
560 	if (dtr) val |= UCHCOM_DTR_MASK;
561 	if (rts) val |= UCHCOM_RTS_MASK;
562 
563 	if (sc->sc_version < UCHCOM_VER_20)
564 		err = uchcom_set_dtrrts_10(sc, ~val);
565 	else
566 		err = uchcom_set_dtrrts_20(sc, ~val);
567 
568 	if (err) {
569 		printf("%s: cannot set DTR/RTS: %s\n",
570 		       sc->sc_dev.dv_xname, usbd_errstr(err));
571 		return EIO;
572 	}
573 
574 	return 0;
575 }
576 
577 int
578 uchcom_set_break(struct uchcom_softc *sc, int onoff)
579 {
580 	usbd_status err;
581 	uint8_t brk1, brk2;
582 
583 	err = uchcom_read_reg(sc, UCHCOM_REG_BREAK1, &brk1, UCHCOM_REG_BREAK2,
584 	    &brk2);
585 	if (err)
586 		return EIO;
587 	if (onoff) {
588 		/* on - clear bits */
589 		brk1 &= ~UCHCOM_BRK1_MASK;
590 		brk2 &= ~UCHCOM_BRK2_MASK;
591 	} else {
592 		/* off - set bits */
593 		brk1 |= UCHCOM_BRK1_MASK;
594 		brk2 |= UCHCOM_BRK2_MASK;
595 	}
596 	err = uchcom_write_reg(sc, UCHCOM_REG_BREAK1, brk1, UCHCOM_REG_BREAK2,
597 	    brk2);
598 	if (err)
599 		return EIO;
600 
601 	return 0;
602 }
603 
604 int
605 uchcom_calc_divider_settings(struct uchcom_divider *dp, uint32_t rate)
606 {
607 	int i;
608 	const struct uchcom_divider_record *rp;
609 	uint32_t div, rem, mod;
610 
611 	/* find record */
612 	for (i=0; i<nitems(dividers); i++) {
613 		if (dividers[i].dvr_high >= rate &&
614 		    dividers[i].dvr_low <= rate) {
615 			rp = &dividers[i];
616 			goto found;
617 		}
618 	}
619 	return -1;
620 
621 found:
622 	dp->dv_prescaler = rp->dvr_divider.dv_prescaler;
623 	if (rp->dvr_base_clock == UCHCOM_BASE_UNKNOWN)
624 		dp->dv_div = rp->dvr_divider.dv_div;
625 	else {
626 		div = rp->dvr_base_clock / rate;
627 		rem = rp->dvr_base_clock % rate;
628 		if (div==0 || div>=0xFF)
629 			return -1;
630 		if ((rem<<1) >= rate)
631 			div += 1;
632 		dp->dv_div = (uint8_t)-div;
633 	}
634 
635 	mod = UCHCOM_BPS_MOD_BASE/rate + UCHCOM_BPS_MOD_BASE_OFS;
636 	mod = mod + mod/2;
637 
638 	dp->dv_mod = mod / 0x100;
639 
640 	return 0;
641 }
642 
643 int
644 uchcom_set_dte_rate(struct uchcom_softc *sc, uint32_t rate)
645 {
646 	usbd_status err;
647 	struct uchcom_divider dv;
648 
649 	if (uchcom_calc_divider_settings(&dv, rate))
650 		return EINVAL;
651 
652 	if ((err = uchcom_write_reg(sc,
653 			     UCHCOM_REG_BPS_PRE, dv.dv_prescaler,
654 			     UCHCOM_REG_BPS_DIV, dv.dv_div)) ||
655 	    (err = uchcom_write_reg(sc,
656 			     UCHCOM_REG_BPS_MOD, dv.dv_mod,
657 			     UCHCOM_REG_BPS_PAD, 0))) {
658 		printf("%s: cannot set DTE rate: %s\n",
659 		       sc->sc_dev.dv_xname, usbd_errstr(err));
660 		return EIO;
661 	}
662 
663 	return 0;
664 }
665 
666 int
667 uchcom_set_line_control(struct uchcom_softc *sc, tcflag_t cflag)
668 {
669 	/*
670 	 * XXX: it is difficult to handle the line control appropriately:
671 	 *   work as chip default - CS8, no parity, !CSTOPB
672 	 *   other modes are not supported.
673 	 */
674 
675 	switch (ISSET(cflag, CSIZE)) {
676 	case CS5:
677 	case CS6:
678 	case CS7:
679 		return EINVAL;
680 	case CS8:
681 		break;
682 	}
683 
684 	if (ISSET(cflag, PARENB) || ISSET(cflag, CSTOPB))
685 		return EINVAL;
686 
687 	return 0;
688 }
689 
690 int
691 uchcom_clear_chip(struct uchcom_softc *sc)
692 {
693 	usbd_status err;
694 
695 	DPRINTF(("%s: clear\n", sc->sc_dev.dv_xname));
696 	err = uchcom_generic_control_out(sc, UCHCOM_REQ_RESET, 0, 0);
697 	if (err) {
698 		printf("%s: cannot clear: %s\n",
699 		       sc->sc_dev.dv_xname, usbd_errstr(err));
700 		return EIO;
701 	}
702 
703 	return 0;
704 }
705 
706 int
707 uchcom_reset_chip(struct uchcom_softc *sc)
708 {
709 	usbd_status err;
710 
711 	DPRINTF(("%s: reset\n", sc->sc_dev.dv_xname));
712 
713 	err = uchcom_generic_control_out(sc, UCHCOM_REQ_RESET,
714 					 UCHCOM_RESET_VALUE,
715 					 UCHCOM_RESET_INDEX);
716 	if (err)
717 		goto failed;
718 
719 	return 0;
720 
721 failed:
722 	printf("%s: cannot reset: %s\n",
723 	       sc->sc_dev.dv_xname, usbd_errstr(err));
724 	return EIO;
725 }
726 
727 int
728 uchcom_setup_comm(struct uchcom_softc *sc)
729 {
730 	int ret;
731 
732 	ret = uchcom_update_version(sc);
733 	if (ret)
734 		return ret;
735 
736 	ret = uchcom_clear_chip(sc);
737 	if (ret)
738 		return ret;
739 
740 	ret = uchcom_set_dte_rate(sc, TTYDEF_SPEED);
741 	if (ret)
742 		return ret;
743 
744 	ret = uchcom_set_line_control(sc, CS8);
745 	if (ret)
746 		return ret;
747 
748 	ret = uchcom_update_status(sc);
749 	if (ret)
750 		return ret;
751 
752 	ret = uchcom_reset_chip(sc);
753 	if (ret)
754 		return ret;
755 
756 	ret = uchcom_set_dte_rate(sc, TTYDEF_SPEED); /* XXX */
757 	if (ret)
758 		return ret;
759 
760 	sc->sc_dtr = sc->sc_rts = 1;
761 	ret = uchcom_set_dtrrts(sc, sc->sc_dtr, sc->sc_rts);
762 	if (ret)
763 		return ret;
764 
765 	return 0;
766 }
767 
768 int
769 uchcom_setup_intr_pipe(struct uchcom_softc *sc)
770 {
771 	usbd_status err;
772 
773 	if (sc->sc_intr_endpoint != -1 && sc->sc_intr_pipe == NULL) {
774 		sc->sc_intr_buf = malloc(sc->sc_intr_size, M_USBDEV, M_WAITOK);
775 		err = usbd_open_pipe_intr(sc->sc_iface,
776 					  sc->sc_intr_endpoint,
777 					  USBD_SHORT_XFER_OK,
778 					  &sc->sc_intr_pipe, sc,
779 					  sc->sc_intr_buf,
780 					  sc->sc_intr_size,
781 					  uchcom_intr, USBD_DEFAULT_INTERVAL);
782 		if (err) {
783 			printf("%s: cannot open interrupt pipe: %s\n",
784 			       sc->sc_dev.dv_xname,
785 			       usbd_errstr(err));
786 			return EIO;
787 		}
788 	}
789 	return 0;
790 }
791 
792 void
793 uchcom_close_intr_pipe(struct uchcom_softc *sc)
794 {
795 	usbd_status err;
796 
797 	if (sc->sc_intr_pipe != NULL) {
798 		usbd_abort_pipe(sc->sc_intr_pipe);
799 		err = usbd_close_pipe(sc->sc_intr_pipe);
800 		if (err)
801 			printf("%s: close interrupt pipe failed: %s\n",
802 			       sc->sc_dev.dv_xname, usbd_errstr(err));
803 		free(sc->sc_intr_buf, M_USBDEV, 0);
804 		sc->sc_intr_pipe = NULL;
805 	}
806 }
807 
808 
809 /* ----------------------------------------------------------------------
810  * methods for ucom
811  */
812 void
813 uchcom_get_status(void *arg, int portno, u_char *rlsr, u_char *rmsr)
814 {
815 	struct uchcom_softc *sc = arg;
816 
817 	if (usbd_is_dying(sc->sc_udev))
818 		return;
819 
820 	*rlsr = sc->sc_lsr;
821 	*rmsr = sc->sc_msr;
822 }
823 
824 void
825 uchcom_set(void *arg, int portno, int reg, int onoff)
826 {
827 	struct uchcom_softc *sc = arg;
828 
829 	if (usbd_is_dying(sc->sc_udev))
830 		return;
831 
832 	switch (reg) {
833 	case UCOM_SET_DTR:
834 		sc->sc_dtr = !!onoff;
835 		uchcom_set_dtrrts(sc, sc->sc_dtr, sc->sc_rts);
836 		break;
837 	case UCOM_SET_RTS:
838 		sc->sc_rts = !!onoff;
839 		uchcom_set_dtrrts(sc, sc->sc_dtr, sc->sc_rts);
840 		break;
841 	case UCOM_SET_BREAK:
842 		uchcom_set_break(sc, onoff);
843 		break;
844 	}
845 }
846 
847 int
848 uchcom_param(void *arg, int portno, struct termios *t)
849 {
850 	struct uchcom_softc *sc = arg;
851 	int ret;
852 
853 	if (usbd_is_dying(sc->sc_udev))
854 		return 0;
855 
856 	ret = uchcom_set_line_control(sc, t->c_cflag);
857 	if (ret)
858 		return ret;
859 
860 	ret = uchcom_set_dte_rate(sc, t->c_ospeed);
861 	if (ret)
862 		return ret;
863 
864 	return 0;
865 }
866 
867 int
868 uchcom_open(void *arg, int portno)
869 {
870 	int ret;
871 	struct uchcom_softc *sc = arg;
872 
873 	if (usbd_is_dying(sc->sc_udev))
874 		return EIO;
875 
876 	ret = uchcom_setup_intr_pipe(sc);
877 	if (ret)
878 		return ret;
879 
880 	ret = uchcom_setup_comm(sc);
881 	if (ret)
882 		return ret;
883 
884 	return 0;
885 }
886 
887 void
888 uchcom_close(void *arg, int portno)
889 {
890 	struct uchcom_softc *sc = arg;
891 
892 	uchcom_close_intr_pipe(sc);
893 }
894 
895 
896 /* ----------------------------------------------------------------------
897  * callback when the modem status is changed.
898  */
899 void
900 uchcom_intr(struct usbd_xfer *xfer, void *priv, usbd_status status)
901 {
902 	struct uchcom_softc *sc = priv;
903 	u_char *buf = sc->sc_intr_buf;
904 
905 	if (usbd_is_dying(sc->sc_udev))
906 		return;
907 
908 	if (status != USBD_NORMAL_COMPLETION) {
909 		if (status == USBD_NOT_STARTED || status == USBD_CANCELLED)
910 			return;
911 
912 		DPRINTF(("%s: abnormal status: %s\n",
913 			 sc->sc_dev.dv_xname, usbd_errstr(status)));
914 		usbd_clear_endpoint_stall_async(sc->sc_intr_pipe);
915 		return;
916 	}
917 	DPRINTF(("%s: intr: 0x%02X 0x%02X 0x%02X 0x%02X "
918 		 "0x%02X 0x%02X 0x%02X 0x%02X\n",
919 		 sc->sc_dev.dv_xname,
920 		 (unsigned)buf[0], (unsigned)buf[1],
921 		 (unsigned)buf[2], (unsigned)buf[3],
922 		 (unsigned)buf[4], (unsigned)buf[5],
923 		 (unsigned)buf[6], (unsigned)buf[7]));
924 
925 	uchcom_convert_status(sc, buf[UCHCOM_INTR_STAT1]);
926 	ucom_status_change((struct ucom_softc *) sc->sc_subdev);
927 }
928