summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoshua Stein <jcs@cvs.openbsd.org>2019-06-07 16:07:00 +0000
committerJoshua Stein <jcs@cvs.openbsd.org>2019-06-07 16:07:00 +0000
commitbc44e76bbe9f66d4189a4ae315c2f1ac034b63e5 (patch)
tree1f9d76631329a17498e52e6b51cbce2a41e8605e
parentfd80027764cb94e19c2e9a11b067e6723b8c215b (diff)
add ukspan(4), a driver for the Keyspan USA19HS USB serial adapter
written by Cody Cutler <ccutler at csail.mit.edu>
-rw-r--r--share/man/man4/Makefile4
-rw-r--r--share/man/man4/ucom.45
-rw-r--r--share/man/man4/ukspan.439
-rw-r--r--share/man/man4/usb.46
-rw-r--r--sys/arch/amd64/conf/GENERIC4
-rw-r--r--sys/dev/usb/files.usb7
-rw-r--r--sys/dev/usb/ukspan.c594
7 files changed, 651 insertions, 8 deletions
diff --git a/share/man/man4/Makefile b/share/man/man4/Makefile
index 7d470b6ca47..0a31a21ef29 100644
--- a/share/man/man4/Makefile
+++ b/share/man/man4/Makefile
@@ -1,4 +1,4 @@
-# $OpenBSD: Makefile,v 1.712 2019/05/12 00:49:30 jsg Exp $
+# $OpenBSD: Makefile,v 1.713 2019/06/07 16:06:59 jcs Exp $
MAN= aac.4 abcrtc.4 ac97.4 acphy.4 acrtc.4 \
acpi.4 acpiac.4 acpials.4 acpiasus.4 acpibat.4 \
@@ -71,7 +71,7 @@ MAN= aac.4 abcrtc.4 ac97.4 acphy.4 acrtc.4 \
tlphy.4 thmc.4 tpm.4 tqphy.4 trm.4 trunk.4 tsl.4 tty.4 tun.4 tap.4 \
twe.4 \
txp.4 txphy.4 uaudio.4 uark.4 uath.4 ubcmtp.4 uberry.4 ubsa.4 \
- ubsec.4 ucom.4 uchcom.4 ucrcom.4 ucycom.4 uslhcom.4 \
+ ubsec.4 ucom.4 uchcom.4 ucrcom.4 ucycom.4 ukspan.4 uslhcom.4 \
udav.4 udcf.4 udl.4 udp.4 udsbr.4 \
uftdi.4 ugen.4 ugl.4 ugold.4 uguru.4 uhci.4 uhid.4 uhidev.4 uipaq.4 \
uk.4 ukbd.4 \
diff --git a/share/man/man4/ucom.4 b/share/man/man4/ucom.4
index e14df75675b..aaa44796291 100644
--- a/share/man/man4/ucom.4
+++ b/share/man/man4/ucom.4
@@ -1,4 +1,4 @@
-.\" $OpenBSD: ucom.4,v 1.32 2019/03/27 22:11:21 kettenis Exp $
+.\" $OpenBSD: ucom.4,v 1.33 2019/06/07 16:06:59 jcs Exp $
.\" $NetBSD: ucom.4,v 1.3 2000/04/14 14:55:18 augustss Exp $
.\"
.\" Copyright (c) 1999 The NetBSD Foundation, Inc.
@@ -28,7 +28,7 @@
.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
.\" POSSIBILITY OF SUCH DAMAGE.
.\"
-.Dd $Mdocdate: March 27 2019 $
+.Dd $Mdocdate: June 7 2019 $
.Dt UCOM 4
.Os
.Sh NAME
@@ -42,6 +42,7 @@
.Cd "ucom* at ucycom?" # Cypress
.Cd "ucom* at uftdi?" # FTDI
.Cd "ucom* at uipaq?" # iPAQ
+.Cd "ucom* at ukspan?" # Keyspan
.Cd "ucom* at umcs?" # MosChip Semiconductor multiport
.Cd "ucom* at umct?" # MCT
.Cd "ucom* at umodem?" # Standardized umodem
diff --git a/share/man/man4/ukspan.4 b/share/man/man4/ukspan.4
new file mode 100644
index 00000000000..6f1d78033b4
--- /dev/null
+++ b/share/man/man4/ukspan.4
@@ -0,0 +1,39 @@
+.\" Copyright (c) 2019 Cody Cutler <ccutler@csail.mit.edu>
+.\"
+.\" Permission to use, copy, modify, and distribute this software for any
+.\" purpose with or without fee is hereby granted, provided that the above
+.\" copyright notice and this permission notice appear in all copies.
+.\"
+.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+.\"
+.Dd $Mdocdate: June 7 2019 $
+.Dt UKSPAN 4
+.Os
+.Sh NAME
+.Nm ukspan
+.Nd Keyspan USB serial adapter
+.Sh SYNOPSIS
+.Cd "ukspan* at uhub?"
+.Cd "ucom* at ukspan?"
+.Sh DESCRIPTION
+The
+.Nm
+driver supports the TrippLite Keyspan USA-19HS serial adapter, which is made
+accessible through
+.Xr ucom 4 .
+.Sh SEE ALSO
+.Xr tty 4 ,
+.Xr ucom 4 ,
+.Xr uhub 4 ,
+.Xr usb 4
+.Sh AUTHORS
+The
+.Nm
+driver was written by
+.An Cody Cutler Aq Mt ccutler@csail.mit.edu .
diff --git a/share/man/man4/usb.4 b/share/man/man4/usb.4
index 520d513f5b1..f4f636ae553 100644
--- a/share/man/man4/usb.4
+++ b/share/man/man4/usb.4
@@ -1,4 +1,4 @@
-.\" $OpenBSD: usb.4,v 1.194 2019/05/12 06:09:59 jmc Exp $
+.\" $OpenBSD: usb.4,v 1.195 2019/06/07 16:06:59 jcs Exp $
.\" $NetBSD: usb.4,v 1.15 1999/07/29 14:20:32 augustss Exp $
.\"
.\" Copyright (c) 1999 The NetBSD Foundation, Inc.
@@ -28,7 +28,7 @@
.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
.\" POSSIBILITY OF SUCH DAMAGE.
.\"
-.Dd $Mdocdate: May 12 2019 $
+.Dd $Mdocdate: June 7 2019 $
.Dt USB 4
.Os
.Sh NAME
@@ -188,6 +188,8 @@ FTDI USB serial adapter
iPAQ USB units
.It Xr ulpt 4
USB printer support
+.It Xr ukspan 4
+Keyspan serial adapter
.It Xr umcs 4
MosChip Semiconductor based USB multiport serial adapter
.It Xr umct 4
diff --git a/sys/arch/amd64/conf/GENERIC b/sys/arch/amd64/conf/GENERIC
index ad192f4ea1d..ee8cf18fdb5 100644
--- a/sys/arch/amd64/conf/GENERIC
+++ b/sys/arch/amd64/conf/GENERIC
@@ -1,4 +1,4 @@
-# $OpenBSD: GENERIC,v 1.472 2019/05/21 22:40:06 jsg Exp $
+# $OpenBSD: GENERIC,v 1.473 2019/06/07 16:06:59 jcs Exp $
#
# For further information on compiling OpenBSD kernels, see the config(8)
# man page.
@@ -224,6 +224,8 @@ uvscom* at uhub? # SUNTAC Slipper U VS-10U serial
ucom* at uvscom?
ubsa* at uhub? # Belkin serial adapter
ucom* at ubsa?
+ukspan* at uhub? # Keyspan USA19HS serial adapter
+ucom* at ukspan?
uftdi* at uhub? # FTDI FT8U100AX serial adapter
ucom* at uftdi?
uplcom* at uhub? # I/O DATA USB-RSAQ2 serial adapter
diff --git a/sys/dev/usb/files.usb b/sys/dev/usb/files.usb
index 1036cf36232..2f2c47fd19f 100644
--- a/sys/dev/usb/files.usb
+++ b/sys/dev/usb/files.usb
@@ -1,4 +1,4 @@
-# $OpenBSD: files.usb,v 1.138 2019/05/09 00:20:57 kettenis Exp $
+# $OpenBSD: files.usb,v 1.139 2019/06/07 16:06:59 jcs 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.
@@ -317,6 +317,11 @@ device ubsa: ucombus
attach ubsa at uhub
file dev/usb/ubsa.c ubsa
+# Keyspan USA19HS serial
+device ukspan: ucombus
+attach ukspan at uhub
+file dev/usb/ukspan.c ukspan
+
# Silicon Laboratories CP210x serial
device uslcom: ucombus
attach uslcom at uhub
diff --git a/sys/dev/usb/ukspan.c b/sys/dev/usb/ukspan.c
new file mode 100644
index 00000000000..5f5e9583641
--- /dev/null
+++ b/sys/dev/usb/ukspan.c
@@ -0,0 +1,594 @@
+/*
+ * Copyright (c) 2019 Cody Cutler <ccutler@csail.mit.edu>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * I don't know of any technical documentation for the Keyspan USA-19HS. I
+ * inspected the Linux driver (drivers/usb/serial/keyspan_usa90msg.h) to learn
+ * the device message format and the procedure for setting the baud rate.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/tty.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdevs.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+
+#include <dev/usb/ucomvar.h>
+
+/*#define UKSPAN_DEBUG */
+
+#ifdef UKSPAN_DEBUG
+ #define DPRINTF(x...) do { printf(x); } while (0)
+#else
+ #define DPRINTF(x...) do { ; } while (0)
+#endif
+
+#define UKSPAN_PARITY_NONE 0x0
+#define UKSPAN_PARITY_ODD 0x08
+#define UKSPAN_PARITY_EVEN 0x18
+
+#define UKSPAN_DATA_5 0x0
+#define UKSPAN_DATA_6 0x1
+#define UKSPAN_DATA_7 0x2
+#define UKSPAN_DATA_8 0x3
+
+#define UKSPAN_STOP_1 0x0
+#define UKSPAN_STOP_2 0x4
+
+#define UKSPAN_MAGIC 0x2
+
+#define UKSPAN_CLOCK 14769231
+
+/*
+ * The following USB indexes and endpoint addresses may be specific to the
+ * Keyspan USA19HS device
+ */
+#define UKSPAN_CONFIG_IDX 1
+#define UKSPAN_IFACE_IDX 0
+
+#define UKSPAN_EA_BULKIN (UE_DIR_IN | 1)
+#define UKSPAN_EA_BULKOUT (UE_DIR_OUT | 1)
+#define UKSPAN_EA_CONFIGIN (UE_DIR_IN | 2)
+#define UKSPAN_EA_CONFIGOUT (UE_DIR_OUT | 2)
+
+/* Sent to device on control out endpoint */
+struct ukspan_cmsg {
+ uint8_t setclock;
+ uint8_t baudlo;
+ uint8_t baudhi;
+ uint8_t setlcr;
+ uint8_t lcr;
+ uint8_t setrxmode;
+ uint8_t rxmode;
+ uint8_t settxmode;
+ uint8_t txmode;
+ uint8_t settxflowcontrol;
+ uint8_t txflowcontrol;
+ uint8_t setrxflowcontrol;
+ uint8_t rxflowcontrol;
+ uint8_t sendxoff;
+ uint8_t sendxon;
+ uint8_t xonchar;
+ uint8_t xoffchar;
+ uint8_t sendchar;
+ uint8_t txchar;
+ uint8_t setrts;
+ uint8_t rts;
+ uint8_t setdtr;
+ uint8_t dtr;
+
+ uint8_t rxforwardingchars;
+ uint8_t rxforwardingtimeoutms;
+ uint8_t txacksetting;
+
+ uint8_t portenabled;
+ uint8_t txflush;
+ uint8_t txbreak;
+ uint8_t loopbackmode;
+
+ uint8_t rxflush;
+ uint8_t rxforward;
+ uint8_t cancelrxoff;
+ uint8_t returnstatus;
+} __packed;
+
+/* Received from device on control in endpoint */
+struct ukspan_smsg {
+ uint8_t msr;
+ uint8_t cts;
+ uint8_t dcd;
+ uint8_t dsr;
+ uint8_t ri;
+ uint8_t txxoff;
+ uint8_t rxbreak;
+ uint8_t rxoverrun;
+ uint8_t rxparity;
+ uint8_t rxframe;
+ uint8_t portstate;
+ uint8_t messageack;
+ uint8_t charack;
+ uint8_t controlresp;
+} __packed;
+
+struct ukspan_softc {
+ struct device sc_dev;
+ struct usbd_device *udev;
+ struct usbd_interface *iface;
+ struct usbd_pipe *cout_pipe;
+ struct usbd_pipe *cin_pipe;
+ struct usbd_xfer *ixfer;
+ struct usbd_xfer *oxfer;
+ struct device *ucom_dev;
+ struct ukspan_smsg smsg;
+ struct ukspan_cmsg cmsg;
+ u_char lsr;
+ u_char msr;
+};
+
+int ukspan_match(struct device *, void *, void *);
+void ukspan_attach(struct device *, struct device *, void *);
+int ukspan_detach(struct device *, int);
+
+void ukspan_close(void *, int);
+int ukspan_open(void *, int);
+int ukspan_param(void *, int, struct termios *);
+void ukspan_set(void *, int, int, int);
+void ukspan_get_status(void *, int, u_char *, u_char *);
+
+void ukspan_cmsg_init(bool, struct ukspan_cmsg *);
+int ukspan_cmsg_send(struct ukspan_softc *);
+void ukspan_incb(struct usbd_xfer *, void *, usbd_status);
+void ukspan_outcb(struct usbd_xfer *, void *, usbd_status);
+void ukspan_destroy(struct ukspan_softc *);
+
+struct cfdriver ukspan_cd = {
+ NULL, "ukspan", DV_DULL
+};
+
+const struct cfattach ukspan_ca = {
+ sizeof(struct ukspan_softc), ukspan_match, ukspan_attach,
+ ukspan_detach
+};
+
+static const struct usb_devno ukspan_devs[] = {
+ { USB_VENDOR_KEYSPAN, USB_PRODUCT_KEYSPAN_USA19HS },
+};
+
+static struct ucom_methods ukspan_methods = {
+ .ucom_get_status = ukspan_get_status,
+ .ucom_set = ukspan_set,
+ .ucom_param = ukspan_param,
+ .ucom_ioctl = NULL,
+ .ucom_open = ukspan_open,
+ .ucom_close = ukspan_close,
+ .ucom_read = NULL,
+ .ucom_write = NULL,
+};
+
+int
+ukspan_match(struct device *parent, void *match, void *aux)
+{
+ struct usb_attach_arg *uaa = aux;
+
+ if (uaa->iface != NULL)
+ return UMATCH_NONE;
+
+ int found = usb_lookup(ukspan_devs, uaa->vendor, uaa->product) != NULL;
+ return found ? UMATCH_VENDOR_PRODUCT : UMATCH_NONE;
+}
+
+void
+ukspan_attach(struct device *parent, struct device *self, void *aux)
+{
+ struct ukspan_softc *sc = (struct ukspan_softc *)self;
+ struct usb_attach_arg *uaa = aux;
+ struct usbd_device *dev = uaa->device;
+ struct ucom_attach_args uca = {0};
+ usb_endpoint_descriptor_t *ed;
+ const char *devname = sc->sc_dev.dv_xname;
+ usbd_status err;
+ int t1, t2, t3, t4;
+
+ DPRINTF("attach\n");
+
+ sc->udev = dev;
+ sc->cin_pipe = sc->cout_pipe = NULL;
+ sc->ixfer = sc->oxfer = NULL;
+ sc->ucom_dev = NULL;
+
+ /*
+ * Switch to configuration 1 where the transfer mode of the input
+ * endpoints is bulk instead of interrupt, as ucom expects
+ */
+ err = usbd_set_config_index(sc->udev, UKSPAN_CONFIG_IDX, 1);
+ if (err) {
+ printf("%s: set config failed\n", devname);
+ goto fail;
+ }
+
+ err = usbd_device2interface_handle(sc->udev, UKSPAN_IFACE_IDX,
+ &sc->iface);
+ if (err) {
+ printf("%s: get interface failed\n", devname);
+ goto fail;
+ }
+
+ ed = usbd_get_endpoint_descriptor(sc->iface, UKSPAN_EA_BULKIN);
+ t1 = UE_GET_XFERTYPE(ed->bmAttributes);
+ uca.ibufsize = UGETW(ed->wMaxPacketSize);
+ uca.bulkin = UKSPAN_EA_BULKIN;
+
+ ed = usbd_get_endpoint_descriptor(sc->iface, UKSPAN_EA_BULKOUT);
+ t2 = UE_GET_XFERTYPE(ed->bmAttributes);
+ uca.obufsize = UGETW(ed->wMaxPacketSize);
+ uca.bulkout = UKSPAN_EA_BULKOUT;
+
+ ed = usbd_get_endpoint_descriptor(sc->iface, UKSPAN_EA_CONFIGIN);
+ t3 = UE_GET_XFERTYPE(ed->bmAttributes);
+ if (UGETW(ed->wMaxPacketSize) < sizeof(struct ukspan_smsg)) {
+ printf("%s: in config packet size too small\n", devname);
+ goto fail;
+ }
+
+ ed = usbd_get_endpoint_descriptor(sc->iface, UKSPAN_EA_CONFIGOUT);
+ t4 = UE_GET_XFERTYPE(ed->bmAttributes);
+ if (UGETW(ed->wMaxPacketSize) < sizeof(struct ukspan_cmsg)) {
+ printf("%s: out config packet size too small\n", devname);
+ goto fail;
+ }
+
+ if (t1 != UE_BULK || t2 != UE_BULK || t3 != UE_BULK || t4 != UE_BULK) {
+ printf("%s: unexpected xfertypes %x %x %x %x != %x\n", devname,
+ t1, t2, t3, t4, UE_BULK);
+ goto fail;
+ }
+
+ /* Resource acquisition starts here */
+ err = usbd_open_pipe(sc->iface, UKSPAN_EA_CONFIGOUT, USBD_EXCLUSIVE_USE,
+ &sc->cout_pipe);
+ if (err) {
+ printf("%s: failed to create control out pipe\n", devname);
+ goto fail;
+ }
+
+ err = usbd_open_pipe(sc->iface, UKSPAN_EA_CONFIGIN, USBD_EXCLUSIVE_USE,
+ &sc->cin_pipe);
+ if (err) {
+ printf("%s: failed to create control out pipe\n", devname);
+ goto fail;
+ }
+
+ sc->ixfer = usbd_alloc_xfer(sc->udev);
+ sc->oxfer = usbd_alloc_xfer(sc->udev);
+ if (!sc->ixfer || !sc->oxfer) {
+ printf("%s: failed to allocate xfers\n", devname);
+ goto fail;
+ }
+
+ usbd_setup_xfer(sc->ixfer, sc->cin_pipe, sc, &sc->smsg,
+ sizeof(sc->smsg), 0, USBD_NO_TIMEOUT, ukspan_incb);
+ err = usbd_transfer(sc->ixfer);
+ if (err && err != USBD_IN_PROGRESS) {
+ printf("%s: failed to start ixfer\n", devname);
+ goto fail;
+ }
+
+ uca.portno = UCOM_UNK_PORTNO;
+ uca.ibufsizepad = uca.ibufsize;
+ uca.opkthdrlen = 0;
+ uca.device = dev;
+ uca.iface = sc->iface;
+ uca.methods = &ukspan_methods;
+ uca.arg = sc;
+ uca.info = NULL;
+
+ sc->ucom_dev = config_found_sm(self, &uca, ucomprint, ucomsubmatch);
+
+ DPRINTF("attach done\n");
+
+ return;
+fail:
+ ukspan_destroy(sc);
+ usbd_deactivate(sc->udev);
+}
+
+int
+ukspan_detach(struct device *self, int flags)
+{
+ struct ukspan_softc *sc = (struct ukspan_softc *)self;
+ DPRINTF("detach\n");
+
+ ukspan_destroy(sc);
+
+ if (sc->ucom_dev) {
+ config_detach(sc->ucom_dev, flags);
+ sc->ucom_dev = NULL;
+ }
+ return 0;
+}
+
+void
+ukspan_outcb(struct usbd_xfer *xfer, void *priv, usbd_status status)
+{
+ struct ukspan_softc *sc = priv;
+ const char *devname = sc->sc_dev.dv_xname;
+
+ DPRINTF("outcb\n");
+
+ if (usbd_is_dying(sc->udev)) {
+ DPRINTF("usb dying\n");
+ return;
+ }
+ if (status != USBD_NORMAL_COMPLETION) {
+ printf("%s: oxfer failed\n", devname);
+ return;
+ }
+}
+
+void
+ukspan_incb(struct usbd_xfer *xfer, void *priv, usbd_status status)
+{
+ struct ukspan_softc *sc = priv;
+ const char *devname = sc->sc_dev.dv_xname;
+ const struct ukspan_smsg *smsg = &sc->smsg;
+ usbd_status err;
+ u_int32_t len;
+
+ DPRINTF("incb\n");
+
+ if (usbd_is_dying(sc->udev)) {
+ printf("%s: usb dying\n", devname);
+ return;
+ }
+ if (!sc->cin_pipe || !sc->ixfer) {
+ printf("%s: no cin_pipe, but not dying?\n", devname);
+ return;
+ }
+ if (status != USBD_NORMAL_COMPLETION) {
+ if (status != USBD_NOT_STARTED && status != USBD_CANCELLED)
+ printf("%s: ixfer failed\n", devname);
+ return;
+ }
+
+ usbd_get_xfer_status(xfer, NULL, NULL, &len, NULL);
+ if (len < sizeof(struct ukspan_smsg)) {
+ printf("%s: short read\n", devname);
+ return;
+ }
+
+ /* The device provides the actual MSR register */
+ sc->msr = smsg->msr;
+ /* But not LSR... */
+ sc->lsr = (smsg->rxoverrun ? ULSR_OE : 0) |
+ (smsg->rxparity ? ULSR_PE : 0) |
+ (smsg->rxframe ? ULSR_FE : 0) |
+ (smsg->rxbreak ? ULSR_BI : 0);
+ ucom_status_change((struct ucom_softc *)sc->ucom_dev);
+
+ usbd_setup_xfer(sc->ixfer, sc->cin_pipe, sc, &sc->smsg,
+ sizeof(sc->smsg), USBD_SHORT_XFER_OK, USBD_NO_TIMEOUT,
+ ukspan_incb);
+ err = usbd_transfer(sc->ixfer);
+ if (err && err != USBD_IN_PROGRESS)
+ printf("%s: usbd transfer failed\n", devname);
+}
+
+void
+ukspan_get_status(void *addr, int portno, u_char *lsr, u_char *msr)
+{
+ struct ukspan_softc *sc = addr;
+ DPRINTF("get status\n");
+ if (lsr)
+ *lsr = sc->lsr;
+ if (msr)
+ *msr = sc->msr;
+}
+
+void
+ukspan_cmsg_init(bool opening, struct ukspan_cmsg *omsg)
+{
+ bzero(omsg, sizeof(*omsg));
+
+ omsg->xonchar = 17;
+ omsg->xoffchar = 19;
+
+ omsg->rxforwardingchars = 16;
+ omsg->rxforwardingtimeoutms = 16;
+ omsg->txacksetting = 0;
+ omsg->txbreak = 0;
+ if (opening) {
+ omsg->portenabled = 1;
+ omsg->rxflush = 1;
+ }
+}
+
+int
+ukspan_cmsg_send(struct ukspan_softc *sc)
+{
+ const char *devname = sc->sc_dev.dv_xname;
+ usbd_status err;
+
+ usbd_setup_xfer(sc->oxfer, sc->cout_pipe, sc, &sc->cmsg,
+ sizeof(sc->cmsg), USBD_SYNCHRONOUS, USBD_NO_TIMEOUT, ukspan_outcb);
+ err = usbd_transfer(sc->oxfer);
+ if (err != USBD_NORMAL_COMPLETION) {
+ printf("%s: control xfer failed\n", devname);
+ return EIO;
+ }
+ return 0;
+}
+
+void
+ukspan_set(void *addr, int portno, int reg, int onoff)
+{
+ struct ukspan_softc *sc = addr;
+ const char *devname = sc->sc_dev.dv_xname;
+ DPRINTF("set %#x = %#x\n", reg, onoff);
+ int flag = !!onoff;
+ switch (reg) {
+ case UCOM_SET_DTR:
+ sc->cmsg.setdtr = 1;
+ sc->cmsg.dtr = flag;
+ break;
+ case UCOM_SET_RTS:
+ sc->cmsg.setrts = 1;
+ sc->cmsg.rts = flag;
+ break;
+ case UCOM_SET_BREAK:
+ sc->cmsg.txbreak = flag;
+ break;
+ default:
+ printf("%s: unhandled reg %#x\n", devname, reg);
+ return;
+ }
+ ukspan_cmsg_send(sc);
+}
+
+int
+ukspan_param(void *addr, int portno, struct termios *ti)
+{
+ struct ukspan_softc *sc = addr;
+ const char *devname = sc->sc_dev.dv_xname;
+ struct ukspan_cmsg *cmsg = &sc->cmsg;
+ speed_t baud;
+ tcflag_t cflag;
+ u_int32_t div;
+ u_int8_t lcr;
+
+ DPRINTF("param: %#x %#x %#x\n", ti->c_ospeed, ti->c_cflag, ti->c_iflag);
+
+ /* Set baud */
+ div = 1;
+ baud = ti->c_ospeed;
+ switch (baud) {
+ case B300:
+ case B600:
+ case B1200:
+ case B2400:
+ case B4800:
+ case B9600:
+ case B19200:
+ case B38400:
+ case B57600:
+ case B115200:
+ case B230400:
+ div = UKSPAN_CLOCK / (baud * 16);
+ break;
+ default:
+ printf("%s: unexpected baud: %d\n", devname, baud);
+ return EINVAL;
+ }
+
+ cmsg->setclock = 1;
+ cmsg->baudlo = div & 0xff;
+ cmsg->baudhi = div >> 8;
+
+ cmsg->setrxmode = 1;
+ cmsg->settxmode = 1;
+ if (baud > 57600)
+ cmsg->rxmode = cmsg->txmode = UKSPAN_MAGIC;
+ else
+ cmsg->rxmode = cmsg->txmode = 0;
+
+ /* Set parity, data, and stop bits */
+ if ((cflag & CIGNORE) == 0) {
+ cflag = ti->c_cflag;
+ if (cflag & PARENB)
+ lcr = (cflag & PARODD) ? UKSPAN_PARITY_ODD :
+ UKSPAN_PARITY_EVEN;
+ else
+ lcr = UKSPAN_PARITY_NONE;
+ switch (cflag & CSIZE) {
+ case CS5:
+ lcr |= UKSPAN_DATA_5;
+ break;
+ case CS6:
+ lcr |= UKSPAN_DATA_6;
+ break;
+ case CS7:
+ lcr |= UKSPAN_DATA_7;
+ break;
+ case CS8:
+ lcr |= UKSPAN_DATA_8;
+ break;
+ }
+
+ lcr |= (cflag & CSTOPB) ? UKSPAN_STOP_2 : UKSPAN_STOP_1;
+
+ cmsg->setlcr = 1;
+ cmsg->lcr = lcr;
+ }
+
+ /* XXX flow control? */
+
+ ukspan_cmsg_send(sc);
+ return 0;
+}
+
+int
+ukspan_open(void *addr, int portno)
+{
+ struct ukspan_softc *sc = addr;
+ int ret;
+
+ DPRINTF("open\n");
+ if (usbd_is_dying(sc->udev)) {
+ DPRINTF("usb dying\n");
+ return ENXIO;
+ }
+
+ ukspan_cmsg_init(true, &sc->cmsg);
+ ret = ukspan_cmsg_send(sc);
+ return ret;
+}
+
+void
+ukspan_close(void *addr, int portno)
+{
+ struct ukspan_softc *sc = addr;
+ DPRINTF("close\n");
+ if (usbd_is_dying(sc->udev)) {
+ DPRINTF("usb dying\n");
+ return;
+ }
+ ukspan_cmsg_init(false, &sc->cmsg);
+ ukspan_cmsg_send(sc);
+}
+
+void
+ukspan_destroy(struct ukspan_softc *sc)
+{
+ DPRINTF("destroy\n");
+ if (sc->cin_pipe) {
+ usbd_close_pipe(sc->cin_pipe);
+ sc->cin_pipe = NULL;
+ }
+ if (sc->cout_pipe) {
+ usbd_close_pipe(sc->cout_pipe);
+ sc->cout_pipe = NULL;
+ }
+ if (sc->oxfer) {
+ usbd_free_xfer(sc->oxfer);
+ sc->oxfer = NULL;
+ }
+ if (sc->ixfer) {
+ usbd_free_xfer(sc->ixfer);
+ sc->ixfer = NULL;
+ }
+}