install easily. connect securely. manage remotely.

Linux High-Speed Generic USB Serial HOWTO

Issue

Wireless WAN PC Card modem devices such as the Novatel Merlin V620/S620 and Kyocera KPC650 on a 1xEV-DO network experience a stalled data connection due to high data rates when operated on Linux kernel 2.4.x and 2.6.x. This issue does not occur when the cards are connected at 1xRTT data rates.

Background

The affected modems are PCMCIA cards that use a USB host controller interface to expose a serial device to the Linux operating system. The generic usbserial driver can be used to talk to these devices as if they were serial modems.

There are 2 potential problems that this work-around resolves:

1. The current usbcore and usbserial driver do not correctly recognize the maximum packet size on the inbound bulk endpoint.

2. The cards themselves are not advertising the correct maximum data packet size to the usb sub-system on Linux.

By default the linux usb core sees only 64 bytes of capacity. Without this work-around there is no way to specify what the maximum packet size on the inbound bulk endpoint should be. If you consider that the MTU on these cards is set at 1500 and the usb bulk endpoint callbacks are only reading 64 bytes at a time off of the serial tty, it doesn't take long to start dropping packets and seriously junkify your connection.

This patch adds a third module parameter so you can specify what you think the inbound endpoint maximum packet size should be. If the parameter is not set at module loading time, it defaults to 0 and the maximum packet size detected by the usb core will be used.

The parameter name is : maxSize.

        modprobe usbserial vendor=0x1410 product=0x1110 maxSize=2048

Warning

THIS PATCH HAS ONLY BEEN TESTED ON KERNEL 2.4.20.

In theory it should work with any 2.4.x kernel. The change is small enough that it should be easily ported to the 2.6 kernel series.

Instructions

To apply the patch you need a functional Linux kernel source tree installed. Linux kernel sources can be obtained from www.kernel.org.

The patch was created with the following command from /usr/src/linux-2.4.20/:

        diff -Naur drivers/usb/serial/usbserial.c drivers/usb/serial/usbserial_junxion.c

You can apply the patch with:

        patch -p0 <patchfile

To compile the module:

        cd /usr/src/linux-2.4.20/drivers/usb/serial/
        gcc -D__KERNEL__ -I/usr/src/linux-2.4.20/include -Wall -Wstrict-prototypes \
	-Wno-trigraphs -02 -fno-strict-aliasing -fno-common \
	-fomit-frame-pointer -pipe -mpreferred-stack-boundary=2 -march=i386 \
	-DMODULE -nostdinc -iwithprefix include -DKBUILD_BASENAME=usbserial \
	-DEXPORT_SYMTAB -c usbserial.c

[start 2.4.x patch]

--- drivers/usb/serial/usbserial.c      Thu Nov 28 15:53:15 2002
+++ drivers/usb/serial/usbserial_junxion.c      Wed Jul  6 08:42:25 2005
@@ -331,6 +331,7 @@
 #ifdef CONFIG_USB_SERIAL_GENERIC
 static __u16   vendor  = 0x05f9;
 static __u16   product = 0xffff;
+static int     maxSize = 0;

 static struct usb_device_id generic_device_ids[2]; /* Initially all zeroes. */

@@ -1255,6 +1256,9 @@
                        goto probe_error;
                }
                buffer_size = endpoint->wMaxPacketSize;
+#ifdef CONFIG_USB_SERIAL_GENERIC
+               buffer_size = (endpoint->wMaxPacketSize > maxSize)?endpoint->wMaxPacketSize:maxSize;
+#endif
                port->bulk_in_endpointAddress = endpoint->bEndpointAddress;
                port->bulk_in_buffer = kmalloc (buffer_size, GFP_KERNEL);
                if (!port->bulk_in_buffer) {
@@ -1608,4 +1612,7 @@

 MODULE_PARM(product, "h");
 MODULE_PARM_DESC(product, "User specified USB idProduct");
+
+MODULE_PARM(maxSize,"i");
+MODULE_PARM_DESC(maxSize,"User specified USB endpoint size");
 #endif

[end 2.4.x patch]

[start 2.6.x patch]

# diff -Naur usb-serial.c usb-serial-v620.c

--- usb-serial.c        2005-03-01 23:38:37.000000000 -0800
+++ usb-serial-v620.c   2005-07-22 10:09:59.000000000 -0700
@@ -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);
@@ -1060,7 +1061,7 @@
                        dev_err(&interface->dev, "No free urbs available\n");
                        goto probe_error;
                }
-               buffer_size = le16_to_cpu(endpoint->wMaxPacketSize);
+               buffer_size = (endpoint->wMaxPacketSize > maxSize)?endpoint->wMaxPacketSize:maxSize;
                port->bulk_in_size = buffer_size;
                port->bulk_in_endpointAddress = endpoint->bEndpointAddress;
                port->bulk_in_buffer = kmalloc (buffer_size, GFP_KERNEL);
@@ -1433,3 +1434,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");

[end 2.6.x patch]