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(ðer_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 */