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