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