Add ftdi_eeprom_free() to free allocated memory in eeprom
[libftdi] / src / ftdi.c
index df25d44..7f7e5a0 100644 (file)
         return code;                       \
    } while(0);
 
+#define ftdi_error_return_free_device_list(code, str, devs) do {    \
+        libusb_free_device_list(devs,1);   \
+        ftdi->error_str = str;             \
+        return code;                       \
+   } while(0);
+
 
 /**
     Internal function to close usb device pointer.
@@ -135,7 +141,7 @@ struct ftdi_context *ftdi_new(void)
 */
 int ftdi_set_interface(struct ftdi_context *ftdi, enum ftdi_interface interface)
 {
-    if (ftdi == NULL || ftdi->usb_dev == NULL)
+    if (ftdi == NULL)
         ftdi_error_return(-2, "USB device unavailable");
 
     switch (interface)
@@ -185,6 +191,7 @@ void ftdi_deinit(struct ftdi_context *ftdi)
         free(ftdi->readbuffer);
         ftdi->readbuffer = NULL;
     }
+    libusb_exit(NULL);
 }
 
 /**
@@ -442,7 +449,7 @@ int ftdi_usb_open_dev(struct ftdi_context *ftdi, libusb_device *dev)
 {
     struct libusb_device_descriptor desc;
     struct libusb_config_descriptor *config0;
-    int cfg, cfg0;
+    int cfg, cfg0, detach_errno = 0;
 
     if (ftdi == NULL)
         ftdi_error_return(-8, "ftdi context invalid");
@@ -458,22 +465,17 @@ int ftdi_usb_open_dev(struct ftdi_context *ftdi, libusb_device *dev)
     cfg0 = config0->bConfigurationValue;
     libusb_free_config_descriptor (config0);
 
-#ifdef LIBUSB_HAS_GET_DRIVER_NP
     // Try to detach ftdi_sio kernel module.
-    // Returns ENODATA if driver is not loaded.
     //
     // The return code is kept in a separate variable and only parsed
     // if usb_set_configuration() or usb_claim_interface() fails as the
     // detach operation might be denied and everything still works fine.
     // Likely scenario is a static ftdi_sio kernel module.
-    ret = libusb_detach_kernel_driver(ftdi->usb_dev, ftdi->interface);
-    if (ret < 0 && ret != LIBUSB_ERROR_NOT_FOUND)
-        ftdi_error_return(-11, "libusb_detach_kernel_driver () failed");
-#endif
+    if (libusb_detach_kernel_driver(ftdi->usb_dev, ftdi->interface) !=0)
+        detach_errno = errno;
 
     if (libusb_get_configuration (ftdi->usb_dev, &cfg) < 0)
         ftdi_error_return(-12, "libusb_get_configuration () failed");
-
     // 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)
@@ -482,14 +484,28 @@ int ftdi_usb_open_dev(struct ftdi_context *ftdi, libusb_device *dev)
         if (libusb_set_configuration(ftdi->usb_dev, cfg0) < 0)
         {
             ftdi_usb_close_internal (ftdi);
-            ftdi_error_return(-3, "unable to set usb configuration. Make sure ftdi_sio is unloaded!");
+            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 driver is not in use");
+            }
         }
     }
 
     if (libusb_claim_interface(ftdi->usb_dev, ftdi->interface) < 0)
     {
         ftdi_usb_close_internal (ftdi);
-        ftdi_error_return(-5, "unable to claim usb device. Make sure ftdi_sio is unloaded!");
+        if(detach_errno == EPERM)
+        {
+            ftdi_error_return(-8, "inappropriate permissions on device!");
+        }
+        else
+        {
+            ftdi_error_return(-5, "unable to claim usb device. Make sure the default FTDI driver is not in use");
+        }
     }
 
     if (ftdi_usb_reset (ftdi) != 0)
@@ -616,30 +632,31 @@ int ftdi_usb_open_desc_index(struct ftdi_context *ftdi, int vendor, int product,
     if (libusb_init(NULL) < 0)
         ftdi_error_return(-11, "libusb_init() failed");
 
-    if (libusb_get_device_list(NULL, &devs) < 0)
-        ftdi_error_return(-12, "libusb_get_device_list() failed");
-
     if (ftdi == NULL)
         ftdi_error_return(-11, "ftdi context invalid");
 
+    if (libusb_get_device_list(NULL, &devs) < 0)
+        ftdi_error_return(-12, "libusb_get_device_list() failed");
+
     while ((dev = devs[i++]) != NULL)
     {
         struct libusb_device_descriptor desc;
+        int res;
 
         if (libusb_get_device_descriptor(dev, &desc) < 0)
-            ftdi_error_return(-13, "libusb_get_device_descriptor() failed");
+            ftdi_error_return_free_device_list(-13, "libusb_get_device_descriptor() failed", devs);
 
         if (desc.idVendor == vendor && desc.idProduct == product)
         {
             if (libusb_open(dev, &ftdi->usb_dev) < 0)
-                ftdi_error_return(-4, "usb_open() failed");
+                ftdi_error_return_free_device_list(-4, "usb_open() failed", devs);
 
             if (description != NULL)
             {
                 if (libusb_get_string_descriptor_ascii(ftdi->usb_dev, desc.iProduct, (unsigned char *)string, sizeof(string)) < 0)
                 {
                     libusb_close (ftdi->usb_dev);
-                    ftdi_error_return(-8, "unable to fetch product description");
+                    ftdi_error_return_free_device_list(-8, "unable to fetch product description", devs);
                 }
                 if (strncmp(string, description, sizeof(string)) != 0)
                 {
@@ -652,7 +669,7 @@ int ftdi_usb_open_desc_index(struct ftdi_context *ftdi, int vendor, int product,
                 if (libusb_get_string_descriptor_ascii(ftdi->usb_dev, desc.iSerialNumber, (unsigned char *)string, sizeof(string)) < 0)
                 {
                     ftdi_usb_close_internal (ftdi);
-                    ftdi_error_return(-9, "unable to fetch serial number");
+                    ftdi_error_return_free_device_list(-9, "unable to fetch serial number", devs);
                 }
                 if (strncmp(string, serial, sizeof(string)) != 0)
                 {
@@ -669,12 +686,14 @@ int ftdi_usb_open_desc_index(struct ftdi_context *ftdi, int vendor, int product,
                     continue;
                 }
 
-            return ftdi_usb_open_dev(ftdi, dev);
+            res = ftdi_usb_open_dev(ftdi, dev);
+            libusb_free_device_list(devs,1);
+            return res;
         }
     }
 
     // device not found
-    ftdi_error_return(-3, "device not found");
+    ftdi_error_return_free_device_list(-3, "device not found", devs);
 }
 
 /**
@@ -727,17 +746,22 @@ int ftdi_usb_open_string(struct ftdi_context *ftdi, const char* description)
 
         /* XXX: This doesn't handle symlinks/odd paths/etc... */
         if (sscanf (description + 2, "%u/%u", &bus_number, &device_address) != 2)
