diff options
| author | Tim Marsland <Tim.Marsland@Sun.COM> | 2009-07-13 17:22:34 -0700 |
|---|---|---|
| committer | Tim Marsland <Tim.Marsland@Sun.COM> | 2009-07-13 17:22:34 -0700 |
| commit | 4634c44f9aff3ceaf027e46cee4258d7ab23b40f (patch) | |
| tree | 6e8fbe32c5796e7719b2c5a81aa7b87ac84893e6 | |
| parent | 00f97612f54773488cf831ce00c14a7a026aa0b3 (diff) | |
| download | illumos-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
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 })) |
