ChangeSet 1.1101.3.9, 2003/04/09 17:03:54-07:00, david-b@pacbell.net

[PATCH] USB: CDC Ether fix notifications

The event notification logic (disabled in 2.5 cdc-ether FWIW)
seems incorrect ... it doesn't match anything I can find in
the CDC spec, section 6.3 where notifications are defined.

In particular when a CONNECTION_SPEED_CHANGE event arrives,
the device sends sixteen bytes ... and this driver tries to
read that into an eight byte buffer, causing endless overruns
and filling logfiles with nonsense.

This patch pre-allocates a bigger buffer and interprets the
event data according to the spec.  Which makes at least one
device talk better to that driver, and won't worsen others.
It also handles high speed interrupt intervals correctly;
they're encoded as log2(interval in uframes).


 drivers/usb/CDCEther.c |   50 ++++++++++++++++++++++++++++++-------------------
 drivers/usb/CDCEther.h |    3 +-
 2 files changed, 33 insertions(+), 20 deletions(-)


diff -Nru a/drivers/usb/CDCEther.c b/drivers/usb/CDCEther.c
--- a/drivers/usb/CDCEther.c	Fri Apr 18 15:00:48 2003
+++ b/drivers/usb/CDCEther.c	Fri Apr 18 15:00:48 2003
@@ -209,33 +209,42 @@
 {
 	ether_dev_t *ether_dev = urb->context;
 	struct net_device *net;
-	__u8	*d;
+	struct usb_ctrlrequest *event;
+#define bNotification	bRequest
 
 	if ( !ether_dev )
 		return;
-	dbg("got intr callback");
+	net = ether_dev->net;
 	switch ( urb->status ) {
 		case USB_ST_NOERROR:
 			break;
 		case USB_ST_URB_KILLED:
-			return;
 		default:
-			dbg("intr status %d", urb->status);
+			dbg("%s intr status %d", net->name, urb->status);
+			return;
 	}
 
-	d = urb->transfer_buffer;
-	dbg("d: %x", d[0]);
-	net = ether_dev->net;
-	if ( d[0] & 0xfc ) {
-		ether_dev->stats.tx_errors++;
-		if ( d[0] & TX_UNDERRUN )
-			ether_dev->stats.tx_fifo_errors++;
-		if ( d[0] & (EXCESSIVE_COL | JABBER_TIMEOUT) )
-			ether_dev->stats.tx_aborted_errors++;
-		if ( d[0] & LATE_COL )
-			ether_dev->stats.tx_window_errors++;
-		if ( d[0] & (NO_CARRIER | LOSS_CARRIER) )
-			netif_carrier_off(net);
+	event = urb->transfer_buffer;
+	if (event->bRequestType != 0xA1)
+		dbg ("%s unknown event type %x", net->name,
+			event->bRequestType);
+	else switch (event->bNotification) {
+	case 0x00:		// NETWORK CONNECTION
+		dbg ("%s network %s", net->name,
+			event->wValue ? "connect" : "disconnect");
+		if (event->wValue)
+			netif_carrier_on (net);
+		else
+			netif_carrier_off (net);
+		break;
+	case 0x2A:		// CONNECTION SPEED CHANGE
+		dbg ("%s speed change", net->name);
+		/* ignoring eight bytes of data */
+		break;
+	case 0x01:		// RESPONSE AVAILABLE (none requested)
+	default:		// else undefined for CDC Ether
+		err ("%s illegal notification %02x", net->name,
+			event->bNotification);
 	}
 }
 
@@ -390,10 +399,13 @@
 			ether_dev->usb,
 			usb_rcvintpipe(ether_dev->usb, ether_dev->comm_ep_in),
 			ether_dev->intr_buff,
-			8, /* Transfer buffer length */
+			sizeof ether_dev->intr_buff,
 			intr_callback,
 			ether_dev,
-			ether_dev->intr_interval);
+			(ether_dev->usb->speed == USB_SPEED_HIGH)
+				? ( 1 << ether_dev->intr_interval)
+				: ether_dev->intr_interval
+			);
 		if ( (res = usb_submit_urb(&ether_dev->intr_urb)) ) {
 			warn("%s failed intr_urb %d", __FUNCTION__, res );
 		}
diff -Nru a/drivers/usb/CDCEther.h b/drivers/usb/CDCEther.h
--- a/drivers/usb/CDCEther.h	Fri Apr 18 15:00:48 2003
+++ b/drivers/usb/CDCEther.h	Fri Apr 18 15:00:48 2003
@@ -79,7 +79,8 @@
 	struct urb		rx_urb, tx_urb, intr_urb, ctrl_urb;
 	unsigned char		rx_buff[CDC_ETHER_MAX_MTU] __attribute__((aligned(L1_CACHE_BYTES)));
 	unsigned char		tx_buff[CDC_ETHER_MAX_MTU] __attribute__((aligned(L1_CACHE_BYTES)));
-	unsigned char		intr_buff[8] __attribute__((aligned(L1_CACHE_BYTES))) ;
+	unsigned char		intr_buff[16]
+				__attribute__((aligned(L1_CACHE_BYTES))) ;
 } ether_dev_t;
 
 /* These definitions used in the Ethernet Packet Filtering requests */