-           ftdi_error_return(-11, "illegal description format");
+           ftdi_error_return_free_device_list(-11, "illegal description format", devs);
 
        while ((dev = devs[i++]) != NULL)
         {
+            int ret;
            if (bus_number == libusb_get_bus_number (dev)
                && device_address == libusb_get_device_address (dev))
-                return ftdi_usb_open_dev(ftdi, dev);
+            {
+                ret = ftdi_usb_open_dev(ftdi, dev);
+                libusb_free_device_list(devs,1);
+                return ret;
+            }
         }
 
         // device not found
-        ftdi_error_return(-3, "device not found");
+        ftdi_error_return_free_device_list(-3, "device not found", devs);
     }
     else if (description[0] == 'i' || description[0] == 's')
     {
@@ -2181,6 +2205,27 @@ void ftdi_eeprom_initdefaults(struct ftdi_eeprom *eeprom)
 }
 
 /**
+    Frees allocated memory in eeprom.
+
+    \param eeprom Pointer to ftdi_eeprom
+*/
+void ftdi_eeprom_free(struct ftdi_eeprom *eeprom)
+{
+    if (eeprom->manufacturer != 0) {
+        free(eeprom->manufacturer);
+        eeprom->manufacturer = 0;
+    }
+    if (eeprom->product != 0) {
+        free(eeprom->product);
+        eeprom->product = 0;
+    }
+    if (eeprom->serial != 0) {
+        free(eeprom->serial);
+        eeprom->serial = 0;
+    }
+}
+
+/**
     Build binary output from ftdi_eeprom structure.
     Output is suitable for ftdi_write_eeprom().