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