1
0
forked from drath/libratbag

Merge pull request #53 from whot/liblur

Add liblur, a library for logitech unifying receivers
This commit is contained in:
bentiss
2015-12-11 16:04:42 +01:00
6 changed files with 871 additions and 3 deletions

1
.gitignore vendored
View File

@@ -49,3 +49,4 @@ test-*.log
ratbag-command
hidpp10-dump-page
hidpp20-dump-page
lur-command

View File

@@ -1,9 +1,11 @@
lib_LTLIBRARIES = libratbag.la
lib_LTLIBRARIES = libratbag.la \
liblur.la
noinst_LTLIBRARIES = libhidpp.la \
libutil.la
include_HEADERS = \
libratbag.h
libratbag.h \
liblur.h
libratbag_la_SOURCES = \
driver-etekcity.c \
@@ -33,6 +35,17 @@ EXTRA_libratbag_la_DEPENDENCIES = $(srcdir)/libratbag.sym
libratbag_la_LDFLAGS = -version-info $(LIBRATBAG_LT_VERSION) -shared \
-Wl,--version-script=$(srcdir)/libratbag.sym
liblur_la_SOURCES = \
liblur.c \
liblur.h
liblur_la_LIBADD = \
libhidpp.la \
libutil.la
liblur_la_CFLAGS = \
-I$(top_srcdir)/include \
$(GCC_CFLAGS)
liblur_la_LDFLAGS = -shared
pkgconfigdir = $(libdir)/pkgconfig
pkgconfig_DATA = libratbag.pc

349
src/liblur.c Normal file
View File

