- 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
This commit is contained in:
parent
2648cd35d8
commit
17c78d345c
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=227463
@ -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 @@ uslcom_control_callback(struct usb_xfer *xfer, usb_error_t error)
|
||||
break;
|
||||
|
||||
default: /* error */
|
||||
if (error != USB_ERR_CANCELLED) {
|
||||
if (error != USB_ERR_CANCELLED)
|
||||
DPRINTF("error=%s\n", usbd_errstr(error));
|
||||
goto tr_setup;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user