Hi

I've had problems with my Netcomm USB modem with acm 21.  This patch
resolves my issues with the driver.  Should something like this be
integrated into acm?

Thanks
Simon Gittins




SYMPTOMS:
When using a Netcomm Roadster II 56 USB modem with acm 0.21, the serial
state (carrier detect, DSR etc) was garbage.

BUG:
The acm driver assumes that each notification will arrive in a single call
to acm_ctrl_irq().  It does not allow for a message to span several
interrupts.  With this particular modem, the size of the interrupt endpoint
buffer is 8 bytes, which means that the SERIAL_STATE notification (10 bytes
long) needs to span two interrupts.  The driver did not cater for this, and
overran the buffer trying to read the last two bytes.

PATCH:
Find the patch below which implements a per acm buffer which is used to
assemble packets over a number of interrupts.



------------------------------SNIP-----------------------------------

--- /usr/src/linux-2.4.14/drivers/usb/acm.c     Sat Oct  6 05:06:08 2001
+++ linux-2.4.x/drivers/usb/acm.c       Wed Dec 19 20:57:17 2001
@@ -24,6 +24,7 @@
  *     v0.19 - fixed CLOCAL handling (thanks to Richard Shih-Ping Chan)
  *     v0.20 - switched to probing on interface (rather than device) class
  *     v0.21 - revert to probing on device for devices with multiple
configs
+ *      v0.22 - implemented notifications spanning multiple isr's (Simon
Gittins [EMAIL PROTECTED])
  */
 
 /*
@@ -55,7 +56,8 @@
 #include <linux/tty_flip.h>
 #include <linux/module.h>
 #include <linux/smp_lock.h>
-#undef DEBUG
+#define DEBUG 1
+//#undef DEBUG
 #include <linux/usb.h>
 
 /*
@@ -101,6 +103,8 @@
  * IRQs.
  */
 
+#define ACM_REQUEST_TYPE        0xA1
+
 #define ACM_IRQ_NETWORK                0x00
 #define ACM_IRQ_LINE_STATE     0x20
 
@@ -125,6 +129,12 @@
 #define ACM_CTRL_OVERRUN       0x40
 
 /*
+ * Input control buffer size
+ */
+
+#define ACM_INPUT_SIZE      32
+
+/*
  * Line speed and caracter encoding.
  */
 
@@ -153,6 +163,8 @@
        unsigned int minor;                             /* acm minor number
*/
        unsigned char throttle;                         /* throttled by tty
layer */
        unsigned char clocal;                           /* termios CLOCAL */
+        unsigned char input_ctrl_buffer[ACM_INPUT_SIZE];/* used to buffer
incoming control messages */
+        unsigned int input_ctrl_index;                  /* index into the
incoming control buffer */
 };
 
 static struct usb_driver acm_driver;