@@ -0,0 +1,349 @@
/*
* Copyright 2015 Red Hat, Inc
*
* liblur - Logitech Unifying Receiver access library
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#include <assert.h>
#include <stdbool.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <linux/hidraw.h>
#include "hidpp10.h"
#include "libratbag-util.h"
#include "liblur.h"
#define _EXPORT_ __attribute__ ((visibility("default")))
#define MAX_DEVICES 6
#define VENDOR_ID_LOGITECH 0x046d
struct lur_receiver {
int refcount;
int fd;
void *userdata;
struct hidpp10_device *hidppdev;
struct list devices;
};
struct lur_device {
struct lur_receiver *receiver;
int refcount;
char *name;
uint16_t vid, pid;
uint32_t serial;
enum lur_device_type type;
int hidppidx;
struct list node;
bool present; /* used during re-enumeration */
};
_EXPORT_ const char *
lur_device_get_name(struct lur_device *dev)
{
return dev->name;
}
_EXPORT_ uint16_t
lur_device_get_vendor_id(struct lur_device *dev)
{
return dev->vid;
}
_EXPORT_ uint16_t
lur_device_get_product_id(struct lur_device *dev)
{
return dev->pid;
}
_EXPORT_ enum lur_device_type
lur_device_get_type(struct lur_device *dev)
{
return dev->type;
}
_EXPORT_ uint32_t
lur_device_get_serial(struct lur_device *dev)
{
return dev->serial;
}
_EXPORT_ int
lur_device_disconnect(struct lur_device *dev)
{
int rc;
rc = hidpp10_disconnect(dev->receiver->hidppdev, dev->hidppidx);
if (rc == 0) {
list_remove(&dev->node);
list_init(&dev->node);
}
return rc;
}
_EXPORT_ int
lur_is_receiver(uint16_t vid, uint16_t pid)
{
return (vid == VENDOR_ID_LOGITECH && (pid == 0xc52b || pid == 0xc532));
}
static bool
hidraw_is_receiver(int fd)
{
struct hidraw_devinfo info;
int rc;
rc = ioctl(fd, HIDIOCGRAWINFO, &info);
if (rc < 0)
return false;
if (!lur_is_receiver(info.vendor, info.product))
return false;
return true;
}
static struct hidpp10_device *
hidpp10_init(int fd)
{
struct hidpp_device base;
hidpp_device_init(&base, fd);
return hidpp10_device_new(&base, HIDPP_RECEIVER_IDX,
HIDPP10_PROFILE_UNKNOWN);
}
_EXPORT_ struct lur_receiver*
lur_receiver_new_from_hidraw(int fd, void *userdata)
{
struct lur_receiver *receiver;
if (!hidraw_is_receiver(fd))
return NULL;
receiver = zalloc(sizeof(*receiver));
receiver->refcount = 1;
receiver->fd = fd;
receiver->userdata = userdata;
list_init(&receiver->devices);
receiver->hidppdev = hidpp10_init(fd);
if (!receiver->hidppdev)
goto error;
return receiver;
error:
free(receiver);
return NULL;
}
_EXPORT_ int
lur_receiver_enumerate(struct lur_receiver *lur,
struct lur_device ***devices_out)
{
int i;
int ndevices = 0;
struct hidpp_device base;
struct lur_device *dev, *tmp;
int rc;
struct lur_device **devices;
hidpp_device_init(&base, lur->fd);
list_for_each(dev, &lur->devices, node)
dev->present = false;
for (i = 0; i < MAX_DEVICES; i++) {
struct hidpp10_device *d;
size_t name_size = 64;
char name[name_size];
uint8_t interval, type;
uint16_t wpid;
uint32_t serial;
bool is_new_device = true;
d = hidpp10_device_new(&base, i,
HIDPP10_PROFILE_UNKNOWN);
if (!d)
continue;
rc = hidpp10_get_pairing_information_device_name(d, name, &name_size);
if (rc)
continue;
rc = hidpp10_get_pairing_information(d, &interval, &wpid, &type);
if (rc)
continue;
rc = hidpp10_get_extended_pairing_information(d, &serial);
if (rc)
continue;
/* check if we have the device already in the list */
list_for_each(dev, &lur->devices, node) {
if (dev->pid == wpid &&
dev->type == type &&
dev->serial == serial &&
streq(dev->name, name)) {
/* index may have changed, doesn't make it a
* new device, just update it */
dev->hidppidx = i;
dev->present = true;
is_new_device = false;
break;
}
}
if (is_new_device) {
dev = zalloc(sizeof *dev);
dev->receiver = lur;
lur_receiver_ref(lur);
dev->refcount = 1;
dev->name = strdup_safe(name);
dev->vid = VENDOR_ID_LOGITECH;
dev->pid = wpid;
dev->type = type;
dev->serial = serial;
dev->hidppidx = i;
dev->present = true;
list_insert(&lur->devices, &dev->node);
}
hidpp10_device_destroy(d);
}
devices = zalloc(MAX_DEVICES * sizeof(*devices));
/* Now drop all devices that disappeared */
list_for_each_safe(dev, tmp, &lur->devices, node) {
if (dev->present) {
devices[ndevices++] = dev;
} else {
list_remove(&dev->node);
list_init(&dev->node);
lur_device_unref(dev);
}
}
*devices_out = devices;
return ndevices;
}
_EXPORT_ int
lur_receiver_open(struct lur_receiver *lur, uint8_t timeout)
{
return !!hidpp10_open_lock(lur->hidppdev, timeout);
}
_EXPORT_ int
lur_receiver_close(struct lur_receiver *lur)
{
return 0;
}
_EXPORT_ int
lur_receiver_get_fd(struct lur_receiver *lur)
{
return lur->fd;
}
_EXPORT_ struct lur_receiver *
lur_receiver_ref(struct lur_receiver *lur)
{
assert(lur->refcount > 0);
lur->refcount++;
return lur;
}
_EXPORT_ struct lur_receiver *
lur_receiver_unref(struct lur_receiver *lur)
{
if (lur == NULL)
return NULL;
assert(lur->refcount > 0);
lur->refcount--;
if (lur->refcount > 0)
return NULL;
/* when we get here, all the devices have alrady been removed from
* the receiver */
hidpp10_device_destroy(lur->hidppdev);
free(lur);
return NULL;
}
_EXPORT_ void
lur_receiver_set_user_data(struct lur_receiver *lur, void *userdata)
{
lur->userdata = userdata;
}
_EXPORT_ void*
lur_receiver_get_user_data(const struct lur_receiver *lur)
{
return lur->userdata;
}
_EXPORT_ struct lur_device *
lur_device_ref(struct lur_device *dev)
{
assert(dev->refcount > 0);
dev->refcount++;
return dev;
}
_EXPORT_ struct lur_device *
lur_device_unref(struct lur_device *dev)
{
if (dev == NULL)
return NULL;
assert(dev->refcount > 0);
dev->refcount--;
if (dev->refcount > 0)
return NULL;
list_remove(&dev->node);
lur_receiver_unref(dev->receiver);
free(dev->name);
free(dev);
return NULL;
}

