There were a few (new) usbdevfs capabilities which libusbx could not
discover in any other way then checking the kernel version. There are 3
problems with this:
1) It is just not very pretty
2) Given the tendency of enterprise distros to backport stuff it is not
   reliable
3) Some of these features turn out to not work with certain host controllers,
   making depending on them based on the kernel version not a good idea

Therefor a new USBDEVFS_GET_CAPABILITIES ioctl has been added to the kernel
to offer a better way to find out a device's capabilities (technically
the capabilities of the host controller to which the device is attached,
but that does not matter).

Signed-off-by: Hans de Goede <hdego...@redhat.com>
---
 libusb/os/linux_usbfs.c | 24 ++++++++++++++++++++----
 libusb/os/linux_usbfs.h | 10 ++++++++++
 2 files changed, 30 insertions(+), 4 deletions(-)

diff --git a/libusb/os/linux_usbfs.c b/libusb/os/linux_usbfs.c
index dad7536..df74c3e 100644
--- a/libusb/os/linux_usbfs.c
+++ b/libusb/os/linux_usbfs.c
@@ -116,6 +116,7 @@ struct linux_device_priv {
 
 struct linux_device_handle_priv {
        int fd;
+       __u32 caps;
 };
 
 enum reap_action {
@@ -1284,6 +1285,7 @@ static int op_open(struct libusb_device_handle *handle)
 {
        struct linux_device_handle_priv *hpriv = _device_handle_priv(handle);
        char filename[PATH_MAX];
+       int r;
 
        _get_usbfs_path(handle->dev, filename);
        usbi_dbg("opening %s", filename);
@@ -1306,6 +1308,20 @@ static int op_open(struct libusb_device_handle *handle)
                }
        }
 
+       r = ioctl(hpriv->fd, IOCTL_USBFS_GET_CAPABILITIES, &hpriv->caps);
+       if (r < 0) {
+               if (errno == ENOTTY)
+                       usbi_dbg("%s: getcap not available", filename);
+               else
+                       usbi_err(HANDLE_CTX(handle),
+                                "%s: getcap failed (%d)", filename, errno);
+               hpriv->caps = 0;
+               if (supports_flag_zero_packet)
+                       hpriv->caps |= USBFS_CAP_ZERO_PACKET;
+               if (supports_flag_bulk_continuation)
+                       hpriv->caps |= USBFS_CAP_BULK_CONTINUATION;
+       }
+
        return usbi_add_pollfd(HANDLE_CTX(handle), hpriv->fd, POLLOUT);
 }
 
@@ -1659,8 +1675,8 @@ static int submit_bulk_transfer(struct usbi_transfer 
*itransfer,
        if (tpriv->urbs)
                return LIBUSB_ERROR_BUSY;
 
-       if (is_out && transfer->flags & LIBUSB_TRANSFER_ADD_ZERO_PACKET &&
-           !supports_flag_zero_packet)
+       if (is_out && (transfer->flags & LIBUSB_TRANSFER_ADD_ZERO_PACKET) &&
+                       !(dpriv->caps & USBFS_CAP_ZERO_PACKET))
                return LIBUSB_ERROR_NOT_SUPPORTED;
 
        /* usbfs places a 16kb limit on bulk URBs. we divide up larger requests
@@ -1694,7 +1710,7 @@ static int submit_bulk_transfer(struct usbi_transfer 
*itransfer,
                urb->type = urb_type;
                urb->endpoint = transfer->endpoint;
                urb->buffer = transfer->buffer + (i * MAX_BULK_BUFFER_LENGTH);
-               if (supports_flag_bulk_continuation && !is_out)
+               if ((dpriv->caps & USBFS_CAP_BULK_CONTINUATION) && !is_out)
                        urb->flags = USBFS_URB_SHORT_NOT_OK;
                if (i == num_urbs - 1 && last_urb_partial)
                        urb->buffer_length = transfer->length % 
MAX_BULK_BUFFER_LENGTH;
@@ -1703,7 +1719,7 @@ static int submit_bulk_transfer(struct usbi_transfer 
*itransfer,
                else
                        urb->buffer_length = MAX_BULK_BUFFER_LENGTH;
 
-               if (i > 0 && supports_flag_bulk_continuation)
+               if (i > 0 && (dpriv->caps & USBFS_CAP_BULK_CONTINUATION))
                        urb->flags |= USBFS_URB_BULK_CONTINUATION;
 
                /* we have already checked that the flag is supported */
diff --git a/libusb/os/linux_usbfs.h b/libusb/os/linux_usbfs.h
index 3a80261..593e736 100644
--- a/libusb/os/linux_usbfs.h
+++ b/libusb/os/linux_usbfs.h
@@ -21,6 +21,8 @@
 #ifndef LIBUSB_USBFS_H
 #define LIBUSB_USBFS_H
 
+#include <linux/types.h>
+
 #define SYSFS_DEVICE_PATH "/sys/bus/usb/devices"
 
 struct usbfs_ctrltransfer {
@@ -116,6 +118,11 @@ struct usbfs_hub_portinfo {
        unsigned char port[127];        /* port to device num mapping */
 };
 
+#define USBFS_CAP_ZERO_PACKET          0x01
+#define USBFS_CAP_BULK_CONTINUATION    0x02
+#define USBFS_CAP_NO_PACKET_SIZE_LIM   0x04
+#define USBFS_CAP_BULK_SCATTER_GATHER  0x08
+
 #define IOCTL_USBFS_CONTROL    _IOWR('U', 0, struct usbfs_ctrltransfer)
 #define IOCTL_USBFS_BULK               _IOWR('U', 2, struct usbfs_bulktransfer)
 #define IOCTL_USBFS_RESETEP    _IOR('U', 3, unsigned int)
@@ -135,5 +142,8 @@ struct usbfs_hub_portinfo {
 #define IOCTL_USBFS_CLEAR_HALT _IOR('U', 21, unsigned int)
 #define IOCTL_USBFS_DISCONNECT _IO('U', 22)
 #define IOCTL_USBFS_CONNECT    _IO('U', 23)
+#define IOCTL_USBFS_CLAIM_PORT _IOR('U', 24, unsigned int)
+#define IOCTL_USBFS_RELEASE_PORT       _IOR('U', 25, unsigned int)
+#define IOCTL_USBFS_GET_CAPABILITIES   _IOR('U', 26, __u32)
 
 #endif
-- 
1.7.11.4


------------------------------------------------------------------------------
Live Security Virtual Conference
Exclusive live event will cover all the ways today's security and 
threat landscape has changed and how IT managers can respond. Discussions 
will include endpoint security, mobile security and the latest in malware 
threats. http://www.accelacomm.com/jaw/sfrnl04242012/114/50122263/
_______________________________________________
libusbx-devel mailing list
libusbx-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/libusbx-devel

Reply via email to