summaryrefslogtreecommitdiff
path: root/gusb
diff options
context:
space:
mode:
authorRichard Hughes <richard@hughsie.com>2011-08-16 17:06:54 +0100
committerRichard Hughes <richard@hughsie.com>2011-08-16 17:06:56 +0100
commitfb561b2fe3ed792b3e054bbed6b723900a609e7e (patch)
treeaf6d39ffb872af0f96c923b466abddb803ee9dda /gusb
parent195f12cf7bd688f02ab47f5a8aa9ebd62fefa281 (diff)
downloadgusb-fb561b2fe3ed792b3e054bbed6b723900a609e7e.tar.gz
Add functionality to GUsbDevice to do sync bulk, control and interrupt transfers
We'll be also adding _async() versions too which use a GUsbSource, and hopefully then we can also do something sane with GCancellable.
Diffstat (limited to 'gusb')
-rw-r--r--gusb/gusb-device.c193
-rw-r--r--gusb/gusb-device.h67
-rw-r--r--gusb/gusb-self-test.c99
3 files changed, 359 insertions, 0 deletions
diff --git a/gusb/gusb-device.c b/gusb/gusb-device.c
index 95d0768..f4e3734 100644
--- a/gusb/gusb-device.c
+++ b/gusb/gusb-device.c
@@ -278,6 +278,199 @@ out:
}
/**
+ * g_usb_device_control_transfer:
+ * @device: a #GUsbDevice
+ * @request_type: the request type field for the setup packet
+ * @request: the request field for the setup packet
+ * @value: the value field for the setup packet
+ * @index: the index field for the setup packet
+ * @data: a suitably-sized data buffer for either input or output
+ * @length: the length field for the setup packet.
+ * @actual_length: the actual number of bytes sent, or %NULL
+ * @timeout: timeout timeout (in millseconds) that this function should wait
+ * before giving up due to no response being received. For an unlimited
+ * timeout, use 0.
+ * @cancellable: a #GCancellable, or %NULL
+ * @error: a #GError, or %NULL
+ *
+ * Perform a USB control transfer.
+ *
+ * Warning: this function is syncronous, and cannot be cancelled.
+ *
+ * Return value: %TRUE on success
+ **/
+gboolean
+g_usb_device_control_transfer (GUsbDevice *device,
+ GUsbDeviceDirection direction,
+ GUsbDeviceRequestType request_type,
+ GUsbDeviceRecipient recipient,
+ guint8 request,
+ guint16 value,
+ guint16 index,
+ guint8 *data,
+ gsize length,
+ gsize *actual_length,
+ guint timeout,
+ GCancellable *cancellable,
+ GError **error)
+{
+ gboolean ret = TRUE;
+ gint rc;
+ guint8 request_type_raw = 0;
+
+ /* munge back to flags */
+ if (direction == G_USB_DEVICE_DIRECTION_DEVICE_TO_HOST)
+ request_type_raw |= 0x80;
+ request_type_raw |= (request_type << 5);
+ request_type_raw |= recipient;
+
+ if (device->priv->handle == NULL) {
+ ret = FALSE;
+ g_set_error_literal (error,
+ G_USB_DEVICE_ERROR,
+ G_USB_DEVICE_ERROR_NOT_OPEN,
+ "The device has not been opened");
+ goto out;
+ }
+
+ /* TODO: setup an async transfer so we can cancel it */
+ rc = libusb_control_transfer (device->priv->handle,
+ request_type_raw,
+ request,
+ value,
+ index,
+ data,
+ length,
+ timeout);
+ if (rc < 0) {
+ ret = g_usb_device_libusb_error_to_gerror (device, rc, error);
+ goto out;
+ }
+ if (actual_length != NULL)
+ *actual_length = rc;
+out:
+ return ret;
+}
+
+/**
+ * g_usb_device_bulk_transfer:
+ * @device: a #GUsbDevice
+ * @endpoint: the address of a valid endpoint to communicate with
+ * @data: a suitably-sized data buffer for either input or output
+ * @length: the length field for the setup packet.
+ * @actual_length: the actual number of bytes sent, or %NULL
+ * @timeout: timeout timeout (in millseconds) that this function should wait
+ * before giving up due to no response being received. For an unlimited
+ * timeout, use 0.
+ * @cancellable: a #GCancellable, or %NULL
+ * @error: a #GError, or %NULL
+ *
+ * Perform a USB bulk transfer.
+ *
+ * Warning: this function is syncronous, and cannot be cancelled.
+ *
+ * Return value: %TRUE on success
+ **/
+gboolean
+g_usb_device_bulk_transfer (GUsbDevice *device,
+ guint8 endpoint,
+ guint8 *data,
+ gsize length,
+ gsize *actual_length,
+ guint timeout,
+ GCancellable *cancellable,
+ GError **error)
+{
+ gboolean ret = TRUE;
+ gint rc;
+ gint transferred;
+
+ if (device->priv->handle == NULL) {
+ ret = FALSE;
+ g_set_error_literal (error,
+ G_USB_DEVICE_ERROR,
+ G_USB_DEVICE_ERROR_NOT_OPEN,
+ "The device has not been opened");
+ goto out;
+ }
+
+ /* TODO: setup an async transfer so we can cancel it */
+ rc = libusb_bulk_transfer (device->priv->handle,
+ endpoint,
+ data,
+ length,
+ &transferred,
+ timeout);
+ if (rc < 0) {
+ ret = g_usb_device_libusb_error_to_gerror (device, rc, error);
+ goto out;
+ }
+ if (actual_length != NULL)
+ *actual_length = transferred;
+out:
+ return ret;
+}
+
+/**
+ * g_usb_device_interrupt_transfer:
+ * @device: a #GUsbDevice
+ * @endpoint: the address of a valid endpoint to communicate with
+ * @data: a suitably-sized data buffer for either input or output
+ * @length: the length field for the setup packet.
+ * @actual_length: the actual number of bytes sent, or %NULL
+ * @timeout: timeout timeout (in millseconds) that this function should wait
+ * before giving up due to no response being received. For an unlimited
+ * timeout, use 0.
+ * @cancellable: a #GCancellable, or %NULL
+ * @error: a #GError, or %NULL
+ *
+ * Perform a USB interrupt transfer.
+ *
+ * Warning: this function is syncronous, and cannot be cancelled.
+ *
+ * Return value: %TRUE on success
+ **/
+gboolean
+g_usb_device_interrupt_transfer (GUsbDevice *device,
+ guint8 endpoint,
+ guint8 *data,
+ gsize length,
+ gsize *actual_length,
+ guint timeout,
+ GCancellable *cancellable,
+ GError **error)
+{
+ gboolean ret = TRUE;
+ gint rc;
+ gint transferred;
+
+ if (device->priv->handle == NULL) {
+ ret = FALSE;
+ g_set_error_literal (error,
+ G_USB_DEVICE_ERROR,
+ G_USB_DEVICE_ERROR_NOT_OPEN,
+ "The device has not been opened");
+ goto out;
+ }
+
+ /* TODO: setup an async transfer so we can cancel it */
+ rc = libusb_interrupt_transfer (device->priv->handle,
+ endpoint,
+ data,
+ length,
+ &transferred,
+ timeout);
+ if (rc < 0) {
+ ret = g_usb_device_libusb_error_to_gerror (device, rc, error);
+ goto out;
+ }
+ if (actual_length != NULL)
+ *actual_length = transferred;
+out:
+ return ret;
+}
+
+/**
* usb_device_set_property:
**/
static void
diff --git a/gusb/gusb-device.h b/gusb/gusb-device.h
index f296a42..d1e9216 100644
--- a/gusb/gusb-device.h
+++ b/gusb/gusb-device.h
@@ -37,6 +37,40 @@ typedef struct _GUsbDevice GUsbDevice;
typedef struct _GUsbDeviceClass GUsbDeviceClass;
/**
+ * GUsbDeviceDirection:
+ *
+ * The message direction.
+ **/
+typedef enum {
+ G_USB_DEVICE_DIRECTION_DEVICE_TO_HOST, /* IN */
+ G_USB_DEVICE_DIRECTION_HOST_TO_DEVICE /* OUT */
+} GUsbDeviceDirection;
+
+/**
+ * GUsbDeviceRequestType:
+ *
+ * The message request type.
+ **/
+typedef enum {
+ G_USB_DEVICE_REQUEST_TYPE_STANDARD,
+ G_USB_DEVICE_REQUEST_TYPE_CLASS,
+ G_USB_DEVICE_REQUEST_TYPE_VENDOR,
+ G_USB_DEVICE_REQUEST_TYPE_RESERVED
+} GUsbDeviceRequestType;
+
+/**
+ * GUsbDeviceRecipient:
+ *
+ * The message recipient.
+ **/
+typedef enum {
+ G_USB_DEVICE_RECIPIENT_DEVICE,
+ G_USB_DEVICE_RECIPIENT_INTERFACE,
+ G_USB_DEVICE_RECIPIENT_ENDPOINT,
+ G_USB_DEVICE_RECIPIENT_OTHER
+} GUsbDeviceRecipient;
+
+/**
* GUsbDeviceError:
*
* The error code.
@@ -80,6 +114,39 @@ gboolean g_usb_device_open (GUsbDevice *device,
gboolean g_usb_device_close (GUsbDevice *device,
GError **error);
+/* sync -- TODO: use GCancellable and GUsbSource */
+gboolean g_usb_device_control_transfer (GUsbDevice *device,
+ GUsbDeviceDirection direction,
+ GUsbDeviceRequestType request_type,
+ GUsbDeviceRecipient recipient,
+ guint8 request,
+ guint16 value,
+ guint16 index,
+ guint8 *data,
+ gsize length,
+ gsize *actual_length,
+ guint timeout,
+ GCancellable *cancellable,
+ GError **error);
+
+gboolean g_usb_device_bulk_transfer (GUsbDevice *device,
+ guint8 endpoint,
+ guint8 *data,
+ gsize length,
+ gsize *actual_length,
+ guint timeout,
+ GCancellable *cancellable,
+ GError **error);
+
+gboolean g_usb_device_interrupt_transfer (GUsbDevice *device,
+ guint8 endpoint,
+ guint8 *data,
+ gsize length,
+ gsize *actual_length,
+ guint timeout,
+ GCancellable *cancellable,
+ GError **error);
+
G_END_DECLS
#endif /* __GUSB_DEVICE_H__ */
diff --git a/gusb/gusb-self-test.c b/gusb/gusb-self-test.c
index 5729d8d..0c09d23 100644
--- a/gusb/gusb-self-test.c
+++ b/gusb/gusb-self-test.c
@@ -178,6 +178,104 @@ gusb_device_list_func (void)
g_object_unref (ctx);
}
+static void
+gusb_device_huey_func (void)
+{
+ GUsbContext *ctx;
+ GUsbDeviceList *list;
+ GError *error = NULL;
+ GUsbDevice *device;
+ gboolean ret;
+ GCancellable *cancellable = NULL;
+ const gchar request[8] = { 0x0e, 'G', 'r', 'M', 'b', 'k', 'e', 'd' };
+
+ ctx = g_usb_context_new (&error);
+ g_assert_no_error (error);
+ g_assert (ctx != NULL);
+
+ g_usb_context_set_debug (ctx, G_LOG_LEVEL_ERROR);
+
+ list = g_usb_device_list_new (ctx);
+ g_assert (list != NULL);
+
+ /* coldplug, and get the huey */
+ g_usb_device_list_coldplug (list);
+ device = g_usb_device_list_find_by_vid_pid (list,
+ 0x0971,
+ 0x2005,
+ &error);
+ g_assert_no_error (error);
+ g_assert (device != NULL);
+
+ /* close not opened */
+ ret = g_usb_device_close (device, &error);
+ g_assert_error (error,
+ G_USB_DEVICE_ERROR,
+ G_USB_DEVICE_ERROR_NOT_OPEN);
+ g_assert (!ret);
+ g_clear_error (&error);
+
+ /* open */
+ ret = g_usb_device_open (device, 0x01, 0x00, cancellable, &error);
+ g_assert_no_error (error);
+ g_assert (ret);
+
+ /* open opened */
+ ret = g_usb_device_open (device, 0x01, 0x00, cancellable, &error);
+ g_assert_error (error,
+ G_USB_DEVICE_ERROR,
+ G_USB_DEVICE_ERROR_ALREADY_OPEN);
+ g_assert (!ret);
+ g_clear_error (&error);
+
+ /* do a request (unlock) */
+ ret = g_usb_device_control_transfer (device,
+ G_USB_DEVICE_DIRECTION_HOST_TO_DEVICE,
+ G_USB_DEVICE_REQUEST_TYPE_CLASS,
+ G_USB_DEVICE_RECIPIENT_INTERFACE,
+ 0x09, /* request */
+ 0x0200, /* value */
+ 0, /* index */
+ (guint8*) request,
+ 8,
+ NULL,
+ 2000,
+ cancellable,
+ &error);
+ g_assert_no_error (error);
+ g_assert (ret);
+
+ /* do a request we know is going to fail */
+ ret = g_usb_device_control_transfer (device,
+ G_USB_DEVICE_DIRECTION_DEVICE_TO_HOST,
+ G_USB_DEVICE_REQUEST_TYPE_CLASS,
+ G_USB_DEVICE_RECIPIENT_INTERFACE,
+ 0x09, /* request */
+ 0x0200, /* value */
+ 0, /* index */
+ (guint8*) request,
+ 8,
+ NULL,
+ 2000,
+ cancellable,
+ &error);
+ g_assert_error (error,
+ G_USB_DEVICE_ERROR,
+ G_USB_DEVICE_ERROR_NOT_SUPPORTED);
+ g_assert (!ret);
+ g_clear_error (&error);
+
+ /* close */
+ ret = g_usb_device_close (device, &error);
+ g_assert_no_error (error);
+ g_assert (ret);
+
+ g_object_unref (device);
+
+ g_object_unref (list);
+ g_object_unref (ctx);
+}
+
int
main (int argc, char **argv)
{
@@ -193,6 +291,7 @@ main (int argc, char **argv)
g_test_add_func ("/gusb/source", gusb_source_func);
g_test_add_func ("/gusb/device", gusb_device_func);
g_test_add_func ("/gusb/device-list", gusb_device_list_func);
+ g_test_add_func ("/gusb/device[huey]", gusb_device_huey_func);
return g_test_run ();
}