245
src/liblur.h Normal file
View File

@@ -0,0 +1,245 @@
/*
* liblur - Logitech Unifying Receiver access library
*
* Copyright 2015 Red Hat, Inc
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#ifndef _LIBLUR_H_
#define _LIBLUR_H_ 1
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
/**
* @struct lur_receiver
*
* A handle for accessing Logitech Unifying Receivers.
*
* This struct is refcounted, use lur_device_ref() and lur_device_unref().
*/
struct lur_receiver;
/**
* @struct lur_device
*
* A handle for accessing devices paired with a lur_receiver.
*
* This struct is refcounted, use lur_device_ref() and lur_device_unref().
*/
struct lur_device;
enum lur_device_type {
LUR_DEVICE_TYPE_UNKNOWN = 0x00,
LUR_DEVICE_TYPE_KEYBOARD = 0x01,
LUR_DEVICE_TYPE_MOUSE = 0x02,
LUR_DEVICE_TYPE_NUMPAD = 0x03,
LUR_DEVICE_TYPE_PRESENTER = 0x04,
LUR_DEVICE_TYPE_TRACKBALL = 0x08,
LUR_DEVICE_TYPE_TOUCHPAD = 0x09,
};
const char *
lur_device_get_name(struct lur_device *dev);
uint16_t
lur_device_get_vendor_id(struct lur_device *dev);
uint16_t
lur_device_get_product_id(struct lur_device *dev);
enum lur_device_type
lur_device_get_type(struct lur_device *dev);
uint32_t
lur_device_get_serial(struct lur_device *dev);
/**
* Disconnect this device from the receiver it is currently paired with.
*
* @param lur A valid receiver object
* @return 0 on success or nonzero on error
*/
int
lur_device_disconnect(struct lur_device *dev);
/**
* Returns non-zero if a device with the given vid/pid is a logitech
* unifying receiver device.
*
* @param vid The vendor ID
* @param pid The product ID
*
* @return non-zero if the device is a unifying receiver, zero otherwise
*/
int
lur_is_receiver(uint16_t vid, uint16_t pid);
/**
* Creates a new Logitech Unifying Receiver object from the file descriptor.
* The fd must be a hidraw device, opened in O_RDWR.
*
* The returned struct has a refcount of at least 1, use lur_device_unref()
* to release resources associated with it.
* It is the caller's responsibility to close the fd after the
* resources associated with this object have been freed.
*
* @param fd An O_RDWR file descriptor pointing to a /dev/hidraw node
* @param userdata Caller-specific data
*
* @return A lur object, or NULL on error.
*/
struct lur_receiver*
lur_receiver_new_from_hidraw(int fd, void *userdata);
/**
* Enumerate devices currently paired with the given receiver.
*
* liblur does not have a device detection mechanism, it is recommend that a
* caller monitors udev for hidraw devices being added and removed. When
* such devices appear or disappear, a call to lur_receiver_enumerate()
* yields the new list of devices. If no new unifying devices are available,
* the returned list is identical to the list returned in the last call to
* lur_receiver_enumerate(). Otherwise, the diff between the two lists
* indicate the set of newly added and/or removed devices.
*
* The devices returned have a refcount of at least 1, use
* lur_device_unref(). Repeated calls to this function do not increase the
* devices' refcount.
*
* @param lur A valid receiver object
* @param[out] devices An array of devices paired with this receiver. Use
* free() to free the array, and lur_device_unref() to destroy each device.
*
* @return The number of devices returned, or -1 on error.
*/
int
lur_receiver_enumerate(struct lur_receiver *lur,
struct lur_device ***devices);
/**
* Allow devices to be paired with this receiver for the given timeout.
* Once 'open', the receiver will pair with a currently disconnected
* device.
*
* @param lur A valid receiver object
* @param timeout The time in seconds the receiver should accept new
* pairings. The value 0 uses the receiver's default value (usually 30s).
*
* @return 0 on success or nonzero on error
*/
int
lur_receiver_open(struct lur_receiver *lur, uint8_t timeout);
/**
* If a receiver is currently accepting devices, stop doing so. If the
* receiver is not currently accepting devices, this function has no effect
* and returns success.
*
* @param lur A valid receiver object
* @return 0 on success or nonzero on error
*/
int
lur_receiver_close(struct lur_receiver *lur);
/**
* Return the file descriptor used to initialize this receiver.
*
* @param lur A valid receiver object
* @return The file descriptor passed into lur_receiver_new_from_hidraw
*/
int
lur_receiver_get_fd(struct lur_receiver* lur);
/**
* Add a reference to the context. A context is destroyed whenever the
* reference count reaches 0. See @ref lur_unref.
*
* @param lur A valid receiver object
* @return The passed context
*/
struct lur_receiver *
lur_receiver_ref(struct lur_receiver *lur);
/**
* Dereference the context. After this, the context may have been
* destroyed, if the last reference was dereferenced. If so, the context is
* invalid and may not be interacted with.
*
* @param lur A valid receiver object
* @retval NULL
*/
struct lur_receiver *
lur_receiver_unref(struct lur_receiver *lur);
/**
* Set caller-specific data associated with this object. liblur does
* not manage, look at, or modify this data. The caller must ensure the
* data is valid.
*
* Setting userdata overrides the one provided to
* lur_receiver_new_from_hidraw().
*
* @param lur A valid receiver object
* @param userdata Caller-specific data passed to the various callback
* interfaces.
*/
void
lur_receiver_set_user_data(struct lur_receiver *lur, void *userdata);
/**
* Get the caller-specific data associated with this object, if any.
*
* @param lur A valid receiver object
* @return The caller-specific data previously assigned in
* lur_receiver_new_from_hidraw (or lur_receiver_set_user_data()).
*/
void*
lur_receiver_get_user_data(const struct lur_receiver *lur);
/**
* Add a reference to the device. A device is destroyed whenever the
* reference count reaches 0. See @ref lur_unref.
*
* @param lur A valid device object
* @return The passed device
*/
struct lur_device *
lur_device_ref(struct lur_device *dev);
/**
* Dereference the device. After this, the device may have been
* destroyed, if the last reference was dereferenced. If so, the device is
* invalid and may not be interacted with.
*
* @param lur A valid device object
* @retval NULL
*/
struct lur_device *
lur_device_unref(struct lur_device *dev);
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -1,5 +1,5 @@
noinst_PROGRAMS = hidpp10-dump-page hidpp20-dump-page
bin_PROGRAMS = ratbag-command
bin_PROGRAMS = ratbag-command lur-command
noinst_LTLIBRARIES = libshared.la
AM_CPPFLAGS = -I$(top_srcdir)/include \
@@ -25,3 +25,8 @@ hidpp20_dump_page_SOURCES = hidpp20-dump-page.c
hidpp20_dump_page_LDADD = ../src/libhidpp.la
hidpp20_dump_page_LDFLAGS = -no-install
hidpp20_dump_page_CFLAGS = -I$(top_srcdir)/src
lur_command_SOURCeS = lur-command.c
lur_command_LDADD = ../src/liblur.la
lur_command_LDFLAGS = -no-install
lur_command_CFLAGS = -I$(top_srcdir)/src

