summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTim Marsland <Tim.Marsland@Sun.COM>2009-07-13 17:22:34 -0700
committerTim Marsland <Tim.Marsland@Sun.COM>2009-07-13 17:22:34 -0700
commit4634c44f9aff3ceaf027e46cee4258d7ab23b40f (patch)
tree6e8fbe32c5796e7719b2c5a81aa7b87ac84893e6
parent00f97612f54773488cf831ce00c14a7a026aa0b3 (diff)
downloadillumos-joyent-4634c44f9aff3ceaf027e46cee4258d7ab23b40f.tar.gz
6851840 usbftdi/tcdrain(3c) sleeps for 500ms and doesn't check for tx complete
6850067 SUNWuftdi has a duplicate copy of i.preserve
-rw-r--r--usr/src/pkgdefs/SUNWuftdi/Makefile2
-rw-r--r--usr/src/pkgdefs/SUNWuftdi/i.preserve33
-rw-r--r--usr/src/uts/common/io/usb/clients/usbser/usbftdi/uftdi_dsd.c173
-rw-r--r--usr/src/uts/common/io/warlock/usbftdi.wlcmd1
-rw-r--r--usr/src/uts/common/io/warlock/usbftdi_with_usba.wlcmd1
-rw-r--r--usr/src/uts/common/sys/usb/clients/usbser/usbftdi/uftdi_reg.h9
-rw-r--r--usr/src/uts/common/sys/usb/clients/usbser/usbftdi/uftdi_var.h4
7 files changed, 158 insertions, 65 deletions
diff --git a/usr/src/pkgdefs/SUNWuftdi/Makefile b/usr/src/pkgdefs/SUNWuftdi/Makefile
index 6acf7333a6..e015d216f2 100644
--- a/usr/src/pkgdefs/SUNWuftdi/Makefile
+++ b/usr/src/pkgdefs/SUNWuftdi/Makefile
@@ -25,6 +25,8 @@
include ../Makefile.com
+DATAFILES += i.preserve
+
.KEEP_STATE:
all: $(FILES) depend postinstall preremove
diff --git a/usr/src/pkgdefs/SUNWuftdi/i.preserve b/usr/src/pkgdefs/SUNWuftdi/i.preserve
deleted file mode 100644
index d3bc772c2b..0000000000
--- a/usr/src/pkgdefs/SUNWuftdi/i.preserve
+++ /dev/null
@@ -1,33 +0,0 @@
-#!/bin/sh
-#
-# CDDL HEADER START
-#
-# The contents of this file are subject to the terms of the
-# Common Development and Distribution License (the "License").
-# You may not use this file except in compliance with the License.
-#
-# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
-# or http://www.opensolaris.org/os/licensing.
-# See the License for the specific language governing permissions
-# and limitations under the License.
-#
-# When distributing Covered Code, include this CDDL HEADER in each
-# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
-# If applicable, add the following below this CDDL HEADER, with the
-# fields enclosed by brackets "[]" replaced with your own identifying
-# information: Portions Copyright [yyyy] [name of copyright owner]
-#
-# CDDL HEADER END
-
-#
-# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
-# Use is subject to license terms.
-#
-
-while read src dest
-do
- if [ ! -f $dest ] ; then
- cp $src $dest
- fi
-done
-exit 0
diff --git a/usr/src/uts/common/io/usb/clients/usbser/usbftdi/uftdi_dsd.c b/usr/src/uts/common/io/usb/clients/usbser/usbftdi/uftdi_dsd.c
index a9fdb3969b..62a57618b0 100644
--- a/usr/src/uts/common/io/usb/clients/usbser/usbftdi/uftdi_dsd.c
+++ b/usr/src/uts/common/io/usb/clients/usbser/usbftdi/uftdi_dsd.c
@@ -1070,7 +1070,10 @@ static int
uftdi_fifo_drain(ds_hdl_t hdl, uint_t portno, int timeout)
{
uftdi_state_t *uf = (uftdi_state_t *)hdl;
- int rval = USB_SUCCESS;
+ unsigned int count;
+ const uint_t countmax = 50; /* at least 500ms */
+ const uint8_t txempty =
+ FTDI_LSR_STATUS_TEMT | FTDI_LSR_STATUS_THRE;
USB_DPRINTF_L4(DPRINT_CTLOP, uf->uf_lh, "uftdi_fifo_drain");
@@ -1082,12 +1085,25 @@ uftdi_fifo_drain(ds_hdl_t hdl, uint_t portno, int timeout)
return (USB_FAILURE);
}
- mutex_exit(&uf->uf_lock);
+ /*
+ * Wait for the TX fifo to indicate empty.
+ *
+ * At all but the slowest baud rates, this is
+ * likely to be a one-shot test that instantly
+ * succeeds, but poll for at least 'countmax'
+ * tries before giving up.
+ */
+ for (count = 0; count < countmax; count++) {
+ if ((uf->uf_lsr & txempty) == txempty)
+ break;
+ mutex_exit(&uf->uf_lock);
+ delay(drv_usectohz(10*1000)); /* 10ms */
+ mutex_enter(&uf->uf_lock);
+ }
- /* wait 500 ms until hw fifo drains */
- delay(drv_usectohz(500*1000));
+ mutex_exit(&uf->uf_lock);
- return (rval);
+ return (count < countmax ? USB_SUCCESS : USB_FAILURE);
}
@@ -1518,6 +1534,55 @@ uftdi_reconnect_pipes(uftdi_state_t *uf)
return (uftdi_open_pipes(uf));
}
+
+static void
+uftdi_rxerr_put(mblk_t **rx_mpp, mblk_t *data, uint8_t lsr)
+{
+ uchar_t errflg;
+
+ if (lsr & FTDI_LSR_STATUS_BI) {
+ /*
+ * parity and framing errors only "count" if they
+ * occur independently of a break being received.
+ */
+ lsr &= ~(uint8_t)(FTDI_LSR_STATUS_PE | FTDI_LSR_STATUS_FE);
+ }
+ errflg =
+ ((lsr & FTDI_LSR_STATUS_OE) ? DS_OVERRUN_ERR : 0) |
+ ((lsr & FTDI_LSR_STATUS_PE) ? DS_PARITY_ERR : 0) |
+ ((lsr & FTDI_LSR_STATUS_FE) ? DS_FRAMING_ERR : 0) |
+ ((lsr & FTDI_LSR_STATUS_BI) ? DS_BREAK_ERR : 0);
+
+ /*
+ * If there's no actual data, we send a NUL character along
+ * with the error flags. Otherwise, the data mblk contains
+ * some number of highly questionable characters.
+ *
+ * According to FTDI tech support, there is no synchronous
+ * error reporting i.e. we cannot assume that only the
+ * first character in the mblk is bad -- so we treat all
+ * of them them as if they have the error noted in the LSR.
+ */
+ do {
+ mblk_t *mp;
+ uchar_t c = (MBLKL(data) == 0) ? '\0' : *data->b_rptr++;
+
+ if ((mp = allocb(2, BPRI_HI)) != NULL) {
+ DB_TYPE(mp) = M_BREAK;
+ *mp->b_wptr++ = errflg;
+ *mp->b_wptr++ = c;
+ uftdi_put_tail(rx_mpp, mp);
+ } else {
+ /*
+ * low memory - just discard the bad data
+ */
+ data->b_rptr = data->b_wptr;
+ break;
+ }
+ } while (MBLKL(data) > 0);
+}
+
+
/*
* bulk in pipe normal and exception callback handler
*/
@@ -1525,54 +1590,106 @@ uftdi_reconnect_pipes(uftdi_state_t *uf)
static void
uftdi_bulkin_cb(usb_pipe_handle_t pipe, usb_bulk_req_t *req)
{
- uftdi_state_t *uf = (uftdi_state_t *)req->bulk_client_private;
+ uftdi_state_t *uf = (uftdi_state_t *)req->bulk_client_private;
mblk_t *data;
int data_len;
- int notify = 0;
data = req->bulk_data;
data_len = data ? MBLKL(data) : 0;
/*
- * The first two bytes of data are actually status register bytes
- * that arrive with every packet from the device. Strip
- * them here before handing the data on. Note that the device
- * will send us these bytes at least every 40 milliseconds,
- * even if there's no data ..
+ * The first two bytes of data are status register bytes
+ * that arrive with every packet from the device. Process
+ * them here before handing the rest of the data on.
+ *
+ * When active, the device will send us these bytes at least
+ * every 40 milliseconds, even if there's no received data.
*/
if (req->bulk_completion_reason == USB_CR_OK && data_len >= 2) {
uint8_t msr = FTDI_GET_MSR(data->b_rptr);
uint8_t lsr = FTDI_GET_LSR(data->b_rptr);
+ int new_rx_err;
+
+ data->b_rptr += 2;
mutex_enter(&uf->uf_lock);
- if (uf->uf_msr != msr ||
- (uf->uf_lsr & FTDI_LSR_MASK) != (lsr & FTDI_LSR_MASK)) {
+
+ if (uf->uf_msr != msr) {
+ /*
+ * modem status register changed
+ */
USB_DPRINTF_L3(DPRINT_IN_PIPE, uf->uf_lh,
- "uftdi_bulkin_cb: status change "
- "0x%02x.0x%02x, was 0x%02x.0x%02x",
- msr, lsr, uf->uf_msr, uf->uf_lsr);
+ "uftdi_bulkin_cb: new msr: 0x%02x -> 0x%02x",
+ uf->uf_msr, msr);
+
uf->uf_msr = msr;
- uf->uf_lsr = lsr;
+
+ if (uf->uf_port_state == UFTDI_PORT_OPEN &&
+ uf->uf_cb.cb_status) {
+ mutex_exit(&uf->uf_lock);
+ uf->uf_cb.cb_status(uf->uf_cb.cb_arg);
+ mutex_enter(&uf->uf_lock);
+ }
+ }
+
+ if ((uf->uf_lsr & FTDI_LSR_MASK) != (lsr & FTDI_LSR_MASK)) {
+ /*
+ * line status register *receive* bits changed
+ *
+ * (The THRE and TEMT (transmit) status bits are
+ * masked out above.)
+ */
+ USB_DPRINTF_L3(DPRINT_IN_PIPE, uf->uf_lh,
+ "uftdi_bulkin_cb: new lsr: 0x%02x -> 0x%02x",
+ uf->uf_lsr, lsr);
+ new_rx_err = B_TRUE;
+ } else
+ new_rx_err = B_FALSE;
+
+ uf->uf_lsr = lsr; /* THRE and TEMT captured here */
+
+ if ((lsr & FTDI_LSR_MASK) != 0 &&
+ (MBLKL(data) > 0 || new_rx_err) &&
+ uf->uf_port_state == UFTDI_PORT_OPEN) {
/*
- * If we're waiting for a modem status change,
- * sending an empty message will cause us to
- * reexamine the modem flags.
+ * The current line status register value indicates
+ * that there's been some sort of unusual condition
+ * on the receive side. We either received a break,
+ * or got some badly formed characters from the
+ * serial port - framing errors, overrun, parity etc.
+ * So there's either some new data to post, or a
+ * new error (break) to post, or both.
+ *
+ * Invoke uftdi_rxerr_put() to place the inbound
+ * characters as M_BREAK messages on the receive
+ * mblk chain, decorated with error flag(s) for
+ * upper-level modules (e.g. ldterm) to process.
*/
- notify = 1;
+ mutex_exit(&uf->uf_lock);
+ uftdi_rxerr_put(&uf->uf_rx_mp, data, lsr);
+ ASSERT(MBLKL(data) == 0);
+
+ /*
+ * Since we've converted all the received
+ * characters into M_BREAK messages, we
+ * invoke the rx callback to shove the mblks
+ * up the STREAM.
+ */
+ if (uf->uf_cb.cb_rx)
+ uf->uf_cb.cb_rx(uf->uf_cb.cb_arg);
+ mutex_enter(&uf->uf_lock);
}
- mutex_exit(&uf->uf_lock);
- data_len -= 2;
- data->b_rptr += 2;
+ mutex_exit(&uf->uf_lock);
+ data_len = MBLKL(data);
}
- notify |= (data_len > 0);
-
USB_DPRINTF_L4(DPRINT_IN_PIPE, uf->uf_lh, "uftdi_bulkin_cb: "
"cr=%d len=%d", req->bulk_completion_reason, data_len);
/* save data and notify GSD */
- if (notify && uf->uf_port_state == UFTDI_PORT_OPEN &&
+ if (data_len > 0 &&
+ uf->uf_port_state == UFTDI_PORT_OPEN &&
req->bulk_completion_reason == USB_CR_OK) {
req->bulk_data = NULL;
uftdi_put_tail(&uf->uf_rx_mp, data);
diff --git a/usr/src/uts/common/io/warlock/usbftdi.wlcmd b/usr/src/uts/common/io/warlock/usbftdi.wlcmd
index 3757e21f4d..08c948b31e 100644
--- a/usr/src/uts/common/io/warlock/usbftdi.wlcmd
+++ b/usr/src/uts/common/io/warlock/usbftdi.wlcmd
@@ -80,6 +80,7 @@ add ds_ops::ds_in_pipe targets uftdi_in_pipe
add uftdi_state::uf_cb.cb_tx targets usbser_tx_cb
add uftdi_state::uf_cb.cb_rx targets usbser_rx_cb
+add uftdi_state::uf_cb.cb_status targets usbser_status_cb
add bus_ops::bus_add_eventcall targets warlock_dummy
add bus_ops::bus_get_eventcookie targets warlock_dummy
diff --git a/usr/src/uts/common/io/warlock/usbftdi_with_usba.wlcmd b/usr/src/uts/common/io/warlock/usbftdi_with_usba.wlcmd
index 5f40e18082..87b6829422 100644
--- a/usr/src/uts/common/io/warlock/usbftdi_with_usba.wlcmd
+++ b/usr/src/uts/common/io/warlock/usbftdi_with_usba.wlcmd
@@ -197,6 +197,7 @@ add ds_ops::ds_in_pipe targets uftdi_in_pipe
add uftdi_state::uf_cb.cb_tx targets usbser_tx_cb
add uftdi_state::uf_cb.cb_rx targets usbser_rx_cb
+add uftdi_state::uf_cb.cb_status targets usbser_status_cb
add hubd::h_cleanup_child targets warlock_dummy
add usb_ctrl_req::ctrl_cb targets warlock_dummy
diff --git a/usr/src/uts/common/sys/usb/clients/usbser/usbftdi/uftdi_reg.h b/usr/src/uts/common/sys/usb/clients/usbser/usbftdi/uftdi_reg.h
index 0c139504fa..921b00a4b3 100644
--- a/usr/src/uts/common/sys/usb/clients/usbser/usbftdi/uftdi_reg.h
+++ b/usr/src/uts/common/sys/usb/clients/usbser/usbftdi/uftdi_reg.h
@@ -386,13 +386,20 @@ enum {
#define FTDI_MSR_MASK 0xf0
#define FTDI_GET_MSR(p) (((p)[0]) & FTDI_MSR_MASK)
#define FTDI_GET_LSR(p) ((p)[1])
-#define FTDI_LSR_MASK (~0x60) /* interesting bits */
+#define FTDI_LSR_MASK (~0x60) /* interesting rx bits */
#define FTDI_MSR_STATUS_CTS 0x10
#define FTDI_MSR_STATUS_DSR 0x20
#define FTDI_MSR_STATUS_RI 0x40
#define FTDI_MSR_STATUS_RLSD 0x80 /* aka Carrier Detect */
+#define FTDI_LSR_STATUS_OE 0x02 /* overrun */
+#define FTDI_LSR_STATUS_PE 0x04 /* parity */
+#define FTDI_LSR_STATUS_FE 0x08 /* framing */
+#define FTDI_LSR_STATUS_BI 0x10 /* break */
+#define FTDI_LSR_STATUS_THRE 0x20 /* tx hold register is now empty */
+#define FTDI_LSR_STATUS_TEMT 0x40 /* tx shift register is now empty */
+
#define FTDI_OUT_TAG(len, port) (((len) << 2) | (port))
#ifdef __cplusplus
diff --git a/usr/src/uts/common/sys/usb/clients/usbser/usbftdi/uftdi_var.h b/usr/src/uts/common/sys/usb/clients/usbser/usbftdi/uftdi_var.h
index a440e94433..698a9cd454 100644
--- a/usr/src/uts/common/sys/usb/clients/usbser/usbftdi/uftdi_var.h
+++ b/usr/src/uts/common/sys/usb/clients/usbser/usbftdi/uftdi_var.h
@@ -114,9 +114,7 @@ _NOTE(DATA_READABLE_WITHOUT_LOCK(uftdi_state::{
uf_xfer_sz
uf_pm
uf_port_state
- uf_cb.cb_rx
- uf_cb.cb_tx
- uf_cb.cb_arg
+ uf_cb
uf_bulkin_ph
uf_bulkout_ph
}))