xref: /openbsd/sys/dev/usb/uchcom.c (revision 404b540a)
1 /*	$OpenBSD: uchcom.c,v 1.9 2009/10/13 19:33:17 pirofti 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 *, int);
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, int act)
344 {
345 	struct uchcom_softc *sc = (struct uchcom_softc *)self;
346 	int rv = 0;
347 
348 	switch (act) {
349 	case DVACT_ACTIVATE:
350 		break;
351 	case DVACT_DEACTIVATE:
352 		uchcom_close_intr_pipe(sc);
353 		sc->sc_dying = 1;
354 		if (sc->sc_subdev != NULL)
355 			rv = config_deactivate(sc->sc_subdev);
356 		break;
357 	}
358 	return rv;
359 }
360 
361 int
362 uchcom_set_config(struct uchcom_softc *sc)
363 {
364 	usbd_status err;
365 
366 	err = usbd_set_config_index(sc->sc_udev, UCHCOM_CONFIG_INDEX, 1);
367 	if (err) {
368 		printf("%s: failed to set configuration: %s\n",
369 		       sc->sc_dev.dv_xname, usbd_errstr(err));
370 		return -1;
371 	}
372 
373 	return 0;
374 }
375 
376 int
377 uchcom_find_ifaces(struct uchcom_softc *sc, usbd_interface_handle *riface)
378 {
379 	usbd_status err;
380 
381 	err = usbd_device2interface_handle(sc->sc_udev, UCHCOM_IFACE_INDEX,
382 					   riface);
383 	if (err) {
384 		printf("\n%s: failed to get interface: %s\n",
385 			sc->sc_dev.dv_xname, usbd_errstr(err));
386 		return -1;
387 	}
388 
389 	return 0;
390 }
391 
392 int
393 uchcom_find_endpoints(struct uchcom_softc *sc,
394     struct uchcom_endpoints *endpoints)
395 {
396 	int i, bin=-1, bout=-1, intr=-1, isize=0;
397 	usb_interface_descriptor_t *id;
398 	usb_endpoint_descriptor_t *ed;
399 
400 	id = usbd_get_interface_descriptor(sc->sc_iface);
401 
402 	for (i = 0; i < id->bNumEndpoints; i++) {
403 		ed = usbd_interface2endpoint_descriptor(sc->sc_iface, i);
404 		if (ed == NULL) {
405 			printf("%s: no endpoint descriptor for %d\n",
406 				sc->sc_dev.dv_xname, i);
407 			return -1;
408 		}
409 
410 		if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
411 		    UE_GET_XFERTYPE(ed->bmAttributes) == UE_INTERRUPT) {
412 			intr = ed->bEndpointAddress;
413 			isize = UGETW(ed->wMaxPacketSize);
414 		} else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
415 			   UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
416 			bin = ed->bEndpointAddress;
417 		} else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT &&
418 			   UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
419 			bout = ed->bEndpointAddress;
420 		}
421 	}
422 
423 	if (intr == -1 || bin == -1 || bout == -1) {
424 		if (intr == -1) {
425 			printf("%s: no interrupt end point\n",
426 			       sc->sc_dev.dv_xname);
427 		}
428 		if (bin == -1) {
429 			printf("%s: no data bulk in end point\n",
430 			       sc->sc_dev.dv_xname);
431 		}
432 		if (bout == -1) {
433 			printf("%s: no data bulk out end point\n",
434 			       sc->sc_dev.dv_xname);
435 		}
436 		return -1;
437 	}
438 	if (isize < UCHCOM_INTR_LEAST) {
439 		printf("%s: intr pipe is too short", sc->sc_dev.dv_xname);
440 		return -1;
441 	}
442 
443 	DPRINTF(("%s: bulkin=%d, bulkout=%d, intr=%d, isize=%d\n",
444 		 sc->sc_dev.dv_xname, bin, bout, intr, isize));
445 
446 	endpoints->ep_intr = intr;
447 	endpoints->ep_intr_size = isize;
448 	endpoints->ep_bulkin = bin;
449 	endpoints->ep_bulkout = bout;
450 
451 	return 0;
452 }
453 
454 
455 /* ----------------------------------------------------------------------
456  * low level i/o
457  */
458 
459 usbd_status
460 uchcom_generic_control_out(struct uchcom_softc *sc, uint8_t reqno,
461     uint16_t value, uint16_t index)
462 {
463 	usb_device_request_t req;
464 
465 	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
466 	req.bRequest = reqno;
467 	USETW(req.wValue, value);
468 	USETW(req.wIndex, index);
469 	USETW(req.wLength, 0);
470 
471 	return usbd_do_request(sc->sc_udev, &req, 0);
472 }
473 
474 usbd_status
475 uchcom_generic_control_in(struct uchcom_softc *sc, uint8_t reqno,
476     uint16_t value, uint16_t index, void *buf, int buflen, int *actlen)
477 {
478 	usb_device_request_t req;
479 
480 	req.bmRequestType = UT_READ_VENDOR_DEVICE;
481 	req.bRequest = reqno;
482 	USETW(req.wValue, value);
483 	USETW(req.wIndex, index);
484 	USETW(req.wLength, (uint16_t)buflen);
485 
486 	return usbd_do_request_flags(sc->sc_udev, &req, buf,
487 				     USBD_SHORT_XFER_OK, actlen,
488 				     USBD_DEFAULT_TIMEOUT);
489 }
490 
491 usbd_status
492 uchcom_write_reg(struct uchcom_softc *sc,
493     uint8_t reg1, uint8_t val1, uint8_t reg2, uint8_t val2)
494 {
495 	DPRINTF(("uchcom: write reg 0x%02X<-0x%02X, 0x%02X<-0x%02X\n",
496 		 (unsigned)reg1, (unsigned)val1,
497 		 (unsigned)reg2, (unsigned)val2));
498 	return uchcom_generic_control_out(
499 		sc, UCHCOM_REQ_WRITE_REG,
500 		reg1|((uint16_t)reg2<<8), val1|((uint16_t)val2<<8));
501 }
502 
503 usbd_status
504 uchcom_read_reg(struct uchcom_softc *sc,
505     uint8_t reg1, uint8_t *rval1, uint8_t reg2, uint8_t *rval2)
506 {
507 	uint8_t buf[UCHCOM_INPUT_BUF_SIZE];
508 	usbd_status err;
509 	int actin;
510 
511 	err = uchcom_generic_control_in(
512 		sc, UCHCOM_REQ_READ_REG,
513 		reg1|((uint16_t)reg2<<8), 0, buf, sizeof buf, &actin);
514 	if (err)
515 		return err;
516 
517 	DPRINTF(("uchcom: read reg 0x%02X->0x%02X, 0x%02X->0x%02X\n",
518 		 (unsigned)reg1, (unsigned)buf[0],
519 		 (unsigned)reg2, (unsigned)buf[1]));
520 
521 	if (rval1) *rval1 = buf[0];
522 	if (rval2) *rval2 = buf[1];
523 
524 	return USBD_NORMAL_COMPLETION;
525 }
526 
527 usbd_status
528 uchcom_get_version(struct uchcom_softc *sc, uint8_t *rver)
529 {
530 	uint8_t buf[UCHCOM_INPUT_BUF_SIZE];
531 	usbd_status err;
532 	int actin;
533 
534 	err = uchcom_generic_control_in(
535 		sc, UCHCOM_REQ_GET_VERSION, 0, 0, buf, sizeof buf, &actin);
536 	if (err)
537 		return err;
538 
539 	if (rver) *rver = buf[0];
540 
541 	return USBD_NORMAL_COMPLETION;
542 }
543 
544 usbd_status
545 uchcom_read_status(struct uchcom_softc *sc, uint8_t *rval)
546 {
547 	return uchcom_read_reg(sc, UCHCOM_REG_STAT1, rval, UCHCOM_REG_STAT2,
548 	    NULL);
549 }
550 
551 usbd_status
552 uchcom_set_dtrrts_10(struct uchcom_softc *sc, uint8_t val)
553 {
554 	return uchcom_write_reg(sc, UCHCOM_REG_STAT1, val, UCHCOM_REG_STAT1,
555 	    val);
556 }
557 
558 usbd_status
559 uchcom_set_dtrrts_20(struct uchcom_softc *sc, uint8_t val)
560 {
561 	return uchcom_generic_control_out(sc, UCHCOM_REQ_SET_DTRRTS, val, 0);
562 }
563 
564 
565 /* ----------------------------------------------------------------------
566  * middle layer
567  */
568 
569 int
570 uchcom_update_version(struct uchcom_softc *sc)
571 {
572 	usbd_status err;
573 
574 	err = uchcom_get_version(sc, &sc->sc_version);
575 	if (err) {
576 		printf("%s: cannot get version: %s\n",
577 		       sc->sc_dev.dv_xname, usbd_errstr(err));
578 		return EIO;
579 	}
580 
581 	return 0;
582 }
583 
584 void
585 uchcom_convert_status(struct uchcom_softc *sc, uint8_t cur)
586 {
587 	sc->sc_dtr = !(cur & UCHCOM_DTR_MASK);
588 	sc->sc_rts = !(cur & UCHCOM_RTS_MASK);
589 
590 	cur = ~cur & 0x0F;
591 	sc->sc_msr = (cur << 4) | ((sc->sc_msr >> 4) ^ cur);
592 }
593 
594 int
595 uchcom_update_status(struct uchcom_softc *sc)
596 {
597 	usbd_status err;
598 	uint8_t cur;
599 
600 	err = uchcom_read_status(sc, &cur);
601 	if (err) {
602 		printf("%s: cannot update status: %s\n",
603 		       sc->sc_dev.dv_xname, usbd_errstr(err));
604 		return EIO;
605 	}
606 	uchcom_convert_status(sc, cur);
607 
608 	return 0;
609 }
610 
611 
612 int
613 uchcom_set_dtrrts(struct uchcom_softc *sc, int dtr, int rts)
614 {
615 	usbd_status err;
616 	uint8_t val = 0;
617 
618 	if (dtr) val |= UCHCOM_DTR_MASK;
619 	if (rts) val |= UCHCOM_RTS_MASK;
620 
621 	if (sc->sc_version < UCHCOM_VER_20)
622 		err = uchcom_set_dtrrts_10(sc, ~val);
623 	else
624 		err = uchcom_set_dtrrts_20(sc, ~val);
625 
626 	if (err) {
627 		printf("%s: cannot set DTR/RTS: %s\n",
628 		       sc->sc_dev.dv_xname, usbd_errstr(err));
629 		return EIO;
630 	}
631 
632 	return 0;
633 }
634 
635 int
636 uchcom_set_break(struct uchcom_softc *sc, int onoff)
637 {
638 	usbd_status err;
639 	uint8_t brk1, brk2;
640 
641 	err = uchcom_read_reg(sc, UCHCOM_REG_BREAK1, &brk1, UCHCOM_REG_BREAK2,
642 	    &brk2);
643 	if (err)
644 		return EIO;
645 	if (onoff) {
646 		/* on - clear bits */
647 		brk1 &= ~UCHCOM_BRK1_MASK;
648 		brk2 &= ~UCHCOM_BRK2_MASK;
649 	} else {
650 		/* off - set bits */
651 		brk1 |= UCHCOM_BRK1_MASK;
652 		brk2 |= UCHCOM_BRK2_MASK;
653 	}
654 	err = uchcom_write_reg(sc, UCHCOM_REG_BREAK1, brk1, UCHCOM_REG_BREAK2,
655 	    brk2);
656 	if (err)
657 		return EIO;
658 
659 	return 0;
660 }
661 
662 int
663 uchcom_calc_divider_settings(struct uchcom_divider *dp, uint32_t rate)
664 {
665 	int i;
666 	const struct uchcom_divider_record *rp;
667 	uint32_t div, rem, mod;
668 
669 	/* find record */
670 	for (i=0; i<NUM_DIVIDERS; i++) {
671 		if (dividers[i].dvr_high >= rate &&
672 		    dividers[i].dvr_low <= rate) {
673 			rp = &dividers[i];
674 			goto found;
675 		}
676 	}
677 	return -1;
678 
679 found:
680 	dp->dv_prescaler = rp->dvr_divider.dv_prescaler;
681 	if (rp->dvr_base_clock == UCHCOM_BASE_UNKNOWN)
682 		dp->dv_div = rp->dvr_divider.dv_div;
683 	else {
684 		div = rp->dvr_base_clock / rate;
685 		rem = rp->dvr_base_clock % rate;
686 		if (div==0 || div>=0xFF)
687 			return -1;
688 		if ((rem<<1) >= rate)
689 			div += 1;
690 		dp->dv_div = (uint8_t)-div;
691 	}
692 
693 	mod = UCHCOM_BPS_MOD_BASE/rate + UCHCOM_BPS_MOD_BASE_OFS;
694 	mod = mod + mod/2;
695 
696 	dp->dv_mod = mod / 0x100;
697 
698 	return 0;
699 }
700 
701 int
702 uchcom_set_dte_rate(struct uchcom_softc *sc, uint32_t rate)
703 {
704 	usbd_status err;
705 	struct uchcom_divider dv;
706 
707 	if (uchcom_calc_divider_settings(&dv, rate))
708 		return EINVAL;
709 
710 	if ((err = uchcom_write_reg(sc,
711 			     UCHCOM_REG_BPS_PRE, dv.dv_prescaler,
712 			     UCHCOM_REG_BPS_DIV, dv.dv_div)) ||
713 	    (err = uchcom_write_reg(sc,
714 			     UCHCOM_REG_BPS_MOD, dv.dv_mod,
715 			     UCHCOM_REG_BPS_PAD, 0))) {
716 		printf("%s: cannot set DTE rate: %s\n",
717 		       sc->sc_dev.dv_xname, usbd_errstr(err));
718 		return EIO;
719 	}
720 
721 	return 0;
722 }
723 
724 int
725 uchcom_set_line_control(struct uchcom_softc *sc, tcflag_t cflag)
726 {
727 	usbd_status err;
728 	uint8_t lcr1 = 0, lcr2 = 0;
729 
730 	err = uchcom_read_reg(sc, UCHCOM_REG_LCR1, &lcr1, UCHCOM_REG_LCR2,
731 	    &lcr2);
732 	if (err) {
733 		printf("%s: cannot get LCR: %s\n",
734 		       sc->sc_dev.dv_xname, usbd_errstr(err));
735 		return EIO;
736 	}
737 
738 	lcr1 &= ~UCHCOM_LCR1_MASK;
739 	lcr2 &= ~UCHCOM_LCR2_MASK;
740 
741 	/*
742 	 * XXX: it is difficult to handle the line control appropriately:
743 	 *   - CS8, !CSTOPB and any parity mode seems ok, but
744 	 *   - the chip doesn't have the function to calculate parity
745 	 *     in !CS8 mode.
746 	 *   - it is unclear that the chip supports CS5,6 mode.
747 	 *   - it is unclear how to handle stop bits.
748 	 */
749 
750 	switch (ISSET(cflag, CSIZE)) {
751 	case CS5:
752 	case CS6:
753 	case CS7:
754 		return EINVAL;
755 	case CS8:
756 		break;
757 	}
758 
759 	if (ISSET(cflag, PARENB)) {
760 		lcr1 |= UCHCOM_LCR1_PARENB;
761 		if (ISSET(cflag, PARODD))
762 			lcr2 |= UCHCOM_LCR2_PARODD;
763 		else
764 			lcr2 |= UCHCOM_LCR2_PAREVEN;
765 	}
766 
767 	err = uchcom_write_reg(sc, UCHCOM_REG_LCR1, lcr1, UCHCOM_REG_LCR2,
768 	    lcr2);
769 	if (err) {
770 		printf("%s: cannot set LCR: %s\n",
771 		       sc->sc_dev.dv_xname, usbd_errstr(err));
772 		return EIO;
773 	}
774 
775 	return 0;
776 }
777 
778 int
779 uchcom_clear_chip(struct uchcom_softc *sc)
780 {
781 	usbd_status err;
782 
783 	DPRINTF(("%s: clear\n", sc->sc_dev.dv_xname));
784 	err = uchcom_generic_control_out(sc, UCHCOM_REQ_RESET, 0, 0);
785 	if (err) {
786 		printf("%s: cannot clear: %s\n",
787 		       sc->sc_dev.dv_xname, usbd_errstr(err));
788 		return EIO;
789 	}
790 
791 	return 0;
792 }
793 
794 int
795 uchcom_reset_chip(struct uchcom_softc *sc)
796 {
797 	usbd_status err;
798 	uint8_t lcr1, lcr2, pre, div, mod;
799 	uint16_t val=0, idx=0;
800 
801 	err = uchcom_read_reg(sc, UCHCOM_REG_LCR1, &lcr1, UCHCOM_REG_LCR2, &lcr2);
802 	if (err)
803 		goto failed;
804 
805 	err = uchcom_read_reg(sc, UCHCOM_REG_BPS_PRE, &pre, UCHCOM_REG_BPS_DIV,
806 	    &div);
807 	if (err)
808 		goto failed;
809 
810 	err = uchcom_read_reg(sc, UCHCOM_REG_BPS_MOD, &mod, UCHCOM_REG_BPS_PAD,
811 	    NULL);
812 	if (err)
813 		goto failed;
814 
815 	val |= (uint16_t)(lcr1&0xF0) << 8;
816 	val |= 0x01;
817 	val |= (uint16_t)(lcr2&0x0F) << 8;
818 	val |= 0x02;
819 	idx |= pre & 0x07;
820 	val |= 0x04;
821 	idx |= (uint16_t)div << 8;
822 	val |= 0x08;
823 	idx |= mod & 0xF8;
824 	val |= 0x10;
825 
826 	DPRINTF(("%s: reset v=0x%04X, i=0x%04X\n",
827 		 sc->sc_dev.dv_xname, val, idx));
828 
829 	err = uchcom_generic_control_out(sc, UCHCOM_REQ_RESET, val, idx);
830 	if (err)
831 		goto failed;
832 
833 	return 0;
834 
835 failed:
836 	printf("%s: cannot reset: %s\n",
837 	       sc->sc_dev.dv_xname, usbd_errstr(err));
838 	return EIO;
839 }
840 
841 int
842 uchcom_setup_comm(struct uchcom_softc *sc)
843 {
844 	int ret;
845 
846 	ret = uchcom_update_version(sc);
847 	if (ret)
848 		return ret;
849 
850 	ret = uchcom_clear_chip(sc);
851 	if (ret)
852 		return ret;
853 
854 	ret = uchcom_set_dte_rate(sc, TTYDEF_SPEED);
855 	if (ret)
856 		return ret;
857 
858 	ret = uchcom_set_line_control(sc, CS8);
859 	if (ret)
860 		return ret;
861 
862 	ret = uchcom_update_status(sc);
863 	if (ret)
864 		return ret;
865 
866 	ret = uchcom_reset_chip(sc);
867 	if (ret)
868 		return ret;
869 
870 	ret = uchcom_set_dte_rate(sc, TTYDEF_SPEED); /* XXX */
871 	if (ret)
872 		return ret;
873 
874 	sc->sc_dtr = sc->sc_rts = 1;
875 	ret = uchcom_set_dtrrts(sc, sc->sc_dtr, sc->sc_rts);
876 	if (ret)
877 		return ret;
878 
879 	return 0;
880 }
881 
882 int
883 uchcom_setup_intr_pipe(struct uchcom_softc *sc)
884 {
885 	usbd_status err;
886 
887 	if (sc->sc_intr_endpoint != -1 && sc->sc_intr_pipe == NULL) {
888 		sc->sc_intr_buf = malloc(sc->sc_intr_size, M_USBDEV, M_WAITOK);
889 		err = usbd_open_pipe_intr(sc->sc_iface,
890 					  sc->sc_intr_endpoint,
891 					  USBD_SHORT_XFER_OK,
892 					  &sc->sc_intr_pipe, sc,
893 					  sc->sc_intr_buf,
894 					  sc->sc_intr_size,
895 					  uchcom_intr, USBD_DEFAULT_INTERVAL);
896 		if (err) {
897 			printf("%s: cannot open interrupt pipe: %s\n",
898 			       sc->sc_dev.dv_xname,
899 			       usbd_errstr(err));
900 			return EIO;
901 		}
902 	}
903 	return 0;
904 }
905 
906 void
907 uchcom_close_intr_pipe(struct uchcom_softc *sc)
908 {
909 	usbd_status err;
910 
911 	if (sc->sc_dying)
912 		return;
913 
914 	if (sc->sc_intr_pipe != NULL) {
915 		err = usbd_abort_pipe(sc->sc_intr_pipe);
916 		if (err)
917 			printf("%s: abort interrupt pipe failed: %s\n",
918 			       sc->sc_dev.dv_xname, usbd_errstr(err));
919 		err = usbd_close_pipe(sc->sc_intr_pipe);
920 		if (err)
921 			printf("%s: close interrupt pipe failed: %s\n",
922 			       sc->sc_dev.dv_xname, usbd_errstr(err));
923 		free(sc->sc_intr_buf, M_USBDEV);
924 		sc->sc_intr_pipe = NULL;
925 	}
926 }
927 
928 
929 /* ----------------------------------------------------------------------
930  * methods for ucom
931  */
932 void
933 uchcom_get_status(void *arg, int portno, u_char *rlsr, u_char *rmsr)
934 {
935 	struct uchcom_softc *sc = arg;
936 
937 	if (sc->sc_dying)
938 		return;
939 
940 	*rlsr = sc->sc_lsr;
941 	*rmsr = sc->sc_msr;
942 }
943 
944 void
945 uchcom_set(void *arg, int portno, int reg, int onoff)
946 {
947 	struct uchcom_softc *sc = arg;
948 
949 	if (sc->sc_dying)
950 		return;
951 
952 	switch (reg) {
953 	case UCOM_SET_DTR:
954 		sc->sc_dtr = !!onoff;
955 		uchcom_set_dtrrts(sc, sc->sc_dtr, sc->sc_rts);
956 		break;
957 	case UCOM_SET_RTS:
958 		sc->sc_rts = !!onoff;
959 		uchcom_set_dtrrts(sc, sc->sc_dtr, sc->sc_rts);
960 		break;
961 	case UCOM_SET_BREAK:
962 		uchcom_set_break(sc, onoff);
963 		break;
964 	}
965 }
966 
967 int
968 uchcom_param(void *arg, int portno, struct termios *t)
969 {
970 	struct uchcom_softc *sc = arg;
971 	int ret;
972 
973 	if (sc->sc_dying)
974 		return 0;
975 
976 	ret = uchcom_set_line_control(sc, t->c_cflag);
977 	if (ret)
978 		return ret;
979 
980 	ret = uchcom_set_dte_rate(sc, t->c_ospeed);
981 	if (ret)
982 		return ret;
983 
984 	return 0;
985 }
986 
987 int
988 uchcom_open(void *arg, int portno)
989 {
990 	int ret;
991 	struct uchcom_softc *sc = arg;
992 
993 	if (sc->sc_dying)
994 		return EIO;
995 
996 	ret = uchcom_setup_intr_pipe(sc);
997 	if (ret)
998 		return ret;
999 
1000 	ret = uchcom_setup_comm(sc);
1001 	if (ret)
1002 		return ret;
1003 
1004 	return 0;
1005 }
1006 
1007 void
1008 uchcom_close(void *arg, int portno)
1009 {
1010 	struct uchcom_softc *sc = arg;
1011 
1012 	if (sc->sc_dying)
1013 		return;
1014 
1015 	uchcom_close_intr_pipe(sc);
1016 }
1017 
1018 
1019 /* ----------------------------------------------------------------------
1020  * callback when the modem status is changed.
1021  */
1022 void
1023 uchcom_intr(usbd_xfer_handle xfer, usbd_private_handle priv,
1024     usbd_status status)
1025 {
1026 	struct uchcom_softc *sc = priv;
1027 	u_char *buf = sc->sc_intr_buf;
1028 
1029 	if (sc->sc_dying)
1030 		return;
1031 
1032 	if (status != USBD_NORMAL_COMPLETION) {
1033 		if (status == USBD_NOT_STARTED || status == USBD_CANCELLED)
1034 			return;
1035 
1036 		DPRINTF(("%s: abnormal status: %s\n",
1037 			 sc->sc_dev.dv_xname, usbd_errstr(status)));
1038 		usbd_clear_endpoint_stall_async(sc->sc_intr_pipe);
1039 		return;
1040 	}
1041 	DPRINTF(("%s: intr: 0x%02X 0x%02X 0x%02X 0x%02X "
1042 		 "0x%02X 0x%02X 0x%02X 0x%02X\n",
1043 		 sc->sc_dev.dv_xname,
1044 		 (unsigned)buf[0], (unsigned)buf[1],
1045 		 (unsigned)buf[2], (unsigned)buf[3],
1046 		 (unsigned)buf[4], (unsigned)buf[5],
1047 		 (unsigned)buf[6], (unsigned)buf[7]));
1048 
1049 	uchcom_convert_status(sc, buf[UCHCOM_INTR_STAT1]);
1050 	ucom_status_change((struct ucom_softc *) sc->sc_subdev);
1051 }
1052