xref: /openbsd-src/sys/dev/usb/uticom.c (revision 81508fe356eb7772a68118f65f91723ce5261d7d)
1*81508fe3Sjsg /*	$OpenBSD: uticom.c,v 1.36 2024/05/23 03:21:09 jsg Exp $	*/
243ee0e3eSderaadt /*
343ee0e3eSderaadt  * Copyright (c) 2005 Dmitry Komissaroff <dxi@mail.ru>.
443ee0e3eSderaadt  *
543ee0e3eSderaadt  * Redistribution and use in source and binary forms, with or without
643ee0e3eSderaadt  * modification, are permitted provided that the following conditions
743ee0e3eSderaadt  * are met:
843ee0e3eSderaadt  * 1. Redistributions of source code must retain the above copyright
943ee0e3eSderaadt  *    notice, this list of conditions and the following disclaimer.
1043ee0e3eSderaadt  * 2. Redistributions in binary form must reproduce the above copyright
1143ee0e3eSderaadt  *    notice, this list of conditions and the following disclaimer in the
1243ee0e3eSderaadt  *    documentation and/or other materials provided with the distribution.
1343ee0e3eSderaadt  *
1443ee0e3eSderaadt  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1543ee0e3eSderaadt  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1643ee0e3eSderaadt  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1743ee0e3eSderaadt  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1843ee0e3eSderaadt  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1943ee0e3eSderaadt  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2043ee0e3eSderaadt  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2143ee0e3eSderaadt  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2243ee0e3eSderaadt  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2343ee0e3eSderaadt  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2443ee0e3eSderaadt  * SUCH DAMAGE.
2543ee0e3eSderaadt  */
2643ee0e3eSderaadt 
2743ee0e3eSderaadt #include <sys/param.h>
2843ee0e3eSderaadt #include <sys/systm.h>
2943ee0e3eSderaadt #include <sys/device.h>
3043ee0e3eSderaadt #include <sys/malloc.h>
3143ee0e3eSderaadt #include <sys/tty.h>
3243ee0e3eSderaadt 
3343ee0e3eSderaadt #include <machine/bus.h>
3443ee0e3eSderaadt 
3543ee0e3eSderaadt #include <dev/usb/usb.h>
3643ee0e3eSderaadt #include <dev/usb/usbdi.h>
3743ee0e3eSderaadt #include <dev/usb/usbdivar.h>
3843ee0e3eSderaadt #include <dev/usb/usbdi_util.h>
3943ee0e3eSderaadt #include <dev/usb/usbdevs.h>
4043ee0e3eSderaadt #include <dev/usb/usbcdc.h>
4143ee0e3eSderaadt 
4243ee0e3eSderaadt #include <dev/usb/ucomvar.h>
4343ee0e3eSderaadt 
44ae343004Smpi #ifdef UTICOM_DEBUG
4543ee0e3eSderaadt static int uticomdebug = 0;
4697b8fa93Swiniger #define DPRINTFN(n, x)	do { if (uticomdebug > (n)) printf x; } while (0)
4743ee0e3eSderaadt #else
4843ee0e3eSderaadt #define DPRINTFN(n, x)
4943ee0e3eSderaadt #endif
5043ee0e3eSderaadt 
5143ee0e3eSderaadt #define DPRINTF(x) DPRINTFN(0, x)
5243ee0e3eSderaadt 
5343ee0e3eSderaadt #define	UTICOM_CONFIG_INDEX	1
5443ee0e3eSderaadt #define	UTICOM_ACTIVE_INDEX	2
5543ee0e3eSderaadt 
5643ee0e3eSderaadt #define	UTICOM_IFACE_INDEX	0
5743ee0e3eSderaadt 
5843ee0e3eSderaadt /*
5943ee0e3eSderaadt  * These are the maximum number of bytes transferred per frame.
6043ee0e3eSderaadt  * The output buffer size cannot be increased due to the size encoding.
6143ee0e3eSderaadt  */
6243ee0e3eSderaadt #define UTICOM_IBUFSZ		64
6343ee0e3eSderaadt #define UTICOM_OBUFSZ		64
6443ee0e3eSderaadt 
6543ee0e3eSderaadt #define UTICOM_FW_BUFSZ		16284
6643ee0e3eSderaadt 
6743ee0e3eSderaadt #define UTICOM_INTR_INTERVAL	100	/* ms */
6843ee0e3eSderaadt 
6943ee0e3eSderaadt #define UTICOM_RQ_LINE		0
7043ee0e3eSderaadt /* Used to sync data0/1-toggle on reopen bulk pipe. */
7143ee0e3eSderaadt #define UTICOM_RQ_SOF		1
7243ee0e3eSderaadt #define UTICOM_RQ_SON		2
7343ee0e3eSderaadt 
7443ee0e3eSderaadt #define UTICOM_RQ_BAUD		3
7543ee0e3eSderaadt #define UTICOM_RQ_LCR		4
7643ee0e3eSderaadt #define UTICOM_RQ_FCR		5
7743ee0e3eSderaadt #define UTICOM_RQ_RTS		6
7843ee0e3eSderaadt #define UTICOM_RQ_DTR		7
7943ee0e3eSderaadt #define UTICOM_RQ_BREAK		8
8043ee0e3eSderaadt #define UTICOM_RQ_CRTSCTS	9
8143ee0e3eSderaadt 
8243ee0e3eSderaadt #define UTICOM_BRATE_REF	923077
8343ee0e3eSderaadt 
8443ee0e3eSderaadt #define UTICOM_SET_DATA_BITS(x)	(x - 5)
8543ee0e3eSderaadt 
8643ee0e3eSderaadt #define UTICOM_STOP_BITS_1	0x00
8743ee0e3eSderaadt #define UTICOM_STOP_BITS_2	0x40
8843ee0e3eSderaadt 
8943ee0e3eSderaadt #define UTICOM_PARITY_NONE	0x00
9043ee0e3eSderaadt #define UTICOM_PARITY_ODD	0x08
9143ee0e3eSderaadt #define UTICOM_PARITY_EVEN	0x18
9243ee0e3eSderaadt 
9343ee0e3eSderaadt #define UTICOM_LCR_OVR		0x1
9443ee0e3eSderaadt #define UTICOM_LCR_PTE		0x2
9543ee0e3eSderaadt #define UTICOM_LCR_FRE		0x4
9643ee0e3eSderaadt #define UTICOM_LCR_BRK		0x8
9743ee0e3eSderaadt 
9843ee0e3eSderaadt #define UTICOM_MCR_CTS		0x1
9943ee0e3eSderaadt #define UTICOM_MCR_DSR		0x2
10043ee0e3eSderaadt #define UTICOM_MCR_CD		0x4
10143ee0e3eSderaadt #define UTICOM_MCR_RI		0x8
10243ee0e3eSderaadt 
10343ee0e3eSderaadt /* Structures */
10443ee0e3eSderaadt struct uticom_fw_header {
10543ee0e3eSderaadt 	uint16_t	length;
10643ee0e3eSderaadt 	uint8_t		checkSum;
107f117630dSjakemsr } __packed;
10843ee0e3eSderaadt 
10943ee0e3eSderaadt struct uticom_buf {
11043ee0e3eSderaadt 	unsigned int		buf_size;
11143ee0e3eSderaadt 	char			*buf_buf;
11243ee0e3eSderaadt 	char			*buf_get;
11343ee0e3eSderaadt 	char			*buf_put;
11443ee0e3eSderaadt };
11543ee0e3eSderaadt 
11643ee0e3eSderaadt struct	uticom_softc {
11743ee0e3eSderaadt 	struct device		 sc_dev;	/* base device */
118ab0b1be7Smglocker 	struct usbd_device	*sc_udev;	/* device */
119ab0b1be7Smglocker 	struct usbd_interface	*sc_iface;	/* interface */
12043ee0e3eSderaadt 
121ab0b1be7Smglocker 	struct usbd_interface	*sc_intr_iface;	/* interrupt interface */
12243ee0e3eSderaadt 	int			sc_intr_number;	/* interrupt number */
123ab0b1be7Smglocker 	struct usbd_pipe	*sc_intr_pipe;	/* interrupt pipe */
12443ee0e3eSderaadt 	u_char			*sc_intr_buf;	/* interrupt buffer */
12543ee0e3eSderaadt 	int			sc_isize;
12643ee0e3eSderaadt 
12743ee0e3eSderaadt 	u_char			sc_dtr;		/* current DTR state */
12843ee0e3eSderaadt 	u_char			sc_rts;		/* current RTS state */
12943ee0e3eSderaadt 	u_char			sc_status;
13043ee0e3eSderaadt 
13143ee0e3eSderaadt 	u_char			sc_lsr;		/* Local status register */
13243ee0e3eSderaadt 	u_char			sc_msr;		/* uticom status register */
13343ee0e3eSderaadt 
13443ee0e3eSderaadt 	struct device		*sc_subdev;
13543ee0e3eSderaadt };
13643ee0e3eSderaadt 
13743ee0e3eSderaadt static	usbd_status uticom_reset(struct uticom_softc *);
13843ee0e3eSderaadt static	usbd_status uticom_set_crtscts(struct uticom_softc *);
139ab0b1be7Smglocker static	void uticom_intr(struct usbd_xfer *, void *, usbd_status);
14043ee0e3eSderaadt 
14143ee0e3eSderaadt static	void uticom_set(void *, int, int, int);
14243ee0e3eSderaadt static	void uticom_dtr(struct uticom_softc *, int);
14343ee0e3eSderaadt static	void uticom_rts(struct uticom_softc *, int);
14443ee0e3eSderaadt static	void uticom_break(struct uticom_softc *, int);
14543ee0e3eSderaadt static	void uticom_get_status(void *, int, u_char *, u_char *);
14643ee0e3eSderaadt static	int  uticom_param(void *, int, struct termios *);
14743ee0e3eSderaadt static	int  uticom_open(void *, int);
14843ee0e3eSderaadt static	void uticom_close(void *, int);
14943ee0e3eSderaadt 
150ef89f9e6Smpi void uticom_attach_hook(struct device *);
151e5f361e2Sjasper 
15243ee0e3eSderaadt static int uticom_download_fw(struct uticom_softc *sc, int pipeno,
153ab0b1be7Smglocker     struct usbd_device *dev);
15443ee0e3eSderaadt 
155c520a48cSnaddy const struct ucom_methods uticom_methods = {
15643ee0e3eSderaadt 	uticom_get_status,
15743ee0e3eSderaadt 	uticom_set,
15843ee0e3eSderaadt 	uticom_param,
159a1b4eae8Smpi 	NULL,
16043ee0e3eSderaadt 	uticom_open,
16143ee0e3eSderaadt 	uticom_close,
16243ee0e3eSderaadt 	NULL,
16343ee0e3eSderaadt 	NULL
16443ee0e3eSderaadt };
16543ee0e3eSderaadt 
16643ee0e3eSderaadt int	uticom_match(struct device *, void *, void *);
16743ee0e3eSderaadt void	uticom_attach(struct device *, struct device *, void *);
16843ee0e3eSderaadt int	uticom_detach(struct device *, int);
16943ee0e3eSderaadt 
17043ee0e3eSderaadt struct cfdriver uticom_cd = {
17143ee0e3eSderaadt 	NULL, "uticom", DV_DULL
17243ee0e3eSderaadt };
17343ee0e3eSderaadt 
17443ee0e3eSderaadt const struct cfattach uticom_ca = {
17584f0598eSmpi 	sizeof(struct uticom_softc), uticom_match, uticom_attach, uticom_detach
17643ee0e3eSderaadt };
17743ee0e3eSderaadt 
178a0977bbbSjasper static const struct usb_devno uticom_devs[] = {
179a0977bbbSjasper 	{ USB_VENDOR_TI, USB_PRODUCT_TI_TUSB3410 },
1801b0efd47Sjasper 	{ USB_VENDOR_TI, USB_PRODUCT_TI_MSP430_JTAG },
1810561e941Sjasper 	{ USB_VENDOR_STARTECH, USB_PRODUCT_STARTECH_ICUSB232X },
182e5f361e2Sjasper 	{ USB_VENDOR_MOXA, USB_PRODUCT_MOXA_UPORT1110 },
183e5f361e2Sjasper 	{ USB_VENDOR_ABBOTT, USB_PRODUCT_ABBOTT_STEREO_PLUG }
184a0977bbbSjasper };
185a0977bbbSjasper 
18643ee0e3eSderaadt int
uticom_match(struct device * parent,void * match,void * aux)18743ee0e3eSderaadt uticom_match(struct device *parent, void *match, void *aux)
18843ee0e3eSderaadt {
18943ee0e3eSderaadt 	struct usb_attach_arg *uaa = aux;
19043ee0e3eSderaadt 
19143ee0e3eSderaadt 	if (uaa->iface != NULL)
19243ee0e3eSderaadt 		return (UMATCH_NONE);
19343ee0e3eSderaadt 
194a0977bbbSjasper 	return (usb_lookup(uticom_devs, uaa->vendor, uaa->product) != NULL ?
195a0977bbbSjasper 	    UMATCH_VENDOR_PRODUCT : UMATCH_NONE);
19643ee0e3eSderaadt }
19743ee0e3eSderaadt 
19843ee0e3eSderaadt void
uticom_attach(struct device * parent,struct device * self,void * aux)19943ee0e3eSderaadt uticom_attach(struct device *parent, struct device *self, void *aux)
20043ee0e3eSderaadt {
20143ee0e3eSderaadt 	struct uticom_softc	*sc = (struct uticom_softc *)self;
20243ee0e3eSderaadt 	struct usb_attach_arg	*uaa = aux;
203ab0b1be7Smglocker 	struct usbd_device	*dev = uaa->device;
204e5f361e2Sjasper 
205e5f361e2Sjasper 	sc->sc_udev = dev;
206e5f361e2Sjasper 	sc->sc_iface = uaa->iface;
207e5f361e2Sjasper 
208ef89f9e6Smpi 	config_mountroot(self, uticom_attach_hook);
209e5f361e2Sjasper }
210e5f361e2Sjasper 
211e5f361e2Sjasper void
uticom_attach_hook(struct device * self)212ef89f9e6Smpi uticom_attach_hook(struct device *self)
213e5f361e2Sjasper {
214ef89f9e6Smpi 	struct uticom_softc		*sc = (struct uticom_softc *)self;
21543ee0e3eSderaadt 	usb_config_descriptor_t		*cdesc;
21643ee0e3eSderaadt 	usb_interface_descriptor_t	*id;
21743ee0e3eSderaadt 	usb_endpoint_descriptor_t	*ed;
21843ee0e3eSderaadt 	usbd_status			 err;
21943ee0e3eSderaadt 	int				 status, i;
22043ee0e3eSderaadt 	usb_device_descriptor_t		*dd;
22143ee0e3eSderaadt 	struct ucom_attach_args		 uca;
22243ee0e3eSderaadt 
22343ee0e3eSderaadt 	/* Initialize endpoints. */
22443ee0e3eSderaadt 	uca.bulkin = uca.bulkout = -1;
22543ee0e3eSderaadt 	sc->sc_intr_number = -1;
22643ee0e3eSderaadt 	sc->sc_intr_pipe = NULL;
22743ee0e3eSderaadt 
228e5f361e2Sjasper 	dd = usbd_get_device_descriptor(sc->sc_udev);
22943ee0e3eSderaadt 	DPRINTF(("%s: uticom_attach: num of configurations %d\n",
23043ee0e3eSderaadt 	    sc->sc_dev.dv_xname, dd->bNumConfigurations));
23143ee0e3eSderaadt 
23243ee0e3eSderaadt 	/* The device without firmware has single configuration with single
23343ee0e3eSderaadt 	 * bulk out interface. */
23443ee0e3eSderaadt 	if (dd->bNumConfigurations > 1)
23543ee0e3eSderaadt 		goto fwload_done;
23643ee0e3eSderaadt 
23743ee0e3eSderaadt 	/* Loading firmware. */
23843ee0e3eSderaadt 	DPRINTF(("%s: uticom_attach: starting loading firmware\n",
23943ee0e3eSderaadt 	    sc->sc_dev.dv_xname));
24043ee0e3eSderaadt 
241e5f361e2Sjasper 	err = usbd_set_config_index(sc->sc_udev, UTICOM_CONFIG_INDEX, 1);
24243ee0e3eSderaadt 	if (err) {
24343ee0e3eSderaadt 		printf("%s: failed to set configuration: %s\n",
24443ee0e3eSderaadt 		    sc->sc_dev.dv_xname, usbd_errstr(err));
245cbb8e2d4Spirofti 		usbd_deactivate(sc->sc_udev);
24643ee0e3eSderaadt 		return;
24743ee0e3eSderaadt 	}
24843ee0e3eSderaadt 
24943ee0e3eSderaadt 	/* Get the config descriptor. */
25043ee0e3eSderaadt 	cdesc = usbd_get_config_descriptor(sc->sc_udev);
25143ee0e3eSderaadt 
25243ee0e3eSderaadt 	if (cdesc == NULL) {
25343ee0e3eSderaadt 		printf("%s: failed to get configuration descriptor\n",
25443ee0e3eSderaadt 		    sc->sc_dev.dv_xname);
255cbb8e2d4Spirofti 		usbd_deactivate(sc->sc_udev);
25643ee0e3eSderaadt 		return;
25743ee0e3eSderaadt 	}
25843ee0e3eSderaadt 
259e5f361e2Sjasper 	err = usbd_device2interface_handle(sc->sc_udev, UTICOM_IFACE_INDEX,
26043ee0e3eSderaadt 	    &sc->sc_iface);
26143ee0e3eSderaadt 	if (err) {
26243ee0e3eSderaadt 		printf("%s: failed to get interface: %s\n",
26343ee0e3eSderaadt 		    sc->sc_dev.dv_xname, usbd_errstr(err));
264cbb8e2d4Spirofti 		usbd_deactivate(sc->sc_udev);
26543ee0e3eSderaadt 		return;
26643ee0e3eSderaadt 	}
26743ee0e3eSderaadt 
26843ee0e3eSderaadt 	/* Find the bulk out interface used to upload firmware. */
26943ee0e3eSderaadt 	id = usbd_get_interface_descriptor(sc->sc_iface);
27043ee0e3eSderaadt 
27143ee0e3eSderaadt 	for (i = 0; i < id->bNumEndpoints; i++) {
27243ee0e3eSderaadt 		ed = usbd_interface2endpoint_descriptor(sc->sc_iface, i);
27343ee0e3eSderaadt 		if (ed == NULL) {
27443ee0e3eSderaadt 			printf("%s: no endpoint descriptor for %d\n",
27543ee0e3eSderaadt 			    sc->sc_dev.dv_xname, i);
276cbb8e2d4Spirofti 			usbd_deactivate(sc->sc_udev);
27743ee0e3eSderaadt 			return;
27843ee0e3eSderaadt 		}
27943ee0e3eSderaadt 
28043ee0e3eSderaadt 		if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT &&
28143ee0e3eSderaadt 		    UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
28243ee0e3eSderaadt 			uca.bulkout = ed->bEndpointAddress;
28343ee0e3eSderaadt 			DPRINTF(("%s: uticom_attach: data bulk out num: %d\n",
28443ee0e3eSderaadt 			    sc->sc_dev.dv_xname, ed->bEndpointAddress));
28543ee0e3eSderaadt 		}
28643ee0e3eSderaadt 
28743ee0e3eSderaadt 		if (uca.bulkout == -1) {
28843ee0e3eSderaadt 			printf("%s: could not find data bulk out\n",
28943ee0e3eSderaadt 			    sc->sc_dev.dv_xname);
290cbb8e2d4Spirofti 			usbd_deactivate(sc->sc_udev);
29143ee0e3eSderaadt 			return;
29243ee0e3eSderaadt 		}
29343ee0e3eSderaadt 	}
29443ee0e3eSderaadt 
295e5f361e2Sjasper 	status = uticom_download_fw(sc, uca.bulkout, sc->sc_udev);
29643ee0e3eSderaadt 
29743ee0e3eSderaadt 	if (status) {
29843ee0e3eSderaadt 		printf("%s: firmware download failed\n",
29943ee0e3eSderaadt 		    sc->sc_dev.dv_xname);
300cbb8e2d4Spirofti 		usbd_deactivate(sc->sc_udev);
30143ee0e3eSderaadt 		return;
30243ee0e3eSderaadt 	} else {
3039072182fSjasper 		DPRINTF(("%s: firmware download succeeded\n",
3049072182fSjasper 		    sc->sc_dev.dv_xname));
30543ee0e3eSderaadt 	}
30643ee0e3eSderaadt 
307e5f361e2Sjasper 	status = usbd_reload_device_desc(sc->sc_udev);
30843ee0e3eSderaadt 	if (status) {
30943ee0e3eSderaadt 		printf("%s: error reloading device descriptor\n",
31043ee0e3eSderaadt 		    sc->sc_dev.dv_xname);
311cbb8e2d4Spirofti 		usbd_deactivate(sc->sc_udev);
31243ee0e3eSderaadt 		return;
31343ee0e3eSderaadt 	}
31443ee0e3eSderaadt 
31543ee0e3eSderaadt fwload_done:
316e5f361e2Sjasper 	dd = usbd_get_device_descriptor(sc->sc_udev);
31743ee0e3eSderaadt 	DPRINTF(("%s: uticom_attach: num of configurations %d\n",
31843ee0e3eSderaadt 	    sc->sc_dev.dv_xname, dd->bNumConfigurations));
31943ee0e3eSderaadt 
320e5f361e2Sjasper 	err = usbd_set_config_index(sc->sc_udev, UTICOM_ACTIVE_INDEX, 1);
32143ee0e3eSderaadt 	if (err) {
32243ee0e3eSderaadt 		printf("%s: failed to set configuration: %s\n",
32343ee0e3eSderaadt 		    sc->sc_dev.dv_xname, usbd_errstr(err));
324cbb8e2d4Spirofti 		usbd_deactivate(sc->sc_udev);
32543ee0e3eSderaadt 		return;
32643ee0e3eSderaadt 	}
32743ee0e3eSderaadt 
32843ee0e3eSderaadt 	/* Get the config descriptor. */
32943ee0e3eSderaadt 	cdesc = usbd_get_config_descriptor(sc->sc_udev);
33043ee0e3eSderaadt 	if (cdesc == NULL) {
33143ee0e3eSderaadt 		printf("%s: failed to get configuration descriptor\n",
33243ee0e3eSderaadt 		    sc->sc_dev.dv_xname);
333cbb8e2d4Spirofti 		usbd_deactivate(sc->sc_udev);
33443ee0e3eSderaadt 		return;
33543ee0e3eSderaadt 	}
33643ee0e3eSderaadt 
33743ee0e3eSderaadt 	/* Get the interface (XXX: multiport chips are not supported yet). */
338e5f361e2Sjasper 	err = usbd_device2interface_handle(sc->sc_udev, UTICOM_IFACE_INDEX,
33943ee0e3eSderaadt 	    &sc->sc_iface);
34043ee0e3eSderaadt 	if (err) {
341ec88ef36Sjsg 		printf("%s: failed to get interface: %s\n",
34243ee0e3eSderaadt 		    sc->sc_dev.dv_xname, usbd_errstr(err));
343cbb8e2d4Spirofti 		usbd_deactivate(sc->sc_udev);
34443ee0e3eSderaadt 		return;
34543ee0e3eSderaadt 	}
34643ee0e3eSderaadt 
34743ee0e3eSderaadt 	/* Find the interrupt endpoints. */
34843ee0e3eSderaadt 	id = usbd_get_interface_descriptor(sc->sc_iface);
34943ee0e3eSderaadt 
35043ee0e3eSderaadt 	for (i = 0; i < id->bNumEndpoints; i++) {
35143ee0e3eSderaadt 		ed = usbd_interface2endpoint_descriptor(sc->sc_iface, i);
35243ee0e3eSderaadt 		if (ed == NULL) {
353ec88ef36Sjsg 			printf("%s: no endpoint descriptor for %d\n",
35443ee0e3eSderaadt 			    sc->sc_dev.dv_xname, i);
355cbb8e2d4Spirofti 			usbd_deactivate(sc->sc_udev);
35643ee0e3eSderaadt 			return;
35743ee0e3eSderaadt 		}
35843ee0e3eSderaadt 
35943ee0e3eSderaadt 		if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
36043ee0e3eSderaadt 		    UE_GET_XFERTYPE(ed->bmAttributes) == UE_INTERRUPT) {
36143ee0e3eSderaadt 			sc->sc_intr_number = ed->bEndpointAddress;
36243ee0e3eSderaadt 			sc->sc_isize = UGETW(ed->wMaxPacketSize);
363e5f361e2Sjasper 
36443ee0e3eSderaadt 		}
36543ee0e3eSderaadt 	}
36643ee0e3eSderaadt 
36743ee0e3eSderaadt 	if (sc->sc_intr_number == -1) {
36843ee0e3eSderaadt 		printf("%s: could not find interrupt in\n",
36943ee0e3eSderaadt 		    sc->sc_dev.dv_xname);
370cbb8e2d4Spirofti 		usbd_deactivate(sc->sc_udev);
37143ee0e3eSderaadt 		return;
37243ee0e3eSderaadt 	}
37343ee0e3eSderaadt 
37443ee0e3eSderaadt 	/* Keep interface for interrupt. */
37543ee0e3eSderaadt 	sc->sc_intr_iface = sc->sc_iface;
37643ee0e3eSderaadt 
37743ee0e3eSderaadt 	/* Find the bulk{in,out} endpoints. */
37843ee0e3eSderaadt 	id = usbd_get_interface_descriptor(sc->sc_iface);
37943ee0e3eSderaadt 
38043ee0e3eSderaadt 	for (i = 0; i < id->bNumEndpoints; i++) {
381e4571498Sjsg 		ed = usbd_interface2endpoint_descriptor(sc->sc_iface, i);
38243ee0e3eSderaadt 		if (ed == NULL) {
38343ee0e3eSderaadt 			printf("%s: no endpoint descriptor for %d\n",
38443ee0e3eSderaadt 			    sc->sc_dev.dv_xname, i);
385cbb8e2d4Spirofti 			usbd_deactivate(sc->sc_udev);
38643ee0e3eSderaadt 			return;
38743ee0e3eSderaadt 		}
38843ee0e3eSderaadt 
38943ee0e3eSderaadt 		if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
39043ee0e3eSderaadt 		    UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
39143ee0e3eSderaadt 			uca.bulkin = ed->bEndpointAddress;
39243ee0e3eSderaadt 		} else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT &&
39343ee0e3eSderaadt 		    UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
39443ee0e3eSderaadt 			uca.bulkout = ed->bEndpointAddress;
39543ee0e3eSderaadt 		}
39643ee0e3eSderaadt 	}
39743ee0e3eSderaadt 
39843ee0e3eSderaadt 	if (uca.bulkin == -1) {
39943ee0e3eSderaadt 		printf("%s: could not find data bulk in\n",
40043ee0e3eSderaadt 		    sc->sc_dev.dv_xname);
401cbb8e2d4Spirofti 		usbd_deactivate(sc->sc_udev);
40243ee0e3eSderaadt 		return;
40343ee0e3eSderaadt 	}
40443ee0e3eSderaadt 
40543ee0e3eSderaadt 	if (uca.bulkout == -1) {
40643ee0e3eSderaadt 		printf("%s: could not find data bulk out\n",
40743ee0e3eSderaadt 		    sc->sc_dev.dv_xname);
408cbb8e2d4Spirofti 		usbd_deactivate(sc->sc_udev);
40943ee0e3eSderaadt 		return;
41043ee0e3eSderaadt 	}
41143ee0e3eSderaadt 
41243ee0e3eSderaadt 	sc->sc_dtr = sc->sc_rts = -1;
41343ee0e3eSderaadt 
41443ee0e3eSderaadt 	uca.portno = UCOM_UNK_PORTNO;
41543ee0e3eSderaadt 	uca.ibufsize = UTICOM_IBUFSZ;
41643ee0e3eSderaadt 	uca.obufsize = UTICOM_OBUFSZ;
41743ee0e3eSderaadt 	uca.ibufsizepad = UTICOM_IBUFSZ;
418e5f361e2Sjasper 	uca.device = sc->sc_udev;
419e4571498Sjsg 	uca.iface = sc->sc_iface;
42043ee0e3eSderaadt 	uca.opkthdrlen = 0;
42143ee0e3eSderaadt 	uca.methods = &uticom_methods;
42243ee0e3eSderaadt 	uca.arg = sc;
42343ee0e3eSderaadt 	uca.info = NULL;
42443ee0e3eSderaadt 
42543ee0e3eSderaadt 	err = uticom_reset(sc);
42643ee0e3eSderaadt 	if (err) {
42743ee0e3eSderaadt 		printf("%s: reset failed: %s\n",
42843ee0e3eSderaadt 		    sc->sc_dev.dv_xname, usbd_errstr(err));
429cbb8e2d4Spirofti 		usbd_deactivate(sc->sc_udev);
43043ee0e3eSderaadt 		return;
43143ee0e3eSderaadt 	}
43243ee0e3eSderaadt 
43343ee0e3eSderaadt 	DPRINTF(("%s: uticom_attach: in = 0x%x, out = 0x%x, intr = 0x%x\n",
43443ee0e3eSderaadt 	    sc->sc_dev.dv_xname, uca.bulkin,
43543ee0e3eSderaadt 	    uca.bulkout, sc->sc_intr_number));
43643ee0e3eSderaadt 
437e5f361e2Sjasper 	sc->sc_subdev = config_found_sm((struct device *)sc, &uca, ucomprint, ucomsubmatch);
43843ee0e3eSderaadt }
43943ee0e3eSderaadt 
44043ee0e3eSderaadt int
uticom_detach(struct device * self,int flags)44143ee0e3eSderaadt uticom_detach(struct device *self, int flags)
44243ee0e3eSderaadt {
44343ee0e3eSderaadt 	struct uticom_softc *sc = (struct uticom_softc *)self;
44443ee0e3eSderaadt 
44543ee0e3eSderaadt 	DPRINTF(("%s: uticom_detach: sc = %p\n",
44643ee0e3eSderaadt 	    sc->sc_dev.dv_xname, sc));
44743ee0e3eSderaadt 
44843ee0e3eSderaadt 	if (sc->sc_subdev != NULL) {
44943ee0e3eSderaadt 		config_detach(sc->sc_subdev, flags);
45043ee0e3eSderaadt 		sc->sc_subdev = NULL;
45143ee0e3eSderaadt 	}
45243ee0e3eSderaadt 
45343ee0e3eSderaadt 	if (sc->sc_intr_pipe != NULL) {
45443ee0e3eSderaadt 		usbd_close_pipe(sc->sc_intr_pipe);
455234dfda1Sderaadt 		free(sc->sc_intr_buf, M_USBDEV, sc->sc_isize);
45643ee0e3eSderaadt 		sc->sc_intr_pipe = NULL;
45743ee0e3eSderaadt 	}
45843ee0e3eSderaadt 
45943ee0e3eSderaadt 	return (0);
46043ee0e3eSderaadt }
46143ee0e3eSderaadt 
46243ee0e3eSderaadt static usbd_status
uticom_reset(struct uticom_softc * sc)46343ee0e3eSderaadt uticom_reset(struct uticom_softc *sc)
46443ee0e3eSderaadt {
46543ee0e3eSderaadt 	usb_device_request_t req;
46643ee0e3eSderaadt 	usbd_status err;
46743ee0e3eSderaadt 
46843ee0e3eSderaadt 	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
46943ee0e3eSderaadt 	req.bRequest = UTICOM_RQ_SON;
47043ee0e3eSderaadt 	USETW(req.wValue, 0);
47143ee0e3eSderaadt 	USETW(req.wIndex, 0);
47243ee0e3eSderaadt 	USETW(req.wLength, 0);
47343ee0e3eSderaadt 
47443ee0e3eSderaadt 	err = usbd_do_request(sc->sc_udev, &req, NULL);
47543ee0e3eSderaadt 	if (err){
47643ee0e3eSderaadt 		printf("%s: uticom_reset: %s\n",
47743ee0e3eSderaadt 		    sc->sc_dev.dv_xname, usbd_errstr(err));
47843ee0e3eSderaadt 		return (EIO);
47943ee0e3eSderaadt 	}
48043ee0e3eSderaadt 
48143ee0e3eSderaadt 	DPRINTF(("%s: uticom_reset: done\n", sc->sc_dev.dv_xname));
48243ee0e3eSderaadt 	return (0);
48343ee0e3eSderaadt }
48443ee0e3eSderaadt 
48543ee0e3eSderaadt static void
uticom_set(void * addr,int portno,int reg,int onoff)48643ee0e3eSderaadt uticom_set(void *addr, int portno, int reg, int onoff)
48743ee0e3eSderaadt {
48843ee0e3eSderaadt 	struct uticom_softc *sc = addr;
48943ee0e3eSderaadt 
49043ee0e3eSderaadt 	switch (reg) {
49143ee0e3eSderaadt 	case UCOM_SET_DTR:
49243ee0e3eSderaadt 		uticom_dtr(sc, onoff);
49343ee0e3eSderaadt 		break;
49443ee0e3eSderaadt 	case UCOM_SET_RTS:
49543ee0e3eSderaadt 		uticom_rts(sc, onoff);
49643ee0e3eSderaadt 		break;
49743ee0e3eSderaadt 	case UCOM_SET_BREAK:
49843ee0e3eSderaadt 		uticom_break(sc, onoff);
49943ee0e3eSderaadt 		break;
50043ee0e3eSderaadt 	default:
50143ee0e3eSderaadt 		break;
50243ee0e3eSderaadt 	}
50343ee0e3eSderaadt }
50443ee0e3eSderaadt 
50543ee0e3eSderaadt static void
uticom_dtr(struct uticom_softc * sc,int onoff)50643ee0e3eSderaadt uticom_dtr(struct uticom_softc *sc, int onoff)
50743ee0e3eSderaadt {
50843ee0e3eSderaadt 	usb_device_request_t req;
50943ee0e3eSderaadt 	usbd_status err;
51043ee0e3eSderaadt 
51143ee0e3eSderaadt 	DPRINTF(("%s: uticom_dtr: onoff = %d\n", sc->sc_dev.dv_xname,
51243ee0e3eSderaadt 	    onoff));
51343ee0e3eSderaadt 
51443ee0e3eSderaadt 	if (sc->sc_dtr == onoff)
51543ee0e3eSderaadt 		return;
51643ee0e3eSderaadt 	sc->sc_dtr = onoff;
51743ee0e3eSderaadt 
51843ee0e3eSderaadt 	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
51943ee0e3eSderaadt 	req.bRequest = UTICOM_RQ_DTR;
52043ee0e3eSderaadt 	USETW(req.wValue, sc->sc_dtr ? UCDC_LINE_DTR : 0);
52143ee0e3eSderaadt 	USETW(req.wIndex, 0);
52243ee0e3eSderaadt 	USETW(req.wLength, 0);
52343ee0e3eSderaadt 
52443ee0e3eSderaadt 	err = usbd_do_request(sc->sc_udev, &req, NULL);
52543ee0e3eSderaadt 	if (err)
52643ee0e3eSderaadt 		printf("%s: uticom_dtr: %s\n",
52743ee0e3eSderaadt 		    sc->sc_dev.dv_xname, usbd_errstr(err));
52843ee0e3eSderaadt }
52943ee0e3eSderaadt 
53043ee0e3eSderaadt static void
uticom_rts(struct uticom_softc * sc,int onoff)53143ee0e3eSderaadt uticom_rts(struct uticom_softc *sc, int onoff)
53243ee0e3eSderaadt {
53343ee0e3eSderaadt 	usb_device_request_t req;
53443ee0e3eSderaadt 	usbd_status err;
53543ee0e3eSderaadt 
53643ee0e3eSderaadt 	DPRINTF(("%s: uticom_rts: onoff = %d\n", sc->sc_dev.dv_xname,
53743ee0e3eSderaadt 	    onoff));
53843ee0e3eSderaadt 
53943ee0e3eSderaadt 	if (sc->sc_rts == onoff)
54043ee0e3eSderaadt 		return;
54143ee0e3eSderaadt 	sc->sc_rts = onoff;
54243ee0e3eSderaadt 	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
54343ee0e3eSderaadt 	req.bRequest = UTICOM_RQ_RTS;
54443ee0e3eSderaadt 	USETW(req.wValue, sc->sc_rts ? UCDC_LINE_RTS : 0);
54543ee0e3eSderaadt 	USETW(req.wIndex, 0);
54643ee0e3eSderaadt 	USETW(req.wLength, 0);
54743ee0e3eSderaadt 
54843ee0e3eSderaadt 	err = usbd_do_request(sc->sc_udev, &req, NULL);
54943ee0e3eSderaadt 	if (err)
55043ee0e3eSderaadt 		printf("%s: uticom_rts: %s\n",
55143ee0e3eSderaadt 		    sc->sc_dev.dv_xname, usbd_errstr(err));
55243ee0e3eSderaadt }
55343ee0e3eSderaadt 
55443ee0e3eSderaadt static void
uticom_break(struct uticom_softc * sc,int onoff)55543ee0e3eSderaadt uticom_break(struct uticom_softc *sc, int onoff)
55643ee0e3eSderaadt {
55743ee0e3eSderaadt 	usb_device_request_t req;
55843ee0e3eSderaadt 	usbd_status err;
55943ee0e3eSderaadt 
56043ee0e3eSderaadt 	DPRINTF(("%s: uticom_break: onoff = %d\n", sc->sc_dev.dv_xname,
56143ee0e3eSderaadt 	    onoff));
56243ee0e3eSderaadt 
56343ee0e3eSderaadt 	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
56443ee0e3eSderaadt 	req.bRequest = UTICOM_RQ_BREAK;
56543ee0e3eSderaadt 	USETW(req.wValue, onoff ? 1 : 0);
56643ee0e3eSderaadt 	USETW(req.wIndex, 0);
56743ee0e3eSderaadt 	USETW(req.wLength, 0);
56843ee0e3eSderaadt 
56943ee0e3eSderaadt 	err = usbd_do_request(sc->sc_udev, &req, NULL);
57043ee0e3eSderaadt 	if (err)
57143ee0e3eSderaadt 		printf("%s: uticom_break: %s\n",
57243ee0e3eSderaadt 		    sc->sc_dev.dv_xname, usbd_errstr(err));
57343ee0e3eSderaadt }
57443ee0e3eSderaadt 
57543ee0e3eSderaadt static usbd_status
uticom_set_crtscts(struct uticom_softc * sc)57643ee0e3eSderaadt uticom_set_crtscts(struct uticom_softc *sc)
57743ee0e3eSderaadt {
57843ee0e3eSderaadt 	usb_device_request_t req;
57943ee0e3eSderaadt 	usbd_status err;
58043ee0e3eSderaadt 
58143ee0e3eSderaadt 	DPRINTF(("%s: uticom_set_crtscts: on\n", sc->sc_dev.dv_xname));
58243ee0e3eSderaadt 
58343ee0e3eSderaadt 	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
58443ee0e3eSderaadt 	req.bRequest = UTICOM_RQ_CRTSCTS;
58543ee0e3eSderaadt 	USETW(req.wValue, 1);
58643ee0e3eSderaadt 	USETW(req.wIndex, 0);
58743ee0e3eSderaadt 	USETW(req.wLength, 0);
58843ee0e3eSderaadt 
58943ee0e3eSderaadt 	err = usbd_do_request(sc->sc_udev, &req, NULL);
59043ee0e3eSderaadt 	if (err) {
59143ee0e3eSderaadt 		printf("%s: uticom_set_crtscts: %s\n",
59243ee0e3eSderaadt 		    sc->sc_dev.dv_xname, usbd_errstr(err));
59343ee0e3eSderaadt 		return (err);
59443ee0e3eSderaadt 	}
59543ee0e3eSderaadt 
59643ee0e3eSderaadt 	return (USBD_NORMAL_COMPLETION);
59743ee0e3eSderaadt }
59843ee0e3eSderaadt 
59943ee0e3eSderaadt static int
uticom_param(void * vsc,int portno,struct termios * t)60043ee0e3eSderaadt uticom_param(void *vsc, int portno, struct termios *t)
60143ee0e3eSderaadt {
60243ee0e3eSderaadt 	struct uticom_softc *sc = (struct uticom_softc *)vsc;
60343ee0e3eSderaadt 	usb_device_request_t req;
60443ee0e3eSderaadt 	usbd_status err;
60543ee0e3eSderaadt 	uint8_t data;
60643ee0e3eSderaadt 
60743ee0e3eSderaadt 	DPRINTF(("%s: uticom_param\n", sc->sc_dev.dv_xname));
60843ee0e3eSderaadt 
60943ee0e3eSderaadt 	switch (t->c_ospeed) {
61043ee0e3eSderaadt 	case 1200:
61143ee0e3eSderaadt 	case 2400:
61243ee0e3eSderaadt 	case 4800:
61343ee0e3eSderaadt 	case 7200:
61443ee0e3eSderaadt 	case 9600:
61543ee0e3eSderaadt 	case 14400:
61643ee0e3eSderaadt 	case 19200:
61743ee0e3eSderaadt 	case 38400:
61843ee0e3eSderaadt 	case 57600:
61943ee0e3eSderaadt 	case 115200:
62043ee0e3eSderaadt 	case 230400:
62143ee0e3eSderaadt 	case 460800:
62243ee0e3eSderaadt 	case 921600:
62343ee0e3eSderaadt 		req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
62443ee0e3eSderaadt 		req.bRequest = UTICOM_RQ_BAUD;
62543ee0e3eSderaadt 		USETW(req.wValue, (UTICOM_BRATE_REF / t->c_ospeed));
62643ee0e3eSderaadt 		USETW(req.wIndex, 0);
62743ee0e3eSderaadt 		USETW(req.wLength, 0);
62843ee0e3eSderaadt 
62943ee0e3eSderaadt 		err = usbd_do_request(sc->sc_udev, &req, 0);
63043ee0e3eSderaadt 		if (err) {
63143ee0e3eSderaadt 			printf("%s: uticom_param: %s\n",
63243ee0e3eSderaadt 			    sc->sc_dev.dv_xname, usbd_errstr(err));
63343ee0e3eSderaadt 			return (EIO);
63443ee0e3eSderaadt 		}
63543ee0e3eSderaadt 		break;
63643ee0e3eSderaadt 	default:
63743ee0e3eSderaadt 		printf("%s: uticom_param: unsupported baud rate %d\n",
63843ee0e3eSderaadt 		    sc->sc_dev.dv_xname, t->c_ospeed);
63943ee0e3eSderaadt 		return (EINVAL);
64043ee0e3eSderaadt 	}
64143ee0e3eSderaadt 
64243ee0e3eSderaadt 	switch (ISSET(t->c_cflag, CSIZE)) {
64343ee0e3eSderaadt 	case CS5:
64443ee0e3eSderaadt 		data = UTICOM_SET_DATA_BITS(5);
64543ee0e3eSderaadt 		break;
64643ee0e3eSderaadt 	case CS6:
64743ee0e3eSderaadt 		data = UTICOM_SET_DATA_BITS(6);
64843ee0e3eSderaadt 		break;
64943ee0e3eSderaadt 	case CS7:
65043ee0e3eSderaadt 		data = UTICOM_SET_DATA_BITS(7);
65143ee0e3eSderaadt 		break;
65243ee0e3eSderaadt 	case CS8:
65343ee0e3eSderaadt 		data = UTICOM_SET_DATA_BITS(8);
65443ee0e3eSderaadt 		break;
65543ee0e3eSderaadt 	default:
65643ee0e3eSderaadt 		return (EIO);
65743ee0e3eSderaadt 	}
65843ee0e3eSderaadt 
65943ee0e3eSderaadt 	if (ISSET(t->c_cflag, CSTOPB))
66043ee0e3eSderaadt 		data |= UTICOM_STOP_BITS_2;
66143ee0e3eSderaadt 	else
66243ee0e3eSderaadt 		data |= UTICOM_STOP_BITS_1;
66343ee0e3eSderaadt 
66443ee0e3eSderaadt 	if (ISSET(t->c_cflag, PARENB)) {
66543ee0e3eSderaadt 		if (ISSET(t->c_cflag, PARODD))
66643ee0e3eSderaadt 			data |= UTICOM_PARITY_ODD;
66743ee0e3eSderaadt 		else
66843ee0e3eSderaadt 			data |= UTICOM_PARITY_EVEN;
66943ee0e3eSderaadt 	} else
67043ee0e3eSderaadt 		data |= UTICOM_PARITY_NONE;
67143ee0e3eSderaadt 
67243ee0e3eSderaadt 	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
67343ee0e3eSderaadt 	req.bRequest = UTICOM_RQ_LCR;
67443ee0e3eSderaadt 	USETW(req.wIndex, 0);
67543ee0e3eSderaadt 	USETW(req.wLength, 0);
67643ee0e3eSderaadt 	USETW(req.wValue, data);
67743ee0e3eSderaadt 
67843ee0e3eSderaadt 	err = usbd_do_request(sc->sc_udev, &req, NULL);
67943ee0e3eSderaadt 	if (err) {
68043ee0e3eSderaadt 		printf("%s: uticom_param: %s\n",
68143ee0e3eSderaadt 		    sc->sc_dev.dv_xname, usbd_errstr(err));
68243ee0e3eSderaadt 		return (err);
68343ee0e3eSderaadt 	}
68443ee0e3eSderaadt 
68543ee0e3eSderaadt 	if (ISSET(t->c_cflag, CRTSCTS)) {
68643ee0e3eSderaadt 		err = uticom_set_crtscts(sc);
68743ee0e3eSderaadt 		if (err)
68843ee0e3eSderaadt 			return (EIO);
68943ee0e3eSderaadt 	}
69043ee0e3eSderaadt 
69143ee0e3eSderaadt 	return (0);
69243ee0e3eSderaadt }
69343ee0e3eSderaadt 
69443ee0e3eSderaadt static int
uticom_open(void * addr,int portno)69543ee0e3eSderaadt uticom_open(void *addr, int portno)
69643ee0e3eSderaadt {
69743ee0e3eSderaadt 	struct uticom_softc *sc = addr;
69843ee0e3eSderaadt 	usbd_status err;
69943ee0e3eSderaadt 
700cbb8e2d4Spirofti 	if (usbd_is_dying(sc->sc_udev))
70143ee0e3eSderaadt 		return (ENXIO);
70243ee0e3eSderaadt 
70343ee0e3eSderaadt 	DPRINTF(("%s: uticom_open\n", sc->sc_dev.dv_xname));
70443ee0e3eSderaadt 
70543ee0e3eSderaadt 	sc->sc_status = 0; /* clear status bit */
70643ee0e3eSderaadt 
70743ee0e3eSderaadt 	if (sc->sc_intr_number != -1 && sc->sc_intr_pipe == NULL) {
70843ee0e3eSderaadt 		sc->sc_intr_buf = malloc(sc->sc_isize, M_USBDEV, M_WAITOK);
70943ee0e3eSderaadt 		err = usbd_open_pipe_intr(sc->sc_intr_iface, sc->sc_intr_number,
71043ee0e3eSderaadt 		    USBD_SHORT_XFER_OK, &sc->sc_intr_pipe, sc, sc->sc_intr_buf,
71143ee0e3eSderaadt 		    sc->sc_isize, uticom_intr, UTICOM_INTR_INTERVAL);
71243ee0e3eSderaadt 		if (err) {
71343ee0e3eSderaadt 			printf("%s: cannot open interrupt pipe (addr %d)\n",
71443ee0e3eSderaadt 			    sc->sc_dev.dv_xname, sc->sc_intr_number);
71543ee0e3eSderaadt 			return (EIO);
71643ee0e3eSderaadt 		}
71743ee0e3eSderaadt 	}
71843ee0e3eSderaadt 
71943ee0e3eSderaadt 	DPRINTF(("%s: uticom_open: port opened\n", sc->sc_dev.dv_xname));
72043ee0e3eSderaadt 	return (0);
72143ee0e3eSderaadt }
72243ee0e3eSderaadt 
72343ee0e3eSderaadt static void
uticom_close(void * addr,int portno)72443ee0e3eSderaadt uticom_close(void *addr, int portno)
72543ee0e3eSderaadt {
72643ee0e3eSderaadt 	struct uticom_softc *sc = addr;
72743ee0e3eSderaadt 	usb_device_request_t req;
72843ee0e3eSderaadt 	usbd_status err;
72943ee0e3eSderaadt 
730cbb8e2d4Spirofti 	if (usbd_is_dying(sc->sc_udev))
73143ee0e3eSderaadt 		return;
73243ee0e3eSderaadt 
73343ee0e3eSderaadt 	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
73443ee0e3eSderaadt 	req.bRequest = UTICOM_RQ_SON;
73543ee0e3eSderaadt 	USETW(req.wValue, 0);
73643ee0e3eSderaadt 	USETW(req.wIndex, 0);
73743ee0e3eSderaadt 	USETW(req.wLength, 0);
73843ee0e3eSderaadt 
73943ee0e3eSderaadt 	/* Try to reset UART part of chip. */
74043ee0e3eSderaadt 	err = usbd_do_request(sc->sc_udev, &req, NULL);
74143ee0e3eSderaadt 	if (err) {
74243ee0e3eSderaadt 		printf("%s: uticom_close: %s\n",
74343ee0e3eSderaadt 		    sc->sc_dev.dv_xname, usbd_errstr(err));
74443ee0e3eSderaadt 		return;
74543ee0e3eSderaadt 	}
74643ee0e3eSderaadt 
74743ee0e3eSderaadt 	DPRINTF(("%s: uticom_close: close\n", sc->sc_dev.dv_xname));
74843ee0e3eSderaadt 
74943ee0e3eSderaadt 	if (sc->sc_intr_pipe != NULL) {
75043ee0e3eSderaadt 		err = usbd_close_pipe(sc->sc_intr_pipe);
75143ee0e3eSderaadt 		if (err)
75243ee0e3eSderaadt 			printf("%s: close interrupt pipe failed: %s\n",
75343ee0e3eSderaadt 			    sc->sc_dev.dv_xname, usbd_errstr(err));
754234dfda1Sderaadt 		free(sc->sc_intr_buf, M_USBDEV, sc->sc_isize);
75543ee0e3eSderaadt 		sc->sc_intr_pipe = NULL;
75643ee0e3eSderaadt 	}
75743ee0e3eSderaadt }
75843ee0e3eSderaadt 
75943ee0e3eSderaadt static void
uticom_intr(struct usbd_xfer * xfer,void * priv,usbd_status status)760ab0b1be7Smglocker uticom_intr(struct usbd_xfer *xfer, void *priv, usbd_status status)
76143ee0e3eSderaadt {
76243ee0e3eSderaadt 	struct uticom_softc *sc = priv;
76343ee0e3eSderaadt 	u_char *buf = sc->sc_intr_buf;
76443ee0e3eSderaadt 
765cbb8e2d4Spirofti 	if (usbd_is_dying(sc->sc_udev))
76643ee0e3eSderaadt 		return;
76743ee0e3eSderaadt 
76843ee0e3eSderaadt 	if (status != USBD_NORMAL_COMPLETION) {
76943ee0e3eSderaadt 		if (status == USBD_NOT_STARTED || status == USBD_CANCELLED) {
77043ee0e3eSderaadt 			DPRINTF(("%s: uticom_intr: int status: %s\n",
77143ee0e3eSderaadt 			    sc->sc_dev.dv_xname, usbd_errstr(status)));
77243ee0e3eSderaadt 			return;
77343ee0e3eSderaadt 		}
77443ee0e3eSderaadt 
77543ee0e3eSderaadt 		DPRINTF(("%s: uticom_intr: abnormal status: %s\n",
77643ee0e3eSderaadt 		    sc->sc_dev.dv_xname, usbd_errstr(status)));
77743ee0e3eSderaadt 		usbd_clear_endpoint_stall_async(sc->sc_intr_pipe);
77843ee0e3eSderaadt 		return;
77943ee0e3eSderaadt 	}
78043ee0e3eSderaadt 
78143ee0e3eSderaadt 	if (!xfer->actlen)
78243ee0e3eSderaadt 		return;
78343ee0e3eSderaadt 
78443ee0e3eSderaadt 	DPRINTF(("%s: xfer_length = %d\n", sc->sc_dev.dv_xname,
78543ee0e3eSderaadt 	    xfer->actlen));
78643ee0e3eSderaadt 
78743ee0e3eSderaadt 	sc->sc_lsr = sc->sc_msr = 0;
78843ee0e3eSderaadt 
78943ee0e3eSderaadt 	if (buf[0] == 0) {
79043ee0e3eSderaadt 		/* msr registers */
79143ee0e3eSderaadt 		if (buf[1] & UTICOM_MCR_CTS)
79243ee0e3eSderaadt 			sc->sc_msr |= UMSR_CTS;
79343ee0e3eSderaadt 		if (buf[1] & UTICOM_MCR_DSR)
79443ee0e3eSderaadt 			sc->sc_msr |= UMSR_DSR;
79543ee0e3eSderaadt 		if (buf[1] & UTICOM_MCR_CD)
79643ee0e3eSderaadt 			sc->sc_msr |= UMSR_DCD;
79743ee0e3eSderaadt 		if (buf[1] & UTICOM_MCR_RI)
79843ee0e3eSderaadt 			sc->sc_msr |= UMSR_RI;
79943ee0e3eSderaadt 	} else {
80043ee0e3eSderaadt 		/* lsr registers */
80143ee0e3eSderaadt 		if (buf[0] & UTICOM_LCR_OVR)
80243ee0e3eSderaadt 			sc->sc_lsr |= ULSR_OE;
80343ee0e3eSderaadt 		if (buf[0] & UTICOM_LCR_PTE)
80443ee0e3eSderaadt 			sc->sc_lsr |= ULSR_PE;
80543ee0e3eSderaadt 		if (buf[0] & UTICOM_LCR_FRE)
80643ee0e3eSderaadt 			sc->sc_lsr |= ULSR_FE;
80743ee0e3eSderaadt 		if (buf[0] & UTICOM_LCR_BRK)
80843ee0e3eSderaadt 			sc->sc_lsr |= ULSR_BI;
80943ee0e3eSderaadt 	}
81043ee0e3eSderaadt 
81143ee0e3eSderaadt //	if (uticomstickdsr)
81243ee0e3eSderaadt //		sc->sc_msr |= UMSR_DSR;
81343ee0e3eSderaadt 
81443ee0e3eSderaadt 	ucom_status_change((struct ucom_softc *)sc->sc_subdev);
81543ee0e3eSderaadt }
81643ee0e3eSderaadt 
81743ee0e3eSderaadt static void
uticom_get_status(void * addr,int portno,u_char * lsr,u_char * msr)81843ee0e3eSderaadt uticom_get_status(void *addr, int portno, u_char *lsr, u_char *msr)
81943ee0e3eSderaadt {
82043ee0e3eSderaadt #if 0 /* TODO */
82143ee0e3eSderaadt 	struct uticom_softc *sc = addr;
82243ee0e3eSderaadt 
82343ee0e3eSderaadt 	DPRINTF(("uticom_get_status:\n"));
82443ee0e3eSderaadt 
82543ee0e3eSderaadt 	if (lsr != NULL)
82643ee0e3eSderaadt 		*lsr = sc->sc_lsr;
82743ee0e3eSderaadt 	if (msr != NULL)
82843ee0e3eSderaadt 		*msr = sc->sc_msr;
82943ee0e3eSderaadt #endif
83043ee0e3eSderaadt 	return;
83143ee0e3eSderaadt }
83243ee0e3eSderaadt 
83343ee0e3eSderaadt static int
uticom_download_fw(struct uticom_softc * sc,int pipeno,struct usbd_device * dev)83443ee0e3eSderaadt uticom_download_fw(struct uticom_softc *sc, int pipeno,
835ab0b1be7Smglocker     struct usbd_device *dev)
83643ee0e3eSderaadt {
83743ee0e3eSderaadt 	u_char *obuf, *firmware;
83843ee0e3eSderaadt 	size_t firmware_size;
83943ee0e3eSderaadt 	int buffer_size, pos;
84043ee0e3eSderaadt 	uint8_t cs = 0, *buffer;
84143ee0e3eSderaadt 	usbd_status err;
84243ee0e3eSderaadt 	struct uticom_fw_header *header;
843ab0b1be7Smglocker 	struct usbd_xfer *oxfer = 0;
84443ee0e3eSderaadt 	usbd_status error = 0;
845ab0b1be7Smglocker 	struct usbd_pipe *pipe;
84643ee0e3eSderaadt 
84743ee0e3eSderaadt 	error = loadfirmware("tusb3410", &firmware, &firmware_size);
84843ee0e3eSderaadt 	if (error)
84943ee0e3eSderaadt 		return (error);
85043ee0e3eSderaadt 
85143ee0e3eSderaadt 	buffer_size = UTICOM_FW_BUFSZ + sizeof(struct uticom_fw_header);
852e6ca3d0dSmk 	buffer = malloc(buffer_size, M_USBDEV, M_WAITOK | M_CANFAIL);
85343ee0e3eSderaadt 
85443ee0e3eSderaadt 	if (!buffer) {
85543ee0e3eSderaadt 		printf("%s: uticom_download_fw: out of memory\n",
85643ee0e3eSderaadt 		    sc->sc_dev.dv_xname);
857c9ee9455Sderaadt 		free(firmware, M_DEVBUF, firmware_size);
85843ee0e3eSderaadt 		return ENOMEM;
85943ee0e3eSderaadt 	}
86043ee0e3eSderaadt 
86143ee0e3eSderaadt 	memcpy(buffer, firmware, firmware_size);
86243ee0e3eSderaadt 	memset(buffer + firmware_size, 0xff, buffer_size - firmware_size);
86343ee0e3eSderaadt 
86443ee0e3eSderaadt 	for (pos = sizeof(struct uticom_fw_header); pos < buffer_size; pos++)
86543ee0e3eSderaadt 		cs = (uint8_t)(cs + buffer[pos]);
86643ee0e3eSderaadt 
86743ee0e3eSderaadt 	header = (struct uticom_fw_header*)buffer;
86843ee0e3eSderaadt 	header->length = (uint16_t)(buffer_size -
86943ee0e3eSderaadt 	    sizeof(struct uticom_fw_header));
87043ee0e3eSderaadt 	header->checkSum = cs;
87143ee0e3eSderaadt 
87243ee0e3eSderaadt 	DPRINTF(("%s: downloading firmware ...\n",
87343ee0e3eSderaadt 	    sc->sc_dev.dv_xname));
87443ee0e3eSderaadt 
87543ee0e3eSderaadt 	err = usbd_open_pipe(sc->sc_iface, pipeno, USBD_EXCLUSIVE_USE,
87643ee0e3eSderaadt 	    &pipe);
87743ee0e3eSderaadt 	if (err) {
87843ee0e3eSderaadt 		printf("%s: open bulk out error (addr %d): %s\n",
87943ee0e3eSderaadt 		    sc->sc_dev.dv_xname, pipeno, usbd_errstr(err));
88043ee0e3eSderaadt 		error = EIO;
88143ee0e3eSderaadt 		goto finish;
88243ee0e3eSderaadt 	}
88343ee0e3eSderaadt 
88443ee0e3eSderaadt 	oxfer = usbd_alloc_xfer(dev);
88543ee0e3eSderaadt 	if (oxfer == NULL) {
88643ee0e3eSderaadt 		error = ENOMEM;
88743ee0e3eSderaadt 		goto finish;
88843ee0e3eSderaadt 	}
88943ee0e3eSderaadt 
89043ee0e3eSderaadt 	obuf = usbd_alloc_buffer(oxfer, buffer_size);
89143ee0e3eSderaadt 	if (obuf == NULL) {
89243ee0e3eSderaadt 		error = ENOMEM;
89343ee0e3eSderaadt 		goto finish;
89443ee0e3eSderaadt 	}
89543ee0e3eSderaadt 
89643ee0e3eSderaadt 	memcpy(obuf, buffer, buffer_size);
89743ee0e3eSderaadt 
898ab0b1be7Smglocker 	usbd_setup_xfer(oxfer, pipe, (void *)sc, obuf, buffer_size,
899e670b998Sjsg 	    USBD_NO_COPY | USBD_SYNCHRONOUS, USBD_NO_TIMEOUT, 0);
900aa88c704Smpi 	err = usbd_transfer(oxfer);
90143ee0e3eSderaadt 
90243ee0e3eSderaadt 	if (err != USBD_NORMAL_COMPLETION)
90343ee0e3eSderaadt 		printf("%s: uticom_download_fw: error: %s\n",
90443ee0e3eSderaadt 		    sc->sc_dev.dv_xname, usbd_errstr(err));
90543ee0e3eSderaadt 
90643ee0e3eSderaadt finish:
907c9ee9455Sderaadt 	free(firmware, M_DEVBUF, firmware_size);
90843ee0e3eSderaadt 	usbd_free_buffer(oxfer);
90943ee0e3eSderaadt 	usbd_free_xfer(oxfer);
91043ee0e3eSderaadt 	oxfer = NULL;
91143ee0e3eSderaadt 	usbd_close_pipe(pipe);
912c9ee9455Sderaadt 	free(buffer, M_USBDEV, buffer_size);
91343ee0e3eSderaadt 	return err;
91443ee0e3eSderaadt }
915