libftdi Archives

Subject: ftdi_sio backport, was: Re: FT2232H/MPSSE/JTAG Device Freeze

From: Uwe Bonnes <bon@xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx>
To: libftdi@xxxxxxxxxxxxxxxxxxxxxxx
Date: Thu, 8 Oct 2009 16:51:33 +0200
>>>>> "Clifford" == Clifford Wolf <clifford@xxxxxxxxxxx> writes:

    Clifford> Hi again, On Wed, Sep 30, 2009 at 08:07:15PM +0200, Clifford
    Clifford> Wolf wrote:
    >> > bash-3.2$ ./jtag-ft2232h -c > [***] my_ftdi_read_data gives up
    >> polling.  > IO Error: FTDI/USB read failed!  > [***]
    >> my_ftdi_read_data gives up polling.  > IO Error: FTDI/USB read
    >> failed!  > Error while scanning JTAG chain.
    >> 
    >> yes. this is the problem I've been talking about..

    Clifford> Just fji: Updating the kernel from 2.6.22.1 to 2.6.31.3 solved
    Clifford> the problem here now. The other PC (my laptop) on which the
    Clifford> problem couldn't be reproduced was running a more or less up
    Clifford> to date kernel the whole time. So I guess that was the problem
    Clifford> after all...

I have tried to backport the ftdi_sio changes from 2.6.31 to 2.6.27. Find
the patch appended. I also tried to notify stable@xxxxxxxxxx about the need
for backporting these changes, but to no avail son long.

Despite that, there is still unexpected behaviour. I will report in another
mail.

Bye
-- 
Uwe Bonnes                bon@xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Institut fuer Kernphysik  Schlossgartenstrasse 9  64289 Darmstadt
--------- Tel. 06151 162516 -------- Fax. 06151 164321 ----------
--- linux/drivers/usb/serial/ftdi_sio.c.sav     2009-08-16 12:08:43.000000000 
+0200
+++ linux/drivers/usb/serial/ftdi_sio.c 2009-10-04 15:09:49.000000000 +0200
@@ -47,7 +47,7 @@
 /*
  * Version Information
  */
-#define DRIVER_VERSION "v1.4.3"
+#define DRIVER_VERSION "v1.5.0"
 #define DRIVER_AUTHOR "Greg Kroah-Hartman <greg@xxxxxxxxx>, Bill Ryder 
<bryder@xxxxxxx>, Kuba Ober <kuba@xxxxxxxxxxxxxxx>"
 #define DRIVER_DESC "USB FTDI Serial Converters Driver"
 
@@ -56,6 +56,7 @@
 static __u16 product;
 
 struct ftdi_private {
+       struct kref kref;
        ftdi_chip_type_t chip_type;
                                /* type of device, either SIO or FT8U232AM */
        int baud_base;          /* baud base clock for divisor setting */
@@ -81,7 +82,8 @@
        int rx_processed;
        unsigned long rx_bytes;
 
-       __u16 interface;        /* FT2232C port interface (0 for FT232/245) */
+       __u16 interface;        /* FT2232C, FT2232H or FT4232H port interface
+                                  (0 for FT232/245) */
 
        speed_t force_baud;     /* if non-zero, force the baud rate to
                                   this value */
@@ -92,6 +94,7 @@
        unsigned long tx_bytes;
        unsigned long tx_outstanding_bytes;
        unsigned long tx_outstanding_urbs;
+       unsigned short max_packet_size;
 };
 
 /* struct ftdi_sio_quirk is used by devices requiring special attention. */
@@ -161,6 +164,7 @@
        { USB_DEVICE(FTDI_VID, FTDI_8U232AM_ALT_PID) },
        { USB_DEVICE(FTDI_VID, FTDI_232RL_PID) },
        { USB_DEVICE(FTDI_VID, FTDI_8U2232C_PID) },
+       { USB_DEVICE(FTDI_VID, FTDI_4232H_PID) },
        { USB_DEVICE(FTDI_VID, FTDI_MICRO_CHAMELEON_PID) },
        { USB_DEVICE(FTDI_VID, FTDI_RELAIS_PID) },
        { USB_DEVICE(FTDI_VID, FTDI_OPENDCC_PID) },
