libftdi: (tomj) integrated ftdi_sio detach patch from Uwe Bonnes
[libftdi] / src / ftdi.c
index 5bfc27c..8b9dd05 100644 (file)
 
 #include <usb.h>
 #include <string.h>
+#include <errno.h>
 
 #include "ftdi.h"
 
 #define ftdi_error_return(code, str) do {  \
         ftdi->error_str = str;             \
         return code;                       \
-   } while(0);                 
+   } while(0);
 
 
 /* ftdi_init
@@ -120,13 +121,13 @@ void ftdi_set_usbdev (struct ftdi_context *ftdi, usb_dev_handle *usb)
     -2: usb_find_devices() failed
     -3: out of memory
 */
-int ftdi_usb_find_all(struct ftdi_context *ftdi, struct ftdi_device_list **devlist, int vendor, int product) 
+int ftdi_usb_find_all(struct ftdi_context *ftdi, struct ftdi_device_list **devlist, int vendor, int product)
 {
     struct ftdi_device_list **curdev;
     struct usb_bus *bus;
     struct usb_device *dev;
     int count = 0;
-    
+
     usb_init();
     if (usb_find_busses() < 0)
         ftdi_error_return(-1, "usb_find_busses() failed");
@@ -142,7 +143,7 @@ int ftdi_usb_find_all(struct ftdi_context *ftdi, struct ftdi_device_list **devli
                 *curdev = (struct ftdi_device_list*)malloc(sizeof(struct ftdi_device_list));
                 if (!*curdev)
                     ftdi_error_return(-3, "out of memory");
-                
+
                 (*curdev)->next = NULL;
                 (*curdev)->dev = dev;
 
@@ -151,7 +152,7 @@ int ftdi_usb_find_all(struct ftdi_context *ftdi, struct ftdi_device_list **devli
             }
         }
     }
-    
+
     return count;
 }
 
@@ -159,7 +160,7 @@ int ftdi_usb_find_all(struct ftdi_context *ftdi, struct ftdi_device_list **devli
 
    Frees a created device list.
 */
-void ftdi_list_free(struct ftdi_device_list **devlist) 
+void ftdi_list_free(struct ftdi_device_list **devlist)
 {
     struct ftdi_device_list **curdev;
     for (; *devlist == NULL; devlist = curdev) {
@@ -170,6 +171,72 @@ void ftdi_list_free(struct ftdi_device_list **devlist)
     devlist = NULL;
 }
 
+/* ftdi_usb_open_dev
+
+   Opens a ftdi device given by a usb_device.
+   
+   Return codes:
+     0: all fine
+    -4: unable to open device
+    -5: unable to claim device
+    -6: reset failed
+    -7: set baudrate failed
+*/
+int ftdi_usb_open_dev(struct ftdi_context *ftdi, struct usb_device *dev)
+{
+    int detach_errno = 0;
+    if (!(ftdi->usb_dev = usb_open(dev)))
+        ftdi_error_return(-4, "usb_open() failed");
+
+#ifdef LIBUSB_HAS_GET_DRIVER_NP
+    // Try to detach ftdi_sio kernel module
+    // Returns ENODATA if driver is not loaded
+    if (usb_detach_kernel_driver_np(ftdi->usb_dev, ftdi->interface) != 0 && errno != ENODATA)
+        detach_errno = errno;
+#endif
+
+    if (usb_claim_interface(ftdi->usb_dev, ftdi->interface) != 0) {
+        usb_close (ftdi->usb_dev);
+        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!");
+        }
+    }
+
+    if (ftdi_usb_reset (ftdi) != 0) {
+        usb_close (ftdi->usb_dev);
+        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
+            && dev->descriptor.iSerialNumber == 0))
+        ftdi->type = TYPE_BM;
+    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;
+    }
+
+    ftdi_error_return(0, "all fine");
+}
+
+/* ftdi_usb_open
+   
+   Opens the first device with a given vendor and product ids.
+   
+   Return codes:
+   See ftdi_usb_open_desc()
+*/  
 int ftdi_usb_open(struct ftdi_context *ftdi, int vendor, int product)
 {
     return ftdi_usb_open_desc(ftdi, vendor, product, NULL, NULL);
@@ -239,7 +306,7 @@ int ftdi_usb_open_desc(struct ftdi_context *ftdi, int vendor, int product,
 
                 if (usb_close (ftdi->usb_dev) != 0)
                     ftdi_error_return(-10, "unable to close device");
-                
+
                 return ftdi_usb_open_dev(ftdi, dev);
             }
         }
