xref: /dragonfly/sys/bus/u4b/serial/uchcom.c (revision e7d467f4)
1 /*	$NetBSD: uchcom.c,v 1.1 2007/09/03 17:57:37 tshiozak Exp $	*/
2 
3 /*-
4  * Copyright (c) 2007, Takanori Watanabe
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28 
29 /*
30  * Copyright (c) 2007 The NetBSD Foundation, Inc.
31  * All rights reserved.
32  *
33  * This code is derived from software contributed to The NetBSD Foundation
34  * by Takuya SHIOZAKI (tshiozak@netbsd.org).
35  *
36  * Redistribution and use in source and binary forms, with or without
37  * modification, are permitted provided that the following conditions
38  * are met:
39  * 1. Redistributions of source code must retain the above copyright
40  *    notice, this list of conditions and the following disclaimer.
41  * 2. Redistributions in binary form must reproduce the above copyright
42  *    notice, this list of conditions and the following disclaimer in the
43  *    documentation and/or other materials provided with the distribution.
44  * 3. All advertising materials mentioning features or use of this software
45  *    must display the following acknowledgement:
46  *        This product includes software developed by the NetBSD
47  *        Foundation, Inc. and its contributors.
48  * 4. Neither the name of The NetBSD Foundation nor the names of its
49  *    contributors may be used to endorse or promote products derived
50  *    from this software without specific prior written permission.
51  *
52  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
53  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
54  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
55  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
56  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
57  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
58  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
59  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
60  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
61  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
62  * POSSIBILITY OF SUCH DAMAGE.
63  */
64 
65 /*
66  * Driver for WinChipHead CH341/340, the worst USB-serial chip in the
67  * world.
68  */
69 
70 #include <sys/stdint.h>
71 #include <sys/param.h>
72 #include <sys/queue.h>
73 #include <sys/types.h>
74 #include <sys/systm.h>
75 #include <sys/kernel.h>
76 #include <sys/bus.h>
77 #include <sys/module.h>
78 #include <sys/lock.h>
79 #include <sys/condvar.h>
80 #include <sys/sysctl.h>
81 #include <sys/unistd.h>
82 #include <sys/callout.h>
83 #include <sys/malloc.h>
84 #include <sys/priv.h>
85 
86 #include <bus/u4b/usb.h>
87 #include <bus/u4b/usbdi.h>
88 #include <bus/u4b/usbdi_util.h>
89 #include <bus/u4b/usbdevs.h>
90 
91 #define	USB_DEBUG_VAR uchcom_debug
92 #include <bus/u4b/usb_debug.h>
93 #include <bus/u4b/usb_process.h>
94 
95 #include <bus/u4b/serial/usb_serial.h>
96 
97 #ifdef USB_DEBUG
98 static int uchcom_debug = 0;
99 
100 static SYSCTL_NODE(_hw_usb, OID_AUTO, uchcom, CTLFLAG_RW, 0, "USB uchcom");
101 SYSCTL_INT(_hw_usb_uchcom, OID_AUTO, debug, CTLFLAG_RW,
102     &uchcom_debug, 0, "uchcom debug level");
103 #endif
104 
105 #define	UCHCOM_IFACE_INDEX	0
106 #define	UCHCOM_CONFIG_INDEX	0
107 
108 #define	UCHCOM_REV_CH340	0x0250
109 #define	UCHCOM_INPUT_BUF_SIZE	8
110 
111 #define	UCHCOM_REQ_GET_VERSION	0x5F
112 #define	UCHCOM_REQ_READ_REG	0x95
113 #define	UCHCOM_REQ_WRITE_REG	0x9A
114 #define	UCHCOM_REQ_RESET	0xA1
115 #define	UCHCOM_REQ_SET_DTRRTS	0xA4
116 
117 #define	UCHCOM_REG_STAT1	0x06
118 #define	UCHCOM_REG_STAT2	0x07
119 #define	UCHCOM_REG_BPS_PRE	0x12
120 #define	UCHCOM_REG_BPS_DIV	0x13
121 #define	UCHCOM_REG_BPS_MOD	0x14
122 #define	UCHCOM_REG_BPS_PAD	0x0F
123 #define	UCHCOM_REG_BREAK1	0x05
124 #define	UCHCOM_REG_BREAK2	0x18
125 #define	UCHCOM_REG_LCR1		0x18
126 #define	UCHCOM_REG_LCR2		0x25
127 
128 #define	UCHCOM_VER_20		0x20
129 
130 #define	UCHCOM_BASE_UNKNOWN	0
131 #define	UCHCOM_BPS_MOD_BASE	20000000
132 #define	UCHCOM_BPS_MOD_BASE_OFS	1100
133 
134 #define	UCHCOM_DTR_MASK		0x20
135 #define	UCHCOM_RTS_MASK		0x40
136 
137 #define	UCHCOM_BRK1_MASK	0x01
138 #define	UCHCOM_BRK2_MASK	0x40
139 
140 #define	UCHCOM_LCR1_MASK	0xAF
141 #define	UCHCOM_LCR2_MASK	0x07
142 #define	UCHCOM_LCR1_PARENB	0x80
143 #define	UCHCOM_LCR2_PAREVEN	0x07
144 #define	UCHCOM_LCR2_PARODD	0x06
145 #define	UCHCOM_LCR2_PARMARK	0x05
146 #define	UCHCOM_LCR2_PARSPACE	0x04
147 
148 #define	UCHCOM_INTR_STAT1	0x02
149 #define	UCHCOM_INTR_STAT2	0x03
150 #define	UCHCOM_INTR_LEAST	4
151 
152 #define	UCHCOM_BULK_BUF_SIZE 1024	/* bytes */
153 
154 enum {
155 	UCHCOM_BULK_DT_WR,
156 	UCHCOM_BULK_DT_RD,
157 	UCHCOM_INTR_DT_RD,
158 	UCHCOM_N_TRANSFER,
159 };
160 
161 struct uchcom_softc {
162 	struct ucom_super_softc sc_super_ucom;
163 	struct ucom_softc sc_ucom;
164 
165 	struct usb_xfer *sc_xfer[UCHCOM_N_TRANSFER];
166 	struct usb_device *sc_udev;
167 	struct lock sc_lock;
168 
169 	uint8_t	sc_dtr;			/* local copy */
170 	uint8_t	sc_rts;			/* local copy */
171 	uint8_t	sc_version;
172 	uint8_t	sc_msr;
173 	uint8_t	sc_lsr;			/* local status register */
174 };
175 
176 struct uchcom_divider {
177 	uint8_t	dv_prescaler;
178 	uint8_t	dv_div;
179 	uint8_t	dv_mod;
180 };
181 
182 struct uchcom_divider_record {
183 	uint32_t dvr_high;
184 	uint32_t dvr_low;
185 	uint32_t dvr_base_clock;
186 	struct uchcom_divider dvr_divider;
187 };
188 
189 static const struct uchcom_divider_record dividers[] =
190 {
191 	{307200, 307200, UCHCOM_BASE_UNKNOWN, {7, 0xD9, 0}},
192 	{921600, 921600, UCHCOM_BASE_UNKNOWN, {7, 0xF3, 0}},
193 	{2999999, 23530, 6000000, {3, 0, 0}},
194 	{23529, 2942, 750000, {2, 0, 0}},
195 	{2941, 368, 93750, {1, 0, 0}},
196 	{367, 1, 11719, {0, 0, 0}},
197 };
198 
199 #define	NUM_DIVIDERS	(sizeof (dividers) / sizeof (dividers[0]))
200 
201 static const STRUCT_USB_HOST_ID uchcom_devs[] = {
202 	{USB_VPI(USB_VENDOR_WCH, USB_PRODUCT_WCH_CH341SER, 0)},
203 	{USB_VPI(USB_VENDOR_WCH2, USB_PRODUCT_WCH2_CH341SER, 0)},
204 };
205 
206 /* protypes */
207 
208 static int	uchcom_pre_param(struct ucom_softc *, struct termios *);
209 static void	uchcom_cfg_get_status(struct ucom_softc *, uint8_t *,
210 		    uint8_t *);
211 static void	uchcom_cfg_open(struct ucom_softc *ucom);
212 static void	uchcom_cfg_param(struct ucom_softc *, struct termios *);
213 static void	uchcom_cfg_set_break(struct ucom_softc *, uint8_t);
214 static void	uchcom_cfg_set_dtr(struct ucom_softc *, uint8_t);
215 static void	uchcom_cfg_set_rts(struct ucom_softc *, uint8_t);
216 static void	uchcom_start_read(struct ucom_softc *);
217 static void	uchcom_start_write(struct ucom_softc *);
218 static void	uchcom_stop_read(struct ucom_softc *);
219 static void	uchcom_stop_write(struct ucom_softc *);
220 static void	uchcom_update_version(struct uchcom_softc *);
221 static void	uchcom_convert_status(struct uchcom_softc *, uint8_t);
222 static void	uchcom_update_status(struct uchcom_softc *);
223 static void	uchcom_set_dtr_rts(struct uchcom_softc *);
224 static int	uchcom_calc_divider_settings(struct uchcom_divider *, uint32_t);
225 static void	uchcom_set_baudrate(struct uchcom_softc *, uint32_t);
226 static void	uchcom_poll(struct ucom_softc *ucom);
227 
228 static device_probe_t uchcom_probe;
229 static device_attach_t uchcom_attach;
230 static device_detach_t uchcom_detach;
231 
232 static usb_callback_t uchcom_intr_callback;
233 static usb_callback_t uchcom_write_callback;
234 static usb_callback_t uchcom_read_callback;
235 
236 static const struct usb_config uchcom_config_data[UCHCOM_N_TRANSFER] = {
237 
238 	[UCHCOM_BULK_DT_WR] = {
239 		.type = UE_BULK,
240 		.endpoint = UE_ADDR_ANY,
241 		.direction = UE_DIR_OUT,
242 		.bufsize = UCHCOM_BULK_BUF_SIZE,
243 		.flags = {.pipe_bof = 1,.force_short_xfer = 1,},
244 		.callback = &uchcom_write_callback,
245 	},
246 
247 	[UCHCOM_BULK_DT_RD] = {
248 		.type = UE_BULK,
249 		.endpoint = UE_ADDR_ANY,
250 		.direction = UE_DIR_IN,
251 		.bufsize = UCHCOM_BULK_BUF_SIZE,
252 		.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
253 		.callback = &uchcom_read_callback,
254 	},
255 
256 	[UCHCOM_INTR_DT_RD] = {
257 		.type = UE_INTERRUPT,
258 		.endpoint = UE_ADDR_ANY,
259 		.direction = UE_DIR_IN,
260 		.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
261 		.bufsize = 0,	/* use wMaxPacketSize */
262 		.callback = &uchcom_intr_callback,
263 	},
264 };
265 
266 static struct ucom_callback uchcom_callback = {
267 	.ucom_cfg_get_status = &uchcom_cfg_get_status,
268 	.ucom_cfg_set_dtr = &uchcom_cfg_set_dtr,
269 	.ucom_cfg_set_rts = &uchcom_cfg_set_rts,
270 	.ucom_cfg_set_break = &uchcom_cfg_set_break,
271 	.ucom_cfg_open = &uchcom_cfg_open,
272 	.ucom_cfg_param = &uchcom_cfg_param,
273 	.ucom_pre_param = &uchcom_pre_param,
274 	.ucom_start_read = &uchcom_start_read,
275 	.ucom_stop_read = &uchcom_stop_read,
276 	.ucom_start_write = &uchcom_start_write,
277 	.ucom_stop_write = &uchcom_stop_write,
278 	.ucom_poll = &uchcom_poll,
279 };
280 
281 /* ----------------------------------------------------------------------
282  * driver entry points
283  */
284 
285 static int
286 uchcom_probe(device_t dev)
287 {
288 	struct usb_attach_arg *uaa = device_get_ivars(dev);
289 
290 	DPRINTFN(11, "\n");
291 
292 	if (uaa->usb_mode != USB_MODE_HOST) {
293 		return (ENXIO);
294 	}
295 	if (uaa->info.bConfigIndex != UCHCOM_CONFIG_INDEX) {
296 		return (ENXIO);
297 	}
298 	if (uaa->info.bIfaceIndex != UCHCOM_IFACE_INDEX) {
299 		return (ENXIO);
300 	}
301 	return (usbd_lookup_id_by_uaa(uchcom_devs, sizeof(uchcom_devs), uaa));
302 }
303 
304 static int
305 uchcom_attach(device_t dev)
306 {
307 	struct uchcom_softc *sc = device_get_softc(dev);
308 	struct usb_attach_arg *uaa = device_get_ivars(dev);
309 	int error;
310 	uint8_t iface_index;
311 
312 	DPRINTFN(11, "\n");
313 
314 	device_set_usb_desc(dev);
315 	lockinit(&sc->sc_lock, "uchcom", 0, LK_CANRECURSE);
316 
317 	sc->sc_udev = uaa->device;
318 
319 	switch (uaa->info.bcdDevice) {
320 	case UCHCOM_REV_CH340:
321 		device_printf(dev, "CH340 detected\n");
322 		break;
323 	default:
324 		device_printf(dev, "CH341 detected\n");
325 		break;
326 	}
327 
328 	iface_index = UCHCOM_IFACE_INDEX;
329 	error = usbd_transfer_setup(uaa->device,
330 	    &iface_index, sc->sc_xfer, uchcom_config_data,
331 	    UCHCOM_N_TRANSFER, sc, &sc->sc_lock);
332 
333 	if (error) {
334 		DPRINTF("one or more missing USB endpoints, "
335 		    "error=%s\n", usbd_errstr(error));
336 		goto detach;
337 	}
338 
339 	/* clear stall at first run */
340 	lockmgr(&sc->sc_lock, LK_EXCLUSIVE);
341 	usbd_xfer_set_stall(sc->sc_xfer[UCHCOM_BULK_DT_WR]);
342 	usbd_xfer_set_stall(sc->sc_xfer[UCHCOM_BULK_DT_RD]);
343 	lockmgr(&sc->sc_lock, LK_RELEASE);
344 
345 	error = ucom_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc,
346 	    &uchcom_callback, &sc->sc_lock);
347 	if (error) {
348 		goto detach;
349 	}
350 	ucom_set_pnpinfo_usb(&sc->sc_super_ucom, dev);
351 
352 	return (0);
353 
354 detach:
355 	uchcom_detach(dev);
356 	return (ENXIO);
357 }
358 
359 static int
360 uchcom_detach(device_t dev)
361 {
362 	struct uchcom_softc *sc = device_get_softc(dev);
363 
364 	DPRINTFN(11, "\n");
365 
366 	ucom_detach(&sc->sc_super_ucom, &sc->sc_ucom);
367 	usbd_transfer_unsetup(sc->sc_xfer, UCHCOM_N_TRANSFER);
368 	lockuninit(&sc->sc_lock);
369 
370 	return (0);
371 }
372 
373 /* ----------------------------------------------------------------------
374  * low level i/o
375  */
376 
377 static void
378 uchcom_ctrl_write(struct uchcom_softc *sc, uint8_t reqno,
379     uint16_t value, uint16_t index)
380 {
381 	struct usb_device_request req;
382 
383 	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
384 	req.bRequest = reqno;
385 	USETW(req.wValue, value);
386 	USETW(req.wIndex, index);
387 	USETW(req.wLength, 0);
388 
389 	ucom_cfg_do_request(sc->sc_udev,
390 	    &sc->sc_ucom, &req, NULL, 0, 1000);
391 }
392 
393 static void
394 uchcom_ctrl_read(struct uchcom_softc *sc, uint8_t reqno,
395     uint16_t value, uint16_t index, void *buf, uint16_t buflen)
396 {
397 	struct usb_device_request req;
398 
399 	req.bmRequestType = UT_READ_VENDOR_DEVICE;
400 	req.bRequest = reqno;
401 	USETW(req.wValue, value);
402 	USETW(req.wIndex, index);
403 	USETW(req.wLength, buflen);
404 
405 	ucom_cfg_do_request(sc->sc_udev,
406 	    &sc->sc_ucom, &req, buf, USB_SHORT_XFER_OK, 1000);
407 }
408 
409 static void
410 uchcom_write_reg(struct uchcom_softc *sc,
411     uint8_t reg1, uint8_t val1, uint8_t reg2, uint8_t val2)
412 {
413 	DPRINTF("0x%02X<-0x%02X, 0x%02X<-0x%02X\n",
414 	    (unsigned)reg1, (unsigned)val1,
415 	    (unsigned)reg2, (unsigned)val2);
416 	uchcom_ctrl_write(
417 	    sc, UCHCOM_REQ_WRITE_REG,
418 	    reg1 | ((uint16_t)reg2 << 8), val1 | ((uint16_t)val2 << 8));
419 }
420 
421 static void
422 uchcom_read_reg(struct uchcom_softc *sc,
423     uint8_t reg1, uint8_t *rval1, uint8_t reg2, uint8_t *rval2)
424 {
425 	uint8_t buf[UCHCOM_INPUT_BUF_SIZE];
426 
427 	uchcom_ctrl_read(
428 	    sc, UCHCOM_REQ_READ_REG,
429 	    reg1 | ((uint16_t)reg2 << 8), 0, buf, sizeof(buf));
430 
431 	DPRINTF("0x%02X->0x%02X, 0x%02X->0x%02X\n",
432 	    (unsigned)reg1, (unsigned)buf[0],
433 	    (unsigned)reg2, (unsigned)buf[1]);
434 
435 	if (rval1)
436 		*rval1 = buf[0];
437 	if (rval2)
438 		*rval2 = buf[1];
439 }
440 
441 static void
442 uchcom_get_version(struct uchcom_softc *sc, uint8_t *rver)
443 {
444 	uint8_t buf[UCHCOM_INPUT_BUF_SIZE];
445 
446 	uchcom_ctrl_read(sc, UCHCOM_REQ_GET_VERSION, 0, 0, buf, sizeof(buf));
447 
448 	if (rver)
449 		*rver = buf[0];
450 }
451 
452 static void
453 uchcom_get_status(struct uchcom_softc *sc, uint8_t *rval)
454 {
455 	uchcom_read_reg(sc, UCHCOM_REG_STAT1, rval, UCHCOM_REG_STAT2, NULL);
456 }
457 
458 static void
459 uchcom_set_dtr_rts_10(struct uchcom_softc *sc, uint8_t val)
460 {
461 	uchcom_write_reg(sc, UCHCOM_REG_STAT1, val, UCHCOM_REG_STAT1, val);
462 }
463 
464 static void
465 uchcom_set_dtr_rts_20(struct uchcom_softc *sc, uint8_t val)
466 {
467 	uchcom_ctrl_write(sc, UCHCOM_REQ_SET_DTRRTS, val, 0);
468 }
469 
470 
471 /* ----------------------------------------------------------------------
472  * middle layer
473  */
474 
475 static void
476 uchcom_update_version(struct uchcom_softc *sc)
477 {
478 	uchcom_get_version(sc, &sc->sc_version);
479 }
480 
481 static void
482 uchcom_convert_status(struct uchcom_softc *sc, uint8_t cur)
483 {
484 	sc->sc_dtr = !(cur & UCHCOM_DTR_MASK);
485 	sc->sc_rts = !(cur & UCHCOM_RTS_MASK);
486 
487 	cur = ~cur & 0x0F;
488 	sc->sc_msr = (cur << 4) | ((sc->sc_msr >> 4) ^ cur);
489 }
490 
491 static void
492 uchcom_update_status(struct uchcom_softc *sc)
493 {
494 	uint8_t cur;
495 
496 	uchcom_get_status(sc, &cur);
497 	uchcom_convert_status(sc, cur);
498 }
499 
500 
501 static void
502 uchcom_set_dtr_rts(struct uchcom_softc *sc)
503 {
504 	uint8_t val = 0;
505 
506 	if (sc->sc_dtr)
507 		val |= UCHCOM_DTR_MASK;
508 	if (sc->sc_rts)
509 		val |= UCHCOM_RTS_MASK;
510 
511 	if (sc->sc_version < UCHCOM_VER_20)
512 		uchcom_set_dtr_rts_10(sc, ~val);
513 	else
514 		uchcom_set_dtr_rts_20(sc, ~val);
515 }
516 
517 static void
518 uchcom_cfg_set_break(struct ucom_softc *ucom, uint8_t onoff)
519 {
520 	struct uchcom_softc *sc = ucom->sc_parent;
521 	uint8_t brk1;
522 	uint8_t brk2;
523 
524 	uchcom_read_reg(sc, UCHCOM_REG_BREAK1, &brk1, UCHCOM_REG_BREAK2, &brk2);
525 	if (onoff) {
526 		/* on - clear bits */
527 		brk1 &= ~UCHCOM_BRK1_MASK;
528 		brk2 &= ~UCHCOM_BRK2_MASK;
529 	} else {
530 		/* off - set bits */
531 		brk1 |= UCHCOM_BRK1_MASK;
532 		brk2 |= UCHCOM_BRK2_MASK;
533 	}
534 	uchcom_write_reg(sc, UCHCOM_REG_BREAK1, brk1, UCHCOM_REG_BREAK2, brk2);
535 }
536 
537 static int
538 uchcom_calc_divider_settings(struct uchcom_divider *dp, uint32_t rate)
539 {
540 	const struct uchcom_divider_record *rp;
541 	uint32_t div;
542 	uint32_t rem;
543 	uint32_t mod;
544 	uint8_t i;
545 
546 	/* find record */
547 	for (i = 0; i != NUM_DIVIDERS; i++) {
548 		if (dividers[i].dvr_high >= rate &&
549 		    dividers[i].dvr_low <= rate) {
550 			rp = &dividers[i];
551 			goto found;
552 		}
553 	}
554 	return (-1);
555 
556 found:
557 	dp->dv_prescaler = rp->dvr_divider.dv_prescaler;
558 	if (rp->dvr_base_clock == UCHCOM_BASE_UNKNOWN)
559 		dp->dv_div = rp->dvr_divider.dv_div;
560 	else {
561 		div = rp->dvr_base_clock / rate;
562 		rem = rp->dvr_base_clock % rate;
563 		if (div == 0 || div >= 0xFF)
564 			return (-1);
565 		if ((rem << 1) >= rate)
566 			div += 1;
567 		dp->dv_div = (uint8_t)-div;
568 	}
569 
570 	mod = (UCHCOM_BPS_MOD_BASE / rate) + UCHCOM_BPS_MOD_BASE_OFS;
571 	mod = mod + (mod / 2);
572 
573 	dp->dv_mod = (mod + 0xFF) / 0x100;
574 
575 	return (0);
576 }
577 
578 static void
579 uchcom_set_baudrate(struct uchcom_softc *sc, uint32_t rate)
580 {
581 	struct uchcom_divider dv;
582 
583 	if (uchcom_calc_divider_settings(&dv, rate))
584 		return;
585 
586 	uchcom_write_reg(sc,
587 	    UCHCOM_REG_BPS_PRE, dv.dv_prescaler,
588 	    UCHCOM_REG_BPS_DIV, dv.dv_div);
589 	uchcom_write_reg(sc,
590 	    UCHCOM_REG_BPS_MOD, dv.dv_mod,
591 	    UCHCOM_REG_BPS_PAD, 0);
592 }
593 
594 /* ----------------------------------------------------------------------
595  * methods for ucom
596  */
597 static void
598 uchcom_cfg_get_status(struct ucom_softc *ucom, uint8_t *lsr, uint8_t *msr)
599 {
600 	struct uchcom_softc *sc = ucom->sc_parent;
601 
602 	DPRINTF("\n");
603 
604 	*lsr = sc->sc_lsr;
605 	*msr = sc->sc_msr;
606 }
607 
608 static void
609 uchcom_cfg_set_dtr(struct ucom_softc *ucom, uint8_t onoff)
610 {
611 	struct uchcom_softc *sc = ucom->sc_parent;
612 
613 	DPRINTF("onoff = %d\n", onoff);
614 
615 	sc->sc_dtr = onoff;
616 	uchcom_set_dtr_rts(sc);
617 }
618 
619 static void
620 uchcom_cfg_set_rts(struct ucom_softc *ucom, uint8_t onoff)
621 {
622 	struct uchcom_softc *sc = ucom->sc_parent;
623 
624 	DPRINTF("onoff = %d\n", onoff);
625 
626 	sc->sc_rts = onoff;
627 	uchcom_set_dtr_rts(sc);
628 }
629 
630 static void
631 uchcom_cfg_open(struct ucom_softc *ucom)
632 {
633 	struct uchcom_softc *sc = ucom->sc_parent;
634 
635 	DPRINTF("\n");
636 
637 	uchcom_update_version(sc);
638 	uchcom_update_status(sc);
639 }
640 
641 static int
642 uchcom_pre_param(struct ucom_softc *ucom, struct termios *t)
643 {
644 	struct uchcom_divider dv;
645 
646 	switch (t->c_cflag & CSIZE) {
647 	case CS8:
648 		break;
649 	default:
650 		return (EIO);
651 	}
652 
653 	if (uchcom_calc_divider_settings(&dv, t->c_ospeed)) {
654 		return (EIO);
655 	}
656 	return (0);			/* success */
657 }
658 
659 static void
660 uchcom_cfg_param(struct ucom_softc *ucom, struct termios *t)
661 {
662 	struct uchcom_softc *sc = ucom->sc_parent;
663 
664 	uchcom_get_version(sc, 0);
665 	uchcom_ctrl_write(sc, UCHCOM_REQ_RESET, 0, 0);
666 	uchcom_set_baudrate(sc, t->c_ospeed);
667 	uchcom_read_reg(sc, 0x18, 0, 0x25, 0);
668 	uchcom_write_reg(sc, 0x18, 0x50, 0x25, 0x00);
669 	uchcom_update_status(sc);
670 	uchcom_ctrl_write(sc, UCHCOM_REQ_RESET, 0x501f, 0xd90a);
671 	uchcom_set_baudrate(sc, t->c_ospeed);
672 	uchcom_set_dtr_rts(sc);
673 	uchcom_update_status(sc);
674 }
675 
676 static void
677 uchcom_start_read(struct ucom_softc *ucom)
678 {
679 	struct uchcom_softc *sc = ucom->sc_parent;
680 
681 	/* start interrupt endpoint */
682 	usbd_transfer_start(sc->sc_xfer[UCHCOM_INTR_DT_RD]);
683 
684 	/* start read endpoint */
685 	usbd_transfer_start(sc->sc_xfer[UCHCOM_BULK_DT_RD]);
686 }
687 
688 static void
689 uchcom_stop_read(struct ucom_softc *ucom)
690 {
691 	struct uchcom_softc *sc = ucom->sc_parent;
692 
693 	/* stop interrupt endpoint */
694 	usbd_transfer_stop(sc->sc_xfer[UCHCOM_INTR_DT_RD]);
695 
696 	/* stop read endpoint */
697 	usbd_transfer_stop(sc->sc_xfer[UCHCOM_BULK_DT_RD]);
698 }
699 
700 static void
701 uchcom_start_write(struct ucom_softc *ucom)
702 {
703 	struct uchcom_softc *sc = ucom->sc_parent;
704 
705 	usbd_transfer_start(sc->sc_xfer[UCHCOM_BULK_DT_WR]);
706 }
707 
708 static void
709 uchcom_stop_write(struct ucom_softc *ucom)
710 {
711 	struct uchcom_softc *sc = ucom->sc_parent;
712 
713 	usbd_transfer_stop(sc->sc_xfer[UCHCOM_BULK_DT_WR]);
714 }
715 
716 /* ----------------------------------------------------------------------
717  * callback when the modem status is changed.
718  */
719 static void
720 uchcom_intr_callback(struct usb_xfer *xfer, usb_error_t error)
721 {
722 	struct uchcom_softc *sc = usbd_xfer_softc(xfer);
723 	struct usb_page_cache *pc;
724 	uint8_t buf[UCHCOM_INTR_LEAST];
725 	int actlen;
726 
727 	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
728 
729 	switch (USB_GET_STATE(xfer)) {
730 	case USB_ST_TRANSFERRED:
731 
732 		DPRINTF("actlen = %u\n", actlen);
733 
734 		if (actlen >= UCHCOM_INTR_LEAST) {
735 			pc = usbd_xfer_get_frame(xfer, 0);
736 			usbd_copy_out(pc, 0, buf, UCHCOM_INTR_LEAST);
737 
738 			DPRINTF("data = 0x%02X 0x%02X 0x%02X 0x%02X\n",
739 			    (unsigned)buf[0], (unsigned)buf[1],
740 			    (unsigned)buf[2], (unsigned)buf[3]);
741 
742 			uchcom_convert_status(sc, buf[UCHCOM_INTR_STAT1]);
743 			ucom_status_change(&sc->sc_ucom);
744 		}
745 	case USB_ST_SETUP:
746 tr_setup:
747 		usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
748 		usbd_transfer_submit(xfer);
749 		break;
750 
751 	default:			/* Error */
752 		if (error != USB_ERR_CANCELLED) {
753 			/* try to clear stall first */
754 			usbd_xfer_set_stall(xfer);
755 			goto tr_setup;
756 		}
757 		break;
758 	}
759 }
760 
761 static void
762 uchcom_write_callback(struct usb_xfer *xfer, usb_error_t error)
763 {
764 	struct uchcom_softc *sc = usbd_xfer_softc(xfer);
765 	struct usb_page_cache *pc;
766 	uint32_t actlen;
767 
768 	switch (USB_GET_STATE(xfer)) {
769 	case USB_ST_SETUP:
770 	case USB_ST_TRANSFERRED:
771 tr_setup:
772 		pc = usbd_xfer_get_frame(xfer, 0);
773 		if (ucom_get_data(&sc->sc_ucom, pc, 0,
774 		    usbd_xfer_max_len(xfer), &actlen)) {
775 
776 			DPRINTF("actlen = %d\n", actlen);
777 
778 			usbd_xfer_set_frame_len(xfer, 0, actlen);
779 			usbd_transfer_submit(xfer);
780 		}
781 		break;
782 
783 	default:			/* Error */
784 		if (error != USB_ERR_CANCELLED) {
785 			/* try to clear stall first */
786 			usbd_xfer_set_stall(xfer);
787 			goto tr_setup;
788 		}
789 		break;
790 	}
791 }
792 
793 static void
794 uchcom_read_callback(struct usb_xfer *xfer, usb_error_t error)
795 {
796 	struct uchcom_softc *sc = usbd_xfer_softc(xfer);
797 	struct usb_page_cache *pc;
798 	int actlen;
799 
800 	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
801 
802 	switch (USB_GET_STATE(xfer)) {
803 	case USB_ST_TRANSFERRED:
804 
805 		if (actlen > 0) {
806 			pc = usbd_xfer_get_frame(xfer, 0);
807 			ucom_put_data(&sc->sc_ucom, pc, 0, actlen);
808 		}
809 
810 	case USB_ST_SETUP:
811 tr_setup:
812 		usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
813 		usbd_transfer_submit(xfer);
814 		break;
815 
816 	default:			/* Error */
817 		if (error != USB_ERR_CANCELLED) {
818 			/* try to clear stall first */
819 			usbd_xfer_set_stall(xfer);
820 			goto tr_setup;
821 		}
822 		break;
823 	}
824 }
825 
826 static void
827 uchcom_poll(struct ucom_softc *ucom)
828 {
829 	struct uchcom_softc *sc = ucom->sc_parent;
830 	usbd_transfer_poll(sc->sc_xfer, UCHCOM_N_TRANSFER);
831 }
832 
833 static device_method_t uchcom_methods[] = {
834 	/* Device interface */
835 	DEVMETHOD(device_probe, uchcom_probe),
836 	DEVMETHOD(device_attach, uchcom_attach),
837 	DEVMETHOD(device_detach, uchcom_detach),
838 
839 	DEVMETHOD_END
840 };
841 
842 static driver_t uchcom_driver = {
843 	"ucom",
844 	uchcom_methods,
845 	sizeof(struct uchcom_softc)
846 };
847 
848 static devclass_t uchcom_devclass;
849 
850 DRIVER_MODULE(uchcom, uhub, uchcom_driver, uchcom_devclass, NULL, 0);
851 MODULE_DEPEND(uchcom, ucom, 1, 1, 1);
852 MODULE_DEPEND(uchcom, usb, 1, 1, 1);
853 MODULE_VERSION(uchcom, 1);
854