Module Name: src Committed By: jakllsch Date: Sat Jan 14 21:06:01 UTC 2012
Modified Files: src/sys/dev/usb: files.usb uslsa.c Added Files: src/sys/dev/usb: uslsareg.h Log Message: Rework uslsa(4) based on publicly-available Silicon Labs AN571 document. To generate a diff of this commit: cvs rdiff -u -r1.117 -r1.118 src/sys/dev/usb/files.usb cvs rdiff -u -r1.16 -r1.17 src/sys/dev/usb/uslsa.c cvs rdiff -u -r0 -r1.1 src/sys/dev/usb/uslsareg.h Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/dev/usb/files.usb diff -u src/sys/dev/usb/files.usb:1.117 src/sys/dev/usb/files.usb:1.118 --- src/sys/dev/usb/files.usb:1.117 Sat Dec 31 00:08:48 2011 +++ src/sys/dev/usb/files.usb Sat Jan 14 21:06:01 2012 @@ -1,4 +1,4 @@ -# $NetBSD: files.usb,v 1.117 2011/12/31 00:08:48 christos Exp $ +# $NetBSD: files.usb,v 1.118 2012/01/14 21:06:01 jakllsch Exp $ # # Config file and device description for machine-independent USB code. # Included by ports that need it. Ports that use it must provide @@ -339,7 +339,7 @@ file dev/usb/uark.c uark # Silicon Labs CP210x serial driver device uslsa: ucombus -attach uslsa at usbdevif +attach uslsa at usbifif file dev/usb/uslsa.c uslsa # WinChipHead CH341/340 serial driver Index: src/sys/dev/usb/uslsa.c diff -u src/sys/dev/usb/uslsa.c:1.16 src/sys/dev/usb/uslsa.c:1.17 --- src/sys/dev/usb/uslsa.c:1.16 Fri Dec 23 00:51:49 2011 +++ src/sys/dev/usb/uslsa.c Sat Jan 14 21:06:01 2012 @@ -1,4 +1,4 @@ -/* $NetBSD: uslsa.c,v 1.16 2011/12/23 00:51:49 jakllsch Exp $ */ +/* $NetBSD: uslsa.c,v 1.17 2012/01/14 21:06:01 jakllsch Exp $ */ /* from ugensa.c */ @@ -57,12 +57,8 @@ * */ -/* - * Craig Shelley's Linux driver was used for documentation. - */ - #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: uslsa.c,v 1.16 2011/12/23 00:51:49 jakllsch Exp $"); +__KERNEL_RCSID(0, "$NetBSD: uslsa.c,v 1.17 2012/01/14 21:06:01 jakllsch Exp $"); #include <sys/param.h> #include <sys/systm.h> @@ -79,102 +75,45 @@ __KERNEL_RCSID(0, "$NetBSD: uslsa.c,v 1. #include <dev/usb/ucomvar.h> -#ifdef DEBUG -#define USLSA_DEBUG -#endif +#include <dev/usb/uslsareg.h> + +#include <fs/unicode.h> #ifdef USLSA_DEBUG -#define DPRINTF(x) if (uslsadebug) printf x -#define DPRINTFN(n,x) if (uslsadebug>(n)) printf x +#define DPRINTF(x) if (uslsadebug) device_printf x int uslsadebug = 0; #else #define DPRINTF(x) -#define DPRINTFN(n,x) #endif -#define USLSA_REQ_SET_STATE 0x00 - -#define USLSA_REQ_SET_BPS 0x01 -#define USLSA_REQ_GET_BPS 0x02 - -#define USLSA_REQ_SET_DPS 0x03 -#define USLSA_REQ_GET_DPS 0x04 - -#define USLSA_REQ_SET_BREAK 0x05 - -#define USLSA_REQ_SET_FLOW 0x07 -#define USLSA_REQ_GET_FLOW 0x08 - -#define USLSA_REQ_SET_MODEM 0x13 -#define USLSA_REQ_GET_MODEM 0x14 - -#define USLSA_REQ_SET_MISC 0x19 -#define USLSA_REQ_GET_MISC 0x20 - -#define USLSA_STATE_DISABLE 0x0000 -#define USLSA_STATE_ENABLE 0x0001 - -#define USLSA_BPS(b) (3686400/b) - -#define USLSA_DPS_DATA_MASK 0x0f00 -#define USLSA_DPS_DATA_FIVE 0x0500 -#define USLSA_DPS_DATA_SIX 0x0600 -#define USLSA_DPS_DATA_SEVEN 0x0700 -#define USLSA_DPS_DATA_EIGHT 0x0800 -#define USLSA_DPS_DATA_NINE 0x0900 - -#define USLSA_DPS_PARITY_MASK 0x00f0 -#define USLSA_DPS_PARITY_SPACE 0x0040 -#define USLSA_DPS_PARITY_MARK 0x0030 -#define USLSA_DPS_PARITY_EVEN 0x0020 -#define USLSA_DPS_PARITY_ODD 0x0010 -#define USLSA_DPS_PARITY_NONE 0x0000 - -#define USLSA_DPS_STOP_MASK 0x000f -#define USLSA_DPS_STOP_TWO 0x0002 -#define USLSA_DPS_STOP_ONE_FIVE 0x0001 -#define USLSA_DPS_STOP_ONE 0x0000 - -#define USLSA_BREAK_DISABLE 0x0000 -#define USLSA_BREAK_ENABLE 0x0001 - -#define USLSA_FLOW_SET_RTS 0x0200 -#define USLSA_FLOW_SET_DTR 0x0100 -#define USLSA_FLOW_MSR_MASK 0x00f0 -#define USLSA_FLOW_MSR_DCD 0x0080 -#define USLSA_FLOW_MSR_RI 0x0040 -#define USLSA_FLOW_MSR_DSR 0x0020 -#define USLSA_FLOW_MSR_CTS 0x0010 -#define USLSA_FLOW_RTS 0x0002 -#define USLSA_FLOW_DTR 0x0001 - struct uslsa_softc { device_t sc_dev; /* base device */ - usbd_device_handle sc_udev; /* device */ - usbd_interface_handle sc_iface; /* interface */ - device_t sc_subdev; /* ucom device */ - - u_char sc_dying; /* disconnecting */ - - u_char sc_lsr; /* local status register */ - u_char sc_msr; /* uslsa status register */ + usbd_device_handle sc_udev; /* usb device */ + usbd_interface_handle sc_iface; /* interface */ + uint8_t sc_ifnum; /* interface number */ + bool sc_dying; /* disconnecting */ }; static void uslsa_get_status(void *sc, int, u_char *, u_char *); static void uslsa_set(void *, int, int, int); static int uslsa_param(void *, int, struct termios *); +static int uslsa_ioctl(void *, int, u_long, void *, int, proc_t *); + static int uslsa_open(void *, int); static void uslsa_close(void *, int); +static int uslsa_usbd_errno(usbd_status); static int uslsa_request_set(struct uslsa_softc *, uint8_t, uint16_t); -static void uslsa_set_flow(struct uslsa_softc *, tcflag_t, tcflag_t); +static int uslsa_set_flow(struct uslsa_softc *, tcflag_t, tcflag_t); + +static void uslsa_props(struct uslsa_softc *); static const struct ucom_methods uslsa_methods = { uslsa_get_status, uslsa_set, uslsa_param, - NULL, + uslsa_ioctl, uslsa_open, uslsa_close, NULL, @@ -204,7 +143,6 @@ static const struct usb_devno uslsa_devs { USB_VENDOR_SILABS2, USB_PRODUCT_SILABS2_DCU11CLONE }, { USB_VENDOR_USI, USB_PRODUCT_USI_MC60 } }; -#define uslsa_lookup(v, p) usb_lookup(uslsa_devs, v, p) static int uslsa_match(device_t, cfdata_t, void *); static void uslsa_attach(device_t, device_t, void *); @@ -215,59 +153,48 @@ static int uslsa_activate(device_t, enum CFATTACH_DECL2_NEW(uslsa, sizeof(struct uslsa_softc), uslsa_match, uslsa_attach, uslsa_detach, uslsa_activate, NULL, uslsa_childdet); -static int +static int uslsa_match(device_t parent, cfdata_t match, void *aux) { - struct usb_attach_arg *uaa = aux; + const struct usbif_attach_arg *uaa; + + uaa = aux; - return (uslsa_lookup(uaa->vendor, uaa->product) != NULL ? - UMATCH_VENDOR_PRODUCT : UMATCH_NONE); + if (usb_lookup(uslsa_devs, uaa->vendor, uaa->product) != NULL) { + return UMATCH_VENDOR_PRODUCT; + } else { + return UMATCH_NONE; + } } -static void +static void uslsa_attach(device_t parent, device_t self, void *aux) { - struct uslsa_softc *sc = device_private(self); - struct usb_attach_arg *uaa = aux; - usbd_device_handle dev = uaa->device; - usbd_interface_handle iface; - usb_interface_descriptor_t *id; - usb_endpoint_descriptor_t *ed; + struct uslsa_softc *sc; + const struct usbif_attach_arg *uaa; + const usb_interface_descriptor_t *id; + const usb_endpoint_descriptor_t *ed; char *devinfop; - usbd_status err; struct ucom_attach_args uca; int i; - sc->sc_dev = self; + sc = device_private(self); + uaa = aux; - DPRINTFN(10, ("\nuslsa_attach: sc=%p\n", sc)); + sc->sc_dev = self; + sc->sc_udev = uaa->device; + sc->sc_iface = uaa->iface; aprint_naive("\n"); aprint_normal("\n"); - devinfop = usbd_devinfo_alloc(dev, 0); + devinfop = usbd_devinfo_alloc(sc->sc_udev, 0); aprint_normal_dev(self, "%s\n", devinfop); usbd_devinfo_free(devinfop); - /* Move the device into the configured state. */ - err = usbd_set_config_index(dev, USLSA_CONFIG_INDEX, 1); - if (err) { - aprint_error_dev(self, "failed to set configuration, err=%s\n", - usbd_errstr(err)); - goto bad; - } - - err = usbd_device2interface_handle(dev, USLSA_IFACE_INDEX, &iface); - if (err) { - aprint_error_dev(self, "failed to get interface, err=%s\n", - usbd_errstr(err)); - goto bad; - } - - id = usbd_get_interface_descriptor(iface); + id = usbd_get_interface_descriptor(sc->sc_iface); - sc->sc_udev = dev; - sc->sc_iface = iface; + sc->sc_ifnum = id->bInterfaceNumber; uca.info = "Silicon Labs CP210x"; uca.portno = UCOM_UNK_PORTNO; @@ -275,8 +202,8 @@ uslsa_attach(device_t parent, device_t s uca.obufsize = USLSA_BUFSIZE; uca.ibufsizepad = USLSA_BUFSIZE; uca.opkthdrlen = 0; - uca.device = dev; - uca.iface = iface; + uca.device = sc->sc_udev; + uca.iface = sc->sc_iface; uca.methods = &uslsa_methods; uca.arg = sc; @@ -287,42 +214,36 @@ uslsa_attach(device_t parent, device_t s for (i = 0; i < id->bNumEndpoints; i++) { int addr, dir, attr; - ed = usbd_interface2endpoint_descriptor(iface, i); + ed = usbd_interface2endpoint_descriptor(sc->sc_iface, i); if (ed == NULL) { aprint_error_dev(self, - "could not read endpoint descriptor: %s\n", - usbd_errstr(err)); - goto bad; + "could not read endpoint descriptor\n"); + sc->sc_dying = true; + return; } addr = ed->bEndpointAddress; dir = UE_GET_DIR(ed->bEndpointAddress); attr = ed->bmAttributes & UE_XFERTYPE; - if (dir == UE_DIR_IN && attr == UE_BULK) + if (dir == UE_DIR_IN && attr == UE_BULK) { uca.bulkin = addr; - else if (dir == UE_DIR_OUT && attr == UE_BULK) + } else if (dir == UE_DIR_OUT && attr == UE_BULK) { uca.bulkout = addr; - else + } else { aprint_error_dev(self, "unexpected endpoint\n"); + } } - if (uca.bulkin == -1) { - aprint_error_dev(self, "Could not find data bulk in\n"); - goto bad; - } - if (uca.bulkout == -1) { - aprint_error_dev(self, "Could not find data bulk out\n"); - goto bad; + aprint_debug_dev(sc->sc_dev, "EPs: in=%#x out=%#x\n", + uca.bulkin, uca.bulkout); + if ((uca.bulkin == -1) || (uca.bulkout == -1)) { + aprint_error_dev(self, "could not find endpoints\n"); + sc->sc_dying = true; + return; } - DPRINTF(("uslsa: in=0x%x out=0x%x\n", uca.bulkin, uca.bulkout)); sc->sc_subdev = config_found_sm_loc(self, "ucombus", NULL, &uca, ucomprint, ucomsubmatch); return; - -bad: - DPRINTF(("uslsa_attach: ATTACH ERROR\n")); - sc->sc_dying = 1; - return; } static int @@ -332,7 +253,7 @@ uslsa_activate(device_t self, enum devac switch (act) { case DVACT_DEACTIVATE: - sc->sc_dying = 1; + sc->sc_dying = true; return 0; default: return EOPNOTSUPP; @@ -348,18 +269,19 @@ uslsa_childdet(device_t self, device_t c sc->sc_subdev = NULL; } -static int +static int uslsa_detach(device_t self, int flags) { struct uslsa_softc *sc = device_private(self); int rv = 0; - DPRINTF(("uslsa_detach: sc=%p flags=%d\n", sc, flags)); + DPRINTF((self, "%s(%p, %#x)\n", __func__, self, flags)); - sc->sc_dying = 1; + sc->sc_dying = true; - if (sc->sc_subdev != NULL) + if (sc->sc_subdev != NULL) { rv = config_detach(sc->sc_subdev, flags); + } usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_udev, sc->sc_dev); @@ -367,39 +289,61 @@ uslsa_detach(device_t self, int flags) return (rv); } +static int +uslsa_usbd_errno(usbd_status status) +{ + switch (status) { + case USBD_NORMAL_COMPLETION: + return 0; + case USBD_STALLED: + return EINVAL; + default: + return EIO; + } +} + static void uslsa_get_status(void *vsc, int portno, u_char *lsr, u_char *msr) { struct uslsa_softc *sc; usb_device_request_t req; - usbd_status err; - int actlen; - uint16_t flowreg; + usbd_status status; + uint8_t mdmsts; sc = vsc; - DPRINTF(("uslsa_get_status:\n")); + DPRINTF((sc->sc_dev, "%s(%p, %d, ....)\n", __func__, vsc, portno)); + + if (sc->sc_dying) { + return; + } req.bmRequestType = UT_READ_VENDOR_INTERFACE; - req.bRequest = USLSA_REQ_GET_FLOW; + req.bRequest = SLSA_R_GET_MDMSTS; USETW(req.wValue, 0); - USETW(req.wIndex, 0); - USETW(req.wLength, sizeof(flowreg)); + USETW(req.wIndex, sc->sc_ifnum); + USETW(req.wLength, SLSA_RL_GET_MDMSTS); + + status = usbd_do_request(sc->sc_udev, &req, &mdmsts); + if (status != USBD_NORMAL_COMPLETION) { + device_printf(sc->sc_dev, "%s: GET_MDMSTS %s\n", + __func__, usbd_errstr(status)); + return; + } + + DPRINTF((sc->sc_dev, "%s: GET_MDMSTS %#x\n", __func__, mdmsts)); + + if (lsr != NULL) { + *lsr = 0; + } - err = usbd_do_request_flags(sc->sc_udev, &req, &flowreg, - USBD_SHORT_XFER_OK, &actlen, USBD_DEFAULT_TIMEOUT); - if (err) - printf("%s: uslsa_get_status: %s\n", - device_xname(sc->sc_dev), usbd_errstr(err)); - - DPRINTF(("uslsa_get_status: flowreg=0x%x\n", flowreg)); - - sc->sc_msr = (u_char)(USLSA_FLOW_MSR_MASK & flowreg); - - if (lsr != NULL) - *lsr = sc->sc_lsr; - if (msr != NULL) - *msr = sc->sc_msr; + if (msr != NULL) { + *msr = 0; + *msr |= ISSET(mdmsts, SLSA_MDMSTS_CTS) ? UMSR_CTS : 0; + *msr |= ISSET(mdmsts, SLSA_MDMSTS_DSR) ? UMSR_DSR : 0; + *msr |= ISSET(mdmsts, SLSA_MDMSTS_RI) ? UMSR_RI : 0; + *msr |= ISSET(mdmsts, SLSA_MDMSTS_DCD) ? UMSR_DCD : 0; + } } static void @@ -409,30 +353,33 @@ uslsa_set(void *vsc, int portno, int reg sc = vsc; - DPRINTF(("uslsa_set: sc=%p, port=%d reg=%d onoff=%d\n", sc, portno, - reg, onoff)); + DPRINTF((sc->sc_dev, "%s(%p, %d, %d, %d)\n", __func__, vsc, portno, reg, onoff)); + + if (sc->sc_dying) { + return; + } switch (reg) { case UCOM_SET_DTR: - if (uslsa_request_set(sc, USLSA_REQ_SET_FLOW, - (onoff ? (USLSA_FLOW_DTR | USLSA_FLOW_SET_DTR) : - USLSA_FLOW_SET_DTR))) - printf("%s: uslsa_set_dtr failed\n", - device_xname(sc->sc_dev)); + if (uslsa_request_set(sc, SLSA_R_SET_MHS, + SLSA_RV_SET_MHS_DTR_MASK | + (onoff ? SLSA_RV_SET_MHS_DTR : 0))) { + device_printf(sc->sc_dev, "SET_MHS/DTR failed\n"); + } break; case UCOM_SET_RTS: - if (uslsa_request_set(sc, USLSA_REQ_SET_FLOW, - (onoff ? (USLSA_FLOW_RTS | USLSA_FLOW_SET_RTS) : - USLSA_FLOW_SET_RTS))) - printf("%s: uslsa_set_rts failed\n", - device_xname(sc->sc_dev)); + if (uslsa_request_set(sc, SLSA_R_SET_MHS, + SLSA_RV_SET_MHS_RTS_MASK | + (onoff ? SLSA_RV_SET_MHS_RTS : 0))) { + device_printf(sc->sc_dev, "SET_MHS/RTS failed\n"); + } break; case UCOM_SET_BREAK: - if (uslsa_request_set(sc, USLSA_REQ_SET_BREAK, - (onoff ? USLSA_BREAK_ENABLE : - USLSA_BREAK_DISABLE))) - printf("%s: uslsa_set_break failed\n", - device_xname(sc->sc_dev)); + if (uslsa_request_set(sc, SLSA_R_SET_BREAK, + (onoff ? SLSA_RV_SET_BREAK_ENABLE : + SLSA_RV_SET_BREAK_DISABLE))) { + device_printf(sc->sc_dev, "SET_BREAK failed\n"); + } break; default: break; @@ -440,81 +387,92 @@ uslsa_set(void *vsc, int portno, int reg } static int -uslsa_param(void *vsc, int portno, struct termios * t) +uslsa_param(void *vsc, int portno, struct termios *t) { struct uslsa_softc *sc; - uint16_t data; + int ret; + uint16_t value; sc = vsc; - DPRINTF(("uslsa_param: sc=%p\n", sc)); + DPRINTF((sc->sc_dev, "%s(%p, %d, %p)\n", __func__, vsc, portno, t)); - switch (t->c_ospeed) { - case B600: - case B1200: - case B2400: - case B4800: - case B9600: - case B19200: - case B38400: - case B57600: - case B115200: - case B230400: - case B460800: - case B921600: - data = USLSA_BPS(t->c_ospeed); - break; - default: - printf("%s: uslsa_param: unsupported data rate, " - "forcing default of 115200bps\n", - device_xname(sc->sc_dev)); - data = USLSA_BPS(B115200); - }; - - if (uslsa_request_set(sc, USLSA_REQ_SET_BPS, data)) - printf("%s: uslsa_param: setting data rate failed\n", - device_xname(sc->sc_dev)); - - data = 0; - - if (ISSET(t->c_cflag, CSTOPB)) - data |= USLSA_DPS_STOP_TWO; - else - data |= USLSA_DPS_STOP_ONE; + if (sc->sc_dying) { + return EIO; + } + + value = SLSA_RV_BAUDDIV(t->c_ospeed); + + if ((ret = uslsa_request_set(sc, SLSA_R_SET_BAUDDIV, value)) != 0) { + device_printf(sc->sc_dev, "%s: SET_BAUDDIV failed\n", + __func__); + return ret; + } + + value = 0; + + if (ISSET(t->c_cflag, CSTOPB)) { + value |= SLSA_RV_LINE_CTL_STOP_2; + } else { + value |= SLSA_RV_LINE_CTL_STOP_1; + } if (ISSET(t->c_cflag, PARENB)) { - if (ISSET(t->c_cflag, PARODD)) - data |= USLSA_DPS_PARITY_ODD; - else - data |= USLSA_DPS_PARITY_EVEN; - } else - data |= USLSA_DPS_PARITY_NONE; + if (ISSET(t->c_cflag, PARODD)) { + value |= SLSA_RV_LINE_CTL_PARITY_ODD; + } else { + value |= SLSA_RV_LINE_CTL_PARITY_EVEN; + } + } else { + value |= SLSA_RV_LINE_CTL_PARITY_NONE; + } switch (ISSET(t->c_cflag, CSIZE)) { case CS5: - data |= USLSA_DPS_DATA_FIVE; + value |= SLSA_RV_LINE_CTL_LEN_5; break; case CS6: - data |= USLSA_DPS_DATA_SIX; + value |= SLSA_RV_LINE_CTL_LEN_6; break; case CS7: - data |= USLSA_DPS_DATA_SEVEN; + value |= SLSA_RV_LINE_CTL_LEN_7; break; case CS8: - data |= USLSA_DPS_DATA_EIGHT; + value |= SLSA_RV_LINE_CTL_LEN_8; break; } - DPRINTF(("uslsa_param: setting DPS register to 0x%x\n", data)); - if (uslsa_request_set(sc, USLSA_REQ_SET_DPS, data)) - printf("%s: setting DPS register failed: invalid argument\n", - device_xname(sc->sc_dev)); + DPRINTF((sc->sc_dev, "%s: setting LINE_CTL to 0x%x\n", + __func__, value)); + if ((ret = uslsa_request_set(sc, SLSA_R_SET_LINE_CTL, value)) != 0) { + device_printf(sc->sc_dev, "SET_LINE_CTL failed\n"); + return ret; + } - uslsa_set_flow(sc, t->c_cflag, t->c_iflag); + if ((ret = uslsa_set_flow(sc, t->c_cflag, t->c_iflag)) != 0) { + device_printf(sc->sc_dev, "SET_LINE_CTL failed\n"); + } - return 0; + return ret; } +static int +uslsa_ioctl(void *vsc, int portno, u_long cmd, void *data, int flag, proc_t *p) +{ + struct uslsa_softc *sc; + + sc = vsc; + + switch (cmd) { + case TIOCMGET: + ucom_status_change(device_private(sc->sc_subdev)); + return EPASSTHROUGH; + default: + return EPASSTHROUGH; + } + + return 0; +} static int uslsa_open(void *vsc, int portno) @@ -523,15 +481,14 @@ uslsa_open(void *vsc, int portno) sc = vsc; - DPRINTF(("uslsa_open: sc=%p\n", sc)); - - if (sc->sc_dying) - return (EIO); + DPRINTF((sc->sc_dev, "%s(%p, %d)\n", __func__, vsc, portno)); - if (uslsa_request_set(sc, USLSA_REQ_SET_STATE, USLSA_STATE_ENABLE)) - return (EIO); + if (sc->sc_dying) { + return EIO; + } - return 0; + return uslsa_request_set(sc, SLSA_R_IFC_ENABLE, + SLSA_RV_IFC_ENABLE_ENABLE); } static void @@ -541,81 +498,79 @@ uslsa_close(void *vsc, int portno) sc = vsc; - if (sc->sc_dying) - return; + DPRINTF((sc->sc_dev, "%s(%p, %d)\n", __func__, vsc, portno)); - DPRINTF(("uslsa_close: sc=%p\n", sc)); + if (sc->sc_dying) { + return; + } - if (uslsa_request_set(sc, USLSA_REQ_SET_STATE, - USLSA_STATE_DISABLE)) - printf("%s: disable-on-close failed\n", - device_xname(sc->sc_dev)); + (void)uslsa_request_set(sc, SLSA_R_IFC_ENABLE, + SLSA_RV_IFC_ENABLE_DISABLE); } -/* - * uslsa_request_set(), wrapper for doing sets with usbd_do_request() - */ static int uslsa_request_set(struct uslsa_softc * sc, uint8_t request, uint16_t value) { usb_device_request_t req; - usbd_status err; + usbd_status status; req.bmRequestType = UT_WRITE_VENDOR_INTERFACE; req.bRequest = request; USETW(req.wValue, value); - USETW(req.wIndex, 0); + USETW(req.wIndex, sc->sc_ifnum); USETW(req.wLength, 0); - err = usbd_do_request(sc->sc_udev, &req, 0); - if (err) - printf("%s: uslsa_request: %s\n", - device_xname(sc->sc_dev), usbd_errstr(err)); - return err; + status = usbd_do_request(sc->sc_udev, &req, NULL); + + return uslsa_usbd_errno(status); } -/* - * uslsa_set_flow() does some magic to set up hardware flow control - */ -static void +static int uslsa_set_flow(struct uslsa_softc *sc, tcflag_t cflag, tcflag_t iflag) { - uint8_t mysterydata[16]; + struct slsa_fcs fcs; usb_device_request_t req; - usbd_status err; + uint32_t ulControlHandshake; + uint32_t ulFlowReplace; + usbd_status status; - DPRINTF(("uslsa_set_flow: cflag = 0x%x, iflag = 0x%x\n", - cflag, iflag)); + DPRINTF((sc->sc_dev, "%s(%p, %#x, %#x)\n", __func__, sc, cflag, iflag)); req.bmRequestType = UT_READ_VENDOR_INTERFACE; - req.bRequest = USLSA_REQ_GET_MODEM; + req.bRequest = SLSA_R_GET_FLOW; USETW(req.wValue, 0); - USETW(req.wIndex, 0); - USETW(req.wLength, 16); + USETW(req.wIndex, sc->sc_ifnum); + USETW(req.wLength, SLSA_RL_GET_FLOW); + + status = usbd_do_request(sc->sc_udev, &req, &fcs); + if (status != USBD_NORMAL_COMPLETION) { + device_printf(sc->sc_dev, "%s: GET_FLOW %s\n", + __func__, usbd_errstr(status)); + return uslsa_usbd_errno(status); + } - err = usbd_do_request(sc->sc_udev, &req, mysterydata); - if (err) - printf("%s: uslsa_set_flow: %s\n", - device_xname(sc->sc_dev), usbd_errstr(err)); + ulControlHandshake = le32toh(fcs.ulControlHandshake); + ulFlowReplace = le32toh(fcs.ulFlowReplace); if (ISSET(cflag, CRTSCTS)) { - mysterydata[0] &= ~0x7b; - mysterydata[0] |= 0x09; - mysterydata[4] = 0x80; + ulControlHandshake = + SERIAL_CTS_HANDSHAKE | __SHIFTIN(1, SERIAL_DTR_MASK); + ulFlowReplace = __SHIFTIN(2, SERIAL_RTS_MASK); } else { - mysterydata[0] &= ~0x7b; - mysterydata[0] |= 0x01; - mysterydata[4] = 0x40; + ulControlHandshake = __SHIFTIN(1, SERIAL_DTR_MASK); + ulFlowReplace = __SHIFTIN(1, SERIAL_RTS_MASK); } + fcs.ulControlHandshake = htole32(ulControlHandshake); + fcs.ulFlowReplace = htole32(ulFlowReplace); + req.bmRequestType = UT_WRITE_VENDOR_INTERFACE; - req.bRequest = USLSA_REQ_SET_MODEM; + req.bRequest = SLSA_R_SET_FLOW; USETW(req.wValue, 0); - USETW(req.wIndex, 0); - USETW(req.wLength, 16); + USETW(req.wIndex, sc->sc_ifnum); + USETW(req.wLength, SLSA_RL_SET_FLOW); + + status = usbd_do_request(sc->sc_udev, &req, &fcs); - err = usbd_do_request(sc->sc_udev, &req, mysterydata); - if (err) - printf("%s: uslsa_set_flow: %s\n", - device_xname(sc->sc_dev), usbd_errstr(err)); + return uslsa_usbd_errno(status); } Added files: Index: src/sys/dev/usb/uslsareg.h diff -u /dev/null src/sys/dev/usb/uslsareg.h:1.1 --- /dev/null Sat Jan 14 21:06:01 2012 +++ src/sys/dev/usb/uslsareg.h Sat Jan 14 21:06:01 2012 @@ -0,0 +1,240 @@ +/* $NetBSD: uslsareg.h,v 1.1 2012/01/14 21:06:01 jakllsch Exp $ */ + +/* + * Copyright (c) 2011 Jonathan A. Kollasch. + * All rights reserved. + * + * 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 ``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 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. + * + */ + +#ifndef _SLSAREG_H_ +#define _SLSAREG_H_ + +#include <lib/libkern/libkern.h> +#include <sys/systm.h> +#include <sys/types.h> + +/* From Silicon Laboratories Application Note AN571 */ + +#define SLSA_FREQ 3686400 + +/* USB Control Requests */ +#define SLSA_R_IFC_ENABLE 0x00 +#define SLSA_R_SET_BAUDDIV 0x01 +#define SLSA_R_GET_BAUDDIV 0x02 +#define SLSA_R_SET_LINE_CTL 0x03 +#define SLSA_R_GET_LINE_CTL 0x04 +#define SLSA_R_SET_BREAK 0x05 +#define SLSA_R_IMM_CHAR 0x06 +#define SLSA_R_SET_MHS 0x07 +#define SLSA_R_GET_MDMSTS 0x08 +#define SLSA_R_SET_XON 0x09 +#define SLSA_R_SET_XOFF 0x0a +#define SLSA_R_SET_EVENTMASK 0x0b +#define SLSA_R_GET_EVENTMASK 0x0c +#define SLSA_R_SET_CHAR 0x0d +#define SLSA_R_GET_CHARS 0x0e +#define SLSA_R_GET_PROPS 0x0f +#define SLSA_R_GET_COMM_STATUS 0x10 +#define SLSA_R_RESET 0x11 +#define SLSA_R_PURGE 0x12 +#define SLSA_R_SET_FLOW 0x13 +#define SLSA_R_GET_FLOW 0x14 +#define SLSA_R_EMBED_EVENTS 0x15 +#define SLSA_R_SET_CHARS 0x19 +#define SLSA_R_GET_BAUDRATE 0x1d +#define SLSA_R_SET_BAUDRATE 0x1e +#define SLSA_R_VENDOR_SPECIFIC 0xff + + +#define SLSA_RV_IFC_ENABLE_DISABLE 0x0000 +#define SLSA_RV_IFC_ENABLE_ENABLE 0x0001 + + +#define SLSA_RV_BAUDDIV(b) (SLSA_FREQ/(b)) + + +#define SLSA_RV_LINE_CTL_STOP __BITS(3,0) +#define SLSA_RV_LINE_CTL_PARITY __BITS(7,4) +#define SLSA_RV_LINE_CTL_LEN __BITS(15,8) + +#define SLSA_RV_LINE_CTL_STOP_1 __SHIFTIN(0, SLSA_RV_LINE_CTL_STOP) +#define SLSA_RV_LINE_CTL_STOP_1_5 __SHIFTIN(1, SLSA_RV_LINE_CTL_STOP) +#define SLSA_RV_LINE_CTL_STOP_2 __SHIFTIN(2, SLSA_RV_LINE_CTL_STOP) + +#define SLSA_RV_LINE_CTL_PARITY_NONE __SHIFTIN(0, SLSA_RV_LINE_CTL_PARITY) +#define SLSA_RV_LINE_CTL_PARITY_ODD __SHIFTIN(1, SLSA_RV_LINE_CTL_PARITY) +#define SLSA_RV_LINE_CTL_PARITY_EVEN __SHIFTIN(2, SLSA_RV_LINE_CTL_PARITY) +#define SLSA_RV_LINE_CTL_PARITY_MARK __SHIFTIN(3, SLSA_RV_LINE_CTL_PARITY) +#define SLSA_RV_LINE_CTL_PARITY_SPACE __SHIFTIN(4, SLSA_RV_LINE_CTL_PARITY) + +#define SLSA_RV_LINE_CTL_LEN_5 __SHIFTIN(5, SLSA_RV_LINE_CTL_LEN) +#define SLSA_RV_LINE_CTL_LEN_6 __SHIFTIN(6, SLSA_RV_LINE_CTL_LEN) +#define SLSA_RV_LINE_CTL_LEN_7 __SHIFTIN(7, SLSA_RV_LINE_CTL_LEN) +#define SLSA_RV_LINE_CTL_LEN_8 __SHIFTIN(8, SLSA_RV_LINE_CTL_LEN) + + +#define SLSA_RV_SET_BREAK_DISABLE 0x0000 +#define SLSA_RV_SET_BREAK_ENABLE 0x0001 + + +#define SLSA_RV_SET_MHS_DTR __BIT(0) +#define SLSA_RV_SET_MHS_RTS __BIT(1) +/* AN571 calls these next two masks, they're more like change-enables */ +#define SLSA_RV_SET_MHS_DTR_MASK __BIT(8) +#define SLSA_RV_SET_MHS_RTS_MASK __BIT(9) + + +#define SLSA_RL_GET_MDMSTS 1 +/* data in uint8_t returned from GET_MDMSTS */ +#define SLSA_MDMSTS_DTR __BIT(0) +#define SLSA_MDMSTS_RTS __BIT(1) +#define SLSA_MDMSTS_CTS __BIT(4) +#define SLSA_MDMSTS_DSR __BIT(5) +#define SLSA_MDMSTS_RI __BIT(6) +#define SLSA_MDMSTS_DCD __BIT(7) + + +#define SLSA_RL_SET_EVENTMASK 0 +#define SLSA_RL_GET_EVENTMASK 2 +#define SLSA_RL_GET_EVENTSTATE 2 +#define SLSA_EVENT_TERI __BIT(0) /* RI trailing edge */ +#define SLSA_EVENT_RB80 __BIT(2) /* Rx buf 80% full */ +#define SLSA_EVENT_CR __BIT(8) /* char received */ +#define SLSA_EVENT_SCR __BIT(9) /* special char received */ +#define SLSA_EVENT_TQE __BIT(10) /* Tx queue empty */ +#define SLSA_EVENT_DCTS __BIT(11) /* CTS changed */ +#define SLSA_EVENT_DDSR __BIT(12) /* DSR changed */ +#define SLSA_EVENT_DDCD __BIT(13) /* DCD changed */ +#define SLSA_EVENT_BI __BIT(14) /* line break received */ +#define SLSA_EVENT_LSE __BIT(15) /* line status error */ + + +/* USETW2(wValue, char, index) */ +#define SLSA_RV_SET_CHAR_EofChar 0 +#define SLSA_RV_SET_CHAR_ErrorChar 1 +#define SLSA_RV_SET_CHAR_BreakChar 2 +#define SLSA_RV_SET_CHAR_EventChar 3 +#define SLSA_RV_SET_CHAR_XonChar 4 +#define SLSA_RV_SET_CHAR_XoffChar 5 + + +#define SLSA_RV_PURGE_TX __BIT(0) +#define SLSA_RV_PURGE_RX __BIT(1) +#define SLSA_RV_PURGE_TX1 __BIT(2) /* what's the second set for? */ +#define SLSA_RV_PURGE_RX1 __BIT(3) + + +/* Communication Properties Response Table 7. */ +struct slsa_cpr { + uint16_t wLength; + uint16_t bcdVersion; + uint32_t ulServiceMask; + uint32_t _reserved8; + uint32_t ulMaxTxQueue; + uint32_t ulMaxRxQueue; + uint32_t ulMaxBaud; + uint32_t ulProvSubType; + uint32_t ulProvCapabilities; + uint32_t ulSettableParams; + uint32_t ulSettableBaud; + uint16_t wSettableData; + uint16_t _reserved42; + uint32_t ulCurrentTxQueue; + uint32_t ulCurrentRxQueue; + uint32_t _reserved52; + uint32_t _reserved56; + uint16_t uniProvName[0]; +}; +CTASSERT(offsetof(struct slsa_cpr, _reserved8) == 8); +CTASSERT(offsetof(struct slsa_cpr, _reserved42) == 42); +CTASSERT(offsetof(struct slsa_cpr, uniProvName[0]) == 60); +#define SLSA_CPR_MINLEN 60 + +#define SLSA_RL_GET_COMM_STATUS 19 +/* Serial Status Response Table 8. */ +struct slsa_ssr { + uint32_t ulErrors; + uint32_t ulHoldReasons; + uint32_t ulAmountInInQueue; + uint32_t ulAmountInOutQueue; + uint8_t bEofReceived; + uint8_t bWaitForImmediate; + uint8_t bReserved; +}; +CTASSERT(offsetof(struct slsa_ssr, bReserved) == 18); +CTASSERT(sizeof(struct slsa_ssr) >= SLSA_RL_GET_COMM_STATUS); + +#define SLSA_RL_SET_FLOW 16 +#define SLSA_RL_GET_FLOW 16 +/* Flow Control State Setting/Response Table 9. */ +struct slsa_fcs { + uint32_t ulControlHandshake; +#define SERIAL_DTR_MASK __BITS(0, 1) +#define SERIAL_CTS_HANDSHAKE __BIT(3) +#define SERIAL_DSR_HANDSHAKE __BIT(4) +#define SERIAL_DCD_HANDSHAKE __BIT(5) +#define SERIAL_DSR_SENSITIVITY __BIT(6) + uint32_t ulFlowReplace; +#define SERIAL_AUTO_TRANSMIT __BIT(0) +#define SERIAL_AUTO_RECEIVE __BIT(1) +#define SERIAL_ERROR_CHAR __BIT(2) +#define SERIAL_NULL_STRIPPING __BIT(3) +#define SERIAL_BREAK_CHAR __BIT(4) +#define SERIAL_RTS_MASK __BITS(6, 7) +#define SERIAL_XOFF_CONTINUE __BIT(31) + uint32_t ulXonLimit; + uint32_t ulXoffLimit; +}; +CTASSERT(sizeof(struct slsa_fcs) == SLSA_RL_SET_FLOW); +CTASSERT(sizeof(struct slsa_fcs) == SLSA_RL_GET_FLOW); + +#define SLSA_RL_SET_CHARS 6 +#define SLSA_RL_GET_CHARS 6 +/* Special Characters Response Table 12. */ +struct slsa_scr { + uint8_t bEofChar; + uint8_t bErrorChar; + uint8_t bBreakChar; + uint8_t bEventChar; + uint8_t bXonChar; + uint8_t bXoffChar; +}; +CTASSERT(sizeof(struct slsa_scr) == SLSA_RL_SET_CHARS); +CTASSERT(sizeof(struct slsa_scr) == SLSA_RL_GET_CHARS); + + +#define SLSA_RV_VENDOR_SPECIFIC_READ_LATCH 0x00c2 +#define SLSA_RL_VENDOR_SPECIFIC_READ_LATCH 1 + +#define SLSA_RV_VENDOR_SPECIFIC_WRITE_LATCH 0x37e1 +/* + * on CP2103/CP2104 the latch state and latch mask are + * written in the high and low bytes of wIndex respectively + * + * on CP2105, wIndex is the interface number, and the same + * latch/mask is written as data instead. + */ +#define SLSA_RL_VENDOR_SPECIFIC_WRITE_LATCH_CP2103 0 +#define SLSA_RL_VENDOR_SPECIFIC_WRITE_LATCH_CP2105 2 + +#endif /* _SLSAREG_H_ */