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