/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- * * Copyright (C) 2011 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ #include "config.h" #include "gusb-context-private.h" static void gusb_device_func(void) { GUsbDevice *device; g_autoptr(GError) error = NULL; g_autoptr(GPtrArray) array = NULL; g_autoptr(GUsbContext) ctx = NULL; #ifdef __FreeBSD__ g_test_skip("Root hubs on FreeBSD have vid and pid set to zero"); return; #endif 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); array = g_usb_context_get_devices(ctx); g_assert(array != NULL); g_assert_cmpint(array->len, >, 0); device = G_USB_DEVICE(g_ptr_array_index(array, 0)); g_assert_cmpint(g_usb_device_get_vid(device), >, 0x0000); g_assert_cmpint(g_usb_device_get_pid(device), >, 0x0000); g_assert_false(g_usb_device_has_tag(device, "foobar")); g_usb_device_add_tag(device, "foobar"); g_usb_device_add_tag(device, "foobar"); g_assert_true(g_usb_device_has_tag(device, "foobar")); g_usb_device_remove_tag(device, "foobar"); g_assert_false(g_usb_device_has_tag(device, "foobar")); } static void gusb_context_lookup_func(void) { GError *error = NULL; const gchar *tmp; g_autoptr(GUsbContext) ctx = NULL; ctx = g_usb_context_new(&error); g_assert_no_error(error); g_assert(ctx != NULL); tmp = _g_usb_context_lookup_vendor(ctx, 0x04d8, &error); g_assert_no_error(error); g_assert_cmpstr(tmp, ==, "Microchip Technology, Inc."); tmp = _g_usb_context_lookup_product(ctx, 0x04d8, 0xf8da, &error); g_assert_no_error(error); g_assert_cmpstr(tmp, ==, "Hughski Ltd. ColorHug"); } static void gusb_context_func(void) { GPtrArray *array; guint old_number_of_devices; guint8 bus, address; GUsbDevice *device; g_autoptr(GError) error = NULL; g_autoptr(GUsbContext) ctx = NULL; #ifdef __FreeBSD__ g_test_skip("Root hubs on FreeBSD have vid and pid set to zero"); return; #endif 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); /* coldplug, and ensure we got some devices */ array = g_usb_context_get_devices(ctx); g_assert(array != NULL); g_assert_cmpint(array->len, >, 0); old_number_of_devices = array->len; /* Print a list (also exercising various bits of g_usb_device) */ g_print("\n"); for (guint i = 0; i < array->len; i++) { g_autofree gchar *manufacturer = NULL; g_autofree gchar *product = NULL; device = G_USB_DEVICE(g_ptr_array_index(array, i)); g_assert_cmpint(g_usb_device_get_vid(device), >, 0x0000); g_assert_cmpint(g_usb_device_get_pid(device), >, 0x0000); /* Needed for g_usb_device_get_string_descriptor below, not error checked to allow running basic tests without needing r/w rights on /dev/bus/usb nodes */ g_usb_context_set_debug(ctx, 0); g_usb_device_open(device, NULL); g_usb_context_set_debug(ctx, G_LOG_LEVEL_ERROR); /* We don't error check these as not all devices have these (and the device_open may have failed). */ manufacturer = g_usb_device_get_string_descriptor(device, g_usb_device_get_manufacturer_index(device), NULL); product = g_usb_device_get_string_descriptor(device, g_usb_device_get_product_index(device), NULL); g_usb_device_close(device, NULL); g_print("Found %04x:%04x, %s %s\n", g_usb_device_get_vid(device), g_usb_device_get_pid(device), manufacturer ? manufacturer : "", product ? product : ""); } g_ptr_array_unref(array); /* coldplug again, and ensure we did not duplicate devices */ array = g_usb_context_get_devices(ctx); g_assert(array != NULL); g_assert_cmpint(array->len, ==, old_number_of_devices); device = G_USB_DEVICE(g_ptr_array_index(array, 0)); bus = g_usb_device_get_bus(device); address = g_usb_device_get_address(device); g_ptr_array_unref(array); /* ensure we can search for the same device */ device = g_usb_context_find_by_bus_address(ctx, bus, address, &error); g_assert_no_error(error); g_assert(device != NULL); g_assert_cmpint(bus, ==, g_usb_device_get_bus(device)); g_assert_cmpint(address, ==, g_usb_device_get_address(device)); g_object_unref(device); /* get a device that can't exist */ device = g_usb_context_find_by_vid_pid(ctx, 0xffff, 0xffff, &error); g_assert_error(error, G_USB_DEVICE_ERROR, G_USB_DEVICE_ERROR_NO_DEVICE); g_assert(device == NULL); } static void gusb_device_huey_func(void) { gboolean ret; GCancellable *cancellable = NULL; const gchar request[8] = {0x0e, 'G', 'r', 'M', 'b', 'k', 'e', 'd'}; g_autoptr(GError) error = NULL; g_autoptr(GUsbContext) ctx = NULL; g_autoptr(GUsbDevice) device = NULL; 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); /* coldplug, and get the huey */ device = g_usb_context_find_by_vid_pid(ctx, 0x0971, 0x2005, &error); if (device == NULL && error->domain == G_USB_DEVICE_ERROR && error->code == G_USB_DEVICE_ERROR_NO_DEVICE) { g_print("No device detected!\n"); return; } 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, &error); g_assert_no_error(error); g_assert(ret); /* open opened */ ret = g_usb_device_open(device, &error); g_assert_error(error, G_USB_DEVICE_ERROR, G_USB_DEVICE_ERROR_ALREADY_OPEN); g_assert(!ret); g_clear_error(&error); /* claim interface 0 */ ret = g_usb_device_claim_interface(device, 0x00, G_USB_DEVICE_CLAIM_INTERFACE_BIND_KERNEL_DRIVER, &error); g_assert_no_error(error); g_assert(ret); /* 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); /* release interface 0 */ ret = g_usb_device_release_interface(device, 0x00, G_USB_DEVICE_CLAIM_INTERFACE_BIND_KERNEL_DRIVER, &error); g_assert_no_error(error); g_assert(ret); /* close */ ret = g_usb_device_close(device, &error); g_assert_no_error(error); g_assert(ret); } typedef struct { guint8 *buffer; guint buffer_len; GMainLoop *loop; } GUsbDeviceAsyncHelper; static void g_usb_device_print_data(const gchar *title, const guchar *data, gsize length) { if (g_strcmp0(title, "request") == 0) g_print("%c[%dm", 0x1B, 31); if (g_strcmp0(title, "reply") == 0) g_print("%c[%dm", 0x1B, 34); g_print("%s\t", title); for (guint i = 0; i < length; i++) { g_print("%02x [%c]\t", data[i], g_ascii_isprint(data[i]) ? data[i] : '?'); } g_print("%c[%dm\n", 0x1B, 0); } static void g_usb_test_button_pressed_cb(GObject *source_object, GAsyncResult *res, gpointer user_data) { gssize rc; GUsbDeviceAsyncHelper *helper = (GUsbDeviceAsyncHelper *)user_data; g_autoptr(GError) error = NULL; rc = g_usb_device_interrupt_transfer_finish(G_USB_DEVICE(source_object), res, &error); if (rc < 0) { g_error("%s", error->message); return; } g_usb_device_print_data("button press", helper->buffer, helper->buffer_len); g_main_loop_quit(helper->loop); g_free(helper->buffer); } static void gusb_device_munki_func(void) { gboolean ret; GCancellable *cancellable = NULL; guint8 request[24]; GUsbDeviceAsyncHelper *helper; g_autoptr(GError) error = NULL; g_autoptr(GUsbContext) ctx = NULL; g_autoptr(GUsbDevice) device = NULL; 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); /* coldplug, and get the ColorMunki */ device = g_usb_context_find_by_vid_pid(ctx, 0x0971, 0x2007, &error); if (device == NULL && error->domain == G_USB_DEVICE_ERROR && error->code == G_USB_DEVICE_ERROR_NO_DEVICE) { g_print("No device detected!\n"); return; } 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, &error); g_assert_no_error(error); g_assert(ret); /* open opened */ ret = g_usb_device_open(device, &error); g_assert_error(error, G_USB_DEVICE_ERROR, G_USB_DEVICE_ERROR_ALREADY_OPEN); g_assert(!ret); g_clear_error(&error); /* claim interface 0 */ ret = g_usb_device_claim_interface(device, 0x00, G_USB_DEVICE_CLAIM_INTERFACE_BIND_KERNEL_DRIVER, &error); g_assert_no_error(error); g_assert(ret); /* do a request (get chip id) */ ret = g_usb_device_control_transfer(device, G_USB_DEVICE_DIRECTION_DEVICE_TO_HOST, G_USB_DEVICE_REQUEST_TYPE_VENDOR, G_USB_DEVICE_RECIPIENT_DEVICE, 0x86, /* request */ 0x0000, /* value */ 0, /* index */ (guint8 *)request, 24, 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, 0x00, /* request */ 0x0200, /* value */ 0, /* index */ (guint8 *)request, 8, NULL, 100, cancellable, &error); g_assert_error(error, G_USB_DEVICE_ERROR, G_USB_DEVICE_ERROR_TIMED_OUT); g_assert(!ret); g_clear_error(&error); /* do async read of button event */ helper = g_slice_new0(GUsbDeviceAsyncHelper); helper->buffer_len = 8; helper->buffer = g_new0(guint8, helper->buffer_len); helper->loop = g_main_loop_new(NULL, FALSE); g_usb_device_interrupt_transfer_async(device, 0x83, helper->buffer, 8, 30 * 1000, cancellable, /* TODO; use GCancellable */ g_usb_test_button_pressed_cb, helper); g_main_loop_run(helper->loop); g_main_loop_unref(helper->loop); /* release interface 0 */ ret = g_usb_device_release_interface(device, 0x00, G_USB_DEVICE_CLAIM_INTERFACE_BIND_KERNEL_DRIVER, &error); g_assert_no_error(error); g_assert(ret); /* close */ ret = g_usb_device_close(device, &error); g_assert_no_error(error); g_assert(ret); } static void gusb_device_json_func(void) { JsonObject *json_obj; gboolean ret; guint8 idx; g_autoptr(GUsbDevice) device = NULL; g_autofree gchar *tmp = NULL; g_autoptr(GUsbContext) ctx = NULL; g_autoptr(GError) error = NULL; g_autoptr(JsonParser) parser = json_parser_new(); const gchar *json = "{" " \"UsbDevices\" : [" " {" " \"PlatformId\" : \"usb:01:00:06\"," " \"IdVendor\" : 10047," " \"IdProduct\" : 4100," " \"Device\" : 2," " \"USB\" : 512," " \"Manufacturer\" : 1," " \"UsbInterfaces\" : [" " {" " \"Length\" : 9," " \"DescriptorType\" : 4," " \"InterfaceNumber\" : 1," " \"InterfaceClass\" : 255," " \"InterfaceSubClass\" : 70," " \"InterfaceProtocol\" : 87," " \"Interface\" : 3" " }," " {" " \"Length\" : 9," " \"DescriptorType\" : 4," " \"InterfaceNumber\" : 2," " \"InterfaceClass\" : 255," " \"InterfaceSubClass\" : 71," " \"InterfaceProtocol\" : 85," " \"Interface\" : 4" " }," " {" " \"Length\" : 9," " \"DescriptorType\" : 4," " \"InterfaceClass\" : 3," " \"UsbEndpoints\" : [" " {" " \"DescriptorType\" : 5," " \"EndpointAddress\" : 129," " \"Interval\" : 1," " \"MaxPacketSize\" : 64" " }," " {" " \"DescriptorType\" : 5," " \"EndpointAddress\" : 1," " \"Interval\" : 1," " \"MaxPacketSize\" : 64" " }" " ]," " \"ExtraData\" : \"CSERAQABIh0A\"" " }" " ]," " \"UsbEvents\" : [" " {" " \"Id\" : " "\"GetCustomIndex:ClassId=0xff,SubclassId=0x46,ProtocolId=0x57\"," " \"Data\" : \"Aw==\"" " }," " {" " \"Id\" : " "\"GetCustomIndex:ClassId=0xff,SubclassId=0x46,ProtocolId=0x57\"," " \"Data\" : \"Aw==\"" " }," " {" " \"Id\" : \"GetStringDescriptor:DescIndex=0x03\"," " \"Data\" : " "\"Mi4wLjcAAAAR3HzMiH8AAAAAAAAAAAAA8HBIAQAAAACQ6AsW/n8AADeVQAAAAAAAsOgLFv5/" "AABNkkAAVwAAAEboCxb/" "fwAAkAZLAQAAAAAAZkAAAAAAAwAAAAAAAAAAsPpJAQAAAAAwBksBAAAAAJAZTwEAAAAAAFZZJp63C7g=\"" " }" " ]" " }" " ]" "}"; ctx = g_usb_context_new(&error); g_assert_no_error(error); g_assert(ctx != NULL); /* parse */ ret = json_parser_load_from_data(parser, json, -1, &error); g_assert_no_error(error); g_assert(ret); json_obj = json_node_get_object(json_parser_get_root(parser)); ret = g_usb_context_load(ctx, json_obj, &error); g_assert_no_error(error); g_assert(ret); /* get vendor data */ device = g_usb_context_find_by_vid_pid(ctx, 0x273f, 0x1004, &error); g_assert_no_error(error); g_assert(device != NULL); idx = g_usb_device_get_custom_index(device, G_USB_DEVICE_CLASS_VENDOR_SPECIFIC, 'F', 'W', &error); g_assert_no_error(error); g_assert_cmpint(idx, ==, 3); /* in-order, repeat */ idx = g_usb_device_get_custom_index(device, G_USB_DEVICE_CLASS_VENDOR_SPECIFIC, 'F', 'W', &error); g_assert_no_error(error); g_assert_cmpint(idx, ==, 3); /* out-of-order */ idx = g_usb_device_get_custom_index(device, G_USB_DEVICE_CLASS_VENDOR_SPECIFIC, 'F', 'W', &error); g_assert_no_error(error); g_assert_cmpint(idx, ==, 3); /* get the firmware version */ tmp = g_usb_device_get_string_descriptor(device, idx, &error); g_assert_no_error(error); g_assert_cmpstr(tmp, ==, "2.0.7"); } static void gusb_device_ch2_func(void) { gboolean ret; guint8 idx; g_autofree gchar *data = NULL; g_autofree gchar *tmp = NULL; g_autoptr(GError) error = NULL; g_autoptr(GUsbContext) ctx = NULL; g_autoptr(GUsbDevice) device = NULL; g_autoptr(JsonBuilder) json_builder = json_builder_new(); g_autoptr(JsonGenerator) json_generator = NULL; g_autoptr(JsonNode) json_root = NULL; ctx = g_usb_context_new(&error); g_usb_context_set_flags(ctx, G_USB_CONTEXT_FLAGS_SAVE_EVENTS); g_assert_no_error(error); g_assert(ctx != NULL); g_usb_context_set_debug(ctx, G_LOG_LEVEL_ERROR); /* coldplug, and get the ColorHug */ device = g_usb_context_find_by_vid_pid(ctx, 0x273f, 0x1004, &error); if (device == NULL && error->domain == G_USB_DEVICE_ERROR && error->code == G_USB_DEVICE_ERROR_NO_DEVICE) { g_print("No device detected!\n"); return; } g_assert_no_error(error); g_assert(device != NULL); /* open */ ret = g_usb_device_open(device, &error); g_assert_no_error(error); g_assert(ret); /* get vendor data */ idx = g_usb_device_get_custom_index(device, G_USB_DEVICE_CLASS_VENDOR_SPECIFIC, 'F', 'W', &error); g_assert_no_error(error); g_assert_cmpint(idx, ==, 3); /* get the firmware version */ tmp = g_usb_device_get_string_descriptor(device, idx, &error); g_assert_no_error(error); g_assert_cmpstr(tmp, ==, "2.0.7"); /* close */ ret = g_usb_device_close(device, &error); g_assert_no_error(error); g_assert(ret); /* save context */ ret = g_usb_context_save(ctx, json_builder, &error); g_assert_no_error(error); g_assert(ret); /* export as a string */ json_root = json_builder_get_root(json_builder); json_generator = json_generator_new(); json_generator_set_pretty(json_generator, TRUE); json_generator_set_root(json_generator, json_root); data = json_generator_to_data(json_generator, NULL); g_assert_nonnull(data); g_print("%s\n", data); } int main(int argc, char **argv) { g_test_init(&argc, &argv, NULL); /* only critical and error are fatal */ g_log_set_fatal_mask(NULL, G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL); /* tests go here */ g_test_add_func("/gusb/context", gusb_context_func); g_test_add_func("/gusb/context{lookup}", gusb_context_lookup_func); g_test_add_func("/gusb/device", gusb_device_func); g_test_add_func("/gusb/device[huey]", gusb_device_huey_func); g_test_add_func("/gusb/device[munki]", gusb_device_munki_func); g_test_add_func("/gusb/device[colorhug2]", gusb_device_ch2_func); g_test_add_func("/gusb/device[json]", gusb_device_json_func); return g_test_run(); }