diff options
author | Theo de Raadt <deraadt@cvs.openbsd.org> | 2007-11-16 16:25:32 +0000 |
---|---|---|
committer | Theo de Raadt <deraadt@cvs.openbsd.org> | 2007-11-16 16:25:32 +0000 |
commit | d630a0775a4b13dc3469d2bcf240bfda9fc3d869 (patch) | |
tree | 7e9351287e9c1762566f0c0cb5764c58648c904b /sys/dev | |
parent | 0ff5f27e92155577d1eb039c30b4b7c565d0e648 (diff) |
driver for TI TUSB3410 usb serial, written by dxi@mail.ru and
hasso@dragonfly.org, ported but not yet tested.. waiting for hardware
Diffstat (limited to 'sys/dev')
-rw-r--r-- | sys/dev/usb/files.usb | 7 | ||||
-rw-r--r-- | sys/dev/usb/uticom.c | 975 |
2 files changed, 981 insertions, 1 deletions
diff --git a/sys/dev/usb/files.usb b/sys/dev/usb/files.usb index 3f4440a9006..7b86cbdb134 100644 --- a/sys/dev/usb/files.usb +++ b/sys/dev/usb/files.usb @@ -1,4 +1,4 @@ -# $OpenBSD: files.usb,v 1.72 2007/11/10 12:58:08 mbalmer Exp $ +# $OpenBSD: files.usb,v 1.73 2007/11/16 16:25:31 deraadt Exp $ # $NetBSD: files.usb,v 1.16 2000/02/14 20:29:54 augustss Exp $ # # Config file and device description for machine-independent USB code. @@ -242,6 +242,11 @@ device uchcom: ucombus attach uchcom at uhub file dev/usb/uchcom.c uchcom +# TI TUSB3410 serial +device uticom: ucombus +attach uticom at uhub +file dev/usb/uticom.c uticom + # Scanners # Generic scanner support device uscanner diff --git a/sys/dev/usb/uticom.c b/sys/dev/usb/uticom.c new file mode 100644 index 00000000000..07fcb3b753e --- /dev/null +++ b/sys/dev/usb/uticom.c @@ -0,0 +1,975 @@ +/* + * Copyright (c) 2005 Dmitry Komissaroff <dxi@mail.ru>. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/device.h> +#include <sys/malloc.h> +#include <sys/conf.h> +#include <sys/tty.h> + +#include <machine/bus.h> + +#include <dev/usb/usb.h> +#include <dev/usb/usbhid.h> + +#include <dev/usb/usbdi.h> +#include <dev/usb/usbdivar.h> +#include <dev/usb/usbdi_util.h> +#include <dev/usb/usbdevs.h> +#include <dev/usb/usbcdc.h> + +#include <dev/usb/ucomvar.h> + +#ifdef USB_DEBUG +static int uticomdebug = 0; + +#define DPRINTFN(n, x) do { if (uticomdebug > (n)) kprintf x; } while (0) +#else +#define DPRINTFN(n, x) +#endif + +#define DPRINTF(x) DPRINTFN(0, x) + +#define UTICOM_CONFIG_INDEX 1 +#define UTICOM_ACTIVE_INDEX 2 + +#define UTICOM_IFACE_INDEX 0 + +/* + * These are the maximum number of bytes transferred per frame. + * The output buffer size cannot be increased due to the size encoding. + */ +#define UTICOM_IBUFSZ 64 +#define UTICOM_OBUFSZ 64 + +#define UTICOM_FW_BUFSZ 16284 + +#define UTICOM_INTR_INTERVAL 100 /* ms */ + +#define UTICOM_RQ_LINE 0 +/* Used to sync data0/1-toggle on reopen bulk pipe. */ +#define UTICOM_RQ_SOF 1 +#define UTICOM_RQ_SON 2 + +#define UTICOM_RQ_BAUD 3 +#define UTICOM_RQ_LCR 4 +#define UTICOM_RQ_FCR 5 +#define UTICOM_RQ_RTS 6 +#define UTICOM_RQ_DTR 7 +#define UTICOM_RQ_BREAK 8 +#define UTICOM_RQ_CRTSCTS 9 + +#define UTICOM_BRATE_REF 923077 + +#define UTICOM_SET_DATA_BITS(x) (x - 5) + +#define UTICOM_STOP_BITS_1 0x00 +#define UTICOM_STOP_BITS_2 0x40 + +#define UTICOM_PARITY_NONE 0x00 +#define UTICOM_PARITY_ODD 0x08 +#define UTICOM_PARITY_EVEN 0x18 + +#define UTICOM_LCR_OVR 0x1 +#define UTICOM_LCR_PTE 0x2 +#define UTICOM_LCR_FRE 0x4 +#define UTICOM_LCR_BRK 0x8 + +#define UTICOM_MCR_CTS 0x1 +#define UTICOM_MCR_DSR 0x2 +#define UTICOM_MCR_CD 0x4 +#define UTICOM_MCR_RI 0x8 + +/* Structures */ +struct uticom_fw_header { + uint16_t length; + uint8_t checkSum; +} __attribute__((packed)); + +struct uticom_buf { + unsigned int buf_size; + char *buf_buf; + char *buf_get; + char *buf_put; +}; + +struct uticom_softc { + struct device sc_dev; /* base device */ + usbd_device_handle sc_udev; /* device */ + usbd_interface_handle sc_iface; /* interface */ + + int sc_iface_number; /* interface number */ + + usbd_interface_handle sc_intr_iface; /* interrupt interface */ + int sc_intr_number; /* interrupt number */ + usbd_pipe_handle sc_intr_pipe; /* interrupt pipe */ + u_char *sc_intr_buf; /* interrupt buffer */ + int sc_isize; + + u_char sc_dtr; /* current DTR state */ + u_char sc_rts; /* current RTS state */ + u_char sc_status; + + u_char sc_lsr; /* Local status register */ + u_char sc_msr; /* uticom status register */ + + struct device *sc_subdev; + u_char sc_dying; +}; + +static usbd_status uticom_reset(struct uticom_softc *); +static usbd_status uticom_set_crtscts(struct uticom_softc *); +static void uticom_intr(usbd_xfer_handle, usbd_private_handle, usbd_status); + +static void uticom_set(void *, int, int, int); +static void uticom_dtr(struct uticom_softc *, int); +static void uticom_rts(struct uticom_softc *, int); +static void uticom_break(struct uticom_softc *, int); +static void uticom_get_status(void *, int, u_char *, u_char *); +#if 0 /* TODO */ +static int uticom_ioctl(void *, int, u_long, caddr_t, int, usb_proc_ptr); +#endif +static int uticom_param(void *, int, struct termios *); +static int uticom_open(void *, int); +static void uticom_close(void *, int); + +static int uticom_download_fw(struct uticom_softc *sc, int pipeno, + usbd_device_handle dev); + +struct ucom_methods uticom_methods = { + uticom_get_status, + uticom_set, + uticom_param, + NULL, /* uticom_ioctl, TODO */ + uticom_open, + uticom_close, + NULL, + NULL +}; + +int uticom_match(struct device *, void *, void *); +void uticom_attach(struct device *, struct device *, void *); +int uticom_detach(struct device *, int); +int uticom_activate(struct device *, enum devact); + +struct cfdriver uticom_cd = { + NULL, "uticom", DV_DULL +}; + +const struct cfattach uticom_ca = { + sizeof(struct uticom_softc), + uticom_match, + uticom_attach, + uticom_detach, + uticom_activate, +}; + +int +uticom_match(struct device *parent, void *match, void *aux) +{ + struct usb_attach_arg *uaa = aux; + + if (uaa->iface != NULL) + return (UMATCH_NONE); + + if (uaa->vendor == USB_VENDOR_TI && + uaa->product == USB_PRODUCT_TI_TUSB3410) + return (UMATCH_VENDOR_PRODUCT); + return (0); +} + +void +uticom_attach(struct device *parent, struct device *self, void *aux) +{ + struct uticom_softc *sc = (struct uticom_softc *)self; + struct usb_attach_arg *uaa = aux; + usbd_device_handle dev = uaa->device; + usbd_interface_handle iface; + usb_config_descriptor_t *cdesc; + usb_interface_descriptor_t *id; + usb_endpoint_descriptor_t *ed; + usbd_status err; + int status, i; + usb_device_descriptor_t *dd; + struct ucom_attach_args uca; + + sc->sc_udev = dev; + sc->sc_iface = uaa->iface; + + /* Initialize endpoints. */ + uca.bulkin = uca.bulkout = -1; + sc->sc_intr_number = -1; + sc->sc_intr_pipe = NULL; + + dd = usbd_get_device_descriptor(dev); + DPRINTF(("%s: uticom_attach: num of configurations %d\n", + sc->sc_dev.dv_xname, dd->bNumConfigurations)); + + /* The device without firmware has single configuration with single + * bulk out interface. */ + if (dd->bNumConfigurations > 1) + goto fwload_done; + + /* Loading firmware. */ + DPRINTF(("%s: uticom_attach: starting loading firmware\n", + sc->sc_dev.dv_xname)); + + err = usbd_set_config_index(dev, UTICOM_CONFIG_INDEX, 1); + if (err) { + printf("%s: failed to set configuration: %s\n", + sc->sc_dev.dv_xname, usbd_errstr(err)); + sc->sc_dying = 1; + return; + } + + /* Get the config descriptor. */ + cdesc = usbd_get_config_descriptor(sc->sc_udev); + + if (cdesc == NULL) { + printf("%s: failed to get configuration descriptor\n", + sc->sc_dev.dv_xname); + sc->sc_dying = 1; + return; + } + + err = usbd_device2interface_handle(dev, UTICOM_IFACE_INDEX, + &sc->sc_iface); + if (err) { + printf("%s: failed to get interface: %s\n", + sc->sc_dev.dv_xname, usbd_errstr(err)); + sc->sc_dying = 1; + return; + } + + /* Find the bulk out interface used to upload firmware. */ + id = usbd_get_interface_descriptor(sc->sc_iface); + sc->sc_iface_number = id->bInterfaceNumber; + + for (i = 0; i < id->bNumEndpoints; i++) { + ed = usbd_interface2endpoint_descriptor(sc->sc_iface, i); + if (ed == NULL) { + printf("%s: no endpoint descriptor for %d\n", + sc->sc_dev.dv_xname, i); + sc->sc_dying = 1; + return; + } + + if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT && + UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) { + uca.bulkout = ed->bEndpointAddress; + DPRINTF(("%s: uticom_attach: data bulk out num: %d\n", + sc->sc_dev.dv_xname, ed->bEndpointAddress)); + } + + if (uca.bulkout == -1) { + printf("%s: could not find data bulk out\n", + sc->sc_dev.dv_xname); + sc->sc_dying = 1; + return; + } + } + + status = uticom_download_fw(sc, uca.bulkout, dev); + + if (status) { + printf("%s: firmware download failed\n", + sc->sc_dev.dv_xname); + sc->sc_dying = 1; + return; + } else { + printf("%s: firmware download succeeded\n", + sc->sc_dev.dv_xname); + } + + status = usbd_reload_device_desc(dev); + if (status) { + printf("%s: error reloading device descriptor\n", + sc->sc_dev.dv_xname); + sc->sc_dying = 1; + return; + } + +fwload_done: + dd = usbd_get_device_descriptor(dev); + DPRINTF(("%s: uticom_attach: num of configurations %d\n", + sc->sc_dev.dv_xname, dd->bNumConfigurations)); + + err = usbd_set_config_index(dev, UTICOM_ACTIVE_INDEX, 1); + if (err) { + printf("%s: failed to set configuration: %s\n", + sc->sc_dev.dv_xname, usbd_errstr(err)); + sc->sc_dying = 1; + return; + } + + /* Get the config descriptor. */ + cdesc = usbd_get_config_descriptor(sc->sc_udev); + if (cdesc == NULL) { + printf("%s: failed to get configuration descriptor\n", + sc->sc_dev.dv_xname); + sc->sc_dying = 1; + return; + } + + /* Get the interface (XXX: multiport chips are not supported yet). */ + err = usbd_device2interface_handle(dev, UTICOM_IFACE_INDEX, + &sc->sc_iface); + if (err) { + printf("failed to get interface: %s\n", + sc->sc_dev.dv_xname, usbd_errstr(err)); + sc->sc_dying = 1; + return; + } + + /* Find the interrupt endpoints. */ + id = usbd_get_interface_descriptor(sc->sc_iface); + sc->sc_iface_number = id->bInterfaceNumber; + + for (i = 0; i < id->bNumEndpoints; i++) { + ed = usbd_interface2endpoint_descriptor(sc->sc_iface, i); + if (ed == NULL) { + printf("no endpoint descriptor for %d\n", + sc->sc_dev.dv_xname, i); + sc->sc_dying = 1; + return; + } + + if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN && + UE_GET_XFERTYPE(ed->bmAttributes) == UE_INTERRUPT) { + sc->sc_intr_number = ed->bEndpointAddress; + sc->sc_isize = UGETW(ed->wMaxPacketSize); + } + } + + if (sc->sc_intr_number == -1) { + printf("%s: could not find interrupt in\n", + sc->sc_dev.dv_xname); + sc->sc_dying = 1; + return; + } + + /* Keep interface for interrupt. */ + sc->sc_intr_iface = sc->sc_iface; + + /* Find the bulk{in,out} endpoints. */ + id = usbd_get_interface_descriptor(sc->sc_iface); + sc->sc_iface_number = id->bInterfaceNumber; + + for (i = 0; i < id->bNumEndpoints; i++) { + ed = usbd_interface2endpoint_descriptor(iface, i); + if (ed == NULL) { + printf("%s: no endpoint descriptor for %d\n", + sc->sc_dev.dv_xname, i); + sc->sc_dying = 1; + return; + } + + if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN && + UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) { + uca.bulkin = ed->bEndpointAddress; + } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT && + UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) { + uca.bulkout = ed->bEndpointAddress; + } + } + + if (uca.bulkin == -1) { + printf("%s: could not find data bulk in\n", + sc->sc_dev.dv_xname); + sc->sc_dying = 1; + return; + } + + if (uca.bulkout == -1) { + printf("%s: could not find data bulk out\n", + sc->sc_dev.dv_xname); + sc->sc_dying = 1; + return; + } + + sc->sc_dtr = sc->sc_rts = -1; + + uca.portno = UCOM_UNK_PORTNO; + uca.ibufsize = UTICOM_IBUFSZ; + uca.obufsize = UTICOM_OBUFSZ; + uca.ibufsizepad = UTICOM_IBUFSZ; + uca.device = dev; + uca.iface = iface; + uca.opkthdrlen = 0; + uca.methods = &uticom_methods; + uca.arg = sc; + uca.info = NULL; + + err = uticom_reset(sc); + if (err) { + printf("%s: reset failed: %s\n", + sc->sc_dev.dv_xname, usbd_errstr(err)); + sc->sc_dying = 1; + return; + } + + DPRINTF(("%s: uticom_attach: in = 0x%x, out = 0x%x, intr = 0x%x\n", + sc->sc_dev.dv_xname, uca.bulkin, + uca.bulkout, sc->sc_intr_number)); + + sc->sc_subdev = config_found_sm(self, &uca, ucomprint, ucomsubmatch); +} + +int +uticom_activate(struct device *self, enum devact act) +{ + struct uticom_softc *sc = (struct uticom_softc *)self; + int rv = 0; + + switch (act) { + case DVACT_ACTIVATE: + break; + + case DVACT_DEACTIVATE: + if (sc->sc_subdev != NULL) + rv = config_deactivate(sc->sc_subdev); + sc->sc_dying = 1; + break; + } + return (rv); +} + +int +uticom_detach(struct device *self, int flags) +{ + struct uticom_softc *sc = (struct uticom_softc *)self; + + DPRINTF(("%s: uticom_detach: sc = %p\n", + sc->sc_dev.dv_xname, sc)); + sc->sc_dying = 1; + + if (sc->sc_subdev != NULL) { + config_detach(sc->sc_subdev, flags); + sc->sc_subdev = NULL; + } + + if (sc->sc_intr_pipe != NULL) { + usbd_abort_pipe(sc->sc_intr_pipe); + usbd_close_pipe(sc->sc_intr_pipe); + free(sc->sc_intr_buf, M_USBDEV); + sc->sc_intr_pipe = NULL; + } + + usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_udev, + &sc->sc_dev); + return (0); +} + +static usbd_status +uticom_reset(struct uticom_softc *sc) +{ + usb_device_request_t req; + usbd_status err; + + req.bmRequestType = UT_WRITE_VENDOR_DEVICE; + req.bRequest = UTICOM_RQ_SON; + USETW(req.wValue, 0); + USETW(req.wIndex, 0); + USETW(req.wLength, 0); + + err = usbd_do_request(sc->sc_udev, &req, NULL); + if (err){ + printf("%s: uticom_reset: %s\n", + sc->sc_dev.dv_xname, usbd_errstr(err)); + return (EIO); + } + + DPRINTF(("%s: uticom_reset: done\n", sc->sc_dev.dv_xname)); + return (0); +} + +static void +uticom_set(void *addr, int portno, int reg, int onoff) +{ + struct uticom_softc *sc = addr; + + switch (reg) { + case UCOM_SET_DTR: + uticom_dtr(sc, onoff); + break; + case UCOM_SET_RTS: + uticom_rts(sc, onoff); + break; + case UCOM_SET_BREAK: + uticom_break(sc, onoff); + break; + default: + break; + } +} + +static void +uticom_dtr(struct uticom_softc *sc, int onoff) +{ + usb_device_request_t req; + usbd_status err; + + DPRINTF(("%s: uticom_dtr: onoff = %d\n", sc->sc_dev.dv_xname, + onoff)); + + if (sc->sc_dtr == onoff) + return; + sc->sc_dtr = onoff; + + req.bmRequestType = UT_WRITE_VENDOR_DEVICE; + req.bRequest = UTICOM_RQ_DTR; + USETW(req.wValue, sc->sc_dtr ? UCDC_LINE_DTR : 0); + USETW(req.wIndex, 0); + USETW(req.wLength, 0); + + err = usbd_do_request(sc->sc_udev, &req, NULL); + if (err) + printf("%s: uticom_dtr: %s\n", + sc->sc_dev.dv_xname, usbd_errstr(err)); +} + +static void +uticom_rts(struct uticom_softc *sc, int onoff) +{ + usb_device_request_t req; + usbd_status err; + + DPRINTF(("%s: uticom_rts: onoff = %d\n", sc->sc_dev.dv_xname, + onoff)); + + if (sc->sc_rts == onoff) + return; + sc->sc_rts = onoff; + req.bmRequestType = UT_WRITE_VENDOR_DEVICE; + req.bRequest = UTICOM_RQ_RTS; + USETW(req.wValue, sc->sc_rts ? UCDC_LINE_RTS : 0); + USETW(req.wIndex, 0); + USETW(req.wLength, 0); + + err = usbd_do_request(sc->sc_udev, &req, NULL); + if (err) + printf("%s: uticom_rts: %s\n", + sc->sc_dev.dv_xname, usbd_errstr(err)); +} + +static void +uticom_break(struct uticom_softc *sc, int onoff) +{ + usb_device_request_t req; + usbd_status err; + + DPRINTF(("%s: uticom_break: onoff = %d\n", sc->sc_dev.dv_xname, + onoff)); + + req.bmRequestType = UT_WRITE_VENDOR_DEVICE; + req.bRequest = UTICOM_RQ_BREAK; + USETW(req.wValue, onoff ? 1 : 0); + USETW(req.wIndex, 0); + USETW(req.wLength, 0); + + err = usbd_do_request(sc->sc_udev, &req, NULL); + if (err) + printf("%s: uticom_break: %s\n", + sc->sc_dev.dv_xname, usbd_errstr(err)); +} + +static usbd_status +uticom_set_crtscts(struct uticom_softc *sc) +{ + usb_device_request_t req; + usbd_status err; + + DPRINTF(("%s: uticom_set_crtscts: on\n", sc->sc_dev.dv_xname)); + + req.bmRequestType = UT_WRITE_VENDOR_DEVICE; + req.bRequest = UTICOM_RQ_CRTSCTS; + USETW(req.wValue, 1); + USETW(req.wIndex, 0); + USETW(req.wLength, 0); + + err = usbd_do_request(sc->sc_udev, &req, NULL); + if (err) { + printf("%s: uticom_set_crtscts: %s\n", + sc->sc_dev.dv_xname, usbd_errstr(err)); + return (err); + } + + return (USBD_NORMAL_COMPLETION); +} + +static int +uticom_param(void *vsc, int portno, struct termios *t) +{ + struct uticom_softc *sc = (struct uticom_softc *)vsc; + usb_device_request_t req; + usbd_status err; + uint8_t data; + + DPRINTF(("%s: uticom_param\n", sc->sc_dev.dv_xname)); + + switch (t->c_ospeed) { + case 1200: + case 2400: + case 4800: + case 7200: + case 9600: + case 14400: + case 19200: + case 38400: + case 57600: + case 115200: + case 230400: + case 460800: + case 921600: + req.bmRequestType = UT_WRITE_VENDOR_DEVICE; + req.bRequest = UTICOM_RQ_BAUD; + USETW(req.wValue, (UTICOM_BRATE_REF / t->c_ospeed)); + USETW(req.wIndex, 0); + USETW(req.wLength, 0); + + err = usbd_do_request(sc->sc_udev, &req, 0); + if (err) { + printf("%s: uticom_param: %s\n", + sc->sc_dev.dv_xname, usbd_errstr(err)); + return (EIO); + } + break; + default: + printf("%s: uticom_param: unsupported baud rate %d\n", + sc->sc_dev.dv_xname, t->c_ospeed); + return (EINVAL); + } + + switch (ISSET(t->c_cflag, CSIZE)) { + case CS5: + data = UTICOM_SET_DATA_BITS(5); + break; + case CS6: + data = UTICOM_SET_DATA_BITS(6); + break; + case CS7: + data = UTICOM_SET_DATA_BITS(7); + break; + case CS8: + data = UTICOM_SET_DATA_BITS(8); + break; + default: + return (EIO); + } + + if (ISSET(t->c_cflag, CSTOPB)) + data |= UTICOM_STOP_BITS_2; + else + data |= UTICOM_STOP_BITS_1; + + if (ISSET(t->c_cflag, PARENB)) { + if (ISSET(t->c_cflag, PARODD)) + data |= UTICOM_PARITY_ODD; + else + data |= UTICOM_PARITY_EVEN; + } else + data |= UTICOM_PARITY_NONE; + + req.bmRequestType = UT_WRITE_VENDOR_DEVICE; + req.bRequest = UTICOM_RQ_LCR; + USETW(req.wIndex, 0); + USETW(req.wLength, 0); + USETW(req.wValue, data); + + err = usbd_do_request(sc->sc_udev, &req, NULL); + if (err) { + printf("%s: uticom_param: %s\n", + sc->sc_dev.dv_xname, usbd_errstr(err)); + return (err); + } + + if (ISSET(t->c_cflag, CRTSCTS)) { + err = uticom_set_crtscts(sc); + if (err) + return (EIO); + } + + return (0); +} + +static int +uticom_open(void *addr, int portno) +{ + struct uticom_softc *sc = addr; + usbd_status err; + + if (sc->sc_dying) + return (ENXIO); + + DPRINTF(("%s: uticom_open\n", sc->sc_dev.dv_xname)); + + sc->sc_status = 0; /* clear status bit */ + + if (sc->sc_intr_number != -1 && sc->sc_intr_pipe == NULL) { + sc->sc_intr_buf = malloc(sc->sc_isize, M_USBDEV, M_WAITOK); + err = usbd_open_pipe_intr(sc->sc_intr_iface, sc->sc_intr_number, + USBD_SHORT_XFER_OK, &sc->sc_intr_pipe, sc, sc->sc_intr_buf, + sc->sc_isize, uticom_intr, UTICOM_INTR_INTERVAL); + if (err) { + printf("%s: cannot open interrupt pipe (addr %d)\n", + sc->sc_dev.dv_xname, sc->sc_intr_number); + return (EIO); + } + } + + DPRINTF(("%s: uticom_open: port opened\n", sc->sc_dev.dv_xname)); + return (0); +} + +static void +uticom_close(void *addr, int portno) +{ + struct uticom_softc *sc = addr; + usb_device_request_t req; + usbd_status err; + + if (sc->sc_dying) + return; + + req.bmRequestType = UT_WRITE_VENDOR_DEVICE; + req.bRequest = UTICOM_RQ_SON; + USETW(req.wValue, 0); + USETW(req.wIndex, 0); + USETW(req.wLength, 0); + + /* Try to reset UART part of chip. */ + err = usbd_do_request(sc->sc_udev, &req, NULL); + if (err) { + printf("%s: uticom_close: %s\n", + sc->sc_dev.dv_xname, usbd_errstr(err)); + return; + } + + DPRINTF(("%s: uticom_close: close\n", sc->sc_dev.dv_xname)); + + if (sc->sc_intr_pipe != NULL) { + err = usbd_abort_pipe(sc->sc_intr_pipe); + if (err) + printf("%s: abort interrupt pipe failed: %s\n", + sc->sc_dev.dv_xname, usbd_errstr(err)); + err = usbd_close_pipe(sc->sc_intr_pipe); + if (err) + printf("%s: close interrupt pipe failed: %s\n", + sc->sc_dev.dv_xname, usbd_errstr(err)); + free(sc->sc_intr_buf, M_USBDEV); + sc->sc_intr_pipe = NULL; + } +} + +static void +uticom_intr(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status) +{ + struct uticom_softc *sc = priv; + u_char *buf = sc->sc_intr_buf; + + if (sc->sc_dying) + return; + + if (status != USBD_NORMAL_COMPLETION) { + if (status == USBD_NOT_STARTED || status == USBD_CANCELLED) { + DPRINTF(("%s: uticom_intr: int status: %s\n", + sc->sc_dev.dv_xname, usbd_errstr(status))); + return; + } + + DPRINTF(("%s: uticom_intr: abnormal status: %s\n", + sc->sc_dev.dv_xname, usbd_errstr(status))); + usbd_clear_endpoint_stall_async(sc->sc_intr_pipe); + return; + } + + if (!xfer->actlen) + return; + + DPRINTF(("%s: xfer_length = %d\n", sc->sc_dev.dv_xname, + xfer->actlen)); + + sc->sc_lsr = sc->sc_msr = 0; + + if (buf[0] == 0) { + /* msr registers */ + if (buf[1] & UTICOM_MCR_CTS) + sc->sc_msr |= UMSR_CTS; + if (buf[1] & UTICOM_MCR_DSR) + sc->sc_msr |= UMSR_DSR; + if (buf[1] & UTICOM_MCR_CD) + sc->sc_msr |= UMSR_DCD; + if (buf[1] & UTICOM_MCR_RI) + sc->sc_msr |= UMSR_RI; + } else { + /* lsr registers */ + if (buf[0] & UTICOM_LCR_OVR) + sc->sc_lsr |= ULSR_OE; + if (buf[0] & UTICOM_LCR_PTE) + sc->sc_lsr |= ULSR_PE; + if (buf[0] & UTICOM_LCR_FRE) + sc->sc_lsr |= ULSR_FE; + if (buf[0] & UTICOM_LCR_BRK) + sc->sc_lsr |= ULSR_BI; + } + +// if (uticomstickdsr) +// sc->sc_msr |= UMSR_DSR; + + ucom_status_change((struct ucom_softc *)sc->sc_subdev); +} + +static void +uticom_get_status(void *addr, int portno, u_char *lsr, u_char *msr) +{ +#if 0 /* TODO */ + struct uticom_softc *sc = addr; + + DPRINTF(("uticom_get_status:\n")); + + if (lsr != NULL) + *lsr = sc->sc_lsr; + if (msr != NULL) + *msr = sc->sc_msr; +#endif + return; +} + +#if 0 /* TODO */ +static int +uticom_ioctl(void *addr, int portno, u_long cmd, caddr_t data, int flag, + usb_proc_ptr p) +{ + struct uticom_softc *sc = addr; + int error = 0; + + if (sc->sc_ucom.sc_dying) + return (EIO); + + DPRINTF(("uticom_ioctl: cmd = 0x%08lx\n", cmd)); + + switch (cmd) { + case TIOCNOTTY: + case TIOCMGET: + case TIOCMSET: + case USB_GET_CM_OVER_DATA: + case USB_SET_CM_OVER_DATA: + break; + + default: + DPRINTF(("uticom_ioctl: unknown\n")); + error = ENOTTY; + break; + } + + return (error); +} +#endif + +static int +uticom_download_fw(struct uticom_softc *sc, int pipeno, + usbd_device_handle dev) +{ + u_char *obuf, *firmware; + size_t firmware_size; + int buffer_size, pos; + uint8_t cs = 0, *buffer; + usbd_status err; + struct uticom_fw_header *header; + usbd_xfer_handle oxfer = 0; + usbd_status error = 0; + usbd_pipe_handle pipe; + + error = loadfirmware("tusb3410", &firmware, &firmware_size); + if (error) + return (error); + + buffer_size = UTICOM_FW_BUFSZ + sizeof(struct uticom_fw_header); + buffer = malloc(buffer_size, M_USBDEV, M_WAITOK); + + if (!buffer) { + printf("%s: uticom_download_fw: out of memory\n", + sc->sc_dev.dv_xname); + free(firmware, M_DEVBUF); + return ENOMEM; + } + + memcpy(buffer, firmware, firmware_size); + memset(buffer + firmware_size, 0xff, buffer_size - firmware_size); + + for (pos = sizeof(struct uticom_fw_header); pos < buffer_size; pos++) + cs = (uint8_t)(cs + buffer[pos]); + + header = (struct uticom_fw_header*)buffer; + header->length = (uint16_t)(buffer_size - + sizeof(struct uticom_fw_header)); + header->checkSum = cs; + + DPRINTF(("%s: downloading firmware ...\n", + sc->sc_dev.dv_xname)); + + err = usbd_open_pipe(sc->sc_iface, pipeno, USBD_EXCLUSIVE_USE, + &pipe); + if (err) { + printf("%s: open bulk out error (addr %d): %s\n", + sc->sc_dev.dv_xname, pipeno, usbd_errstr(err)); + error = EIO; + goto finish; + } + + oxfer = usbd_alloc_xfer(dev); + if (oxfer == NULL) { + error = ENOMEM; + goto finish; + } + + obuf = usbd_alloc_buffer(oxfer, buffer_size); + if (obuf == NULL) { + error = ENOMEM; + goto finish; + } + + memcpy(obuf, buffer, buffer_size); + + usbd_setup_xfer(oxfer, pipe, (usbd_private_handle)sc, obuf, buffer_size, + USBD_NO_COPY || USBD_SYNCHRONOUS, USBD_NO_TIMEOUT, 0); + err = usbd_sync_transfer(oxfer); + + if (err != USBD_NORMAL_COMPLETION) + printf("%s: uticom_download_fw: error: %s\n", + sc->sc_dev.dv_xname, usbd_errstr(err)); + +finish: + free(firmware, M_DEVBUF); + usbd_free_buffer(oxfer); + usbd_free_xfer(oxfer); + oxfer = NULL; + usbd_abort_pipe(pipe); + usbd_close_pipe(pipe); + free(buffer, M_USBDEV); + return err; +} |