@@ -683,12 +687,13 @@
        [FT232BM] = "FT232BM",
        [FT2232C] = "FT2232C",
        [FT232RL] = "FT232RL",
+       [FT2232H] = "FT2232H",
+       [FT4232H] = "FT4232H"
 };
 
 
 /* Constants for read urb and write urb */
 #define BUFSZ 512
-#define PKTSZ 64
 
 /* rx_flags */
 #define THROTTLED              0x01
@@ -734,6 +739,8 @@
 static unsigned short int ftdi_232am_baud_to_divisor(int baud);
 static __u32 ftdi_232bm_baud_base_to_divisor(int baud, int base);
 static __u32 ftdi_232bm_baud_to_divisor(int baud);
+static __u32 ftdi_2232h_baud_base_to_divisor(int baud, int base);
+static __u32 ftdi_2232h_baud_to_divisor(int baud);
 
 static struct usb_serial_driver ftdi_sio_device = {
        .driver = {
@@ -827,6 +834,36 @@
         return ftdi_232bm_baud_base_to_divisor(baud, 48000000);
 }
 
+static __u32 ftdi_2232h_baud_base_to_divisor(int baud, int base)
+{
+       static const unsigned char divfrac[8] = { 0, 3, 2, 4, 1, 5, 6, 7 };
+       __u32 divisor;
+       int divisor3;
+
+       /* hi-speed baud rate is 10-bit sampling instead of 16-bit */
+       divisor3 = (base / 10 / baud) * 8;
+
+       divisor = divisor3 >> 3;
+       divisor |= (__u32)divfrac[divisor3 & 0x7] << 14;
+       /* Deal with special cases for highest baud rates. */
+       if (divisor == 1)
+               divisor = 0;
+       else if (divisor == 0x4001)
+               divisor = 1;
+       /*
+        * Set this bit to turn off a divide by 2.5 on baud rate generator
+        * This enables baud rates up to 12Mbaud but cannot reach below 1200
+        * baud with this bit set
+        */
+       divisor |= 0x00020000;
+       return divisor;
+}
+
+static __u32 ftdi_2232h_baud_to_divisor(int baud)
+{
+        return ftdi_2232h_baud_base_to_divisor(baud, 120000000);
+}
+
 #define set_mctrl(port, set)           update_mctrl((port), (set), 0)
 #define clear_mctrl(port, clear)       update_mctrl((port), 0, (clear))
 
@@ -985,6 +1022,19 @@
                        baud = 9600;
                }
                break;
