diff options
-rw-r--r-- | CMakeLists.txt | 11 | ||||
-rw-r--r-- | README.adoc | 5 | ||||
-rw-r--r-- | elksmart-comm.c | 703 |
3 files changed, 2 insertions, 717 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index c1cfa73..e511efa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,14 +19,12 @@ find_package (PkgConfig REQUIRED) pkg_check_modules (x REQUIRED x11 xext xextproto) pkg_check_modules (pulse REQUIRED libpulse) pkg_check_modules (dbus REQUIRED dbus-1) -pkg_check_modules (libusb libusb-1.0) pkg_check_modules (gdm gdm glib-2.0 gio-2.0) include_directories ( ${x_INCLUDE_DIRS} ${pulse_INCLUDE_DIRS} ${dbus_INCLUDE_DIRS}) link_directories ( ${x_LIBRARY_DIRS} ${pulse_LIBRARY_DIRS} ${dbus_LIBRARY_DIRS}) -option (WITH_USB "Compile with USB utilities" ${libusb_FOUND}) option (WITH_GDM "Compile with GDM utilities" ${gdm_FOUND}) # Generate a configuration file @@ -59,13 +57,6 @@ target_link_libraries (wmstatus ${x_LIBRARIES} ${pulse_LIBRARIES} ${dbus_LIBRARIES}) add_threads (wmstatus) -if (WITH_USB) - list (APPEND targets elksmart-comm) - add_executable (elksmart-comm elksmart-comm.c) - target_include_directories (elksmart-comm PUBLIC ${libusb_INCLUDE_DIRS}) - target_link_directories (elksmart-comm PUBLIC ${libusb_LIBRARY_DIRS}) - target_link_libraries (elksmart-comm ${libusb_LIBRARIES}) -endif () if (WITH_GDM) list (APPEND targets gdm-switch-user) add_executable (gdm-switch-user gdm-switch-user.c) @@ -105,7 +96,7 @@ endif () # These should be accessible by users, but need to touch system devices. # Use the setuid bit, for simplicity. -foreach (target brightness input-switch elksmart-comm) +foreach (target brightness input-switch) if (${target} IN_LIST targets) list (REMOVE_ITEM targets ${target}) install (TARGETS ${target} DESTINATION ${CMAKE_INSTALL_BINDIR} diff --git a/README.adoc b/README.adoc index 06b7c26..9f2b5f4 100644 --- a/README.adoc +++ b/README.adoc @@ -27,8 +27,6 @@ to other people as well: pseudo-random data; it should be fast enough to saturate most devices - 'big-brother' tracks the title of the active window and the idle state of the user and writes these events to standard output. - - 'elksmart-comm' interfaces with ELK Smart infrared dongles, - partially reimplementing the Ocrustar mobile app. Few of them are useful outside of Linux. @@ -40,8 +38,7 @@ Building -------- Build dependencies: CMake, pkg-config, liberty (included) + Runtime dependencies: libpulse, libx11, dbus-1 + -Optional runtime dependencies: -libusb-1.0 (elksmart-comm), libgdm (gdm-switch-user) +Optional runtime dependencies: libgdm (gdm-switch-user) $ git clone --recursive https://git.janouch.name/p/desktop-tools.git $ mkdir desktop-tools/build diff --git a/elksmart-comm.c b/elksmart-comm.c deleted file mode 100644 index 98e100f..0000000 --- a/elksmart-comm.c +++ /dev/null @@ -1,703 +0,0 @@ -/* - * elksmart-comm.c: ELK Smart infrared dongle tool (for EKX4S and EKX5S-T) - * - * Copyright (c) 2024, Přemysl Eric Janouch <p@janouch.name> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY - * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION - * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN - * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - * - */ - -#include "config.h" -#undef PROGRAM_NAME -#define PROGRAM_NAME "elksmart-comm" -#include "liberty/liberty.c" - -#include <libusb.h> - -// --- Utilities --------------------------------------------------------------- - -/// Search for a device with given vendor and product ID. -/// This is quite similar to libusb_open_device_with_vid_pid(). -static libusb_device_handle * -find_device (int vendor, int product, int *error) -{ - libusb_device **list = NULL; - libusb_device_handle *handle = NULL; - int result = 0; - - ssize_t len = libusb_get_device_list (NULL, &list); - if (len < 0) - { - result = len; - goto out; - } - - for (ssize_t i = 0; i < len; i++) - { - libusb_device *device = list[i]; - struct libusb_device_descriptor desc = {}; - if ((result = libusb_get_device_descriptor (device, &desc))) - print_debug ("%s", libusb_strerror (result)); - else if (desc.idVendor != vendor || desc.idProduct != product) - continue; - else if (!(result = libusb_open (device, &handle))) - break; - } - - libusb_free_device_list (list, true); -out: - if (error != NULL && result != 0) - *error = result; - return handle; -} - -static void -wait_ms (long ms) -{ - struct timespec ts = { ms / 1000, (ms % 1000) * 1000 * 1000 }; - nanosleep (&ts, NULL); -} - -static void -dump_hex (const unsigned char *buf, size_t len) -{ - for (size_t i = 0; i < len; i++) - printf ("%02x", buf[i]); - printf ("\n"); -} - -static bool -read_hex (const char *string, struct str *out) -{ - static const char *alphabet = "0123456789abcdef"; - str_reset (out); - while (true) - { - while (*string && strchr (" \t\n\r\v\f", *string)) - string++; - if (!*string) - return true; - - const char *hi, *lo; - if (!(hi = strchr (alphabet, tolower_ascii (*string++))) || !*string - || !(lo = strchr (alphabet, tolower_ascii (*string++)))) - return false; - - str_pack_u8 (out, (hi - alphabet) << 4 | (lo - alphabet)); - } -} - -// --- Coding ------------------------------------------------------------------ - -// Values are in microseconds. -struct pulse { unsigned on, off; }; - -static bool -pulse_equal (struct pulse a, struct pulse b) -{ - return a.on == b.on && a.off == b.off; -} - -static size_t -decode_learned_direct (const uint8_t *b, size_t b_len, struct pulse *pulses) -{ - size_t pulses_len = 0; - for (size_t i = 0; i < b_len; ) - { - struct pulse *pulse = &pulses[pulses_len++]; - while (b[i] == 0xff) - { - pulse->on += 4080; - if (++i == b_len) - return 0; - } - pulse->on += b[i++] * 16; - - // Who cares, presumably it stays off. - if (i == b_len) - break; - - while (b[i] == 0xff) - { - pulse->off += 4080; - if (++i == b_len) - return 0; - } - pulse->off += b[i++] * 16; - } - return pulses_len; -} - -static struct pulse * -decode_learned (const struct str *code, size_t *len, struct error **e) -{ - // This conveniently has an upper bound. - struct pulse *pulses = xcalloc (code->len, sizeof *pulses); - if (!(*len = decode_learned_direct - ((const uint8_t *) code->str, code->len, pulses))) - { - error_set (e, "code ends unexpectedly"); - free (pulses); - return NULL; - } - return pulses; -} - -static struct pulse * -encode_nec_byte (struct pulse *p, uint8_t byte) -{ - for (int i = 7; i >= 0; i--) - *p++ = (struct pulse) - { .on = 550, .off = ((byte >> i) & 1) ? 1650 : 550 }; - return p; -} - -static struct pulse * -encode_nec (const struct str *code, size_t *len, struct error **e) -{ - if (code->len % 2) - { - error_set (e, "NEC transmission format requires pairs"); - return NULL; - } - - // The timings seem to be rather tolerant. - *len = code->len / 2 * (1 /* leader */ + 32 + 1 /* stop */); - struct pulse *pulses = xcalloc (*len, sizeof *pulses), *p = pulses; - for (size_t i = 0; i < code->len; i += 2) - { - *p++ = (struct pulse) { .on = 8500, .off = 4250 }; - p = encode_nec_byte (p, code->str[i + 0]); - p = encode_nec_byte (p, ~code->str[i + 0]); - p = encode_nec_byte (p, code->str[i + 1]); - p = encode_nec_byte (p, ~code->str[i + 1]); - *p++ = (struct pulse) { .on = 550, .off = 25000 }; - } - return pulses; -} - -static void -compress_value (unsigned value, struct str *encoded) -{ - if (value <= 2032) - { - // We fix a minor problem in the original Ocrustar algorithm. - uint8_t v = value / 16. + .5; - str_pack_u8 (encoded, MAX (2, v)); - } - else - do - { - uint8_t v = value & 0x7f; - if ((value >>= 7)) - v |= 0x80; - str_pack_u8 (encoded, v); - } while (value); -} - -static void -compress_pulses (const struct pulse *pulses, size_t len, struct str *encoded) -{ - unsigned counts[len]; - memset (counts, 0, sizeof counts); - for (size_t i = 0; i < len; i++) - for (size_t k = 0; k < len; k++) - if (pulse_equal (pulses[i], pulses[k])) - counts[i]++; - - struct pulse p1 = {}, p2 = {}; - size_t top1 = 0, top2 = 0; - for (size_t i = 0; i < len; i++) - if (counts[i] > counts[top1]) - p1 = pulses[top1 = i]; - for (size_t i = 0; i < len; i++) - if (counts[i] < counts[top1] - && counts[i] > counts[top2]) - p2 = pulses[top2 = i]; - else if (counts[top2] == counts[top1]) - p2 = pulses[top2 = i]; - - // Although I haven't really tried it, something tells me that - // this will work even in the degenerated case of len <= 2. - // XXX: The receiver might not like multibyte values here, - // Ocrustar also oddly replaces 0xff with 0xfe for these fields. - compress_value (p2.on, encoded); - compress_value (p2.off, encoded); - compress_value (p1.on, encoded); - compress_value (p1.off, encoded); - str_pack_u8 (encoded, -1); - str_pack_u8 (encoded, -1); - str_pack_u8 (encoded, -1); - - for (size_t i = 0; i < len; i++) - { - if (pulse_equal (pulses[i], p1)) - str_pack_u8 (encoded, 0); - else if (pulse_equal (pulses[i], p2)) - str_pack_u8 (encoded, 1); - else - { - compress_value (pulses[i].on, encoded); - compress_value (pulses[i].off, encoded); - } - } -} - -// --- Device interaction ------------------------------------------------------ - -enum -{ - USB_VENDOR_SMTCTL = 0x045c, - - // 0x134 (EKX5S ~ 5s, 5th generation remote) - // 0x195 (EKX4S ~ 4s, 4th generation remote) - // 0x184 (EKX5S-T, international edition) - USB_PRODUCT_SMTCTL_SMART_EKX4S = 0x0195, - USB_PRODUCT_SMTCTL_SMART_EKX5S_T = 0x0184, - - // There should only ever be one interface. - USB_INTERFACE = 0, -}; - -static uint8_t - c_transmit[] = { -1, -1, -1, -1 }, - c_learn[] = { -2, -2, -2, -2 }, - c_stop[] = { -3, -3, -3, -3 }, - c_identify[] = { -4, -4, -4, -4 }; - -static struct -{ - unsigned char endpoint_out; ///< Outgoing endpoint - unsigned char endpoint_in; ///< Incoming endpoint -} -g; - -static bool -init_device_from_desc (struct libusb_config_descriptor *desc, struct error **e) -{ - // We're not being particuarly strict in here. - if (desc->bNumInterfaces != 1) - return error_set (e, "unexpected USB interface count"); - if (desc->interface->num_altsetting != 1) - return error_set (e, "unexpected alternate setting count"); - - const struct libusb_interface_descriptor *asd = desc->interface->altsetting; - if (asd->bInterfaceClass != LIBUSB_CLASS_COMM) - return error_set (e, "unexpected USB interface class"); - if (asd->bNumEndpoints != 2) - return error_set (e, "unexpected endpoint count"); - - bool have_out = false, have_in = false; - for (uint8_t i = 0; i < asd->bNumEndpoints; i++) - { - const struct libusb_endpoint_descriptor *epd = asd->endpoint + i; - if ((epd->bmAttributes & LIBUSB_TRANSFER_TYPE_MASK) - != LIBUSB_ENDPOINT_TRANSFER_TYPE_BULK) - return error_set (e, "unexpected endpoint transfer type"); - - switch ((epd->bEndpointAddress & LIBUSB_ENDPOINT_DIR_MASK)) - { - break; case LIBUSB_ENDPOINT_OUT: - have_out = true; - g.endpoint_out = epd->bEndpointAddress; - break; case LIBUSB_ENDPOINT_IN: - have_in = true; - g.endpoint_in = epd->bEndpointAddress; - } - } - if (!have_out || !have_in) - return error_set (e, "USB interface is not bidirectional"); - return true; -} - -static bool -init_device (libusb_device_handle *device, struct error **e) -{ - struct libusb_config_descriptor *desc = NULL; - int result = - libusb_get_active_config_descriptor (libusb_get_device (device), &desc); - if (result) - return error_set (e, "%s", libusb_strerror (result)); - - bool ok = true; - if ((result = libusb_kernel_driver_active (device, USB_INTERFACE)) == 1) - ok = error_set (e, "device is claimed by a kernel driver"); - else if (result) - ok = error_set (e, "%s", libusb_strerror (result)); - else - ok = init_device_from_desc (desc, e); - - libusb_free_config_descriptor (desc); - return ok; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -static uint8_t -mangle (uint8_t value) -{ - uint8_t reversed = 0; - for (int i = 0; i < 8; i++) - { - reversed = (reversed << 1) | (value & 1); - value >>= 1; - } - return ~reversed; -} - -static uint8_t -checksum (const uint8_t *b, size_t len) -{ - uint32_t sum = 0; - for (size_t i = 0; i < len; i++) - sum += b[i]; - return mangle ((sum & 0xF0) | ((sum >> 8) & 0x0F)); -} - -static bool -send_transmit (libusb_device_handle *device, unsigned long frequency, - const struct pulse *pulses, size_t pulses_len, struct error **e) -{ - if (g_debug_mode) - for (size_t i = 0; i < pulses_len; ) - { - printf ("%u,%u", pulses[i].on, pulses[i].off); - putchar (++i == pulses_len ? '\n' : ','); - } - - struct str compressed = str_make (); - compress_pulses (pulses, pulses_len, &compressed); - - struct str message = str_make (); - str_append_data (&message, c_transmit, sizeof c_transmit); - frequency += 0x7ffff; - str_pack_u8 (&message, mangle (frequency >> 8)); - str_pack_u8 (&message, mangle (frequency >> 16)); - str_pack_u8 (&message, mangle (frequency)); - str_pack_u8 (&message, mangle (compressed.len >> 8)); - str_pack_u8 (&message, mangle (compressed.len)); - str_append_str (&message, &compressed); - str_free (&compressed); - - size_t i = 0; - uint8_t buffer[64]; - bool ok = true; - while (i != message.len) - { - size_t chunk = MIN (62, message.len - i); - memcpy (buffer, message.str + i, chunk); - i += chunk; - if (chunk == 62) - { - buffer[chunk] = checksum (buffer, chunk); - chunk++; - } - - int result = 0, len = 0; - if ((result = libusb_bulk_transfer (device, g.endpoint_out, - buffer, chunk, &len, 100))) - { - ok = error_set (e, "send: %s", libusb_strerror (result)); - break; - } - wait_ms (2); - } - str_free (&message); - return ok; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -static bool -pulse_is_likely_leader (const struct pulse *p) -{ - return p->on >= 2048 && p->off >= 2048; -} - -static void -try_to_depulse (const struct str *code) -{ - size_t len = 0; - struct pulse *pulses = decode_learned (code, &len, NULL); - if (!pulses) - return; - - struct pulse *p = pulses, *end = p + len; - while (p != end && pulse_is_likely_leader (p)) - { - p++; - printf ("Attempted pulse decode:\n"); - - uint8_t bits = 0, nibble = 0; - for (; p != end && !pulse_is_likely_leader (p); p++) - { - nibble = nibble << 1 | (p->off > 2 * p->on); - if (++bits == 4) - { - putchar ("0123456789abcdef"[nibble]); - bits = nibble = 0; - } - } - putchar ('\n'); - } - free (pulses); -} - -static bool -recv_learn (libusb_device_handle *device, struct str *data, struct error **e) -{ - uint8_t buffer[64] = {}; - int result = 0, len = 0; - while ((result = libusb_bulk_transfer (device, g.endpoint_in, - buffer, sizeof buffer, &len, 100))) - { - if (result != LIBUSB_ERROR_TIMEOUT) - return error_set (e, "learn/recv: %s", libusb_strerror (result)); - print_debug ("learn/recv: %s", libusb_strerror (result)); - } - if (len < 6 || memcmp (buffer, c_learn, sizeof c_learn)) - return error_set (e, "learn/recv: %s", "unexpected response"); - - // This field might only make sense for a later device, - // because it doesn't always correspond with how much data we receive. - // Nonetheless, it does match exactly often enough. - size_t size = buffer[4] << 8 | buffer[5]; - print_debug ("learn: code size: %zu", size); - - str_append_data (data, buffer + 6, len - 6); - dump_hex ((const unsigned char *) data->str, data->len); - while (data->len < size) - { - if (!(result = libusb_bulk_transfer (device, g.endpoint_in, - buffer, sizeof buffer, &len, 100))) - { - dump_hex (buffer, len); - - str_append_data (data, buffer, len); - print_debug ("learn: received %d (have %zu of %zu)", - len, data->len, size); - continue; - } - if (result != LIBUSB_ERROR_TIMEOUT) - return error_set (e, "learn/recv: %s", libusb_strerror (result)); - - // The device seems to queue up its output with pauses. - print_debug ("learn/recv: %s", libusb_strerror (result)); - } - - // As far as I know, this doesn't do anything, - // and the device doesn't accept it while scanning infrared codes either. - if ((result = libusb_bulk_transfer (device, g.endpoint_out, - c_stop, sizeof c_stop, &len, 100))) - return error_set (e, "learn/send: %s", libusb_strerror (result)); - return true; -} - -static bool -send_learn (libusb_device_handle *device, struct error **e) -{ - int result = 0, len = 0; - if ((result = libusb_bulk_transfer (device, g.endpoint_out, - c_learn, sizeof c_learn, &len, 100))) - return error_set (e, "learn/send: %s", libusb_strerror (result)); - - printf ("Reading remote control codes.\n"); - printf ("Press a remote control button from less than a centimeter.\n"); - printf ("The dongle may be unusable until it returns some data.\n"); - // ... Resetting the device using libusb_reset_device() doesn't help then. - printf ("If the code fails to replay, retry the capture.\n"); - - struct str data = str_make (); - bool ok = recv_learn (device, &data, e); - if (ok) - { - printf ("Full command:\n"); - dump_hex ((const unsigned char *) data.str, data.len); - try_to_depulse (&data); - } - - str_free (&data); - return ok; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -static bool -send_identify (libusb_device_handle *device, struct error **e) -{ - uint8_t buffer[64] = {}; - int result = 0, len = 0; - while (!(result = libusb_bulk_transfer (device, g.endpoint_in, - buffer, sizeof buffer, &len, 10))) - /* Flush buffers. */; - - if ((result = libusb_bulk_transfer (device, g.endpoint_out, - c_identify, sizeof c_identify, &len, 100))) - return error_set (e, "identify/send: %s", libusb_strerror (result)); - if ((result = libusb_bulk_transfer (device, g.endpoint_in, - buffer, sizeof buffer, &len, 100))) - return error_set (e, "identify/recv: %s", libusb_strerror (result)); - - // XXX: Sometimes, the device doesn't send any identification values. - if (len != 6 || memcmp (buffer, c_identify, sizeof c_identify) - || buffer[4] != 0x70 || buffer[5] != 0x01) - return error_set (e, "device busy or not supported"); - -#if 0 - // The EKX4S does not respond to this request. - static uint8_t c_serial[] = { -5, -5, -5, -5 }; - if ((result = libusb_bulk_transfer (device, g.endpoint_out, - c_serial, sizeof c_serial, &len, 100))) - return error_set (e, "serial/send: %s", libusb_strerror (result)); - if ((result = libusb_bulk_transfer (device, g.endpoint_in, - buffer, sizeof buffer, &len, 100))) - return error_set (e, "serial/recv: %s", libusb_strerror (result)); - if (len < (int) sizeof c_serial - || memcmp (buffer, c_serial, sizeof c_serial)) - return error_set (e, "serial retrieval failed"); -#endif - return true; -} - -static bool -run (libusb_device_handle *device, unsigned long frequency, bool nec, - char **codes, size_t codes_len, struct error **e) -{ - if (!send_identify (device, e)) - return false; - if (!codes_len) - return send_learn (device, e); - - struct str code = str_make (); - bool ok = true; - for (size_t i = 0; i < codes_len; i++) - { - if (!read_hex (codes[i], &code)) - { - ok = error_set (e, "invalid hex string"); - break; - } - - size_t pulses_len = 0; - struct pulse *pulses = nec - ? encode_nec (&code, &pulses_len, e) - : decode_learned (&code, &pulses_len, e); - - ok = pulses && send_transmit (device, frequency, pulses, pulses_len, e); - free (pulses); - if (!ok) - break; - - wait_ms (100); - } - str_free (&code); - return ok; -} - -// --- Main -------------------------------------------------------------------- - -int -main (int argc, char *argv[]) -{ - unsigned long frequency = 38000; - bool nec = false; - static const struct opt opts[] = - { - { 'd', "debug", NULL, 0, "run in debug mode" }, - { 'f', "frequency", "HZ", 0, "frequency (38000 Hz by default)" }, - { 'n', "nec", NULL, 0, "use the NEC transmission format" }, - { 'h', "help", NULL, 0, "display this help and exit" }, - { 'V', "version", NULL, 0, "output version information and exit" }, - { 0, NULL, NULL, 0, NULL } - }; - - struct opt_handler oh = opt_handler_make (argc, argv, opts, "[COMMAND...]", - "Transmit or receive infrared commands."); - - int c; - while ((c = opt_handler_get (&oh)) != -1) - switch (c) - { - case 'd': - g_debug_mode = true; - break; - case 'f': - if (!xstrtoul (&frequency, optarg, 10) || !frequency) - exit_fatal ("invalid frequency"); - break; - case 'n': - nec = true; - break; - case 'h': - opt_handler_usage (&oh, stdout); - exit (EXIT_SUCCESS); - case 'V': - printf (PROGRAM_NAME " " PROGRAM_VERSION "\n"); - exit (EXIT_SUCCESS); - default: - print_error ("wrong options"); - opt_handler_usage (&oh, stderr); - exit (EXIT_FAILURE); - } - - argc -= optind; - argv += optind; - - opt_handler_free (&oh); - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -#if LIBUSB_API_VERSION >= 0x0100010A - const struct libusb_init_option option = - { - .option = LIBUSB_OPTION_LOG_LEVEL, - .value.ival = LIBUSB_LOG_LEVEL_DEBUG, - }; - int result = libusb_init_context (NULL, &option, g_debug_mode); -#else - int result = libusb_init (NULL); -#endif - if (result) - exit_fatal ("libusb: %s", libusb_strerror (result)); - - libusb_device_handle *device = NULL; - if (!device && !result) - device = find_device (USB_VENDOR_SMTCTL, - USB_PRODUCT_SMTCTL_SMART_EKX4S, &result); - if (!device && !result) - device = find_device (USB_VENDOR_SMTCTL, - USB_PRODUCT_SMTCTL_SMART_EKX5S_T, &result); - - if (result) - exit_fatal ("couldn't open device: %s", libusb_strerror (result)); - else if (!device) - exit_fatal ("no suitable device found"); - - struct error *e = NULL; - if (!init_device (device, &e)) - exit_fatal ("%s", e->message); - if ((result = libusb_claim_interface (device, USB_INTERFACE)) == 1) - exit_fatal ("couldn't claim interface: %s", libusb_strerror (result)); - - if (!run (device, frequency, nec, argv, argc, &e)) - { - print_error ("%s", e->message); - error_free (e); - } - - if ((result = libusb_release_interface (device, USB_INTERFACE)) == 1) - exit_fatal ("couldn't release interface: %s", libusb_strerror (result)); - - libusb_close (device); - libusb_exit (NULL); - return 0; -} |