@@ -408,7 +475,7 @@ static int ftdi_convert_baudrate(int baudrate, struct ftdi_context *ftdi,
     if(ftdi->type == TYPE_2232C) {
         *index = (unsigned short)(encoded_divisor >> 8);
         *index &= 0xFF00;
-        *index |= ftdi->interface;
+        *index |= ftdi->index;
     }
     else
         *index = (unsigned short)(encoded_divisor >> 16);
@@ -464,7 +531,7 @@ int ftdi_set_baudrate(struct ftdi_context *ftdi, int baudrate)
     -1: Setting line property failed
 */
 int ftdi_set_line_property(struct ftdi_context *ftdi, enum ftdi_bits_type bits,
-                    enum ftdi_stopbits_type sbit, enum ftdi_parity_type parity)
+                           enum ftdi_stopbits_type sbit, enum ftdi_parity_type parity)
 {
     unsigned short value = bits;
 
@@ -485,7 +552,7 @@ int ftdi_set_line_property(struct ftdi_context *ftdi, enum ftdi_bits_type bits,
         value |= (0x04 << 8);
         break;
     }
-    
+
     switch(sbit) {
     case STOP_BIT_1:
         value |= (0x00 << 11);
@@ -497,10 +564,10 @@ int ftdi_set_line_property(struct ftdi_context *ftdi, enum ftdi_bits_type bits,
         value |= (0x02 << 11);
         break;
     }
-    
+
     if (usb_control_msg(ftdi->usb_dev, 0x40, 0x04, value, ftdi->index, NULL, 0, ftdi->usb_write_timeout) != 0)
         ftdi_error_return (-1, "Setting new line property failed");
-    
+
     return 0;
 }
 
@@ -968,3 +1035,48 @@ char *ftdi_get_error_string (struct ftdi_context *ftdi)
 {
     return ftdi->error_str;
 }
+
+
+int ftdi_setflowctrl(struct ftdi_context *ftdi, int flowctrl)
+{
+    if (usb_control_msg(ftdi->usb_dev, SIO_SET_FLOW_CTRL_REQUEST_TYPE,
+                        SIO_SET_FLOW_CTRL_REQUEST, 0, (flowctrl | ftdi->interface),
+                        NULL, 0, ftdi->usb_write_timeout) != 0)
+        ftdi_error_return(-1, "set flow control failed");
+
+    return 0;
+}
+
+int ftdi_setdtr(struct ftdi_context *ftdi, int state)
+{
+    unsigned short usb_val;
+
+    if (state)
+        usb_val = SIO_SET_DTR_HIGH;
+    else
+        usb_val = SIO_SET_DTR_LOW;
+
+    if (usb_control_msg(ftdi->usb_dev, SIO_SET_MODEM_CTRL_REQUEST_TYPE,
+                        SIO_SET_MODEM_CTRL_REQUEST, usb_val, ftdi->interface,
+                        NULL, 0, ftdi->usb_write_timeout) != 0)
+        ftdi_error_return(-1, "set dtr failed");
+
+    return 0;
+}
+
+int ftdi_setrts(struct ftdi_context *ftdi, int state)
+{
+    unsigned short usb_val;
+
+    if (state)
+        usb_val = SIO_SET_RTS_HIGH;
+    else
+        usb_val = SIO_SET_RTS_LOW;
+
+    if (usb_control_msg(ftdi->usb_dev, SIO_SET_MODEM_CTRL_REQUEST_TYPE,
+                        SIO_SET_MODEM_CTRL_REQUEST, usb_val, ftdi->interface,
+                        NULL, 0, ftdi->usb_write_timeout) != 0)
+        ftdi_error_return(-1, "set of rts failed");
+
+    return 0;
+}