+       case FT2232H: /* FT2232H chip */
+       case FT4232H: /* FT4232H chip */
+               if ((baud <= 12000000) & (baud >= 1200)) {
+                       div_value = ftdi_2232h_baud_to_divisor(baud);
+               } else if (baud < 1200) {
+                       div_value = ftdi_232bm_baud_to_divisor(baud);
+               } else {
+                       dbg("%s - Baud rate too high!", __func__);
+                       div_value = ftdi_232bm_baud_to_divisor(9600);
+                       div_okay = 0;
+                       baud = 9600;
+               }
+               break;
        } /* priv->chip_type */
 
        if (div_okay) {
@@ -1128,14 +1178,29 @@
        if (interfaces > 1) {
                int inter;
 
-               /* Multiple interfaces.  Assume FT2232C. */
+               /* Multiple interfaces.*/
+               if (version == 0x0800) {
+                       priv->chip_type = FT4232H;
+                       /* Hi-speed - baud clock runs at 120MHz */
+                       priv->baud_base = 120000000 / 2;
+               } else if (version == 0x0700) {
+                       priv->chip_type = FT2232H;
+                       /* Hi-speed - baud clock runs at 120MHz */
+                       priv->baud_base = 120000000 / 2;
+               } else
                priv->chip_type = FT2232C;
+
                /* Determine interface code. */
                inter = serial->interface->altsetting->desc.bInterfaceNumber;
-               if (inter == 0)
-                       priv->interface = PIT_SIOA;
-               else
-                       priv->interface = PIT_SIOB;
+               if (inter == 0) {
+                       priv->interface = INTERFACE_A;
+               } else  if (inter == 1) {
+                       priv->interface = INTERFACE_B;
+               } else  if (inter == 2) {
+                       priv->interface = INTERFACE_C;
+               } else  if (inter == 3) {
+                       priv->interface = INTERFACE_D;
+               }
                /* BM-type devices have a bug where bcdDevice gets set
                 * to 0x200 when iSerialNumber is 0.  */
                if (version < 0x500) {
@@ -1163,6 +1228,45 @@
 }
 
 
+/* Determine the maximum packet size for the device.  This depends on the chip
+ * type and the USB host capabilities.  The value should be obtained from the
+ * device descriptor as the chip will use the appropriate values for the 
host.*/
+static void ftdi_set_max_packet_size(struct usb_serial_port *port)
+{
+       struct ftdi_private *priv = usb_get_serial_port_data(port);
+       struct usb_serial *serial = port->serial;
+       struct usb_device *udev = serial->dev;
+
+       struct usb_interface *interface = serial->interface;
+       struct usb_endpoint_descriptor *ep_desc = 
&interface->cur_altsetting->endpoint[1].desc;
+
+       unsigned num_endpoints;
+       int i = 0;
+
+       num_endpoints = interface->cur_altsetting->desc.bNumEndpoints;
+       dev_info(&udev->dev, "Number of endpoints %d\n", num_endpoints);
+
+       /* NOTE: some customers have programmed FT232R/FT245R devices
+        * with an endpoint size of 0 - not good.  In this case, we
+        * want to override the endpoint descriptor setting and use a
+        * value of 64 for wMaxPacketSize */
+       for (i = 0; i < num_endpoints; i++) {
+               dev_info(&udev->dev, "Endpoint %d MaxPacketSize %d\n", i+1,
+                       
interface->cur_altsetting->endpoint[i].desc.wMaxPacketSize);
+               ep_desc = &interface->cur_altsetting->endpoint[i].desc;
+               if (ep_desc->wMaxPacketSize == 0) {
+                       ep_desc->wMaxPacketSize = cpu_to_le16(0x40);
+                       dev_info(&udev->dev, "Overriding wMaxPacketSize on 
endpoint %d\n", i);
+               }
+       }
+
+       /* set max packet size based on descriptor */
+       priv->max_packet_size = ep_desc->wMaxPacketSize;
+
+       dev_info(&udev->dev, "Setting MaxPacketSize %d\n", 
priv->max_packet_size);
+}
+
+
 /*
  * ***************************************************************************
  * Sysfs Attribute
@@ -1272,7 +1376,9 @@
                if ((!retval) &&
                    (priv->chip_type == FT232BM ||
                     priv->chip_type == FT2232C ||
-                    priv->chip_type == FT232RL)) {
+                    priv->chip_type == FT232RL ||
+                    priv->chip_type == FT2232H ||
+                    priv->chip_type == FT4232H)) {
                        retval = device_create_file(&port->dev,
                                                    &dev_attr_latency_timer);
                }
@@ -1291,7 +1397,9 @@
                device_remove_file(&port->dev, &dev_attr_event_char);
                if (priv->chip_type == FT232BM ||
                    priv->chip_type == FT2232C ||
-                   priv->chip_type == FT232RL) {
+                   priv->chip_type == FT232RL ||
+                   priv->chip_type == FT2232H ||
+                   priv->chip_type == FT4232H) {
                        device_remove_file(&port->dev, &dev_attr_latency_timer);
                }
        }
@@ -1337,6 +1445,7 @@
                return -ENOMEM;
        }
 
+       kref_init(&priv->kref);
        spin_lock_init(&priv->rx_lock);
        spin_lock_init(&priv->tx_lock);
        init_waitqueue_head(&priv->delta_msr_wait);
@@ -1373,6 +1482,7 @@
        usb_set_serial_port_data(port, priv);
 
        ftdi_determine_type(port);
+       ftdi_set_max_packet_size(port);
        create_sysfs_attrs(port);
        return 0;
 }
@@ -1451,6 +1561,13 @@
        dbg("%s", __func__);
 }
 
+static void ftdi_sio_priv_release(struct kref *k)
+{
+       struct ftdi_private *priv = container_of(k, struct ftdi_private, kref);
+
+       kfree(priv);
+}
+
 static int ftdi_sio_port_remove(struct usb_serial_port *port)
 {
        struct ftdi_private *priv = usb_get_serial_port_data(port);
@@ -1459,14 +1576,7 @@
 
        remove_sysfs_attrs(port);
 
-       /* all open ports are closed at this point
-        *    (by usbserial.c:__serial_close, which calls ftdi_close)
-        */
-
-       if (priv) {
-               usb_set_serial_port_data(port, NULL);
-               kfree(priv);
-       }
+       kref_put(&priv->kref, ftdi_sio_priv_release);
 
        return 0;
 }
@@ -1529,7 +1639,8 @@
        if (result)
                err("%s - failed submitting read urb, error %d",
                                                        __func__, result);
-
+       else
+               kref_get(&priv->kref);
 
        return result;
 } /* ftdi_open */
@@ -1571,11 +1682,11 @@
        mutex_unlock(&port->serial->disc_mutex);
 
        /* cancel any scheduled reading */
-       cancel_delayed_work(&priv->rx_work);
-       flush_scheduled_work();
+       cancel_delayed_work_sync(&priv->rx_work);
 
        /* shutdown our bulk read */
        usb_kill_urb(port->read_urb);
+       kref_put(&priv->kref, ftdi_sio_priv_release);
 } /* ftdi_close */
 
 
