forked from drath/libratbag
Add liblur, a library for logitech unifying receivers
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -49,3 +49,4 @@ test-*.log
|
||||
ratbag-command
|
||||
hidpp10-dump-page
|
||||
hidpp20-dump-page
|
||||
lur-command
|
||||
|
||||
@@ -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
349
src/liblur.c
Normal 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
245
src/liblur.h
Normal 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
|
||||
@@ -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
255
tools/lur-command.c
Normal 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;
|
||||
}
|
||||
Reference in New Issue
Block a user