@@ -184,48 +196,78 @@
 static void acm_ctrl_irq(struct urb *urb)
 {
        struct acm *acm = urb->context;
-       devrequest *dr = urb->transfer_buffer;
-       unsigned char *data = (unsigned char *)(dr + 1);
+        devrequest *dr = (devrequest *)acm->input_ctrl_buffer;
+       unsigned char *data;
        int newctrl;
 
-       if (!ACM_READY(acm)) return;
+       if (!ACM_READY(acm)){
+                acm->input_ctrl_index = 0;
+                return;
+        }
 
        if (urb->status < 0) {
                dbg("nonzero ctrl irq status received: %d", urb->status);
+                acm->input_ctrl_index = 0;
                return;
        }
-
-       switch (dr->request) {
-
-               case ACM_IRQ_NETWORK:
-
-                       dbg("%s network", data[0] ? "connected to" :
"disconnected from");
-                       return;
-
-               case ACM_IRQ_LINE_STATE:
-
-                       newctrl = le16_to_cpup((__u16 *) data);
-
-                       if (acm->tty && !acm->clocal && (acm->ctrlin &
~newctrl & ACM_CTRL_DCD)) {
-                               dbg("calling hangup");
-                               tty_hangup(acm->tty);
-                       }
-
-                       acm->ctrlin = newctrl;
-
-                       dbg("input control lines: dcd%c dsr%c break%c ring%c
framing%c parity%c overrun%c",
-                               acm->ctrlin & ACM_CTRL_DCD ? '+' : '-',
acm->ctrlin & ACM_CTRL_DSR ? '+' : '-',
-                               acm->ctrlin & ACM_CTRL_BRK ? '+' : '-',
acm->ctrlin & ACM_CTRL_RI  ? '+' : '-',
-                               acm->ctrlin & ACM_CTRL_FRAMING ? '+' : '-',
acm->ctrlin & ACM_CTRL_PARITY ? '+' : '-',
-                               acm->ctrlin & ACM_CTRL_OVERRUN ? '+' : '-');
-
-                       return;
-
-               default:
-                       dbg("unknown control event received: request %d
index %d len %d data0 %d data1 %d",
-                               dr->request, dr->index, dr->length, data[0],
data[1]);
-                       return;
-       }
+            //if we are starting a new packet, this will 99% make sure we
are not out of sync
+        if(acm->input_ctrl_index == 0 && *(unsigned char
*)urb->transfer_buffer != ACM_REQUEST_TYPE){
+                dbg("input devrequest startfragment is not valid,
requesttype: %d", dr->requesttype);
+                return;
+        }
+        if(urb->actual_length + acm->input_ctrl_index > ACM_INPUT_SIZE){
+                dbg("input packet too large, discarding");
+                acm->input_ctrl_index = 0;
+                return;
+        }    
+        memcpy(&acm->input_ctrl_buffer[acm->input_ctrl_index],
+               urb->transfer_buffer,
+               urb->actual_length);
+        
+        acm->input_ctrl_index += urb->actual_length;
+        
+        if(acm->input_ctrl_index == sizeof(*dr) + dr->length){
+                data = (unsigned char *)(dr + 1);
+                acm->input_ctrl_index = 0; //whatever the outcome, this
packet is finished
+            
+                switch (dr->request) {
+                
+                   case ACM_IRQ_NETWORK:
+                    
+                        dbg("%s network", data[0] ? "connected to" :
"disconnected from");
+                        return;
+                        
+                   case ACM_IRQ_LINE_STATE:
+                    
+                        newctrl = le16_to_cpup((__u16 *) data);
+                      
+                        if (acm->tty && !acm->clocal && (acm->ctrlin &
~newctrl & ACM_CTRL_DCD)) {
+                                dbg("calling hangup");
+                                tty_hangup(acm->tty);
+                        }
+                    
+                        acm->ctrlin = newctrl;
+                    
+                        dbg("input control lines: dcd%c dsr%c break%c
ring%c framing%c parity%c overrun%c",
+                            acm->ctrlin & ACM_CTRL_DCD ? '+' : '-',
acm->ctrlin & ACM_CTRL_DSR ? '+' : '-',
+                            acm->ctrlin & ACM_CTRL_BRK ? '+' : '-',
acm->ctrlin & ACM_CTRL_RI  ? '+' : '-',
+                            acm->ctrlin & ACM_CTRL_FRAMING ? '+' : '-',
acm->ctrlin & ACM_CTRL_PARITY ? '+' : '-',
+                            acm->ctrlin & ACM_CTRL_OVERRUN ? '+' : '-');
+                    
+                        return;
+                    
+                   default:
+                        dbg("unknown control event received: request %d
index %d len %d data0 %d data1 %d",
+                            dr->request, dr->index, dr->length, data[0],
data[1]);
+                        return;
+                }
+        }
+        else if(acm->input_ctrl_index > sizeof(*dr) + dr->length){
+            dbg("framing error, devrequest length %d not valid",
dr->length);
+            acm->input_ctrl_index = 0;
+            return;
+        }
+        
 }
 
 static void acm_read_bulk(struct urb *urb)





_______________________________________________
[EMAIL PROTECTED]
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel

Reply via email to