More generic error message for the FTDI kernel driver
[libftdi] / src / ftdi.c
index 16f8f1f..7d5e029 100644 (file)
@@ -2,7 +2,7 @@
                           ftdi.c  -  description
                              -------------------
     begin                : Fri Apr 4 2003
-    copyright            : (C) 2003-2008 by Intra2net AG
+    copyright            : (C) 2003-2010 by Intra2net AG
     email                : opensource@intra2net.com
  ***************************************************************************/
 
 
 
 /**
+    Internal function to close usb device pointer.
+    Sets ftdi->usb_dev to NULL.
+    \internal
+
+    \param ftdi pointer to ftdi_context
+
+    \retval  zero if all is fine, otherwise error code from usb_close()
+*/
+static int ftdi_usb_close_internal (struct ftdi_context *ftdi)
+{
+    int ret = 0;
+
+    if (ftdi && ftdi->usb_dev)
+    {
+       ret = usb_close (ftdi->usb_dev);
+       ftdi->usb_dev = NULL;
+    }
+
+    return ret;
+}
+
+/**
     Initializes a ftdi_context.
 
     \param ftdi pointer to ftdi_context
@@ -63,7 +85,7 @@
 */
 int ftdi_init(struct ftdi_context *ftdi)
 {
-    int i;
+    unsigned int i;
 
     ftdi->usb_dev = NULL;
     ftdi->usb_read_timeout = 5000;
@@ -71,18 +93,19 @@ int ftdi_init(struct ftdi_context *ftdi)
 
     ftdi->type = TYPE_BM;    /* chip type */
     ftdi->baudrate = -1;
-    ftdi->bitbang_enabled = 0;
+    ftdi->bitbang_enabled = 0;  /* 0: normal mode 1: any of the bitbang modes enabled */
 
     ftdi->readbuffer = NULL;
     ftdi->readbuffer_offset = 0;
     ftdi->readbuffer_remaining = 0;
     ftdi->writebuffer_chunksize = 4096;
+    ftdi->max_packet_size = 0;
 
     ftdi->interface = 0;
     ftdi->index = 0;
     ftdi->in_ep = 0x02;
     ftdi->out_ep = 0x81;
-    ftdi->bitbang_mode = 1; /* 1: Normal bitbang mode, 2: SPI bitbang mode */
+    ftdi->bitbang_mode = 1; /* when bitbang is enabled this holds the number of the mode  */
 
     ftdi->error_str = NULL;
 
@@ -110,7 +133,7 @@ int ftdi_init(struct ftdi_context *ftdi)
 
     \return a pointer to a new ftdi_context, or NULL on failure
 */
-struct ftdi_context *ftdi_new()
+struct ftdi_context *ftdi_new(void)
 {
     struct ftdi_context * ftdi = (struct ftdi_context *)malloc(sizeof(struct ftdi_context));
 
@@ -132,13 +155,17 @@ struct ftdi_context *ftdi_new()
     Open selected channels on a chip, otherwise use first channel.
 
     \param ftdi pointer to ftdi_context
-    \param interface Interface to use for FT2232C chips.
+    \param interface Interface to use for FT2232C/2232H/4232H chips.
 
     \retval  0: all fine
     \retval -1: unknown interface
+    \retval -2: USB device unavailable
 */
 int ftdi_set_interface(struct ftdi_context *ftdi, enum ftdi_interface interface)
 {
+    if (ftdi == NULL)
+        ftdi_error_return(-2, "USB device unavailable");
+
     switch (interface)
     {
         case INTERFACE_ANY:
@@ -151,6 +178,18 @@ int ftdi_set_interface(struct ftdi_context *ftdi, enum ftdi_interface interface)
             ftdi->in_ep     = 0x04;
             ftdi->out_ep    = 0x83;
             break;
+        case INTERFACE_C:
+            ftdi->interface = 2;
+            ftdi->index     = INTERFACE_C;
+            ftdi->in_ep     = 0x06;
+            ftdi->out_ep    = 0x85;
+            break;
+        case INTERFACE_D:
+            ftdi->interface = 3;
+            ftdi->index     = INTERFACE_D;
+            ftdi->in_ep     = 0x08;
+            ftdi->out_ep    = 0x87;
+            break;
         default:
             ftdi_error_return(-1, "Unknown interface");
     }
@@ -164,6 +203,11 @@ int ftdi_set_interface(struct ftdi_context *ftdi, enum ftdi_interface interface)
 */
 void ftdi_deinit(struct ftdi_context *ftdi)
 {
+    if (ftdi == NULL)
+        return;
+
+    ftdi_usb_close_internal (ftdi);
+
     if (ftdi->async_usb_buffer != NULL)
     {
         free(ftdi->async_usb_buffer);
@@ -196,6 +240,9 @@ void ftdi_free(struct ftdi_context *ftdi)
 */
 void ftdi_set_usbdev (struct ftdi_context *ftdi, usb_dev_handle *usb)
 {
+    if (ftdi == NULL)
+        return;
+
     ftdi->usb_dev = usb;
 }
 
@@ -320,7 +367,7 @@ int ftdi_usb_get_strings(struct ftdi_context * ftdi, struct usb_device * dev,
     {
         if (usb_get_string_simple(ftdi->usb_dev, dev->descriptor.iManufacturer, manufacturer, mnf_len) <= 0)
         {
-            usb_close (ftdi->usb_dev);
+            ftdi_usb_close_internal (ftdi);
             ftdi_error_return(-7, usb_strerror());
         }
     }
@@ -329,7 +376,7 @@ int ftdi_usb_get_strings(struct ftdi_context * ftdi, struct usb_device * dev,
     {
         if (usb_get_string_simple(ftdi->usb_dev, dev->descriptor.iProduct, description, desc_len) <= 0)
         {
-            usb_close (ftdi->usb_dev);
+            ftdi_usb_close_internal (ftdi);
             ftdi_error_return(-8, usb_strerror());
         }
     }
@@ -338,19 +385,62 @@ int ftdi_usb_get_strings(struct ftdi_context * ftdi, struct usb_device * dev,
     {
         if (usb_get_string_simple(ftdi->usb_dev, dev->descriptor.iSerialNumber, serial, serial_len) <= 0)
         {
-            usb_close (ftdi->usb_dev);
+            ftdi_usb_close_internal (ftdi);
             ftdi_error_return(-9, usb_strerror());
         }
     }
 
-    if (usb_close (ftdi->usb_dev) != 0)
+    if (ftdi_usb_close_internal (ftdi) != 0)
         ftdi_error_return(-10, usb_strerror());
 
     return 0;
 }
 
 /**
-    Opens a ftdi device given by a usb_device.
+ * Internal function to determine the maximum packet size.
+ * \param ftdi pointer to ftdi_context
+ * \param dev libusb usb_dev to use
+ * \retval Maximum packet size for this device
+ */
+static unsigned int _ftdi_determine_max_packet_size(struct ftdi_context *ftdi, struct usb_device *dev)
+{
+    unsigned int packet_size;
+
+    // Sanity check
+    if (ftdi == NULL || dev == NULL)
+        return 64;
+
+    // Determine maximum packet size. Init with default value.
+    // New hi-speed devices from FTDI use a packet size of 512 bytes
+    // but could be connected to a normal speed USB hub -> 64 bytes packet size.
+    if (ftdi->type == TYPE_2232H || ftdi->type == TYPE_4232H)
+        packet_size = 512;
+    else
+        packet_size = 64;
+
+    if (dev->descriptor.bNumConfigurations > 0 && dev->config)
+    {
+        struct usb_config_descriptor config = dev->config[0];
+
+        if (ftdi->interface < config.bNumInterfaces)
+        {
+            struct usb_interface interface = config.interface[ftdi->interface];
+            if (interface.num_altsetting > 0)
+            {
+                struct usb_interface_descriptor descriptor = interface.altsetting[0];
+                if (descriptor.bNumEndpoints > 0)
+                {
+                    packet_size = descriptor.endpoint[0].wMaxPacketSize;
+                }
+            }
+        }
+    }
+
+    return packet_size;
+}
+
+/**
+    Opens a ftdi device given by an usb_device.
 
     \param ftdi pointer to ftdi_context
     \param dev libusb usb_dev to use
@@ -361,10 +451,16 @@ int ftdi_usb_get_strings(struct ftdi_context * ftdi, struct usb_device * dev,
     \retval -5: unable to claim device
     \retval -6: reset failed
     \retval -7: set baudrate failed
+    \retval -8: ftdi context invalid
 */
 int ftdi_usb_open_dev(struct ftdi_context *ftdi, struct usb_device *dev)
 {
     int detach_errno = 0;
+    int config_val = 1;
+
+    if (ftdi == NULL)
+        ftdi_error_return(-8, "ftdi context invalid");
+
     if (!(ftdi->usb_dev = usb_open(dev)))
         ftdi_error_return(-4, "usb_open() failed");
 
@@ -380,49 +476,52 @@ int ftdi_usb_open_dev(struct ftdi_context *ftdi, struct usb_device *dev)
         detach_errno = errno;
 #endif
 
+#ifdef __WIN32__
     // set configuration (needed especially for windows)
     // tolerate EBUSY: one device with one configuration, but two interfaces
     //    and libftdi sessions to both interfaces (e.g. FT2232)
-    if (dev->descriptor.bNumConfigurations > 0 &&
-            usb_set_configuration(ftdi->usb_dev, dev->config[0].bConfigurationValue) &&
-            errno != EBUSY)
+
+    if (dev->descriptor.bNumConfigurations > 0)
     {
-        usb_close (ftdi->usb_dev);
-        if (detach_errno == EPERM)
-        {
-            ftdi_error_return(-8, "inappropriate permissions on device!");
-        }
-        else
+        // libusb-win32 on Windows 64 can return a null pointer for a valid device
+        if (dev->config)
+            config_val = dev->config[0].bConfigurationValue;
+
+        if (usb_set_configuration(ftdi->usb_dev, config_val) &&
+            errno != EBUSY)
         {
-            ftdi_error_return(-3, "unable to set usb configuration. Make sure ftdi_sio is unloaded!");
+            ftdi_usb_close_internal (ftdi);
+            if (detach_errno == EPERM)
+            {
+                ftdi_error_return(-8, "inappropriate permissions on device!");
+            }
+            else
+            {
+                ftdi_error_return(-3, "unable to set usb configuration. Make sure the default FTDI kernel side driver is unloaded.");
+            }
         }
     }
+#endif
 
     if (usb_claim_interface(ftdi->usb_dev, ftdi->interface) != 0)
     {
-        usb_close (ftdi->usb_dev);
+        ftdi_usb_close_internal (ftdi);
         if (detach_errno == EPERM)
         {
             ftdi_error_return(-8, "inappropriate permissions on device!");
         }
         else
         {
-            ftdi_error_return(-5, "unable to claim usb device. Make sure ftdi_sio is unloaded!");
+            ftdi_error_return(-5, "unable to claim usb device. Make sure the default FTDI kernel side driver is unloaded.");
         }
     }
 
     if (ftdi_usb_reset (ftdi) != 0)
     {
-        usb_close (ftdi->usb_dev);
+        ftdi_usb_close_internal (ftdi);
         ftdi_error_return(-6, "ftdi_usb_reset failed");
     }
 
-    if (ftdi_set_baudrate (ftdi, 9600) != 0)
-    {
-        usb_close (ftdi->usb_dev);
-        ftdi_error_return(-7, "set baudrate failed");
-    }
-
     // Try to guess chip type
     // Bug in the BM type chips: bcdDevice is 0x200 for serial == 0
     if (dev->descriptor.bcdDevice == 0x400 || (dev->descriptor.bcdDevice == 0x200
@@ -431,13 +530,35 @@ int ftdi_usb_open_dev(struct ftdi_context *ftdi, struct usb_device *dev)
     else if (dev->descriptor.bcdDevice == 0x200)
         ftdi->type = TYPE_AM;
     else if (dev->descriptor.bcdDevice == 0x500)
-    {
         ftdi->type = TYPE_2232C;
-        if (!ftdi->index)
-            ftdi->index = INTERFACE_A;
-    }
     else if (dev->descriptor.bcdDevice == 0x600)
         ftdi->type = TYPE_R;
+    else if (dev->descriptor.bcdDevice == 0x700)
+        ftdi->type = TYPE_2232H;
+    else if (dev->descriptor.bcdDevice == 0x800)
+        ftdi->type = TYPE_4232H;
+
+    // Set default interface on dual/quad type chips
+    switch(ftdi->type)
+    {
+        case TYPE_2232C:
+        case TYPE_2232H:
+        case TYPE_4232H:
+            if (!ftdi->index)
+                ftdi->index = INTERFACE_A;
+            break;
+        default:
+            break;
+    }
+
+    // Determine maximum packet size
+    ftdi->max_packet_size = _ftdi_determine_max_packet_size(ftdi, dev);
+
+    if (ftdi_set_baudrate (ftdi, 9600) != 0)
+    {
+        ftdi_usb_close_internal (ftdi);
+        ftdi_error_return(-7, "set baudrate failed");
+    }
 
     ftdi_error_return(0, "all fine");
 }
@@ -481,6 +602,36 @@ int ftdi_usb_open(struct ftdi_context *ftdi, int vendor, int product)
 int ftdi_usb_open_desc(struct ftdi_context *ftdi, int vendor, int product,
                        const char* description, const char* serial)
 {
+    return ftdi_usb_open_desc_index(ftdi,vendor,product,description,serial,0);
+}
+
+/**
+    Opens the index-th device with a given, vendor id, product id,
+    description and serial.
+
+    \param ftdi pointer to ftdi_context
+    \param vendor Vendor ID
+    \param product Product ID
+    \param description Description to search for. Use NULL if not needed.
+    \param serial Serial to search for. Use NULL if not needed.
+    \param index Number of matching device to open if there are more than one, starts with 0.
+
+    \retval  0: all fine
+    \retval -1: usb_find_busses() failed
+    \retval -2: usb_find_devices() failed
+    \retval -3: usb device not found
+    \retval -4: unable to open device
+    \retval -5: unable to claim device
+    \retval -6: reset failed
+    \retval -7: set baudrate failed
+    \retval -8: get product description failed
+    \retval -9: get serial number failed
+    \retval -10: unable to close device
+    \retval -11: ftdi context invalid
+*/
+int ftdi_usb_open_desc_index(struct ftdi_context *ftdi, int vendor, int product,
+                       const char* description, const char* serial, unsigned int index)
+{
     struct usb_bus *bus;
     struct usb_device *dev;
     char string[256];
@@ -492,6 +643,9 @@ int ftdi_usb_open_desc(struct ftdi_context *ftdi, int vendor, int product,
     if (usb_find_devices() < 0)
         ftdi_error_return(-2, "usb_find_devices() failed");
 
+    if (ftdi == NULL)
+        ftdi_error_return(-11, "ftdi context invalid");
+
     for (bus = usb_get_busses(); bus; bus = bus->next)
     {
         for (dev = bus->devices; dev; dev = dev->next)
@@ -506,12 +660,12 @@ int ftdi_usb_open_desc(struct ftdi_context *ftdi, int vendor, int product,
                 {
                     if (usb_get_string_simple(ftdi->usb_dev, dev->descriptor.iProduct, string, sizeof(string)) <= 0)
                     {
-                        usb_close (ftdi->usb_dev);
+                        ftdi_usb_close_internal (ftdi);
                         ftdi_error_return(-8, "unable to fetch product description");
                     }
                     if (strncmp(string, description, sizeof(string)) != 0)
                     {
-                        if (usb_close (ftdi->usb_dev) != 0)
+                        if (ftdi_usb_close_internal (ftdi) != 0)
                             ftdi_error_return(-10, "unable to close device");
                         continue;
                     }
@@ -520,20 +674,26 @@ int ftdi_usb_open_desc(struct ftdi_context *ftdi, int vendor, int product,
                 {
                     if (usb_get_string_simple(ftdi->usb_dev, dev->descriptor.iSerialNumber, string, sizeof(string)) <= 0)
                     {
-                        usb_close (ftdi->usb_dev);
+                        ftdi_usb_close_internal (ftdi);
                         ftdi_error_return(-9, "unable to fetch serial number");
                     }
                     if (strncmp(string, serial, sizeof(string)) != 0)
                     {
-                        if (usb_close (ftdi->usb_dev) != 0)
+                        if (ftdi_usb_close_internal (ftdi) != 0)
                             ftdi_error_return(-10, "unable to close device");
                         continue;
                     }
                 }
 
-                if (usb_close (ftdi->usb_dev) != 0)
+                if (ftdi_usb_close_internal (ftdi) != 0)
                     ftdi_error_return(-10, "unable to close device");
 
+                if (index > 0)
+                {
+                    index--;
+                    continue;
+                }
+
                 return ftdi_usb_open_dev(ftdi, dev);
             }
         }
@@ -544,15 +704,135 @@ int ftdi_usb_open_desc(struct ftdi_context *ftdi, int vendor, int product,
 }
 
 /**
+    Opens the ftdi-device described by a description-string.
+    Intended to be used for parsing a device-description given as commandline argument.
+
+    \param ftdi pointer to ftdi_context
+    \param description NULL-terminated description-string, using this format:
+        \li <tt>d:\<devicenode></tt> path of bus and device-node (e.g. "003/001") within usb device tree (usually at /proc/bus/usb/)
+        \li <tt>i:\<vendor>:\<product></tt> first device with given vendor and product id, ids can be decimal, octal (preceded by "0") or hex (preceded by "0x")
+        \li <tt>i:\<vendor>:\<product>:\<index></tt> as above with index being the number of the device (starting with 0) if there are more than one
+        \li <tt>s:\<vendor>:\<product>:\<serial></tt> first device with given vendor id, product id and serial string
+
+    \note The description format may be extended in later versions.
+
+    \retval  0: all fine
+    \retval -1: usb_find_busses() failed
+    \retval -2: usb_find_devices() failed
+    \retval -3: usb device not found
+    \retval -4: unable to open device
+    \retval -5: unable to claim device
+    \retval -6: reset failed
+    \retval -7: set baudrate failed
+    \retval -8: get product description failed
+    \retval -9: get serial number failed
+    \retval -10: unable to close device
+    \retval -11: illegal description format
+    \retval -12: ftdi context invalid
+*/
+int ftdi_usb_open_string(struct ftdi_context *ftdi, const char* description)
+{
+    if (ftdi == NULL)
+        ftdi_error_return(-12, "ftdi context invalid");
+
+    if (description[0] == 0 || description[1] != ':')
+        ftdi_error_return(-11, "illegal description format");
+
+    if (description[0] == 'd')
+    {
+        struct usb_bus *bus;
+        struct usb_device *dev;
+
+        usb_init();
+
+        if (usb_find_busses() < 0)
+            ftdi_error_return(-1, "usb_find_busses() failed");
+        if (usb_find_devices() < 0)
+            ftdi_error_return(-2, "usb_find_devices() failed");
+
+        for (bus = usb_get_busses(); bus; bus = bus->next)
+        {
+            for (dev = bus->devices; dev; dev = dev->next)
+            {
+                /* XXX: This doesn't handle symlinks/odd paths/etc... */
+                const char *desc = description + 2;
+                size_t len = strlen(bus->dirname);
+                if (strncmp(desc, bus->dirname, len))
+                    continue;
+                desc += len;
+                if (desc[0] != '/')
+                    continue;
+                ++desc;
+                if (strcmp(desc, dev->filename))
+                    continue;
+                return ftdi_usb_open_dev(ftdi, dev);
+            }
+        }
+
+        // device not found
+        ftdi_error_return(-3, "device not found");
+    }
+    else if (description[0] == 'i' || description[0] == 's')
+    {
+        unsigned int vendor;
+        unsigned int product;
+        unsigned int index=0;
+        const char *serial=NULL;
+        const char *startp, *endp;
+
+        errno=0;
+        startp=description+2;
+        vendor=strtoul((char*)startp,(char**)&endp,0);
+        if (*endp != ':' || endp == startp || errno != 0)
+            ftdi_error_return(-11, "illegal description format");
+
+        startp=endp+1;
+        product=strtoul((char*)startp,(char**)&endp,0);
+        if (endp == startp || errno != 0)
+            ftdi_error_return(-11, "illegal description format");
+
+        if (description[0] == 'i' && *endp != 0)
+        {
+            /* optional index field in i-mode */
+            if (*endp != ':')
+                ftdi_error_return(-11, "illegal description format");
+
+            startp=endp+1;
+            index=strtoul((char*)startp,(char**)&endp,0);
+            if (*endp != 0 || endp == startp || errno != 0)
+                ftdi_error_return(-11, "illegal description format");
+        }
+        if (description[0] == 's')
+        {
+            if (*endp != ':')
+                ftdi_error_return(-11, "illegal description format");
+
+            /* rest of the description is the serial */
+            serial=endp+1;
+        }
+
+        return ftdi_usb_open_desc_index(ftdi, vendor, product, NULL, serial, index);
+    }
+    else
+    {
+        ftdi_error_return(-11, "illegal description format");
+    }
+}
+
+/**
     Resets the ftdi device.
 
     \param ftdi pointer to ftdi_context
 
     \retval  0: all fine
     \retval -1: FTDI reset failed
+    \retval -2: USB device unavailable
 */
 int ftdi_usb_reset(struct ftdi_context *ftdi)
 {
+    if (ftdi == NULL || ftdi->usb_dev == NULL)
+        ftdi_error_return(-2, "USB device unavailable");
+
     if (usb_control_msg(ftdi->usb_dev, FTDI_DEVICE_OUT_REQTYPE,
                         SIO_RESET_REQUEST, SIO_RESET_SIO,
                         ftdi->index, NULL, 0, ftdi->usb_write_timeout) != 0)
@@ -572,9 +852,13 @@ int ftdi_usb_reset(struct ftdi_context *ftdi)
 
     \retval  0: all fine
     \retval -1: read buffer purge failed
+    \retval -2: USB device unavailable
 */
 int ftdi_usb_purge_rx_buffer(struct ftdi_context *ftdi)
 {
+    if (ftdi == NULL || ftdi->usb_dev == NULL)
+        ftdi_error_return(-2, "USB device unavailable");
+
     if (usb_control_msg(ftdi->usb_dev, FTDI_DEVICE_OUT_REQTYPE,
                         SIO_RESET_REQUEST, SIO_RESET_PURGE_RX,
                         ftdi->index, NULL, 0, ftdi->usb_write_timeout) != 0)
@@ -594,9 +878,13 @@ int ftdi_usb_purge_rx_buffer(struct ftdi_context *ftdi)
 
     \retval  0: all fine
     \retval -1: write buffer purge failed
+    \retval -2: USB device unavailable
 */
 int ftdi_usb_purge_tx_buffer(struct ftdi_context *ftdi)
 {
+    if (ftdi == NULL || ftdi->usb_dev == NULL)
+        ftdi_error_return(-2, "USB device unavailable");
+
     if (usb_control_msg(ftdi->usb_dev, FTDI_DEVICE_OUT_REQTYPE,
                         SIO_RESET_REQUEST, SIO_RESET_PURGE_TX,
                         ftdi->index, NULL, 0, ftdi->usb_write_timeout) != 0)
@@ -613,11 +901,15 @@ int ftdi_usb_purge_tx_buffer(struct ftdi_context *ftdi)
     \retval  0: all fine
     \retval -1: read buffer purge failed
     \retval -2: write buffer purge failed
+    \retval -3: USB device unavailable
 */
 int ftdi_usb_purge_buffers(struct ftdi_context *ftdi)
 {
     int result;
 
+    if (ftdi == NULL || ftdi->usb_dev == NULL)
+        ftdi_error_return(-3, "USB device unavailable");
+
     result = ftdi_usb_purge_rx_buffer(ftdi);
     if (result < 0)
         return -1;
@@ -629,6 +921,8 @@ int ftdi_usb_purge_buffers(struct ftdi_context *ftdi)
     return 0;
 }
 
+
+
 /**
     Closes the ftdi device. Call ftdi_deinit() if you're cleaning up.
 
@@ -637,26 +931,31 @@ int ftdi_usb_purge_buffers(struct ftdi_context *ftdi)
     \retval  0: all fine
     \retval -1: usb_release failed
     \retval -2: usb_close failed
+    \retval -3: ftdi context invalid
 */
 int ftdi_usb_close(struct ftdi_context *ftdi)
 {
     int rtn = 0;
 
+    if (ftdi == NULL)
+        ftdi_error_return(-3, "ftdi context invalid");
+
 #ifdef LIBFTDI_LINUX_ASYNC_MODE
     /* try to release some kernel resources */
     ftdi_async_complete(ftdi,1);
 #endif
 
-    if (usb_release_interface(ftdi->usb_dev, ftdi->interface) != 0)
-        rtn = -1;
+    if (ftdi->usb_dev != NULL)
+        if (usb_release_interface(ftdi->usb_dev, ftdi->interface) != 0)
+            rtn = -1;
 
-    if (usb_close (ftdi->usb_dev) != 0)
+    if (ftdi_usb_close_internal (ftdi) != 0)
         rtn = -2;
 
     return rtn;
 }
 
-/*
+/**
     ftdi_convert_baudrate returns nearest supported baud rate to that requested.
     Function is only used internally
     \internal
@@ -769,7 +1068,7 @@ static int ftdi_convert_baudrate(int baudrate, struct ftdi_context *ftdi,
     }
     // Split into "value" and "index" values
     *value = (unsigned short)(encoded_divisor & 0xFFFF);
-    if (ftdi->type == TYPE_2232C)
+    if (ftdi->type == TYPE_2232C || ftdi->type == TYPE_2232H || ftdi->type == TYPE_4232H)
     {
         *index = (unsigned short)(encoded_divisor >> 8);
         *index &= 0xFF00;
@@ -791,12 +1090,16 @@ static int ftdi_convert_baudrate(int baudrate, struct ftdi_context *ftdi,
     \retval  0: all fine
     \retval -1: invalid baudrate
     \retval -2: setting baudrate failed
+    \retval -3: USB device unavailable
 */
 int ftdi_set_baudrate(struct ftdi_context *ftdi, int baudrate)
 {
     unsigned short value, index;
     int actual_baudrate;
 
+    if (ftdi == NULL || ftdi->usb_dev == NULL)
+        ftdi_error_return(-3, "USB device unavailable");
+
     if (ftdi->bitbang_enabled)
     {
         baudrate = baudrate*4;
@@ -852,6 +1155,7 @@ int ftdi_set_line_property(struct ftdi_context *ftdi, enum ftdi_bits_type bits,
 
     \retval  0: all fine
     \retval -1: Setting line property failed
+    \retval -2: USB device unavailable
 */
 int ftdi_set_line_property2(struct ftdi_context *ftdi, enum ftdi_bits_type bits,
                             enum ftdi_stopbits_type sbit, enum ftdi_parity_type parity,
@@ -859,6 +1163,9 @@ int ftdi_set_line_property2(struct ftdi_context *ftdi, enum ftdi_bits_type bits,
 {
     unsigned short value = bits;
 
+    if (ftdi == NULL || ftdi->usb_dev == NULL)
+        ftdi_error_return(-2, "USB device unavailable");
+
     switch (parity)
     {
         case NONE:
@@ -916,6 +1223,7 @@ int ftdi_set_line_property2(struct ftdi_context *ftdi, enum ftdi_bits_type bits,
     \param buf Buffer with the data
     \param size Size of the buffer
 
+    \retval -666: USB device unavailable
     \retval <0: error code from usb_bulk_write()
     \retval >0: number of bytes written
 */
@@ -925,6 +1233,9 @@ int ftdi_write_data(struct ftdi_context *ftdi, unsigned char *buf, int size)
     int offset = 0;
     int total_written = 0;
 
+    if (ftdi == NULL || ftdi->usb_dev == NULL)
+        ftdi_error_return(-666, "USB device unavailable");
+
     while (offset < size)
     {
         int write_size = ftdi->writebuffer_chunksize;
@@ -944,6 +1255,9 @@ int ftdi_write_data(struct ftdi_context *ftdi, unsigned char *buf, int size)
 }
 
 #ifdef LIBFTDI_LINUX_ASYNC_MODE
+#ifdef USB_CLASS_PTP
+#error LIBFTDI_LINUX_ASYNC_MODE is not compatible with libusb-compat-0.1!
+#endif
 /* this is strongly dependent on libusb using the same struct layout. If libusb
    changes in some later version this may break horribly (this is for libusb 0.1.12) */
 struct usb_dev_handle
@@ -960,7 +1274,7 @@ static int _usb_get_async_urbs_pending(struct ftdi_context *ftdi)
 {
     struct usbdevfs_urb *urb;
     int pending=0;
-    int i;
+    unsigned int i;
 
     for (i=0; i < ftdi->async_usb_buffer_size; i++)
     {
@@ -1053,8 +1367,8 @@ static int _usb_bulk_write_async(struct ftdi_context *ftdi, int ep, char *bytes,
 {
     struct usbdevfs_urb *urb;
     int bytesdone = 0, requested;
-    int ret, i;
-    int cleanup_count;
+    int ret, cleanup_count;
+    unsigned int i;
 
     do
     {
@@ -1126,6 +1440,7 @@ static int _usb_bulk_write_async(struct ftdi_context *ftdi, int ep, char *bytes,
     \param buf Buffer with the data
     \param size Size of the buffer
 
+    \retval -666: USB device unavailable
     \retval <0: error code from usb_bulk_write()
     \retval >0: number of bytes written
 */
@@ -1135,6 +1450,9 @@ int ftdi_write_data_async(struct ftdi_context *ftdi, unsigned char *buf, int siz
     int offset = 0;
     int total_written = 0;
 
+    if (ftdi == NULL || ftdi->usb_dev == NULL)
+        ftdi_error_return(-666, "USB device unavailable");
+
     while (offset < size)
     {
         int write_size = ftdi->writebuffer_chunksize;
@@ -1162,9 +1480,13 @@ int ftdi_write_data_async(struct ftdi_context *ftdi, unsigned char *buf, int siz
     \param chunksize Chunk size
 
     \retval 0: all fine
+    \retval -1: ftdi context invalid
 */
 int ftdi_write_data_set_chunksize(struct ftdi_context *ftdi, unsigned int chunksize)
 {
+    if (ftdi == NULL)
+        ftdi_error_return(-1, "ftdi context invalid");
+
     ftdi->writebuffer_chunksize = chunksize;
     return 0;
 }
@@ -1176,9 +1498,13 @@ int ftdi_write_data_set_chunksize(struct ftdi_context *ftdi, unsigned int chunks
     \param chunksize Pointer to store chunk size in
 
     \retval 0: all fine
+    \retval -1: ftdi context invalid
 */
 int ftdi_write_data_get_chunksize(struct ftdi_context *ftdi, unsigned int *chunksize)
 {
+    if (ftdi == NULL)
+        ftdi_error_return(-1, "ftdi context invalid");
+
     *chunksize = ftdi->writebuffer_chunksize;
     return 0;
 }
@@ -1192,16 +1518,24 @@ int ftdi_write_data_get_chunksize(struct ftdi_context *ftdi, unsigned int *chunk
     \param buf Buffer to store data in
     \param size Size of the buffer
 
+    \retval -666: USB device unavailable
     \retval <0: error code from usb_bulk_read()
     \retval  0: no data was available
     \retval >0: number of bytes read
 
-    \remark This function is not useful in bitbang mode.
-            Use ftdi_read_pins() to get the current state of the pins.
 */
 int ftdi_read_data(struct ftdi_context *ftdi, unsigned char *buf, int size)
 {
     int offset = 0, ret = 1, i, num_of_chunks, chunk_remains;
+    int packet_size;
+
+    if (ftdi == NULL || ftdi->usb_dev == NULL)
+        ftdi_error_return(-666, "USB device unavailable");
+
+    packet_size = ftdi->max_packet_size;
+    // Packet size sanity check (avoid division by zero)
+    if (packet_size == 0)
+        ftdi_error_return(-1, "max_packet_size is bogus (zero)");
 
     // everything we want is still in the readbuffer?
     if (size <= ftdi->readbuffer_remaining)
@@ -1238,23 +1572,23 @@ int ftdi_read_data(struct ftdi_context *ftdi, unsigned char *buf, int size)
         {
             // skip FTDI status bytes.
             // Maybe stored in the future to enable modem use
-            num_of_chunks = ret / 64;
-            chunk_remains = ret % 64;
+            num_of_chunks = ret / packet_size;
+            chunk_remains = ret % packet_size;
             //printf("ret = %X, num_of_chunks = %X, chunk_remains = %X, readbuffer_offset = %X\n", ret, num_of_chunks, chunk_remains, ftdi->readbuffer_offset);
 
             ftdi->readbuffer_offset += 2;
             ret -= 2;
 
-            if (ret > 62)
+            if (ret > packet_size - 2)
             {
                 for (i = 1; i < num_of_chunks; i++)
-                    memmove (ftdi->readbuffer+ftdi->readbuffer_offset+62*i,
-                             ftdi->readbuffer+ftdi->readbuffer_offset+64*i,
-                             62);
+                    memmove (ftdi->readbuffer+ftdi->readbuffer_offset+(packet_size - 2)*i,
+                             ftdi->readbuffer+ftdi->readbuffer_offset+packet_size*i,
+                             packet_size - 2);
                 if (chunk_remains > 2)
                 {
-                    memmove (ftdi->readbuffer+ftdi->readbuffer_offset+62*i,
-                             ftdi->readbuffer+ftdi->readbuffer_offset+64*i,
+                    memmove (ftdi->readbuffer+ftdi->readbuffer_offset+(packet_size - 2)*i,
+                             ftdi->readbuffer+ftdi->readbuffer_offset+packet_size*i,
                              chunk_remains-2);
                     ret -= 2*num_of_chunks;
                 }
@@ -1313,11 +1647,15 @@ int ftdi_read_data(struct ftdi_context *ftdi, unsigned char *buf, int size)
     \param chunksize Chunk size
 
     \retval 0: all fine
+    \retval -1: ftdi context invalid
 */
 int ftdi_read_data_set_chunksize(struct ftdi_context *ftdi, unsigned int chunksize)
 {
     unsigned char *new_buf;
 
+    if (ftdi == NULL)
+        ftdi_error_return(-1, "ftdi context invalid");
+
     // Invalidate all remaining data
     ftdi->readbuffer_offset = 0;
     ftdi->readbuffer_remaining = 0;
@@ -1338,9 +1676,13 @@ int ftdi_read_data_set_chunksize(struct ftdi_context *ftdi, unsigned int chunksi
     \param chunksize Pointer to store chunk size in
 
     \retval 0: all fine
+    \retval -1: FTDI context invalid
 */
 int ftdi_read_data_get_chunksize(struct ftdi_context *ftdi, unsigned int *chunksize)
 {
+    if (ftdi == NULL)
+        ftdi_error_return(-1, "FTDI context invalid");
+
     *chunksize = ftdi->readbuffer_chunksize;
     return 0;
 }
@@ -1349,7 +1691,7 @@ int ftdi_read_data_get_chunksize(struct ftdi_context *ftdi, unsigned int *chunks
 /**
     Enable bitbang mode.
 
-    For advanced bitbang modes of the FT2232C chip use ftdi_set_bitmode().
+    \deprecated use \ref ftdi_set_bitmode with mode BITMODE_BITBANG instead
 
     \param ftdi pointer to ftdi_context
     \param bitmask Bitmask to configure lines.
@@ -1357,11 +1699,15 @@ int ftdi_read_data_get_chunksize(struct ftdi_context *ftdi, unsigned int *chunks
 
     \retval  0: all fine
     \retval -1: can't enable bitbang mode
+    \retval -2: USB device unavailable
 */
 int ftdi_enable_bitbang(struct ftdi_context *ftdi, unsigned char bitmask)
 {
     unsigned short usb_val;
 
+    if (ftdi == NULL || ftdi->usb_dev == NULL)
+        ftdi_error_return(-2, "USB device unavailable");
+
     usb_val = bitmask; // low byte: bitmask
     /* FT2232C: Set bitbang_mode to 2 to enable SPI */
     usb_val |= (ftdi->bitbang_mode << 8);
@@ -1382,9 +1728,13 @@ int ftdi_enable_bitbang(struct ftdi_context *ftdi, unsigned char bitmask)
 
     \retval  0: all fine
     \retval -1: can't disable bitbang mode
+    \retval -2: USB device unavailable
 */
 int ftdi_disable_bitbang(struct ftdi_context *ftdi)
 {
+    if (ftdi == NULL || ftdi->usb_dev == NULL)
+        ftdi_error_return(-2, "USB device unavailable");
+
     if (usb_control_msg(ftdi->usb_dev, FTDI_DEVICE_OUT_REQTYPE, SIO_SET_BITMODE_REQUEST, 0, ftdi->index, NULL, 0, ftdi->usb_write_timeout) != 0)
         ftdi_error_return(-1, "unable to leave bitbang mode. Perhaps not a BM type chip?");
 
@@ -1393,41 +1743,49 @@ int ftdi_disable_bitbang(struct ftdi_context *ftdi)
 }
 
 /**
-    Enable advanced bitbang mode for FT2232C chips.
+    Enable/disable bitbang modes.
 
     \param ftdi pointer to ftdi_context
     \param bitmask Bitmask to configure lines.
            HIGH/ON value configures a line as output.
-    \param mode Bitbang mode: 1 for normal mode, 2 for SPI mode
+    \param mode Bitbang mode: use the values defined in \ref ftdi_mpsse_mode
 
     \retval  0: all fine
     \retval -1: can't enable bitbang mode
+    \retval -2: USB device unavailable
 */
 int ftdi_set_bitmode(struct ftdi_context *ftdi, unsigned char bitmask, unsigned char mode)
 {
     unsigned short usb_val;
 
+    if (ftdi == NULL || ftdi->usb_dev == NULL)
+        ftdi_error_return(-2, "USB device unavailable");
+
     usb_val = bitmask; // low byte: bitmask
     usb_val |= (mode << 8);
     if (usb_control_msg(ftdi->usb_dev, FTDI_DEVICE_OUT_REQTYPE, SIO_SET_BITMODE_REQUEST, usb_val, ftdi->index, NULL, 0, ftdi->usb_write_timeout) != 0)
-        ftdi_error_return(-1, "unable to configure bitbang mode. Perhaps not a 2232C type chip?");
+        ftdi_error_return(-1, "unable to configure bitbang mode. Perhaps selected mode not supported on your chip?");
 
     ftdi->bitbang_mode = mode;
-    ftdi->bitbang_enabled = (mode == BITMODE_BITBANG || mode == BITMODE_SYNCBB)?1:0;
+    ftdi->bitbang_enabled = (mode == BITMODE_RESET) ? 0 : 1;
     return 0;
 }
 
 /**
-    Directly read pin state. Useful for bitbang mode.
+    Directly read pin state, circumventing the read buffer. Useful for bitbang mode.
 
     \param ftdi pointer to ftdi_context
     \param pins Pointer to store pins into
 
     \retval  0: all fine
     \retval -1: read pins failed
+    \retval -2: USB device unavailable
 */
 int ftdi_read_pins(struct ftdi_context *ftdi, unsigned char *pins)
 {
+    if (ftdi == NULL || ftdi->usb_dev == NULL)
+        ftdi_error_return(-2, "USB device unavailable");
+
     if (usb_control_msg(ftdi->usb_dev, FTDI_DEVICE_IN_REQTYPE, SIO_READ_PINS_REQUEST, 0, ftdi->index, (char *)pins, 1, ftdi->usb_read_timeout) != 1)
         ftdi_error_return(-1, "read pins failed");
 
@@ -1447,6 +1805,7 @@ int ftdi_read_pins(struct ftdi_context *ftdi, unsigned char *pins)
     \retval  0: all fine
     \retval -1: latency out of range
     \retval -2: unable to set latency timer
+    \retval -3: USB device unavailable
 */
 int ftdi_set_latency_timer(struct ftdi_context *ftdi, unsigned char latency)
 {
@@ -1455,6 +1814,9 @@ int ftdi_set_latency_timer(struct ftdi_context *ftdi, unsigned char latency)
     if (latency < 1)
         ftdi_error_return(-1, "latency out of range. Only valid for 1-255");
 
+    if (ftdi == NULL || ftdi->usb_dev == NULL)
+        ftdi_error_return(-3, "USB device unavailable");
+
     usb_val = latency;
     if (usb_control_msg(ftdi->usb_dev, FTDI_DEVICE_OUT_REQTYPE, SIO_SET_LATENCY_TIMER_REQUEST, usb_val, ftdi->index, NULL, 0, ftdi->usb_write_timeout) != 0)
         ftdi_error_return(-2, "unable to set latency timer");
@@ -1470,10 +1832,15 @@ int ftdi_set_latency_timer(struct ftdi_context *ftdi, unsigned char latency)
 
     \retval  0: all fine
     \retval -1: unable to get latency timer
+    \retval -2: USB device unavailable
 */
 int ftdi_get_latency_timer(struct ftdi_context *ftdi, unsigned char *latency)
 {
     unsigned short usb_val;
+
+    if (ftdi == NULL || ftdi->usb_dev == NULL)
+        ftdi_error_return(-2, "USB device unavailable");
+
     if (usb_control_msg(ftdi->usb_dev, FTDI_DEVICE_IN_REQTYPE, SIO_GET_LATENCY_TIMER_REQUEST, 0, ftdi->index, (char *)&usb_val, 1, ftdi->usb_read_timeout) != 1)
         ftdi_error_return(-1, "reading latency timer failed");
 
@@ -1519,11 +1886,15 @@ int ftdi_get_latency_timer(struct ftdi_context *ftdi, unsigned char *latency)
 
     \retval  0: all fine
     \retval -1: unable to retrieve status information
+    \retval -2: USB device unavailable
 */
 int ftdi_poll_modem_status(struct ftdi_context *ftdi, unsigned short *status)
 {
     char usb_val[2];
 
+    if (ftdi == NULL || ftdi->usb_dev == NULL)
+        ftdi_error_return(-2, "USB device unavailable");
+
     if (usb_control_msg(ftdi->usb_dev, FTDI_DEVICE_IN_REQTYPE, SIO_POLL_MODEM_STATUS_REQUEST, 0, ftdi->index, usb_val, 2, ftdi->usb_read_timeout) != 2)
         ftdi_error_return(-1, "getting modem status failed");
 
@@ -1541,9 +1912,13 @@ int ftdi_poll_modem_status(struct ftdi_context *ftdi, unsigned short *status)
 
     \retval  0: all fine
     \retval -1: set flow control failed
+    \retval -2: USB device unavailable
 */
 int ftdi_setflowctrl(struct ftdi_context *ftdi, int flowctrl)
 {
+    if (ftdi == NULL || ftdi->usb_dev == NULL)
+        ftdi_error_return(-2, "USB device unavailable");
+
     if (usb_control_msg(ftdi->usb_dev, FTDI_DEVICE_OUT_REQTYPE,
                         SIO_SET_FLOW_CTRL_REQUEST, 0, (flowctrl | ftdi->index),
                         NULL, 0, ftdi->usb_write_timeout) != 0)
@@ -1560,11 +1935,15 @@ int ftdi_setflowctrl(struct ftdi_context *ftdi, int flowctrl)
 
     \retval  0: all fine
     \retval -1: set dtr failed
+    \retval -2: USB device unavailable
 */
 int ftdi_setdtr(struct ftdi_context *ftdi, int state)
 {
     unsigned short usb_val;
 
+    if (ftdi == NULL || ftdi->usb_dev == NULL)
+        ftdi_error_return(-2, "USB device unavailable");
+
     if (state)
         usb_val = SIO_SET_DTR_HIGH;
     else
@@ -1585,12 +1964,16 @@ int ftdi_setdtr(struct ftdi_context *ftdi, int state)
     \param state state to set line to (1 or 0)
 
     \retval  0: all fine
-    \retval -1 set rts failed
+    \retval -1: set rts failed
+    \retval -2: USB device unavailable
 */
 int ftdi_setrts(struct ftdi_context *ftdi, int state)
 {
     unsigned short usb_val;
 
+    if (ftdi == NULL || ftdi->usb_dev == NULL)
+        ftdi_error_return(-2, "USB device unavailable");
+
     if (state)
         usb_val = SIO_SET_RTS_HIGH;
     else
@@ -1605,19 +1988,23 @@ int ftdi_setrts(struct ftdi_context *ftdi, int state)
 }
 
 /**
- Set dtr and rts line in one pass
+    Set dtr and rts line in one pass
 
- \param ftdi pointer to ftdi_context
- \param dtr  DTR state to set line to (1 or 0)
- \param rts  RTS state to set line to (1 or 0)
+    \param ftdi pointer to ftdi_context
+    \param dtr  DTR state to set line to (1 or 0)
+    \param rts  RTS state to set line to (1 or 0)
 
- \retval  0: all fine
- \retval -1 set dtr/rts failed
+    \retval  0: all fine
+    \retval -1: set dtr/rts failed
+    \retval -2: USB device unavailable
  */
 int ftdi_setdtr_rts(struct ftdi_context *ftdi, int dtr, int rts)
 {
     unsigned short usb_val;
 
+    if (ftdi == NULL || ftdi->usb_dev == NULL)
+        ftdi_error_return(-2, "USB device unavailable");
+
     if (dtr)
         usb_val = SIO_SET_DTR_HIGH;
     else
@@ -1645,12 +2032,16 @@ int ftdi_setdtr_rts(struct ftdi_context *ftdi, int dtr, int rts)
 
     \retval  0: all fine
     \retval -1: unable to set event character
+    \retval -2: USB device unavailable
 */
 int ftdi_set_event_char(struct ftdi_context *ftdi,
                         unsigned char eventch, unsigned char enable)
 {
     unsigned short usb_val;
 
+    if (ftdi == NULL || ftdi->usb_dev == NULL)
+        ftdi_error_return(-2, "USB device unavailable");
+
     usb_val = eventch;
     if (enable)
         usb_val |= 1 << 8;
@@ -1670,12 +2061,16 @@ int ftdi_set_event_char(struct ftdi_context *ftdi,
 
     \retval  0: all fine
     \retval -1: unable to set error character
+    \retval -2: USB device unavailable
 */
 int ftdi_set_error_char(struct ftdi_context *ftdi,
                         unsigned char errorch, unsigned char enable)
 {
     unsigned short usb_val;
 
+    if (ftdi == NULL || ftdi->usb_dev == NULL)
+        ftdi_error_return(-2, "USB device unavailable");
+
     usb_val = errorch;
     if (enable)
         usb_val |= 1 << 8;
@@ -1696,6 +2091,9 @@ int ftdi_set_error_char(struct ftdi_context *ftdi,
 */
 void ftdi_eeprom_setsize(struct ftdi_context *ftdi, struct ftdi_eeprom *eeprom, int size)
 {
+    if (ftdi == NULL)
+        return;
+
     ftdi->eeprom_size=size;
     eeprom->size=size;
 }
@@ -1707,6 +2105,9 @@ void ftdi_eeprom_setsize(struct ftdi_context *ftdi, struct ftdi_eeprom *eeprom,
 */
 void ftdi_eeprom_initdefaults(struct ftdi_eeprom *eeprom)
 {
+    if (eeprom == NULL)
+        return;
+
     eeprom->vendor_id = 0x0403;
     eeprom->product_id = 0x6001;
 
@@ -1731,14 +2132,15 @@ void ftdi_eeprom_initdefaults(struct ftdi_eeprom *eeprom)
 }
 
 /**
-   Build binary output from ftdi_eeprom structure.
-   Output is suitable for ftdi_write_eeprom().
+    Build binary output from ftdi_eeprom structure.
+    Output is suitable for ftdi_write_eeprom().
 
-   \param eeprom Pointer to ftdi_eeprom
-   \param output Buffer of 128 bytes to store eeprom image to
+    \param eeprom Pointer to ftdi_eeprom
+    \param output Buffer of 128 bytes to store eeprom image to
 
-   \retval >0: used eeprom size
-   \retval -1: eeprom size (128 bytes) exceeded by custom strings
+    \retval >0: used eeprom size
+    \retval -1: eeprom size (128 bytes) exceeded by custom strings
+    \retval -2: Invalid eeprom pointer
 */
 int ftdi_eeprom_build(struct ftdi_eeprom *eeprom, unsigned char *output)
 {
@@ -1747,6 +2149,9 @@ int ftdi_eeprom_build(struct ftdi_eeprom *eeprom, unsigned char *output)
     unsigned char manufacturer_size = 0, product_size = 0, serial_size = 0;
     int size_check;
 
+    if (eeprom == NULL)
+        return -2;
+
     if (eeprom->manufacturer != NULL)
         manufacturer_size = strlen(eeprom->manufacturer);
     if (eeprom->product != NULL)
@@ -1909,7 +2314,7 @@ int ftdi_eeprom_build(struct ftdi_eeprom *eeprom, unsigned char *output)
    Decode binary EEPROM image into an ftdi_eeprom structure.
 
    \param eeprom Pointer to ftdi_eeprom which will be filled in.
-   \param output Buffer of \a size bytes of raw eeprom data
+   \param buf Buffer of \a size bytes of raw eeprom data
    \param size size size of eeprom data in bytes
 
    \retval 0: all fine
@@ -1925,6 +2330,9 @@ int ftdi_eeprom_decode(struct ftdi_eeprom *eeprom, unsigned char *buf, int size)
     unsigned char manufacturer_size = 0, product_size = 0, serial_size = 0;
     int size_check;
     int eeprom_size = 128;
+
+    if (eeprom == NULL)
+        return -1;
 #if 0
     size_check = eeprom->size;
     size_check -= 28; // 28 are always in use (fixed)
@@ -1954,7 +2362,8 @@ int ftdi_eeprom_decode(struct ftdi_eeprom *eeprom, unsigned char *buf, int size)
     // Addr 04: Product ID
     eeprom->product_id = buf[0x04] + (buf[0x05] << 8);
 
-    switch (buf[0x06] + (buf[0x07]<<8))
+    value = buf[0x06] + (buf[0x07]<<8);
+    switch (value)
     {
         case 0x0400:
             eeprom->BM_type_chip = 1;
@@ -2071,6 +2480,28 @@ int ftdi_eeprom_decode(struct ftdi_eeprom *eeprom, unsigned char *buf, int size)
 }
 
 /**
+    Read eeprom location
+
+    \param ftdi pointer to ftdi_context
+    \param eeprom_addr Address of eeprom location to be read
+    \param eeprom_val Pointer to store read eeprom location
+
+    \retval  0: all fine
+    \retval -1: read failed
+    \retval -2: USB device unavailable
+*/
+int ftdi_read_eeprom_location (struct ftdi_context *ftdi, int eeprom_addr, unsigned short *eeprom_val)
+{
+    if (ftdi == NULL || ftdi->usb_dev == NULL)
+        ftdi_error_return(-2, "USB device unavailable");
+
+    if (usb_control_msg(ftdi->usb_dev, FTDI_DEVICE_IN_REQTYPE, SIO_READ_EEPROM_REQUEST, 0, eeprom_addr, (char *)eeprom_val, 2, ftdi->usb_read_timeout) != 2)
+        ftdi_error_return(-1, "reading eeprom failed");
+
+    return 0;
+}
+
+/**
     Read eeprom
 
     \param ftdi pointer to ftdi_context
@@ -2078,11 +2509,15 @@ int ftdi_eeprom_decode(struct ftdi_eeprom *eeprom, unsigned char *buf, int size)
 
     \retval  0: all fine
     \retval -1: read failed
+    \retval -2: USB device unavailable
 */
 int ftdi_read_eeprom(struct ftdi_context *ftdi, unsigned char *eeprom)
 {
     int i;
 
+    if (ftdi == NULL || ftdi->usb_dev == NULL)
+        ftdi_error_return(-2, "USB device unavailable");
+
     for (i = 0; i < ftdi->eeprom_size/2; i++)
     {
         if (usb_control_msg(ftdi->usb_dev, FTDI_DEVICE_IN_REQTYPE, SIO_READ_EEPROM_REQUEST, 0, i, eeprom+(i*2), 2, ftdi->usb_read_timeout) != 2)
@@ -2117,11 +2552,15 @@ static unsigned char ftdi_read_chipid_shift(unsigned char value)
 
     \retval  0: all fine
     \retval -1: read failed
+    \retval -2: USB device unavailable
 */
 int ftdi_read_chipid(struct ftdi_context *ftdi, unsigned int *chipid)
 {
     unsigned int a = 0, b = 0;
 
+    if (ftdi == NULL || ftdi->usb_dev == NULL)
+        ftdi_error_return(-2, "USB device unavailable");
+
     if (usb_control_msg(ftdi->usb_dev, FTDI_DEVICE_IN_REQTYPE, SIO_READ_EEPROM_REQUEST, 0, 0x43, (char *)&a, 2, ftdi->usb_read_timeout) == 2)
     {
         a = a << 8 | a >> 8;
@@ -2140,20 +2579,25 @@ int ftdi_read_chipid(struct ftdi_context *ftdi, unsigned int *chipid)
 }
 
 /**
-   Guesses size of eeprom by reading eeprom and comparing halves - will not work with blank eeprom
-   Call this function then do a write then call again to see if size changes, if so write again.
+    Guesses size of eeprom by reading eeprom and comparing halves - will not work with blank eeprom
+    Call this function then do a write then call again to see if size changes, if so write again.
 
-   \param ftdi pointer to ftdi_context
-   \param eeprom Pointer to store eeprom into
-   \param maxsize the size of the buffer to read into
+    \param ftdi pointer to ftdi_context
+    \param eeprom Pointer to store eeprom into
+    \param maxsize the size of the buffer to read into
 
-   \retval size of eeprom
+    \retval -1: eeprom read failed
+    \retval -2: USB device unavailable
+    \retval >=0: size of eeprom
 */
 int ftdi_read_eeprom_getsize(struct ftdi_context *ftdi, unsigned char *eeprom, int maxsize)
 {
     int i=0,j,minsize=32;
     int size=minsize;
 
+    if (ftdi == NULL || ftdi->usb_dev == NULL)
+        ftdi_error_return(-2, "USB device unavailable");
+
     do
     {
         for (j = 0; i < maxsize/2 && j<size; j++)
@@ -2161,7 +2605,7 @@ int ftdi_read_eeprom_getsize(struct ftdi_context *ftdi, unsigned char *eeprom, i
             if (usb_control_msg(ftdi->usb_dev, FTDI_DEVICE_IN_REQTYPE,
                                 SIO_READ_EEPROM_REQUEST, 0, i,
                                 eeprom+(i*2), 2, ftdi->usb_read_timeout) != 2)
-                ftdi_error_return(-1, "reading eeprom failed");
+                ftdi_error_return(-1, "eeprom read failed");
             i++;
         }
         size*=2;
@@ -2172,6 +2616,30 @@ int ftdi_read_eeprom_getsize(struct ftdi_context *ftdi, unsigned char *eeprom, i
 }
 
 /**
+    Write eeprom location
+
+    \param ftdi pointer to ftdi_context
+    \param eeprom_addr Address of eeprom location to be written
+    \param eeprom_val Value to be written
+
+    \retval  0: all fine
+    \retval -1: read failed
+    \retval -2: USB device unavailable
+*/
+int ftdi_write_eeprom_location(struct ftdi_context *ftdi, int eeprom_addr, unsigned short eeprom_val)
+{
+    if (ftdi == NULL || ftdi->usb_dev == NULL)
+        ftdi_error_return(-2, "USB device unavailable");
+
+    if (usb_control_msg(ftdi->usb_dev, FTDI_DEVICE_OUT_REQTYPE,
+                                    SIO_WRITE_EEPROM_REQUEST, eeprom_val, eeprom_addr,
+                                    NULL, 0, ftdi->usb_write_timeout) != 0)
+        ftdi_error_return(-1, "unable to write eeprom");
+
+    return 0;
+}
+
+/**
     Write eeprom
 
     \param ftdi pointer to ftdi_context
@@ -2179,16 +2647,23 @@ int ftdi_read_eeprom_getsize(struct ftdi_context *ftdi, unsigned char *eeprom, i
 
     \retval  0: all fine
     \retval -1: read failed
+    \retval -2: USB device unavailable
 */
 int ftdi_write_eeprom(struct ftdi_context *ftdi, unsigned char *eeprom)
 {
     unsigned short usb_val, status;
-    int i;
+    int i, ret;
+
+    if (ftdi == NULL || ftdi->usb_dev == NULL)
+        ftdi_error_return(-2, "USB device unavailable");
 
     /* These commands were traced while running MProg */
-    ftdi_usb_reset(ftdi);
-    ftdi_poll_modem_status(ftdi, &status);
-    ftdi_set_latency_timer(ftdi, 0x77);
+    if ((ret = ftdi_usb_reset(ftdi)) != 0)
+        return ret;
+    if ((ret = ftdi_poll_modem_status(ftdi, &status)) != 0)
+        return ret;
+    if ((ret = ftdi_set_latency_timer(ftdi, 0x77)) != 0)
+        return ret;
 
     for (i = 0; i < ftdi->eeprom_size/2; i++)
     {
@@ -2212,9 +2687,13 @@ int ftdi_write_eeprom(struct ftdi_context *ftdi, unsigned char *eeprom)
 
     \retval  0: all fine
     \retval -1: erase failed
+    \retval -2: USB device unavailable
 */
 int ftdi_erase_eeprom(struct ftdi_context *ftdi)
 {
+    if (ftdi == NULL || ftdi->usb_dev == NULL)
+        ftdi_error_return(-2, "USB device unavailable");
+
     if (usb_control_msg(ftdi->usb_dev, FTDI_DEVICE_OUT_REQTYPE, SIO_ERASE_EEPROM_REQUEST, 0, 0, NULL, 0, ftdi->usb_write_timeout) != 0)
         ftdi_error_return(-1, "unable to erase eeprom");
 
@@ -2230,6 +2709,9 @@ int ftdi_erase_eeprom(struct ftdi_context *ftdi)
 */
 char *ftdi_get_error_string (struct ftdi_context *ftdi)
 {
+    if (ftdi == NULL)
+        return "";
+
     return ftdi->error_str;
 }