xref: /openbsd-src/sys/dev/usb/uticom.c (revision f2da64fbbbf1b03f09f390ab01267c93dfd77c4c)
1 /*	$OpenBSD: uticom.c,v 1.30 2016/05/24 05:35:01 mpi Exp $	*/
2 /*
3  * Copyright (c) 2005 Dmitry Komissaroff <dxi@mail.ru>.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26 
27 #include <sys/param.h>
28 #include <sys/systm.h>
29 #include <sys/kernel.h>
30 #include <sys/device.h>
31 #include <sys/malloc.h>
32 #include <sys/conf.h>
33 #include <sys/tty.h>
34 
35 #include <machine/bus.h>
36 
37 #include <dev/usb/usb.h>
38 #include <dev/usb/usbdi.h>
39 #include <dev/usb/usbdivar.h>
40 #include <dev/usb/usbdi_util.h>
41 #include <dev/usb/usbdevs.h>
42 #include <dev/usb/usbcdc.h>
43 
44 #include <dev/usb/ucomvar.h>
45 
46 #ifdef UTICOM_DEBUG
47 static int uticomdebug = 0;
48 #define DPRINTFN(n, x)	do { if (uticomdebug > (n)) printf x; } while (0)
49 #else
50 #define DPRINTFN(n, x)
51 #endif
52 
53 #define DPRINTF(x) DPRINTFN(0, x)
54 
55 #define	UTICOM_CONFIG_INDEX	1
56 #define	UTICOM_ACTIVE_INDEX	2
57 
58 #define	UTICOM_IFACE_INDEX	0
59 
60 /*
61  * These are the maximum number of bytes transferred per frame.
62  * The output buffer size cannot be increased due to the size encoding.
63  */
64 #define UTICOM_IBUFSZ		64
65 #define UTICOM_OBUFSZ		64
66 
67 #define UTICOM_FW_BUFSZ		16284
68 
69 #define UTICOM_INTR_INTERVAL	100	/* ms */
70 
71 #define UTICOM_RQ_LINE		0
72 /* Used to sync data0/1-toggle on reopen bulk pipe. */
73 #define UTICOM_RQ_SOF		1
74 #define UTICOM_RQ_SON		2
75 
76 #define UTICOM_RQ_BAUD		3
77 #define UTICOM_RQ_LCR		4
78 #define UTICOM_RQ_FCR		5
79 #define UTICOM_RQ_RTS		6
80 #define UTICOM_RQ_DTR		7
81 #define UTICOM_RQ_BREAK		8
82 #define UTICOM_RQ_CRTSCTS	9
83 
84 #define UTICOM_BRATE_REF	923077
85 
86 #define UTICOM_SET_DATA_BITS(x)	(x - 5)
87 
88 #define UTICOM_STOP_BITS_1	0x00
89 #define UTICOM_STOP_BITS_2	0x40
90 
91 #define UTICOM_PARITY_NONE	0x00
92 #define UTICOM_PARITY_ODD	0x08
93 #define UTICOM_PARITY_EVEN	0x18
94 
95 #define UTICOM_LCR_OVR		0x1
96 #define UTICOM_LCR_PTE		0x2
97 #define UTICOM_LCR_FRE		0x4
98 #define UTICOM_LCR_BRK		0x8
99 
100 #define UTICOM_MCR_CTS		0x1
101 #define UTICOM_MCR_DSR		0x2
102 #define UTICOM_MCR_CD		0x4
103 #define UTICOM_MCR_RI		0x8
104 
105 /* Structures */
106 struct uticom_fw_header {
107 	uint16_t	length;
108 	uint8_t		checkSum;
109 } __packed;
110 
111 struct uticom_buf {
112 	unsigned int		buf_size;
113 	char			*buf_buf;
114 	char			*buf_get;
115 	char			*buf_put;
116 };
117 
118 struct	uticom_softc {
119 	struct device		 sc_dev;	/* base device */
120 	struct usbd_device	*sc_udev;	/* device */
121 	struct usbd_interface	*sc_iface;	/* interface */
122 
123 	int			sc_iface_number; /* interface number */
124 
125 	struct usbd_interface	*sc_intr_iface;	/* interrupt interface */
126 	int			sc_intr_number;	/* interrupt number */
127 	struct usbd_pipe	*sc_intr_pipe;	/* interrupt pipe */
128 	u_char			*sc_intr_buf;	/* interrupt buffer */
129 	int			sc_isize;
130 
131 	u_char			sc_dtr;		/* current DTR state */
132 	u_char			sc_rts;		/* current RTS state */
133 	u_char			sc_status;
134 
135 	u_char			sc_lsr;		/* Local status register */
136 	u_char			sc_msr;		/* uticom status register */
137 
138 	struct device		*sc_subdev;
139 };
140 
141 static	usbd_status uticom_reset(struct uticom_softc *);
142 static	usbd_status uticom_set_crtscts(struct uticom_softc *);
143 static	void uticom_intr(struct usbd_xfer *, void *, usbd_status);
144 
145 static	void uticom_set(void *, int, int, int);
146 static	void uticom_dtr(struct uticom_softc *, int);
147 static	void uticom_rts(struct uticom_softc *, int);
148 static	void uticom_break(struct uticom_softc *, int);
149 static	void uticom_get_status(void *, int, u_char *, u_char *);
150 static	int  uticom_param(void *, int, struct termios *);
151 static	int  uticom_open(void *, int);
152 static	void uticom_close(void *, int);
153 
154 void uticom_attach_hook(struct device *);
155 
156 static int uticom_download_fw(struct uticom_softc *sc, int pipeno,
157     struct usbd_device *dev);
158 
159 struct ucom_methods uticom_methods = {
160 	uticom_get_status,
161 	uticom_set,
162 	uticom_param,
163 	NULL,
164 	uticom_open,
165 	uticom_close,
166 	NULL,
167 	NULL
168 };
169 
170 int	uticom_match(struct device *, void *, void *);
171 void	uticom_attach(struct device *, struct device *, void *);
172 int	uticom_detach(struct device *, int);
173 
174 struct cfdriver uticom_cd = {
175 	NULL, "uticom", DV_DULL
176 };
177 
178 const struct cfattach uticom_ca = {
179 	sizeof(struct uticom_softc), uticom_match, uticom_attach, uticom_detach
180 };
181 
182 static const struct usb_devno uticom_devs[] = {
183 	{ USB_VENDOR_TI, USB_PRODUCT_TI_TUSB3410 },
184 	{ USB_VENDOR_TI, USB_PRODUCT_TI_MSP430_JTAG },
185 	{ USB_VENDOR_STARTECH, USB_PRODUCT_STARTECH_ICUSB232X },
186 	{ USB_VENDOR_MOXA, USB_PRODUCT_MOXA_UPORT1110 },
187 	{ USB_VENDOR_ABBOTT, USB_PRODUCT_ABBOTT_STEREO_PLUG }
188 };
189 
190 int
191 uticom_match(struct device *parent, void *match, void *aux)
192 {
193 	struct usb_attach_arg *uaa = aux;
194 
195 	if (uaa->iface != NULL)
196 		return (UMATCH_NONE);
197 
198 	return (usb_lookup(uticom_devs, uaa->vendor, uaa->product) != NULL ?
199 	    UMATCH_VENDOR_PRODUCT : UMATCH_NONE);
200 }
201 
202 void
203 uticom_attach(struct device *parent, struct device *self, void *aux)
204 {
205 	struct uticom_softc	*sc = (struct uticom_softc *)self;
206 	struct usb_attach_arg	*uaa = aux;
207 	struct usbd_device	*dev = uaa->device;
208 
209 	sc->sc_udev = dev;
210 	sc->sc_iface = uaa->iface;
211 
212 	config_mountroot(self, uticom_attach_hook);
213 }
214 
215 void
216 uticom_attach_hook(struct device *self)
217 {
218 	struct uticom_softc		*sc = (struct uticom_softc *)self;
219 	usb_config_descriptor_t		*cdesc;
220 	usb_interface_descriptor_t	*id;
221 	usb_endpoint_descriptor_t	*ed;
222 	usbd_status			 err;
223 	int				 status, i;
224 	usb_device_descriptor_t		*dd;
225 	struct ucom_attach_args		 uca;
226 
227 	/* Initialize endpoints. */
228 	uca.bulkin = uca.bulkout = -1;
229 	sc->sc_intr_number = -1;
230 	sc->sc_intr_pipe = NULL;
231 
232 	dd = usbd_get_device_descriptor(sc->sc_udev);
233 	DPRINTF(("%s: uticom_attach: num of configurations %d\n",
234 	    sc->sc_dev.dv_xname, dd->bNumConfigurations));
235 
236 	/* The device without firmware has single configuration with single
237 	 * bulk out interface. */
238 	if (dd->bNumConfigurations > 1)
239 		goto fwload_done;
240 
241 	/* Loading firmware. */
242 	DPRINTF(("%s: uticom_attach: starting loading firmware\n",
243 	    sc->sc_dev.dv_xname));
244 
245 	err = usbd_set_config_index(sc->sc_udev, UTICOM_CONFIG_INDEX, 1);
246 	if (err) {
247 		printf("%s: failed to set configuration: %s\n",
248 		    sc->sc_dev.dv_xname, usbd_errstr(err));
249 		usbd_deactivate(sc->sc_udev);
250 		return;
251 	}
252 
253 	/* Get the config descriptor. */
254 	cdesc = usbd_get_config_descriptor(sc->sc_udev);
255 
256 	if (cdesc == NULL) {
257 		printf("%s: failed to get configuration descriptor\n",
258 		    sc->sc_dev.dv_xname);
259 		usbd_deactivate(sc->sc_udev);
260 		return;
261 	}
262 
263 	err = usbd_device2interface_handle(sc->sc_udev, UTICOM_IFACE_INDEX,
264 	    &sc->sc_iface);
265 	if (err) {
266 		printf("%s: failed to get interface: %s\n",
267 		    sc->sc_dev.dv_xname, usbd_errstr(err));
268 		usbd_deactivate(sc->sc_udev);
269 		return;
270 	}
271 
272 	/* Find the bulk out interface used to upload firmware. */
273 	id = usbd_get_interface_descriptor(sc->sc_iface);
274 	sc->sc_iface_number = id->bInterfaceNumber;
275 
276 	for (i = 0; i < id->bNumEndpoints; i++) {
277 		ed = usbd_interface2endpoint_descriptor(sc->sc_iface, i);
278 		if (ed == NULL) {
279 			printf("%s: no endpoint descriptor for %d\n",
280 			    sc->sc_dev.dv_xname, i);
281 			usbd_deactivate(sc->sc_udev);
282 			return;
283 		}
284 
285 		if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT &&
286 		    UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
287 			uca.bulkout = ed->bEndpointAddress;
288 			DPRINTF(("%s: uticom_attach: data bulk out num: %d\n",
289 			    sc->sc_dev.dv_xname, ed->bEndpointAddress));
290 		}
291 
292 		if (uca.bulkout == -1) {
293 			printf("%s: could not find data bulk out\n",
294 			    sc->sc_dev.dv_xname);
295 			usbd_deactivate(sc->sc_udev);
296 			return;
297 		}
298 	}
299 
300 	status = uticom_download_fw(sc, uca.bulkout, sc->sc_udev);
301 
302 	if (status) {
303 		printf("%s: firmware download failed\n",
304 		    sc->sc_dev.dv_xname);
305 		usbd_deactivate(sc->sc_udev);
306 		return;
307 	} else {
308 		DPRINTF(("%s: firmware download succeeded\n",
309 		    sc->sc_dev.dv_xname));
310 	}
311 
312 	status = usbd_reload_device_desc(sc->sc_udev);
313 	if (status) {
314 		printf("%s: error reloading device descriptor\n",
315 		    sc->sc_dev.dv_xname);
316 		usbd_deactivate(sc->sc_udev);
317 		return;
318 	}
319 
320 fwload_done:
321 	dd = usbd_get_device_descriptor(sc->sc_udev);
322 	DPRINTF(("%s: uticom_attach: num of configurations %d\n",
323 	    sc->sc_dev.dv_xname, dd->bNumConfigurations));
324 
325 	err = usbd_set_config_index(sc->sc_udev, UTICOM_ACTIVE_INDEX, 1);
326 	if (err) {
327 		printf("%s: failed to set configuration: %s\n",
328 		    sc->sc_dev.dv_xname, usbd_errstr(err));
329 		usbd_deactivate(sc->sc_udev);
330 		return;
331 	}
332 
333 	/* Get the config descriptor. */
334 	cdesc = usbd_get_config_descriptor(sc->sc_udev);
335 	if (cdesc == NULL) {
336 		printf("%s: failed to get configuration descriptor\n",
337 		    sc->sc_dev.dv_xname);
338 		usbd_deactivate(sc->sc_udev);
339 		return;
340 	}
341 
342 	/* Get the interface (XXX: multiport chips are not supported yet). */
343 	err = usbd_device2interface_handle(sc->sc_udev, UTICOM_IFACE_INDEX,
344 	    &sc->sc_iface);
345 	if (err) {
346 		printf("%s: failed to get interface: %s\n",
347 		    sc->sc_dev.dv_xname, usbd_errstr(err));
348 		usbd_deactivate(sc->sc_udev);
349 		return;
350 	}
351 
352 	/* Find the interrupt endpoints. */
353 	id = usbd_get_interface_descriptor(sc->sc_iface);
354 	sc->sc_iface_number = id->bInterfaceNumber;
355 
356 	for (i = 0; i < id->bNumEndpoints; i++) {
357 		ed = usbd_interface2endpoint_descriptor(sc->sc_iface, i);
358 		if (ed == NULL) {
359 			printf("%s: no endpoint descriptor for %d\n",
360 			    sc->sc_dev.dv_xname, i);
361 			usbd_deactivate(sc->sc_udev);
362 			return;
363 		}
364 
365 		if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
366 		    UE_GET_XFERTYPE(ed->bmAttributes) == UE_INTERRUPT) {
367 			sc->sc_intr_number = ed->bEndpointAddress;
368 			sc->sc_isize = UGETW(ed->wMaxPacketSize);
369 
370 		}
371 	}
372 
373 	if (sc->sc_intr_number == -1) {
374 		printf("%s: could not find interrupt in\n",
375 		    sc->sc_dev.dv_xname);
376 		usbd_deactivate(sc->sc_udev);
377 		return;
378 	}
379 
380 	/* Keep interface for interrupt. */
381 	sc->sc_intr_iface = sc->sc_iface;
382 
383 	/* Find the bulk{in,out} endpoints. */
384 	id = usbd_get_interface_descriptor(sc->sc_iface);
385 	sc->sc_iface_number = id->bInterfaceNumber;
386 
387 	for (i = 0; i < id->bNumEndpoints; i++) {
388 		ed = usbd_interface2endpoint_descriptor(sc->sc_iface, i);
389 		if (ed == NULL) {
390 			printf("%s: no endpoint descriptor for %d\n",
391 			    sc->sc_dev.dv_xname, i);
392 			usbd_deactivate(sc->sc_udev);
393 			return;
394 		}
395 
396 		if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
397 		    UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
398 			uca.bulkin = ed->bEndpointAddress;
399 		} else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT &&
400 		    UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
401 			uca.bulkout = ed->bEndpointAddress;
402 		}
403 	}
404 
405 	if (uca.bulkin == -1) {
406 		printf("%s: could not find data bulk in\n",
407 		    sc->sc_dev.dv_xname);
408 		usbd_deactivate(sc->sc_udev);
409 		return;
410 	}
411 
412 	if (uca.bulkout == -1) {
413 		printf("%s: could not find data bulk out\n",
414 		    sc->sc_dev.dv_xname);
415 		usbd_deactivate(sc->sc_udev);
416 		return;
417 	}
418 
419 	sc->sc_dtr = sc->sc_rts = -1;
420 
421 	uca.portno = UCOM_UNK_PORTNO;
422 	uca.ibufsize = UTICOM_IBUFSZ;
423 	uca.obufsize = UTICOM_OBUFSZ;
424 	uca.ibufsizepad = UTICOM_IBUFSZ;
425 	uca.device = sc->sc_udev;
426 	uca.iface = sc->sc_iface;
427 	uca.opkthdrlen = 0;
428 	uca.methods = &uticom_methods;
429 	uca.arg = sc;
430 	uca.info = NULL;
431 
432 	err = uticom_reset(sc);
433 	if (err) {
434 		printf("%s: reset failed: %s\n",
435 		    sc->sc_dev.dv_xname, usbd_errstr(err));
436 		usbd_deactivate(sc->sc_udev);
437 		return;
438 	}
439 
440 	DPRINTF(("%s: uticom_attach: in = 0x%x, out = 0x%x, intr = 0x%x\n",
441 	    sc->sc_dev.dv_xname, uca.bulkin,
442 	    uca.bulkout, sc->sc_intr_number));
443 
444 	sc->sc_subdev = config_found_sm((struct device *)sc, &uca, ucomprint, ucomsubmatch);
445 }
446 
447 int
448 uticom_detach(struct device *self, int flags)
449 {
450 	struct uticom_softc *sc = (struct uticom_softc *)self;
451 
452 	DPRINTF(("%s: uticom_detach: sc = %p\n",
453 	    sc->sc_dev.dv_xname, sc));
454 
455 	if (sc->sc_subdev != NULL) {
456 		config_detach(sc->sc_subdev, flags);
457 		sc->sc_subdev = NULL;
458 	}
459 
460 	if (sc->sc_intr_pipe != NULL) {
461 		usbd_abort_pipe(sc->sc_intr_pipe);
462 		usbd_close_pipe(sc->sc_intr_pipe);
463 		free(sc->sc_intr_buf, M_USBDEV, 0);
464 		sc->sc_intr_pipe = NULL;
465 	}
466 
467 	return (0);
468 }
469 
470 static usbd_status
471 uticom_reset(struct uticom_softc *sc)
472 {
473 	usb_device_request_t req;
474 	usbd_status err;
475 
476 	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
477 	req.bRequest = UTICOM_RQ_SON;
478 	USETW(req.wValue, 0);
479 	USETW(req.wIndex, 0);
480 	USETW(req.wLength, 0);
481 
482 	err = usbd_do_request(sc->sc_udev, &req, NULL);
483 	if (err){
484 		printf("%s: uticom_reset: %s\n",
485 		    sc->sc_dev.dv_xname, usbd_errstr(err));
486 		return (EIO);
487 	}
488 
489 	DPRINTF(("%s: uticom_reset: done\n", sc->sc_dev.dv_xname));
490 	return (0);
491 }
492 
493 static void
494 uticom_set(void *addr, int portno, int reg, int onoff)
495 {
496 	struct uticom_softc *sc = addr;
497 
498 	switch (reg) {
499 	case UCOM_SET_DTR:
500 		uticom_dtr(sc, onoff);
501 		break;
502 	case UCOM_SET_RTS:
503 		uticom_rts(sc, onoff);
504 		break;
505 	case UCOM_SET_BREAK:
506 		uticom_break(sc, onoff);
507 		break;
508 	default:
509 		break;
510 	}
511 }
512 
513 static void
514 uticom_dtr(struct uticom_softc *sc, int onoff)
515 {
516 	usb_device_request_t req;
517 	usbd_status err;
518 
519 	DPRINTF(("%s: uticom_dtr: onoff = %d\n", sc->sc_dev.dv_xname,
520 	    onoff));
521 
522 	if (sc->sc_dtr == onoff)
523 		return;
524 	sc->sc_dtr = onoff;
525 
526 	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
527 	req.bRequest = UTICOM_RQ_DTR;
528 	USETW(req.wValue, sc->sc_dtr ? UCDC_LINE_DTR : 0);
529 	USETW(req.wIndex, 0);
530 	USETW(req.wLength, 0);
531 
532 	err = usbd_do_request(sc->sc_udev, &req, NULL);
533 	if (err)
534 		printf("%s: uticom_dtr: %s\n",
535 		    sc->sc_dev.dv_xname, usbd_errstr(err));
536 }
537 
538 static void
539 uticom_rts(struct uticom_softc *sc, int onoff)
540 {
541 	usb_device_request_t req;
542 	usbd_status err;
543 
544 	DPRINTF(("%s: uticom_rts: onoff = %d\n", sc->sc_dev.dv_xname,
545 	    onoff));
546 
547 	if (sc->sc_rts == onoff)
548 		return;
549 	sc->sc_rts = onoff;
550 	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
551 	req.bRequest = UTICOM_RQ_RTS;
552 	USETW(req.wValue, sc->sc_rts ? UCDC_LINE_RTS : 0);
553 	USETW(req.wIndex, 0);
554 	USETW(req.wLength, 0);
555 
556 	err = usbd_do_request(sc->sc_udev, &req, NULL);
557 	if (err)
558 		printf("%s: uticom_rts: %s\n",
559 		    sc->sc_dev.dv_xname, usbd_errstr(err));
560 }
561 
562 static void
563 uticom_break(struct uticom_softc *sc, int onoff)
564 {
565 	usb_device_request_t req;
566 	usbd_status err;
567 
568 	DPRINTF(("%s: uticom_break: onoff = %d\n", sc->sc_dev.dv_xname,
569 	    onoff));
570 
571 	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
572 	req.bRequest = UTICOM_RQ_BREAK;
573 	USETW(req.wValue, onoff ? 1 : 0);
574 	USETW(req.wIndex, 0);
575 	USETW(req.wLength, 0);
576 
577 	err = usbd_do_request(sc->sc_udev, &req, NULL);
578 	if (err)
579 		printf("%s: uticom_break: %s\n",
580 		    sc->sc_dev.dv_xname, usbd_errstr(err));
581 }
582 
583 static usbd_status
584 uticom_set_crtscts(struct uticom_softc *sc)
585 {
586 	usb_device_request_t req;
587 	usbd_status err;
588 
589 	DPRINTF(("%s: uticom_set_crtscts: on\n", sc->sc_dev.dv_xname));
590 
591 	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
592 	req.bRequest = UTICOM_RQ_CRTSCTS;
593 	USETW(req.wValue, 1);
594 	USETW(req.wIndex, 0);
595 	USETW(req.wLength, 0);
596 
597 	err = usbd_do_request(sc->sc_udev, &req, NULL);
598 	if (err) {
599 		printf("%s: uticom_set_crtscts: %s\n",
600 		    sc->sc_dev.dv_xname, usbd_errstr(err));
601 		return (err);
602 	}
603 
604 	return (USBD_NORMAL_COMPLETION);
605 }
606 
607 static int
608 uticom_param(void *vsc, int portno, struct termios *t)
609 {
610 	struct uticom_softc *sc = (struct uticom_softc *)vsc;
611 	usb_device_request_t req;
612 	usbd_status err;
613 	uint8_t data;
614 
615 	DPRINTF(("%s: uticom_param\n", sc->sc_dev.dv_xname));
616 
617 	switch (t->c_ospeed) {
618 	case 1200:
619 	case 2400:
620 	case 4800:
621 	case 7200:
622 	case 9600:
623 	case 14400:
624 	case 19200:
625 	case 38400:
626 	case 57600:
627 	case 115200:
628 	case 230400:
629 	case 460800:
630 	case 921600:
631 		req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
632 		req.bRequest = UTICOM_RQ_BAUD;
633 		USETW(req.wValue, (UTICOM_BRATE_REF / t->c_ospeed));
634 		USETW(req.wIndex, 0);
635 		USETW(req.wLength, 0);
636 
637 		err = usbd_do_request(sc->sc_udev, &req, 0);
638 		if (err) {
639 			printf("%s: uticom_param: %s\n",
640 			    sc->sc_dev.dv_xname, usbd_errstr(err));
641 			return (EIO);
642 		}
643 		break;
644 	default:
645 		printf("%s: uticom_param: unsupported baud rate %d\n",
646 		    sc->sc_dev.dv_xname, t->c_ospeed);
647 		return (EINVAL);
648 	}
649 
650 	switch (ISSET(t->c_cflag, CSIZE)) {
651 	case CS5:
652 		data = UTICOM_SET_DATA_BITS(5);
653 		break;
654 	case CS6:
655 		data = UTICOM_SET_DATA_BITS(6);
656 		break;
657 	case CS7:
658 		data = UTICOM_SET_DATA_BITS(7);
659 		break;
660 	case CS8:
661 		data = UTICOM_SET_DATA_BITS(8);
662 		break;
663 	default:
664 		return (EIO);
665 	}
666 
667 	if (ISSET(t->c_cflag, CSTOPB))
668 		data |= UTICOM_STOP_BITS_2;
669 	else
670 		data |= UTICOM_STOP_BITS_1;
671 
672 	if (ISSET(t->c_cflag, PARENB)) {
673 		if (ISSET(t->c_cflag, PARODD))
674 			data |= UTICOM_PARITY_ODD;
675 		else
676 			data |= UTICOM_PARITY_EVEN;
677 	} else
678 		data |= UTICOM_PARITY_NONE;
679 
680 	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
681 	req.bRequest = UTICOM_RQ_LCR;
682 	USETW(req.wIndex, 0);
683 	USETW(req.wLength, 0);
684 	USETW(req.wValue, data);
685 
686 	err = usbd_do_request(sc->sc_udev, &req, NULL);
687 	if (err) {
688 		printf("%s: uticom_param: %s\n",
689 		    sc->sc_dev.dv_xname, usbd_errstr(err));
690 		return (err);
691 	}
692 
693 	if (ISSET(t->c_cflag, CRTSCTS)) {
694 		err = uticom_set_crtscts(sc);
695 		if (err)
696 			return (EIO);
697 	}
698 
699 	return (0);
700 }
701 
702 static int
703 uticom_open(void *addr, int portno)
704 {
705 	struct uticom_softc *sc = addr;
706 	usbd_status err;
707 
708 	if (usbd_is_dying(sc->sc_udev))
709 		return (ENXIO);
710 
711 	DPRINTF(("%s: uticom_open\n", sc->sc_dev.dv_xname));
712 
713 	sc->sc_status = 0; /* clear status bit */
714 
715 	if (sc->sc_intr_number != -1 && sc->sc_intr_pipe == NULL) {
716 		sc->sc_intr_buf = malloc(sc->sc_isize, M_USBDEV, M_WAITOK);
717 		err = usbd_open_pipe_intr(sc->sc_intr_iface, sc->sc_intr_number,
718 		    USBD_SHORT_XFER_OK, &sc->sc_intr_pipe, sc, sc->sc_intr_buf,
719 		    sc->sc_isize, uticom_intr, UTICOM_INTR_INTERVAL);
720 		if (err) {
721 			printf("%s: cannot open interrupt pipe (addr %d)\n",
722 			    sc->sc_dev.dv_xname, sc->sc_intr_number);
723 			return (EIO);
724 		}
725 	}
726 
727 	DPRINTF(("%s: uticom_open: port opened\n", sc->sc_dev.dv_xname));
728 	return (0);
729 }
730 
731 static void
732 uticom_close(void *addr, int portno)
733 {
734 	struct uticom_softc *sc = addr;
735 	usb_device_request_t req;
736 	usbd_status err;
737 
738 	if (usbd_is_dying(sc->sc_udev))
739 		return;
740 
741 	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
742 	req.bRequest = UTICOM_RQ_SON;
743 	USETW(req.wValue, 0);
744 	USETW(req.wIndex, 0);
745 	USETW(req.wLength, 0);
746 
747 	/* Try to reset UART part of chip. */
748 	err = usbd_do_request(sc->sc_udev, &req, NULL);
749 	if (err) {
750 		printf("%s: uticom_close: %s\n",
751 		    sc->sc_dev.dv_xname, usbd_errstr(err));
752 		return;
753 	}
754 
755 	DPRINTF(("%s: uticom_close: close\n", sc->sc_dev.dv_xname));
756 
757 	if (sc->sc_intr_pipe != NULL) {
758 		usbd_abort_pipe(sc->sc_intr_pipe);
759 		err = usbd_close_pipe(sc->sc_intr_pipe);
760 		if (err)
761 			printf("%s: close interrupt pipe failed: %s\n",
762 			    sc->sc_dev.dv_xname, usbd_errstr(err));
763 		free(sc->sc_intr_buf, M_USBDEV, 0);
764 		sc->sc_intr_pipe = NULL;
765 	}
766 }
767 
768 static void
769 uticom_intr(struct usbd_xfer *xfer, void *priv, usbd_status status)
770 {
771 	struct uticom_softc *sc = priv;
772 	u_char *buf = sc->sc_intr_buf;
773 
774 	if (usbd_is_dying(sc->sc_udev))
775 		return;
776 
777 	if (status != USBD_NORMAL_COMPLETION) {
778 		if (status == USBD_NOT_STARTED || status == USBD_CANCELLED) {
779 			DPRINTF(("%s: uticom_intr: int status: %s\n",
780 			    sc->sc_dev.dv_xname, usbd_errstr(status)));
781 			return;
782 		}
783 
784 		DPRINTF(("%s: uticom_intr: abnormal status: %s\n",
785 		    sc->sc_dev.dv_xname, usbd_errstr(status)));
786 		usbd_clear_endpoint_stall_async(sc->sc_intr_pipe);
787 		return;
788 	}
789 
790 	if (!xfer->actlen)
791 		return;
792 
793 	DPRINTF(("%s: xfer_length = %d\n", sc->sc_dev.dv_xname,
794 	    xfer->actlen));
795 
796 	sc->sc_lsr = sc->sc_msr = 0;
797 
798 	if (buf[0] == 0) {
799 		/* msr registers */
800 		if (buf[1] & UTICOM_MCR_CTS)
801 			sc->sc_msr |= UMSR_CTS;
802 		if (buf[1] & UTICOM_MCR_DSR)
803 			sc->sc_msr |= UMSR_DSR;
804 		if (buf[1] & UTICOM_MCR_CD)
805 			sc->sc_msr |= UMSR_DCD;
806 		if (buf[1] & UTICOM_MCR_RI)
807 			sc->sc_msr |= UMSR_RI;
808 	} else {
809 		/* lsr registers */
810 		if (buf[0] & UTICOM_LCR_OVR)
811 			sc->sc_lsr |= ULSR_OE;
812 		if (buf[0] & UTICOM_LCR_PTE)
813 			sc->sc_lsr |= ULSR_PE;
814 		if (buf[0] & UTICOM_LCR_FRE)
815 			sc->sc_lsr |= ULSR_FE;
816 		if (buf[0] & UTICOM_LCR_BRK)
817 			sc->sc_lsr |= ULSR_BI;
818 	}
819 
820 //	if (uticomstickdsr)
821 //		sc->sc_msr |= UMSR_DSR;
822 
823 	ucom_status_change((struct ucom_softc *)sc->sc_subdev);
824 }
825 
826 static void
827 uticom_get_status(void *addr, int portno, u_char *lsr, u_char *msr)
828 {
829 #if 0 /* TODO */
830 	struct uticom_softc *sc = addr;
831 
832 	DPRINTF(("uticom_get_status:\n"));
833 
834 	if (lsr != NULL)
835 		*lsr = sc->sc_lsr;
836 	if (msr != NULL)
837 		*msr = sc->sc_msr;
838 #endif
839 	return;
840 }
841 
842 static int
843 uticom_download_fw(struct uticom_softc *sc, int pipeno,
844     struct usbd_device *dev)
845 {
846 	u_char *obuf, *firmware;
847 	size_t firmware_size;
848 	int buffer_size, pos;
849 	uint8_t cs = 0, *buffer;
850 	usbd_status err;
851 	struct uticom_fw_header *header;
852 	struct usbd_xfer *oxfer = 0;
853 	usbd_status error = 0;
854 	struct usbd_pipe *pipe;
855 
856 	error = loadfirmware("tusb3410", &firmware, &firmware_size);
857 	if (error)
858 		return (error);
859 
860 	buffer_size = UTICOM_FW_BUFSZ + sizeof(struct uticom_fw_header);
861 	buffer = malloc(buffer_size, M_USBDEV, M_WAITOK | M_CANFAIL);
862 
863 	if (!buffer) {
864 		printf("%s: uticom_download_fw: out of memory\n",
865 		    sc->sc_dev.dv_xname);
866 		free(firmware, M_DEVBUF, 0);
867 		return ENOMEM;
868 	}
869 
870 	memcpy(buffer, firmware, firmware_size);
871 	memset(buffer + firmware_size, 0xff, buffer_size - firmware_size);
872 
873 	for (pos = sizeof(struct uticom_fw_header); pos < buffer_size; pos++)
874 		cs = (uint8_t)(cs + buffer[pos]);
875 
876 	header = (struct uticom_fw_header*)buffer;
877 	header->length = (uint16_t)(buffer_size -
878 	    sizeof(struct uticom_fw_header));
879 	header->checkSum = cs;
880 
881 	DPRINTF(("%s: downloading firmware ...\n",
882 	    sc->sc_dev.dv_xname));
883 
884 	err = usbd_open_pipe(sc->sc_iface, pipeno, USBD_EXCLUSIVE_USE,
885 	    &pipe);
886 	if (err) {
887 		printf("%s: open bulk out error (addr %d): %s\n",
888 		    sc->sc_dev.dv_xname, pipeno, usbd_errstr(err));
889 		error = EIO;
890 		goto finish;
891 	}
892 
893 	oxfer = usbd_alloc_xfer(dev);
894 	if (oxfer == NULL) {
895 		error = ENOMEM;
896 		goto finish;
897 	}
898 
899 	obuf = usbd_alloc_buffer(oxfer, buffer_size);
900 	if (obuf == NULL) {
901 		error = ENOMEM;
902 		goto finish;
903 	}
904 
905 	memcpy(obuf, buffer, buffer_size);
906 
907 	usbd_setup_xfer(oxfer, pipe, (void *)sc, obuf, buffer_size,
908 	    USBD_NO_COPY | USBD_SYNCHRONOUS, USBD_NO_TIMEOUT, 0);
909 	err = usbd_transfer(oxfer);
910 
911 	if (err != USBD_NORMAL_COMPLETION)
912 		printf("%s: uticom_download_fw: error: %s\n",
913 		    sc->sc_dev.dv_xname, usbd_errstr(err));
914 
915 finish:
916 	free(firmware, M_DEVBUF, 0);
917 	usbd_free_buffer(oxfer);
918 	usbd_free_xfer(oxfer);
919 	oxfer = NULL;
920 	usbd_abort_pipe(pipe);
921 	usbd_close_pipe(pipe);
922 	free(buffer, M_USBDEV, 0);
923 	return err;
924 }
925