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_ */

Reply via email to