255
tools/lur-command.c Normal file
View File

@@ -0,0 +1,255 @@
/*
* Copyright © 2015 Red Hat, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#define _GNU_SOURCE
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <getopt.h>
#include <unistd.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <liblur.h>
#include <libratbag-util.h>
enum options {
OPT_HELP,
};
static struct lur_receiver *
open_receiver(const char *path)
{
struct lur_receiver *receiver;
int fd = -1;
fd = open(path, O_RDWR);
if (fd < 0) {
fprintf(stderr, "Failed to open %s (%s)\n", path, strerror(errno));
return NULL;
}
receiver = lur_receiver_new_from_hidraw(fd, NULL);
if (!receiver)
close(fd);
return receiver;
}
static void
list_connected_devices(struct lur_receiver *receiver)
{
_cleanup_free_ struct lur_device **devices = NULL;
int ndevices;
int i;
ndevices = lur_receiver_enumerate(receiver, &devices);
if (ndevices < 0) {
fprintf(stderr, "Failed to enumerate devices\n");
return;
} else if (ndevices == 0) {
fprintf(stderr, "No devices connected to this receiver\n");
return;
}
for (i = 0; i < ndevices; i++) {
struct lur_device *dev = devices[i];
const char *name, *strtype;
enum lur_device_type type;
uint32_t serial;
name = lur_device_get_name(dev);
type = lur_device_get_type(dev);
serial = lur_device_get_serial(dev);
switch(type) {
case LUR_DEVICE_TYPE_UNKNOWN: strtype = "unknown"; break;
case LUR_DEVICE_TYPE_KEYBOARD: strtype = "keyboard"; break;
case LUR_DEVICE_TYPE_MOUSE: strtype = "mouse"; break;
case LUR_DEVICE_TYPE_NUMPAD: strtype = "numpad"; break;
case LUR_DEVICE_TYPE_PRESENTER: strtype = "presenter"; break;
case LUR_DEVICE_TYPE_TRACKBALL: strtype = "trackball"; break;
case LUR_DEVICE_TYPE_TOUCHPAD: strtype = "touchpad"; break;
default:
strtype = "<invalid>";
break;
}
printf("%d: %s (%s) serial %#x\n", i, name, strtype, serial);
lur_device_unref(dev);
}
}
static void
disconnect_device(struct lur_receiver *receiver, int index)
{
_cleanup_free_ struct lur_device **devices = NULL;
int ndevices;
int i;
ndevices = lur_receiver_enumerate(receiver, &devices);
if (ndevices < 0) {
fprintf(stderr, "Failed to enumerate devices\n");
return;
} else if (ndevices == 0) {
fprintf(stderr, "No devices connected to this receiver\n");
return;
}
if (index >= ndevices) {
fprintf(stderr, "Invalid index %d, only %d devices connected\n",
index, ndevices);
} else {
lur_device_disconnect(devices[index]);
}
for (i = 0; i < ndevices; i++)
lur_device_unref(devices[i]);
}
static int
filter_hidraw(const struct dirent *entry)
{
return strneq(entry->d_name, "hidraw", 6);
}
static void
find_receiver(void)
{
_cleanup_free_ struct dirent **hidraw_list = NULL;
int n, i;
char path[PATH_MAX] = {0};
bool found = false;
n = scandir("/dev/", &hidraw_list, filter_hidraw, alphasort);
if (n < 0)
return;
for (i = 0; i < n; i++) {
struct lur_receiver *receiver;
sprintf_safe(path, "/dev/%s", hidraw_list[i]->d_name);
receiver = open_receiver(path);
if (receiver) {
found = true;
printf("%s\n", path);
lur_receiver_unref(receiver);
}
free(hidraw_list[i]);
}
if (!found)
fprintf(stderr, "No receivers found.\n");
}
static void
usage(void)
{
printf("Usage: %s COMMAND /dev/hidrawX\n"
"\n"
"Commands:\n"
" list ............. list devices connected to receiver\n"
" open ............. open receiver for pairing (timeout 30s)\n"
" close ............ close receiver if currently open\n"
" disconnect N ..... disconnect device N\n"
" find ............. find a receiver amongst the /dev/hidraw devices \n",
program_invocation_short_name);
}
int
main(int argc, char **argv)
{
struct lur_receiver *receiver;
const char *path;
const char *command;
while (1) {
int c;
int option_index = 0;
static struct option opts[] = {
{ "help", 0, 0, OPT_HELP },
};
c = getopt_long(argc, argv, "+h", opts, &option_index);
if (c == -1)
break;
switch(c) {
case 'h':
case OPT_HELP:
usage();
return EXIT_SUCCESS;
default:
usage();
return EXIT_FAILURE;
}
}
if (argc < 2) {
usage();
return EXIT_FAILURE;
}
command = argv[1];
if (streq(command, "find")) {
find_receiver();
return EXIT_SUCCESS;
}
if (argc < 3) {
usage();
return EXIT_FAILURE;
}
path = argv[argc - 1];
receiver = open_receiver(path);
if (!receiver) {
fprintf(stderr, "Failed to open receiver at %s\n", path);
return EXIT_FAILURE;
}
if (streq(command, "list")) {
list_connected_devices(receiver);
} else if (streq(command, "open")) {
lur_receiver_open(receiver, 0);
} else if (streq(command, "close")) {
lur_receiver_close(receiver);
} else if (streq(command, "disconnect")) {
int index;
if (argc < 4) {
usage();
return EXIT_FAILURE;
}
index = atoi(argv[3]);
disconnect_device(receiver, index);
} else {
usage();
return EXIT_FAILURE;
}
lur_receiver_unref(receiver);
return EXIT_SUCCESS;
}