(Jaako pointed out that the patch had extra spaces added--sorry.
Here it is again without the spaces.)
Jaakko --
Here is a patch against 2.4.26 for io_edgeport.c that I am testing.
The driver still has problems, but you can try this out if you
want.
This includes the changes needed to fix the 2.4.26 OHCI problem
you found, along with some other changes: fix for oops on disconnect,
and spin locks to protect txfifo, txCredits, and rxBytesAvail.
It does sort of work on OHCI. If you push the data rate too
high or transmit data on two many ports it will fail, unable
to open ports or receive data (a bulk in callback is lost).
You will also see occasional data corruption. Same problems
exist on UHCI.
-- Al
diff --exclude-from=diff.exclude -ur linux-2.4.26/drivers/usb/serial/io_edgeport.c
linux-2.4.26-ion/drivers/usb/serial/io_edgeport.c
--- linux-2.4.26/drivers/usb/serial/io_edgeport.c 2004-04-14 08:05:35.000000000
-0500
+++ linux-2.4.26-ion/drivers/usb/serial/io_edgeport.c 2004-06-20 01:45:15.000000000
-0500
@@ -23,6 +23,10 @@
* Edgeport/4D8
* Edgeport/8i
*
+ * For questions or problems with this driver, contact Inside Out
+ * Networks technical support, or Peter Berger <[EMAIL PROTECTED]>,
+ * or Al Borchers <[EMAIL PROTECTED]>.
+ *
* Version history:
*
* 2003_04_03 al borchers
@@ -274,7 +278,7 @@
/*
* Version Information
*/
-#define DRIVER_VERSION "v2.3"
+#define DRIVER_VERSION "v2.3a"
#define DRIVER_AUTHOR "Greg Kroah-Hartman <[EMAIL PROTECTED]> and David Iacovelli"
#define DRIVER_DESC "Edgeport USB Serial Driver"
@@ -311,6 +315,8 @@
#endif
#define PORT_MAGIC 0x7301
+#define EDGE_TMP_BUF_SIZE 4096
+
/* receive port state */
enum RXSTATE {
@@ -342,6 +348,8 @@
struct TxFifo txfifo; /* transmit fifo -- size will
be maxTxCredits */
struct urb *write_urb; /* write URB for this port */
char write_in_progress; /* TRUE while a write URB is
outstanding */
+ struct tasklet_struct send_data_tasklet;
+ spinlock_t ep_lock;
__u8 shadowLCR; /* last LCR value received */
__u8 shadowMCR; /* last MCR value received */
@@ -383,6 +391,9 @@
__u8 bulk_in_endpoint; /* the bulk in
endpoint handle */
unsigned char * bulk_in_buffer; /* the buffer we use
for the bulk in endpoint */
struct urb * read_urb; /* our bulk read urb */
+ int read_in_progress;
+ struct tasklet_struct submit_read_tasklet;
+ spinlock_t es_lock;
__u8 bulk_out_endpoint; /* the bulk out
endpoint handle */
@@ -435,6 +446,9 @@
/* local variables */
static int CmdUrbs = 0; /*
Number of outstanding Command Write Urbs */
+static char edge_tmp_buf[EDGE_TMP_BUF_SIZE];
+static struct semaphore edge_tmp_buf_sem;
+
/* local function prototypes */
@@ -762,16 +776,16 @@
int position;
int txCredits;
int portNumber;
- int result;
+ int do_read;
dbg("%s", __FUNCTION__);
- if (serial_paranoia_check (edge_serial->serial, __FUNCTION__)) {
+ if (urb->status) {
+ dbg("%s - nonzero control read status received: %d", __FUNCTION__,
urb->status);
return;
}
- if (urb->status) {
- dbg("%s - nonzero control read status received: %d", __FUNCTION__,
urb->status);
+ if (serial_paranoia_check (edge_serial->serial, __FUNCTION__)) {
return;
}
@@ -782,19 +796,16 @@
if (length > 1) {
bytes_avail = data[0] | (data[1] << 8);
if (bytes_avail) {
+ spin_lock(&edge_serial->es_lock);
edge_serial->rxBytesAvail += bytes_avail;
- dbg("%s - bytes_avail = %d, rxBytesAvail %d",
__FUNCTION__, bytes_avail, edge_serial->rxBytesAvail);
-
- if ((edge_serial->rxBytesAvail > 0) &&
- (edge_serial->read_urb->status != -EINPROGRESS)) {
- dbg(" --- Posting a read");
+ do_read = (edge_serial->rxBytesAvail > 0);
+ spin_unlock(&edge_serial->es_lock);
+ dbg("%s - bytes_avail = %d, rxBytesAvail %d,
read_in_progress=%d", __FUNCTION__, bytes_avail, edge_serial->rxBytesAvail,
edge_serial->read_in_progress);
+ if (do_read) {
+ dbg("%s - scheduling a read", __FUNCTION__);
/* we have pending bytes on the bulk in pipe,
send a request */
- edge_serial->read_urb->dev =
edge_serial->serial->dev;
- result = usb_submit_urb(edge_serial->read_urb);
- if (result) {
- dbg("%s - usb_submit_urb(read bulk)
failed with result = %d", __FUNCTION__, result);
- }
+
tasklet_schedule(&edge_serial->submit_read_tasklet);
}
}
}
@@ -808,7 +819,9 @@
if (port_paranoia_check (port, __FUNCTION__) == 0) {
edge_port = (struct edgeport_port
*)port->private;
if (edge_port->open) {
+ spin_lock(&edge_port->ep_lock);
edge_port->txCredits += txCredits;
+ spin_unlock(&edge_port->ep_lock);
dbg("%s - txcredits for port%d = %d",
__FUNCTION__, portNumber, edge_port->txCredits);
/* tell the tty driver that something
has changed */
@@ -816,7 +829,7 @@
wake_up_interruptible(&edge_port->port->tty->write_wait);
// Since we have more credit, check if
more data can be sent
- send_more_port_data(edge_serial,
edge_port);
+
tasklet_schedule(&edge_port->send_data_tasklet);
}
}
}
@@ -827,6 +840,39 @@
}
+static void edge_submit_read(unsigned long arg)
+{
+ struct edgeport_serial *edge_serial = (struct edgeport_serial *)arg;
+ int result;
+
+ dbg("%s", __FUNCTION__);
+
+ spin_lock(&edge_serial->es_lock);
+
+ if (!edge_serial->read_in_progress) {
+ edge_serial->read_in_progress = TRUE;
+ edge_serial->read_urb->dev = edge_serial->serial->dev;
+ result = usb_submit_urb(edge_serial->read_urb);
+ if (result) {
+ dbg("%s - usb_submit_urb(read bulk) failed with result = %d",
__FUNCTION__, result);
+ edge_serial->read_in_progress = FALSE;
+ }
+ }
+
+ spin_unlock(&edge_serial->es_lock);
+}
+
+
+static void edge_send_data(unsigned long arg)
+{
+ struct edgeport_port *edge_port = (struct edgeport_port *)arg;
+
+ dbg("%s", __FUNCTION__);
+
+ send_more_port_data((struct edgeport_serial
*)(edge_port->port->serial->private), edge_port);
+}
+
+
/*****************************************************************************
* edge_bulk_in_callback
* this is the callback function for when we have received data on the
@@ -836,17 +882,19 @@
{
struct edgeport_serial *edge_serial = (struct edgeport_serial *)urb->context;
unsigned char *data = urb->transfer_buffer;
- int status;
__u16 raw_data_length;
+ int do_read;
dbg("%s", __FUNCTION__);
- if (serial_paranoia_check (edge_serial->serial, __FUNCTION__)) {
+ if (urb->status) {
+ dbg("%s - nonzero read bulk status received: %d", __FUNCTION__,
urb->status);
+ edge_serial->read_in_progress = FALSE;
return;
}
- if (urb->status) {
- dbg("%s - nonzero read bulk status received: %d", __FUNCTION__,
urb->status);
+ if (serial_paranoia_check (edge_serial->serial, __FUNCTION__)) {
+ edge_serial->read_in_progress = FALSE;
return;
}
@@ -856,24 +904,24 @@
usb_serial_debug_data (__FILE__, __FUNCTION__, raw_data_length, data);
/* decrement our rxBytes available by the number that we just got */
+ spin_lock(&edge_serial->es_lock);
edge_serial->rxBytesAvail -= raw_data_length;
+ do_read = (edge_serial->rxBytesAvail > 0);
+ spin_unlock(&edge_serial->es_lock);
dbg("%s - Received = %d, rxBytesAvail %d", __FUNCTION__,
raw_data_length, edge_serial->rxBytesAvail);
process_rcvd_data (edge_serial, data, urb->actual_length);
+ edge_serial->read_in_progress = FALSE;
+
/* check to see if there's any more data for us to read */
- if ((edge_serial->rxBytesAvail > 0) &&
- (edge_serial->read_urb->status != -EINPROGRESS)) {
- dbg(" --- Posting a read");
-
- /* there is, so resubmit our urb */
- edge_serial->read_urb->dev = edge_serial->serial->dev;
- status = usb_submit_urb(edge_serial->read_urb);
- if (status) {
- err("%s - usb_submit_urb(read bulk) failed, status =
%d", __FUNCTION__, status);
- }
+ if (do_read) {
+ dbg("%s - posting a read", __FUNCTION__);
+ edge_submit_read((unsigned long)edge_serial);
}
+ } else {
+ edge_serial->read_in_progress = FALSE;
}
}
@@ -890,14 +938,14 @@
dbg("%s", __FUNCTION__);
- if (port_paranoia_check (edge_port->port, __FUNCTION__)) {
- return;
- }
-
if (urb->status) {
dbg("%s - nonzero write bulk status received: %d", __FUNCTION__,
urb->status);
}
+ if (port_paranoia_check (edge_port->port, __FUNCTION__)) {
+ return;
+ }
+
tty = edge_port->port->tty;
if (tty && edge_port->open) {
@@ -944,12 +992,12 @@
usb_unlink_urb (urb);
usb_free_urb (urb);
- if (port_paranoia_check (edge_port->port, __FUNCTION__)) {
+ if (status) {
+ dbg("%s - nonzero write bulk status received: %d", __FUNCTION__,
status);
return;
}
- if (status) {
- dbg("%s - nonzero write bulk status received: %d", __FUNCTION__,
status);
+ if (port_paranoia_check (edge_port->port, __FUNCTION__)) {
return;
}
@@ -1080,6 +1128,10 @@
return -ENODEV;
}
+ /* send the current line settings to the port so we are in sync with any
further termios calls */
+ if (port->tty)
+ change_port_settings (edge_port, port->tty->termios);
+
/* create the txfifo */
edge_port->txfifo.head = 0;
edge_port->txfifo.tail = 0;
@@ -1095,6 +1147,7 @@
/* Allocate a URB for the write */
edge_port->write_urb = usb_alloc_urb (0);
+ edge_port->write_in_progress = FALSE;
if (!edge_port->write_urb) {
dbg("%s - no memory", __FUNCTION__);
@@ -1294,15 +1347,31 @@
int bytesleft;
int firsthalf;
int secondhalf;
+ unsigned long flags;
dbg("%s - port %d", __FUNCTION__, port->number);
if (edge_port == NULL)
return -ENODEV;
+ /* copy user data (which can sleep) */
+ if (from_user) {
+ if (count > EDGE_TMP_BUF_SIZE)
+ count = EDGE_TMP_BUF_SIZE;
+ down(&edge_tmp_buf_sem);
+ if (copy_from_user(edge_tmp_buf, data, count) != 0) {
+ up(&edge_tmp_buf_sem);
+ err("%s - cannot copy from user space", __FUNCTION__);
+ return -EFAULT;
+ }
+ data = edge_tmp_buf;
+ }
+
// get a pointer to the Tx fifo
fifo = &edge_port->txfifo;
+ spin_lock_irqsave(&edge_port->ep_lock, flags);
+
// calculate number of bytes to put in fifo
copySize = min ((unsigned int)count, (edge_port->txCredits - fifo->count));
@@ -1311,6 +1380,9 @@
/* catch writes of 0 bytes which the tty driver likes to give us, and when
txCredits is empty */
if (copySize == 0) {
+ spin_unlock_irqrestore(&edge_port->ep_lock, flags);
+ if (from_user)
+ up(&edge_tmp_buf_sem);
dbg("%s - copySize = Zero", __FUNCTION__);
return 0;
}
@@ -1326,12 +1398,7 @@
dbg("%s - copy %d bytes of %d into fifo ", __FUNCTION__, firsthalf, bytesleft);
/* now copy our data */
- if (from_user) {
- if (copy_from_user(&fifo->fifo[fifo->head], data, firsthalf))
- return -EFAULT;
- } else {
- memcpy(&fifo->fifo[fifo->head], data, firsthalf);
- }
+ memcpy(&fifo->fifo[fifo->head], data, firsthalf);
// update the index and size
fifo->head += firsthalf;
@@ -1346,22 +1413,22 @@
if (secondhalf) {
dbg("%s - copy rest of data %d", __FUNCTION__, secondhalf);
- if (from_user) {
- if (copy_from_user(&fifo->fifo[fifo->head], &data[firsthalf],
secondhalf))
- return -EFAULT;
- } else {
- memcpy(&fifo->fifo[fifo->head], &data[firsthalf], secondhalf);
- }
+ memcpy(&fifo->fifo[fifo->head], &data[firsthalf], secondhalf);
// update the index and size
fifo->count += secondhalf;
fifo->head += secondhalf;
// No need to check for wrap since we can not get to end of fifo in
this part
}
+ spin_unlock_irqrestore(&edge_port->ep_lock, flags);
+
if (copySize) {
usb_serial_debug_data (__FILE__, __FUNCTION__, copySize, data);
}
+ if (from_user)
+ up(&edge_tmp_buf_sem);
+
send_more_port_data((struct edgeport_serial *)port->serial->private,
edge_port);
dbg("%s wrote %d byte(s) TxCredits %d, Fifo %d", __FUNCTION__, copySize,
edge_port->txCredits, fifo->count);
@@ -1393,12 +1460,16 @@
int bytesleft;
int firsthalf;
int secondhalf;
+ unsigned long flags;
dbg("%s(%d)", __FUNCTION__, edge_port->port->number);
+ spin_lock_irqsave(&edge_port->ep_lock, flags);
+
if (edge_port->write_in_progress ||
!edge_port->open ||
(fifo->count == 0)) {
+ spin_unlock_irqrestore(&edge_port->ep_lock, flags);
dbg("%s(%d) EXIT - fifo %d, PendingWrite = %d", __FUNCTION__,
edge_port->port->number, fifo->count, edge_port->write_in_progress);
return;
}
@@ -1411,6 +1482,7 @@
// it's better to wait for more credits so we can do a larger
// write.
if (edge_port->txCredits <
EDGE_FW_GET_TX_CREDITS_SEND_THRESHOLD(edge_port->maxTxCredits)) {
+ spin_unlock_irqrestore(&edge_port->ep_lock, flags);
dbg("%s(%d) Not enough credit - fifo %d TxCredit %d", __FUNCTION__,
edge_port->port->number, fifo->count, edge_port->txCredits );
return;
}
@@ -1431,8 +1503,9 @@
count = fifo->count;
buffer = kmalloc (count+2, GFP_ATOMIC);
if (buffer == NULL) {
- err("%s - no more kernel memory...", __FUNCTION__);
edge_port->write_in_progress = FALSE;
+ spin_unlock_irqrestore(&edge_port->ep_lock, flags);
+ err("%s - no more kernel memory...", __FUNCTION__);
return;
}
buffer[0] = IOSP_BUILD_DATA_HDR1 (edge_port->port->number -
edge_port->port->serial->minor, count);
@@ -1482,6 +1555,9 @@
edge_port->txCredits += count;
edge_port->icount.tx -= count;
}
+
+ spin_unlock_irqrestore(&edge_port->ep_lock, flags);
+
dbg("%s wrote %d byte(s) TxCredit %d, Fifo %d", __FUNCTION__, count,
edge_port->txCredits, fifo->count);
}
@@ -1498,6 +1574,7 @@
{
struct edgeport_port *edge_port = (struct edgeport_port *)(port->private);
int room;
+ unsigned long flags;
dbg("%s", __FUNCTION__);
@@ -1514,7 +1591,9 @@
}
// total of both buffers is still txCredit
+ spin_lock_irqsave(&edge_port->ep_lock, flags);
room = edge_port->txCredits - edge_port->txfifo.count;
+ spin_unlock_irqrestore(&edge_port->ep_lock, flags);
dbg("%s - returns %d", __FUNCTION__, room);
return room;
@@ -1534,6 +1613,7 @@
{
struct edgeport_port *edge_port = (struct edgeport_port *)(port->private);
int num_chars;
+ unsigned long flags;
dbg("%s", __FUNCTION__);
@@ -1547,7 +1627,9 @@
return -EINVAL;
}
+ spin_lock_irqsave(&edge_port->ep_lock, flags);
num_chars = edge_port->maxTxCredits - edge_port->txCredits +
edge_port->txfifo.count;
+ spin_unlock_irqrestore(&edge_port->ep_lock, flags);
if (num_chars) {
dbg("%s(port %d) - returns %d", __FUNCTION__, port->number, num_chars);
}
@@ -1718,12 +1800,15 @@
static int get_lsr_info(struct edgeport_port *edge_port, unsigned int *value)
{
unsigned int result = 0;
+ unsigned long flags;
+ spin_lock_irqsave(&edge_port->ep_lock, flags);
if (edge_port->maxTxCredits == edge_port->txCredits &&
edge_port->txfifo.count == 0) {
dbg("%s -- Empty", __FUNCTION__);
result = TIOCSER_TEMT;
}
+ spin_unlock_irqrestore(&edge_port->ep_lock, flags);
if (copy_to_user(value, &result, sizeof(int)))
return -EFAULT;
@@ -2164,10 +2249,6 @@
dbg("%s - Port %u Open Response Inital MSR = %02x TxBufferSize = %d",
__FUNCTION__, edge_serial->rxPort, byte2, edge_port->txCredits);
handle_new_msr (edge_port, byte2);
- /* send the current line settings to the port so we are in sync with
any further termios calls */
- if (edge_port->port->tty)
- change_port_settings (edge_port,
edge_port->port->tty->termios);
-
/* we have completed the open */
edge_port->openPending = FALSE;
edge_port->open = TRUE;
@@ -2507,6 +2588,7 @@
dbg("%s - usb_submit_urb(write bulk) failed", __FUNCTION__);
usb_unlink_urb (urb);
usb_free_urb (urb);
+ CmdUrbs--;
return status;
}
@@ -2970,6 +3052,8 @@
return -ENOMEM;
}
memset (edge_serial, 0, sizeof(struct edgeport_serial));
+ spin_lock_init(&edge_serial->es_lock);
+ tasklet_init(&edge_serial->submit_read_tasklet, edge_submit_read, (unsigned
long)edge_serial);
edge_serial->serial = serial;
serial->private = edge_serial;
@@ -3026,6 +3110,8 @@
return -ENOMEM;
}
memset (edge_port, 0, sizeof(struct edgeport_port));
+ spin_lock_init(&edge_port->ep_lock);
+ tasklet_init(&edge_port->send_data_tasklet, edge_send_data, (unsigned
long)edge_port);
edge_port->port = &serial->port[i];
serial->port[i].private = edge_port;
}
@@ -3042,11 +3128,15 @@
static void edge_shutdown (struct usb_serial *serial)
{
int i;
+ struct edgeport_serial *edge_serial = (struct edgeport_serial
*)serial->private;
dbg("%s", __FUNCTION__);
+ tasklet_kill(&edge_serial->submit_read_tasklet);
+
/* stop reads and writes on all ports */
for (i=0; i < serial->num_ports; ++i) {
+ tasklet_kill(&((struct edgeport_port
*)serial->port[i].private)->send_data_tasklet);
kfree (serial->port[i].private);
serial->port[i].private = NULL;
}
@@ -3066,6 +3156,7 @@
usb_serial_register (&edgeport_4port_device);
usb_serial_register (&edgeport_8port_device);
info(DRIVER_DESC " " DRIVER_VERSION);
+ sema_init(&edge_tmp_buf_sem, 1);
return 0;
}
-------------------------------------------------------
This SF.Net email sponsored by Black Hat Briefings & Training.
Attend Black Hat Briefings & Training, Las Vegas July 24-29 -
digital self defense, top technical experts, no vendor pitches,
unmatched networking opportunities. Visit www.blackhat.com
_______________________________________________
[EMAIL PROTECTED]
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel