This patch was adapted from one posted by Ulmo on http://www.evdoforums.com/about810.html

With this patch, I was able to realize speeds of 800kbps using Verizon Wireless's PC5740 Aircard. This patch was adapted for the RHEL4 kernel 2.6.9. A howto will be posted soon on how this was done.

I am not sure how to get this evaluated for inclusion to the upstream kernel so I am posting here. Any help is much appreciated.

I was also unable to find an email address for the original poster on the forum list so forgive me if credit isn't attributed correctly.

Ivan

diff -u -r linux-2.6.9/drivers/usb/class/cdc-acm.c linux-2.6.9-6.34.EL/drivers/usb/class/cdc-acm.c
--- linux-2.6.9/drivers/usb/class/cdc-acm.c	2004-10-18 17:54:37.000000000 -0400
+++ linux-2.6.9-6.34.EL/drivers/usb/class/cdc-acm.c	2006-06-21 15:45:29.000000000 -0400
@@ -72,6 +72,7 @@
 #define DRIVER_AUTHOR "Armin Fuerst, Pavel Machek, Johannes Erdfelt, Vojtech Pavlik"
 #define DRIVER_DESC "USB Abstract Control Model driver for USB modems and ISDN adapters"
 
+static ushort maxszc = 0, maxszw = 0, maxszr = 0;
 static struct usb_driver acm_driver;
 static struct tty_driver *acm_tty_driver;
 static struct acm *acm_table[ACM_TTY_MINORS];
@@ -102,6 +103,113 @@
 #define acm_send_break(acm, ms)		acm_ctrl_msg(acm, ACM_REQ_SEND_BREAK, ms, NULL, 0)
 
 /*
+ * Write buffer management.
+ * All of these assume proper locks taken by the caller.
+ */
+
+static int acm_wb_alloc(struct acm *acm)
+{
+	int i, wbn;
+	struct acm_wb *wb;
+
+	wbn = acm->write_current;
+	i = 0;
+	for (;;) {
+		wb = &acm->wb[wbn];
+		if (!wb->use) {
+			wb->use = 1;
+			return wbn;
+		}
+		wbn = (wbn + 1) % ACM_NWB;
+		if (++i >= ACM_NWB)
+			return -1;
+	}
+}
+
+static void acm_wb_free(struct acm *acm, int wbn)
+{
+	struct acm_wb *wb = &acm->wb[wbn];
+	wb->use = 0;
+	wb->len = 0;
+}
+
+static int acm_wb_is_avail(struct acm *acm)
+{
+	int i, n;
+
+	n = 0;
+	for (i = 0; i < ACM_NWB; i++) {
+		if (!acm->wb[i].use)
+			n++;
+	}
+	return n;
+}
+
+static inline int acm_wb_is_used(struct acm *acm, int wbn)
+{
+	return acm->wb[wbn].use && (acm->wb[wbn].len != 0);
+}
+
+/*
+ * Finish write.
+ */
+static void acm_write_done(struct acm *acm)
+{
+	unsigned long flags;
+	int wbn;
+
+	spin_lock_irqsave(&acm->write_lock, flags);
+	acm->write_ready = 1;
+	wbn = acm->write_current;
+	acm_wb_free(acm, wbn);
+	acm->write_current = (wbn + 1) % ACM_NWB;
+	spin_unlock_irqrestore(&acm->write_lock, flags);
+}
+
+/*
+ * Poke write.
+ */
+static int acm_write_start(struct acm *acm)
+{
+	unsigned long flags;
+	int wbn;
+	struct acm_wb *wb;
+	int rc;
+
+	spin_lock_irqsave(&acm->write_lock, flags);
+	if (!acm->dev) {
+		spin_unlock_irqrestore(&acm->write_lock, flags);
+		return -ENODEV;
+	}
+
+	if (!acm->write_ready) {
+		spin_unlock_irqrestore(&acm->write_lock, flags);
+		return 0;	/* A white lie */
+	}
+
+	wbn = acm->write_current;
+	if (!acm_wb_is_used(acm, wbn)) {
+		spin_unlock_irqrestore(&acm->write_lock, flags);
+		return 0;
+	}
+	wb = &acm->wb[wbn];
+
+	acm->write_ready = 0;
+	spin_unlock_irqrestore(&acm->write_lock, flags);
+
+	acm->writeurb->transfer_buffer = wb->buf;
+	acm->writeurb->transfer_dma = wb->dmah;
+	acm->writeurb->transfer_buffer_length = wb->len;
+	acm->writeurb->dev = acm->dev;
+
+	if ((rc = usb_submit_urb(acm->writeurb, GFP_ATOMIC)) < 0) {
+		dbg("usb_submit_urb(write bulk) failed: %d", rc);
+		acm_write_done(acm);
+	}
+	return rc;
+}
+
+/*
  * Interrupt handlers for various ACM device responses
  */
 
@@ -232,17 +340,13 @@
 static void acm_write_bulk(struct urb *urb, struct pt_regs *regs)
 {
 	struct acm *acm = (struct acm *)urb->context;
-	dbg("Entering acm_write_bulk with status %d\n", urb->status);
 
-	if (!ACM_READY(acm))
-		goto out;
-
-	if (urb->status)
-		dbg("nonzero write bulk status received: %d", urb->status);
+	dbg("Entering acm_write_bulk with status %d\n", urb->status);
 
-	schedule_work(&acm->work);
-out:
-	acm->ready_for_write = 1;
+	acm_write_done(acm);
+	acm_write_start(acm);
+	if (ACM_READY(acm))
+		schedule_work(&acm->work);
 }
 
 static void acm_softint(void *private)
@@ -339,36 +443,42 @@
 {
 	struct acm *acm = tty->driver_data;
 	int stat;
+	unsigned long flags;
+	int wbn;
+	struct acm_wb *wb;
+
 	dbg("Entering acm_tty_write to write %d bytes from %s space,\n", count, from_user ? "user" : "kernel");
 
 	if (!ACM_READY(acm))
 		return -EINVAL;
-	if (!acm->ready_for_write)
-		return 0;
 	if (!count)
 		return 0;
 
-	count = (count > acm->writesize) ? acm->writesize : count;
+	spin_lock_irqsave(&acm->write_lock, flags);
+	if ((wbn = acm_wb_alloc(acm)) < 0) {
+		spin_unlock_irqrestore(&acm->write_lock, flags);
+		acm_write_start(acm);
+		return 0;
+	}
+	wb = &acm->wb[wbn];
+	spin_unlock_irqrestore(&acm->write_lock, flags);
 
+	count = (count > acm->writesize) ? acm->writesize : count;
 	dbg("Get %d bytes from %s space...", count, from_user ? "user" : "kernel");
 	if (from_user) {
-		if (copy_from_user(acm->write_buffer, (void __user *)buf, count))
+		if (copy_from_user(wb->buf, (void __user *)buf, count)) {
+			spin_lock_irqsave(&acm->write_lock, flags);
+			acm_wb_free(acm, wbn);
+			spin_unlock_irqrestore(&acm->write_lock, flags);
 			return -EFAULT;
+		}
 	} else
-		memcpy(acm->write_buffer, buf, count);
+		memcpy(wb->buf, buf, count);
 	dbg("  Successfully copied.\n");
+	wb->len = count;
 
-	acm->writeurb->transfer_buffer_length = count;
-	acm->writeurb->dev = acm->dev;
-
-	acm->ready_for_write = 0;
-	stat = usb_submit_urb(acm->writeurb, from_user ? GFP_KERNEL : GFP_ATOMIC);
-	if (stat < 0) {
-		dbg("usb_submit_urb(write bulk) failed");
-		acm->ready_for_write = 1;
+	if ((stat = acm_write_start(acm)) < 0)
 		return stat;
-	}
-
 	return count;
 }
 
@@ -377,7 +487,11 @@
 	struct acm *acm = tty->driver_data;
 	if (!ACM_READY(acm))
 		return -EINVAL;
-	return !acm->ready_for_write ? 0 : acm->writesize;
+	/*
+	 * Do not let the line discipline to know that we have a reserve,
+	 * or it might get too enthusiastic.
+	 */
+	return (acm->write_ready && acm_wb_is_avail(acm)) ? acm->writesize : 0;
 }
 
 static int acm_tty_chars_in_buffer(struct tty_struct *tty)
@@ -385,7 +499,10 @@
 	struct acm *acm = tty->driver_data;
 	if (!ACM_READY(acm))
 		return -EINVAL;
-	return !acm->ready_for_write ? acm->writeurb->transfer_buffer_length : 0;
+	/*
+	 * This is inaccurate (overcounts), but it works.
+	 */
+	return (ACM_NWB - acm_wb_is_avail(acm)) * acm->writesize;
 }
 
 static void acm_tty_throttle(struct tty_struct *tty)
@@ -516,6 +633,39 @@
  * USB probe and disconnect routines.
  */
 
+/* Little helper: write buffers free */
+static void acm_write_buffers_free(struct acm *acm)
+{
+	int i;
+	struct acm_wb *wb;
+
+	for (wb = &acm->wb[0], i = 0; i < ACM_NWB; i++, wb++) {
+		usb_buffer_free(acm->dev, acm->writesize, wb->buf, wb->dmah);
+	}
+}
+
+/* Little helper: write buffers allocate */
+static int acm_write_buffers_alloc(struct acm *acm)
+{
+	int i;
+	struct acm_wb *wb;
+
+	for (wb = &acm->wb[0], i = 0; i < ACM_NWB; i++, wb++) {
+		wb->buf = usb_buffer_alloc(acm->dev, acm->writesize, GFP_KERNEL,
+		    &wb->dmah);
+		if (!wb->buf) {
+			while (i != 0) {
+				--i;
+				--wb;
+				usb_buffer_free(acm->dev, acm->writesize,
+				    wb->buf, wb->dmah);
+			}
+			return -ENOMEM;
+		}
+	}
+	return 0;
+}
+
 static int acm_probe (struct usb_interface *intf,
 		      const struct usb_device_id *id)
 {
@@ -648,9 +798,9 @@
 	}
 	memset(acm, 0, sizeof(struct acm));
 
-	ctrlsize = epctrl->wMaxPacketSize;
-	readsize = epread->wMaxPacketSize;
-	acm->writesize = epwrite->wMaxPacketSize;
+	ctrlsize = (le16_to_cpu(epctrl->wMaxPacketSize) > maxszc)?le16_to_cpu(epctrl->wMaxPacketSize):maxszc;
+	readsize = (le16_to_cpu(epread->wMaxPacketSize) > maxszr)?le16_to_cpu(epread->wMaxPacketSize):maxszr;
+	acm->writesize = (le16_to_cpu(epwrite->wMaxPacketSize) > maxszw)?le16_to_cpu(epwrite->wMaxPacketSize):maxszw;
 	acm->control = control_interface;
 	acm->data = data_interface;
 	acm->minor = minor;
@@ -662,7 +812,8 @@
 	acm->bh.data = (unsigned long) acm;
 	INIT_WORK(&acm->work, acm_softint, acm);
 	spin_lock_init(&acm->throttle_lock);
-	acm->ready_for_write = 1;
+	spin_lock_init(&acm->write_lock);
+	acm->write_ready = 1;
 
 	buf = usb_buffer_alloc(usb_dev, ctrlsize, GFP_KERNEL, &acm->ctrl_dma);
 	if (!buf) {
@@ -678,12 +829,10 @@
 	}
 	acm->read_buffer = buf;
 
-	buf = usb_buffer_alloc(usb_dev, acm->writesize, GFP_KERNEL, &acm->write_dma);
-	if (!buf) {
+	if (acm_write_buffers_alloc(acm) < 0) {
 		dev_dbg(&intf->dev, "out of memory (write buffer alloc)\n");
 		goto alloc_fail4;
 	}
-	acm->write_buffer = buf;	
 
 	acm->ctrlurb = usb_alloc_urb(0, GFP_KERNEL);
 	if (!acm->ctrlurb) {
@@ -712,9 +861,9 @@
 	acm->readurb->transfer_dma = acm->read_dma;
 
 	usb_fill_bulk_urb(acm->writeurb, usb_dev, usb_sndbulkpipe(usb_dev, epwrite->bEndpointAddress),
-			  acm->write_buffer, acm->writesize, acm_write_bulk, acm);
+			  NULL, acm->writesize, acm_write_bulk, acm);
 	acm->writeurb->transfer_flags |= URB_NO_FSBR | URB_NO_TRANSFER_DMA_MAP;
-	acm->writeurb->transfer_dma = acm->write_dma;
+	/* acm->writeurb->transfer_dma = 0; */
 
 	dev_info(&intf->dev, "ttyACM%d: USB ACM device\n", minor);
 
@@ -737,7 +886,7 @@
 alloc_fail6:
 	usb_free_urb(acm->ctrlurb);
 alloc_fail5:
-	usb_buffer_free(usb_dev, acm->writesize, acm->write_buffer, acm->write_dma);
+	acm_write_buffers_free(acm);
 alloc_fail4:
 	usb_buffer_free(usb_dev, readsize, acm->read_buffer, acm->read_dma);
 alloc_fail3:
@@ -768,7 +917,7 @@
 
 	flush_scheduled_work(); /* wait for acm_softint */
 
-	usb_buffer_free(usb_dev, acm->writesize, acm->write_buffer, acm->write_dma);
+	acm_write_buffers_free(acm);
 	usb_buffer_free(usb_dev, acm->readsize, acm->read_buffer, acm->read_dma);
 	usb_buffer_free(usb_dev, acm->ctrlsize, acm->ctrl_buffer, acm->ctrl_dma);
 
@@ -891,4 +1040,9 @@
 MODULE_AUTHOR( DRIVER_AUTHOR );
 MODULE_DESCRIPTION( DRIVER_DESC );
 MODULE_LICENSE("GPL");
-
+module_param(maxszc, ushort,0);
+MODULE_PARM_DESC(maxszc,"User specified USB endpoint control size");
+module_param(maxszr, ushort,0);
+MODULE_PARM_DESC(maxszr,"User specified USB endpoint read size");
+module_param(maxszw, ushort,0);
+MODULE_PARM_DESC(maxszw,"User specified USB endpoint write size");
Only in linux-2.6.9-6.34.EL/drivers/usb/class: cdc-acm.c.orig
diff -u -r linux-2.6.9/drivers/usb/class/cdc-acm.h linux-2.6.9-6.34.EL/drivers/usb/class/cdc-acm.h
--- linux-2.6.9/drivers/usb/class/cdc-acm.h	2004-10-18 17:55:29.000000000 -0400
+++ linux-2.6.9-6.34.EL/drivers/usb/class/cdc-acm.h	2006-06-21 15:42:02.000000000 -0400
@@ -80,14 +80,34 @@
  * Internal driver structures.
  */
 
+/*
+ * The only reason to have several buffers is to accomodate assumptions
+ * in line disciplines. They ask for empty space amount, receive our URB size,
+ * and proceed to issue several 1-character writes, assuming they will fit.
+ * The very first write takes a complete URB. Fortunately, this only happens
+ * when processing onlcr, so we only need 2 buffers.
+ */
+#define ACM_NWB  2
+struct acm_wb {
+	unsigned char *buf;
+	dma_addr_t dmah;
+	int len;
+	int use;
+};
+
 struct acm {
 	struct usb_device *dev;				/* the corresponding usb device */
 	struct usb_interface *control;			/* control interface */
 	struct usb_interface *data;			/* data interface */
 	struct tty_struct *tty;				/* the corresponding tty */
 	struct urb *ctrlurb, *readurb, *writeurb;	/* urbs */
-	u8 *ctrl_buffer, *read_buffer, *write_buffer;	/* buffers of urbs */
-	dma_addr_t ctrl_dma, read_dma, write_dma;	/* dma handles of buffers */
+	u8 *ctrl_buffer, *read_buffer;			/* buffers of urbs */
+	dma_addr_t ctrl_dma, read_dma;			/* dma handles of buffers */
+	struct acm_wb wb[ACM_NWB];
+	int write_current;				/* current write buffer */
+	int write_used;					/* number of non-empty write buffers */
+	int write_ready;				/* write urb is not running */
+	spinlock_t write_lock;
 	struct acm_line line;				/* line coding (bits, stop, parity) */
 	struct work_struct work;			/* work queue entry for line discipline waking up */
 	struct tasklet_struct bh;			/* rx processing */
@@ -100,7 +120,6 @@
 	unsigned int minor;				/* acm minor number */
 	unsigned char throttle;				/* throttled by tty layer */
 	unsigned char clocal;				/* termios CLOCAL */
-	unsigned char ready_for_write;			/* write urb can be used */
 	unsigned char resubmit_to_unthrottle;		/* throtteling has disabled the read urb */
 	unsigned int ctrl_caps;				/* control capabilities from the class specific header */
 };
diff -u -r linux-2.6.9/drivers/usb/serial/usb-serial.c linux-2.6.9-6.34.EL/drivers/usb/serial/usb-serial.c
--- linux-2.6.9/drivers/usb/serial/usb-serial.c	2004-10-18 17:55:36.000000000 -0400
+++ linux-2.6.9-6.34.EL/drivers/usb/serial/usb-serial.c	2006-06-21 15:53:55.000000000 -0400
@@ -361,6 +361,7 @@
    drivers depend on it.
 */
 
+static ushort maxSize = 0;
 static int debug;
 static struct usb_serial *serial_table[SERIAL_TTY_MINORS];	/* initially all NULL */
 static LIST_HEAD(usb_serial_driver_list);
@@ -1052,7 +1053,7 @@
 			dev_err(&interface->dev, "No free urbs available\n");
 			goto probe_error;
 		}
-		buffer_size = endpoint->wMaxPacketSize;
+		buffer_size = (le16_to_cpu(endpoint->wMaxPacketSize) > maxSize)?le16_to_cpu(endpoint->wMaxPacketSize):maxSize;
 		port->bulk_in_endpointAddress = endpoint->bEndpointAddress;
 		port->bulk_in_buffer = kmalloc (buffer_size, GFP_KERNEL);
 		if (!port->bulk_in_buffer) {
@@ -1075,7 +1076,7 @@
 			dev_err(&interface->dev, "No free urbs available\n");
 			goto probe_error;
 		}
-		buffer_size = endpoint->wMaxPacketSize;
+		buffer_size = (le16_to_cpu(endpoint->wMaxPacketSize) > maxSize)?le16_to_cpu(endpoint->wMaxPacketSize):maxSize;
 		port->bulk_out_size = buffer_size;
 		port->bulk_out_endpointAddress = endpoint->bEndpointAddress;
 		port->bulk_out_buffer = kmalloc (buffer_size, GFP_KERNEL);
@@ -1099,7 +1100,7 @@
 			dev_err(&interface->dev, "No free urbs available\n");
 			goto probe_error;
 		}
-		buffer_size = endpoint->wMaxPacketSize;
+		buffer_size = (le16_to_cpu(endpoint->wMaxPacketSize) > maxSize)?le16_to_cpu(endpoint->wMaxPacketSize):maxSize;
 		port->interrupt_in_endpointAddress = endpoint->bEndpointAddress;
 		port->interrupt_in_buffer = kmalloc (buffer_size, GFP_KERNEL);
 		if (!port->interrupt_in_buffer) {
@@ -1373,3 +1374,5 @@
 
 module_param(debug, bool, S_IRUGO | S_IWUSR);
 MODULE_PARM_DESC(debug, "Debug enabled or not");
+module_param(maxSize, ushort,0);
+MODULE_PARM_DESC(maxSize,"User specified USB endpoint size");
-------------------------------------------------------------------------
Using Tomcat but need to do more? Need to support web services, security?
Get stuff done quickly with pre-integrated technology to make your job easier
Download IBM WebSphere Application Server v.1.0.1 based on Apache Geronimo
http://sel.as-us.falkag.net/sel?cmd=lnk&kid=120709&bid=263057&dat=121642
_______________________________________________
linux-usb-devel@lists.sourceforge.net
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel

Reply via email to