diff options
author | hselasky <hselasky@FreeBSD.org> | 2011-11-12 08:40:52 +0000 |
---|---|---|
committer | hselasky <hselasky@FreeBSD.org> | 2011-11-12 08:40:52 +0000 |
commit | 09979ece0f8c1fa923e05b4a4c24b716f4f2f5fe (patch) | |
tree | 147cb461c64862e6cad250f2d50040d0d9d14f77 /sys/dev/usb | |
parent | 31271e910c52b50e9779d799eba28cad87311d2f (diff) | |
download | FreeBSD-src-09979ece0f8c1fa923e05b4a4c24b716f4f2f5fe.zip FreeBSD-src-09979ece0f8c1fa923e05b4a4c24b716f4f2f5fe.tar.gz |
- This patch adds custom IOCTLs to read and write the 4 GPIO pins on the
cp2103 usb-to-serial chip.
- This patch also makes the line status polling asynchronous, to reduce
the time needed to change the GPIO pins.
Submitted by: JD Louw
MFC after: 1 week
Diffstat (limited to 'sys/dev/usb')
-rw-r--r-- | sys/dev/usb/serial/uslcom.c | 93 | ||||
-rw-r--r-- | sys/dev/usb/usb_ioctl.h | 4 |
2 files changed, 86 insertions, 11 deletions
diff --git a/sys/dev/usb/serial/uslcom.c b/sys/dev/usb/serial/uslcom.c index fb2b420..848f2d5 100644 --- a/sys/dev/usb/serial/uslcom.c +++ b/sys/dev/usb/serial/uslcom.c @@ -41,6 +41,7 @@ __FBSDID("$FreeBSD$"); #include <dev/usb/usb.h> #include <dev/usb/usbdi.h> #include <dev/usb/usbdi_util.h> +#include <dev/usb/usb_ioctl.h> #include "usbdevs.h" #define USB_DEBUG_VAR uslcom_debug @@ -75,6 +76,7 @@ SYSCTL_INT(_hw_usb_uslcom, OID_AUTO, debug, CTLFLAG_RW, #define USLCOM_CTRL 0x07 #define USLCOM_RCTRL 0x08 #define USLCOM_SET_FLOWCTRL 0x13 +#define USLCOM_VENDOR_SPECIFIC 0xff /* USLCOM_UART values */ #define USLCOM_UART_DISABLE 0x00 @@ -113,6 +115,10 @@ SYSCTL_INT(_hw_usb_uslcom, OID_AUTO, debug, CTLFLAG_RW, #define USLCOM_FLOW_RTS_ON 0x00000040 /* RTS static active */ #define USLCOM_FLOW_RTS_HS 0x00000080 /* RTS handshake */ +/* USLCOM_VENDOR_SPECIFIC values */ +#define USLCOM_WRITE_LATCH 0x37E1 +#define USLCOM_READ_LATCH 0x00C2 + enum { USLCOM_BULK_DT_WR, USLCOM_BULK_DT_RD, @@ -123,6 +129,7 @@ enum { struct uslcom_softc { struct ucom_super_softc sc_super_ucom; struct ucom_softc sc_ucom; + struct usb_callout sc_watchdog; struct usb_xfer *sc_xfer[USLCOM_N_TRANSFER]; struct usb_device *sc_udev; @@ -145,6 +152,8 @@ static void uslcom_close(struct ucom_softc *); static void uslcom_set_dtr(struct ucom_softc *, uint8_t); static void uslcom_set_rts(struct ucom_softc *, uint8_t); static void uslcom_set_break(struct ucom_softc *, uint8_t); +static int uslcom_ioctl(struct ucom_softc *, uint32_t, caddr_t, int, + struct thread *); static int uslcom_pre_param(struct ucom_softc *, struct termios *); static void uslcom_param(struct ucom_softc *, struct termios *); static void uslcom_get_status(struct ucom_softc *, uint8_t *, uint8_t *); @@ -177,7 +186,6 @@ static const struct usb_config uslcom_config[USLCOM_N_TRANSFER] = { .type = UE_CONTROL, .endpoint = 0x00, .direction = UE_DIR_ANY, - .interval = 150, /* poll status every 150 ms */ .bufsize = sizeof(struct usb_device_request) + 8, .flags = {.pipe_bof = 1,}, .callback = &uslcom_control_callback, @@ -192,6 +200,7 @@ static struct ucom_callback uslcom_callback = { .ucom_cfg_set_dtr = &uslcom_set_dtr, .ucom_cfg_set_rts = &uslcom_set_rts, .ucom_cfg_set_break = &uslcom_set_break, + .ucom_ioctl = &uslcom_ioctl, .ucom_cfg_param = &uslcom_param, .ucom_pre_param = &uslcom_pre_param, .ucom_start_read = &uslcom_start_read, @@ -308,6 +317,19 @@ MODULE_DEPEND(uslcom, ucom, 1, 1, 1); MODULE_DEPEND(uslcom, usb, 1, 1, 1); MODULE_VERSION(uslcom, 1); +static void +uslcom_watchdog(void *arg) +{ + struct uslcom_softc *sc = arg; + + mtx_assert(&sc->sc_mtx, MA_OWNED); + + usbd_transfer_start(sc->sc_xfer[USLCOM_CTRL_DT_RD]); + + usb_callout_reset(&sc->sc_watchdog, + hz / 4, &uslcom_watchdog, sc); +} + static int uslcom_probe(device_t dev) { @@ -338,6 +360,7 @@ uslcom_attach(device_t dev) device_set_usb_desc(dev); mtx_init(&sc->sc_mtx, "uslcom", NULL, MTX_DEF); + usb_callout_init_mtx(&sc->sc_watchdog, &sc->sc_mtx, 0); sc->sc_udev = uaa->device; @@ -378,6 +401,8 @@ uslcom_detach(device_t dev) ucom_detach(&sc->sc_super_ucom, &sc->sc_ucom); usbd_transfer_unsetup(sc->sc_xfer, USLCOM_N_TRANSFER); + + usb_callout_drain(&sc->sc_watchdog); mtx_destroy(&sc->sc_mtx); return (0); @@ -399,8 +424,9 @@ uslcom_open(struct ucom_softc *ucom) &req, NULL, 0, 1000)) { DPRINTF("UART enable failed (ignored)\n"); } - /* Start polling status */ - usbd_transfer_start(sc->sc_xfer[USLCOM_CTRL_DT_RD]); + + /* start polling status */ + uslcom_watchdog(sc); } static void @@ -409,8 +435,8 @@ uslcom_close(struct ucom_softc *ucom) struct uslcom_softc *sc = ucom->sc_parent; struct usb_device_request req; - /* Stop polling status */ - usbd_transfer_stop(sc->sc_xfer[USLCOM_CTRL_DT_RD]); + /* stop polling status */ + usb_callout_stop(&sc->sc_watchdog); req.bmRequestType = USLCOM_WRITE; req.bRequest = USLCOM_UART; @@ -591,6 +617,55 @@ uslcom_set_break(struct ucom_softc *ucom, uint8_t onoff) } } +static int +uslcom_ioctl(struct ucom_softc *ucom, uint32_t cmd, caddr_t data, + int flag, struct thread *td) +{ + struct uslcom_softc *sc = ucom->sc_parent; + struct usb_device_request req; + int error = 0; + uint8_t latch; + + DPRINTF("cmd=0x%08x\n", cmd); + + switch (cmd) { + case USB_GET_GPIO: + req.bmRequestType = USLCOM_READ; + req.bRequest = USLCOM_VENDOR_SPECIFIC; + USETW(req.wValue, USLCOM_READ_LATCH); + USETW(req.wIndex, 0); + USETW(req.wLength, sizeof(latch)); + + if (ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, + &req, &latch, 0, 1000)) { + DPRINTF("Get LATCH failed\n"); + error = EIO; + } + *(int *)data = latch; + break; + + case USB_SET_GPIO: + req.bmRequestType = USLCOM_WRITE; + req.bRequest = USLCOM_VENDOR_SPECIFIC; + USETW(req.wValue, USLCOM_WRITE_LATCH); + USETW(req.wIndex, (*(int *)data)); + USETW(req.wLength, 0); + + if (ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, + &req, NULL, 0, 1000)) { + DPRINTF("Set LATCH failed\n"); + error = EIO; + } + break; + + default: + DPRINTF("Unknown IOCTL\n"); + error = ENOIOCTL; + break; + } + return (error); +} + static void uslcom_write_callback(struct usb_xfer *xfer, usb_error_t error) { @@ -681,11 +756,9 @@ uslcom_control_callback(struct usb_xfer *xfer, usb_error_t error) sc->sc_msr = msr; ucom_status_change(&sc->sc_ucom); } - - /* FALLTHROUGH */ + break; case USB_ST_SETUP: -tr_setup: req.bmRequestType = USLCOM_READ; req.bRequest = USLCOM_RCTRL; USETW(req.wValue, 0); @@ -702,10 +775,8 @@ tr_setup: break; default: /* error */ - if (error != USB_ERR_CANCELLED) { + if (error != USB_ERR_CANCELLED) DPRINTF("error=%s\n", usbd_errstr(error)); - goto tr_setup; - } break; } } diff --git a/sys/dev/usb/usb_ioctl.h b/sys/dev/usb/usb_ioctl.h index d35fa10..9af6ee5 100644 --- a/sys/dev/usb/usb_ioctl.h +++ b/sys/dev/usb/usb_ioctl.h @@ -289,6 +289,10 @@ struct usb_gen_quirk { #define USB_GET_CM_OVER_DATA _IOR ('U', 180, int) #define USB_SET_CM_OVER_DATA _IOW ('U', 181, int) +/* GPIO control */ +#define USB_GET_GPIO _IOR ('U', 182, int) +#define USB_SET_GPIO _IOW ('U', 183, int) + /* USB file system interface */ #define USB_FS_START _IOW ('U', 192, struct usb_fs_start) #define USB_FS_STOP _IOW ('U', 193, struct usb_fs_stop) |