@@ -1621,8 +1732,8 @@
        if (data_offset > 0) {
                /* Original sio needs control bytes too... */
                transfer_size += (data_offset *
-                               ((count + (PKTSZ - 1 - data_offset)) /
-                                (PKTSZ - data_offset)));
+                               ((count + (priv->max_packet_size - 1 - 
data_offset)) /
+                                (priv->max_packet_size - data_offset)));
        }
 
        buffer = kmalloc(transfer_size, GFP_ATOMIC);
@@ -1643,7 +1754,7 @@
        if (data_offset > 0) {
                /* Original sio requires control byte at start of
                   each packet. */
-               int user_pktsz = PKTSZ - data_offset;
+               int user_pktsz = priv->max_packet_size - data_offset;
                int todo = count;
                unsigned char *first_byte = buffer;
                const unsigned char *current_position = buf;
@@ -1738,7 +1849,7 @@
        data_offset = priv->write_offset;
        if (data_offset > 0) {
                /* Subtract the control bytes */
-               countback -= (data_offset * DIV_ROUND_UP(countback, PKTSZ));
+               countback -= (data_offset * DIV_ROUND_UP(countback, 
priv->max_packet_size));
        }
        spin_lock_irqsave(&priv->tx_lock, flags);
        --priv->tx_outstanding_urbs;
@@ -1838,7 +1949,7 @@
 
        /* count data bytes, but not status bytes */
        countread = urb->actual_length;
-       countread -= 2 * DIV_ROUND_UP(countread, PKTSZ);
+       countread -= 2 * DIV_ROUND_UP(countread, priv->max_packet_size);
        spin_lock_irqsave(&priv->rx_lock, flags);
        priv->rx_bytes += countread;
        spin_unlock_irqrestore(&priv->rx_lock, flags);
@@ -1910,7 +2021,7 @@
 
        need_flip = 0;
        for (packet_offset = priv->rx_processed;
-               packet_offset < urb->actual_length; packet_offset += PKTSZ) {
+               packet_offset < urb->actual_length; packet_offset += 
priv->max_packet_size) {
                int length;
 
                /* Compare new line status to the old one, signal if different/
@@ -1927,7 +2038,7 @@
                        }
                }
 
-               length = min(PKTSZ, urb->actual_length-packet_offset)-2;
+               length = min_t(u32, priv->max_packet_size, 
urb->actual_length-packet_offset)-2;
                if (length < 0) {
                        err("%s - bad packet length: %d", __func__, length+2);
                        length = 0;
@@ -2271,6 +2382,8 @@
        case FT232BM:
        case FT2232C:
        case FT232RL:
+       case FT2232H:
+       case FT4232H:
                /* the 8U232AM returns a two byte value (the sio is a 1 byte
                   value) - in the same format as the data returned from the in
                   point */

--
libftdi - see http://www.intra2net.com/en/developer/libftdi for details.
To unsubscribe send a mail to libftdi+unsubscribe@xxxxxxxxxxxxxxxxxxxxxxx   

Current Thread