Fixing the cp210x open - Unable to enable UART Error - 04/17/2011

From Pharos Testbed Wiki
Jump to: navigation, search

Sometimes when connecting to an iRobot Create via a Roostick, the serial port cannot be opened and the command "dmesg | grep tty" reports error "cp210x ttyUSB0: cp210x_open - Unable to enable UART" as shown below. Till now, when this occurs, your only recourse was to power cycle the robot. Here is a better solution.

The error is caused by the cp210x driver timing out too quickly. This might be due to the robot entering a low-power state that introduces additional latencies the cp210x driver cannot handle. Others have noted that this problem is more evident in Ubuntu 10.x relative to Ubuntu 9.x. To fix this problem, modify the cp210x drivers by increasing the timeout values, then re-compile and install the driver. Here are instructions on how to do this.

RoombaCommTest-ubuntu-serial-error.png

Contents

Download the Linux Source Code

Open a terminal and execute the following commands. Note that your version of Linux may differ slightly -- adjust accordingly.

$ cd ~
$ sudo apt-get install build-essential linux-source
$ cp /usr/src/linux-source-2.6.32.tar.bz2 .
$ bunzip2 linux-source-2.6.32.tar.bz2 
$ tar xf linux-source-2.6.32.tar 
$ cd ~/linux-source-2.6.32

Modify the cp210x Driver

Increase the timeout when sending USB control messages:

$ cd ~/linux-source-2.6.32
$ vim drivers/usb/serial/cp210x.c

Search for calls to method usb_control_msg(...). The last parameter of this method is the timeout, which is 300ms by default. Increase this value to be 3000.

Below is an example of the modified methods. Note the increase in the timeout value from 300 (0.3 seconds) to 3000 (3 seconds) on lines 268, 317, 322, and 338. After making these modifications, save and exit vim.

  1. /*
  2.  * cp210x_get_config
  3.  * Reads from the CP210x configuration registers
  4.  * 'size' is specified in bytes.
  5.  * 'data' is a pointer to a pre-allocated array of integers large
  6.  * enough to hold 'size' bytes (with 4 bytes to each integer)
  7.  */
  8. static int cp210x_get_config(struct usb_serial_port *port, u8 request,
  9.                 unsigned int *data, int size)
  10. {
  11.         struct usb_serial *serial = port->serial;
  12.         __le32 *buf;
  13.         int result, i, length;
  14.  
  15.         /* Number of integers required to contain the array */
  16.         length = (((size - 1) | 3) + 1)/4;
  17.  
  18.         buf = kcalloc(length, sizeof(__le32), GFP_KERNEL);
  19.         if (!buf) {
  20.                 dev_err(&port->dev, "%s - out of memory.\n", __func__);
  21.                 return -ENOMEM;
  22.         }
  23.  
  24.         /* Issue the request, attempting to read 'size' bytes */
  25.         result = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0),
  26.                                 request, REQTYPE_DEVICE_TO_HOST, 0x0000,
  27.                                 0, buf, size, 3000); // Increase timeout
  28.  
  29.         /* Convert data into an array of integers */
  30.         for (i = 0; i < length; i++)
  31.                 data[i] = le32_to_cpu(buf[i]);
  32.  
  33.         kfree(buf);
  34.  
  35.         if (result != size) {
  36.                 dbg("%s - Unable to send config request, "
  37.                                 "request=0x%x size=%d result=%d\n",
  38.                                 __func__, request, size, result);
  39.                 return -EPROTO;
  40.         }
  41.  
  42.         return 0;
  43. }
  44.  
  45. /*
  46.  * cp210x_set_config
  47.  * Writes to the CP210x configuration registers
  48.  * Values less than 16 bits wide are sent directly
  49.  * 'size' is specified in bytes.
  50.  */
  51. static int cp210x_set_config(struct usb_serial_port *port, u8 request,
  52.                 unsigned int *data, int size)
  53. {
  54.         struct usb_serial *serial = port->serial;
  55.         __le32 *buf;
  56.         int result, i, length;
  57.  
  58.         /* Number of integers required to contain the array */
  59.         length = (((size - 1) | 3) + 1)/4;
  60.  
  61.         buf = kmalloc(length * sizeof(__le32), GFP_KERNEL);
  62.         if (!buf) {
  63.                 dev_err(&port->dev, "%s - out of memory.\n",
  64.                                 __func__);
  65.                 return -ENOMEM;
  66.         }
  67.  
  68.         /* Array of integers into bytes */
  69.         for (i = 0; i < length; i++)
  70.                 buf[i] = cpu_to_le32(data[i]);
  71.  
  72.         if (size > 2) {
  73.                 result = usb_control_msg(serial->dev,
  74.                                 usb_sndctrlpipe(serial->dev, 0),
  75.                                 request, REQTYPE_HOST_TO_DEVICE, 0x0000,
  76.                                 0, buf, size, 3000); // Increase timeout
  77.         } else {
  78.                 result = usb_control_msg(serial->dev,
  79.                                 usb_sndctrlpipe(serial->dev, 0),
  80.                                 request, REQTYPE_HOST_TO_DEVICE, data[0],
  81.                                 0, NULL, 0, 3000); // Increase timeout 
  82.         }
  83.  
  84.         kfree(buf);
  85.  
  86.         if ((size > 2 && result != size) || result < 0) {
  87.                 dbg("%s - Unable to send request, "
  88.                                 "request=0x%x size=%d result=%d\n",
  89.                                 __func__, request, size, result);
  90.                 return -EPROTO;
  91.         }
  92.  
  93.         /* Single data value */
  94.         result = usb_control_msg(serial->dev,
  95.                         usb_sndctrlpipe(serial->dev, 0),
  96.                         request, REQTYPE_HOST_TO_DEVICE, data[0],
  97.                         0, NULL, 0, 3000); // Increase timeout
  98.         return 0;
  99. }

Recompile and Reinstall the cp210x Driver

From within a terminal, execute:

$ cd ~/linux-source-2.6.32
$ make oldconfig
$ make prepare
$ make scripts
$ cp /usr/src/linux-headers-2.6.32-30-generic/Module.symvers .
$ make M=drivers/usb/serial
$ sudo mv /lib/modules/$(uname -r)/kernel/drivers/usb/serial/cp210x.ko /lib/modules/$(uname -r)/kernel/drivers/usb/serial/cp210x.ko.old
$ sudo cp drivers/usb/serial/cp210x.ko /lib/modules/$(uname -r)/kernel/drivers/usb/serial/
$ sudo modprobe -r cp210x
$ sudo modprobe cp210x

Possible Problems

Error When Compiling Driver

If you get errors when compiling the driver, try adding a "-i" flag telling the system to ignore the errors as shown below:

$ make -i M=drivers/usb/serial

Error when Loading cp210x Kernel Module

If you run into the following error:

$ sudo modprobe cp210x
FATAL: Error inserting cp210x (/lib/modules/2.6.35-27-generic-pae/kernel/drivers/usb/serial/cp210x.ko): Invalid argument

And the following errors are recorded in /var/log/messages:

Apr 17 23:23:57 proteus kernel: [  456.475093] cp210x: disagrees about version of symbol usb_serial_deregister
Apr 17 23:23:57 proteus kernel: [  456.475103] cp210x: Unknown symbol usb_serial_deregister (err -22)

You need to upgrade your OS by executing the following:

$ sudo apt-get update
$ sudo apt-get dist-upgrade

Then, reboot your computer to ensure the latest kernel is being used.

Personal tools
Namespaces

Variants
Actions
Navigation
Toolbox