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