Switch to a .ini-file based device database

The hwdb was a quick and easy way to add entries for new devices but we need
more and more info about the various types of devices. It's better to move
this to a proper database.

Side-effect: this also gives us an easy way to point to the list of devices
supported and makes checking for e.g. missing svgs easier.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
This commit is contained in:
Peter Hutterer
2017-08-24 13:50:18 +10:00
committed by Benjamin Tissoires
parent be624fe943
commit 340e5b7d86
42 changed files with 1064 additions and 111 deletions

View File

@@ -47,6 +47,7 @@
#include "libratbag-private.h"
#include "libratbag-hidraw.h"
#include "libratbag-data.h"
struct hidpp10drv_data {
struct hidpp10_device *dev;
@@ -555,9 +556,8 @@ hidpp10drv_probe(struct ratbag_device *device)
struct hidpp10_device *dev = NULL;
struct hidpp_device base;
enum hidpp10_profile_type type = HIDPP10_PROFILE_UNKNOWN;
const char *prop;
const char *profile;
int device_idx = HIDPP_WIRED_DEVICE_IDX;
int nread = 0;
unsigned int profile_count = 1;
rc = ratbag_find_hidraw(device, hidpp10drv_test_hidraw);
@@ -568,31 +568,22 @@ hidpp10drv_probe(struct ratbag_device *device)
hidpp_device_init(&base, device->hidraw.fd);
hidpp_device_set_log_handler(&base, hidpp10_log, HIDPP_LOG_PRIORITY_RAW, device);
prop = ratbag_device_get_udev_property(device, "RATBAG_HIDPP10_PROFILE");
if (prop) {
if (strcasecmp("G500", prop) == 0)
profile = ratbag_device_data_hidpp10_get_profile_type(device->data);
if (profile) {
if (strcasecmp("G500", profile) == 0)
type = HIDPP10_PROFILE_G500;
else if (strcasecmp("G700", prop) == 0)
else if (strcasecmp("G700", profile) == 0)
type = HIDPP10_PROFILE_G700;
else if (strcasecmp("G9", prop) == 0)
else if (strcasecmp("G9", profile) == 0)
type = HIDPP10_PROFILE_G9;
prop = ratbag_device_get_udev_property(device, "RATBAG_HIDPP10_PROFILE_COUNT");
if (prop)
profile_count = atoi(prop);
profile_count = ratbag_device_data_hidpp10_get_profile_count(device->data);
}
prop = ratbag_device_get_udev_property(device, "RATBAG_HIDPP10_INDEX");
if (prop) {
sscanf(prop, "%d%n", &device_idx, &nread);
if (!nread || (prop[nread]) != '\0' || device_idx < 0) {
log_error(device->ratbag,
"Error parsing RATBAG_HIDPP10_INDEX: '%s' for %s\n",
prop,
device->name);
device_idx = HIDPP_WIRED_DEVICE_IDX;
}
}
device_idx = ratbag_device_data_hidpp10_get_index(device->data);
if (device_idx == -1)
device_idx = HIDPP_WIRED_DEVICE_IDX;
/* In the general case, we can treat all devices as wired devices
* here. If we talk to the correct hidraw device the kernel adjusts
* the device index for us, so even for unifying receiver devices
@@ -611,23 +602,24 @@ hidpp10drv_probe(struct ratbag_device *device)
}
if (type != HIDPP10_PROFILE_UNKNOWN) {
prop = ratbag_device_get_udev_property(device, "RATBAG_HIDPP10_DPI");
if (prop) {
rc = hidpp10_build_dpi_table_from_dpi_info(dev, prop);
_cleanup_(dpi_list_freep) struct dpi_list *list = NULL;
_cleanup_(freep) struct dpi_range *range = NULL;
range = ratbag_device_data_hidpp10_get_dpi_range(device->data);
if (range) {
rc = hidpp10_build_dpi_table_from_dpi_info(dev, range);
if (rc)
log_error(device->ratbag,
"Error parsing RATBAG_HIDPP10_DPI: '%s' for %s\n",
prop,
"Error parsing DpiRange for %s\n",
device->name);
}
prop = ratbag_device_get_udev_property(device, "RATBAG_HIDPP10_DPI_LIST");
if (prop) {
rc = hidpp10_build_dpi_table_from_list(dev, prop);
list = ratbag_device_data_hidpp10_get_dpi_list(device->data);
if (list) {
rc = hidpp10_build_dpi_table_from_list(dev, list);
if (rc)
log_error(device->ratbag,
"Error parsing RATBAG_HIDPP10_DPI_LIST: '%s' for %s\n",
prop,
"Error parsing DpiList for %s\n",
device->name);
}

View File

@@ -45,6 +45,7 @@
#include "libratbag-private.h"
#include "libratbag-hidraw.h"
#include "libratbag-data.h"
#define HIDPP_CAP_RESOLUTION_2200 (1 << 0)
#define HIDPP_CAP_SWITCHABLE_RESOLUTION_2201 (1 << 1)
@@ -1014,9 +1015,7 @@ hidpp20drv_probe(struct ratbag_device *device)
struct hidpp20drv_data *drv_data;
struct hidpp_device base;
struct hidpp20_device *dev;
const char *prop;
int device_idx = HIDPP_RECEIVER_IDX;
int nread = 0;
rc = ratbag_find_hidraw(device, hidpp20drv_test_hidraw);
if (rc)
@@ -1027,25 +1026,17 @@ hidpp20drv_probe(struct ratbag_device *device)
hidpp_device_init(&base, device->hidraw.fd);
hidpp_device_set_log_handler(&base, hidpp20_log, HIDPP_LOG_PRIORITY_RAW, device);
prop = ratbag_device_get_udev_property(device, "RATBAG_HIDPP20_INDEX");
if (prop) {
sscanf(prop, "%d%n", &device_idx, &nread);
if (!nread || (prop[nread]) != '\0' || device_idx < 0) {
log_error(device->ratbag,
"Error parsing RATBAG_HIDPP20_INDEX: '%s' for %s\n",
prop,
device->name);
device_idx = HIDPP_RECEIVER_IDX;
}
}
device_idx = ratbag_device_data_hidpp20_get_index(device->data);
if (device_idx == -1)
device_idx = HIDPP_RECEIVER_IDX;
/* In the general case, we can treat all devices as wired devices
* here. If we talk to the correct hidraw device the kernel adjusts
* the device index for us, so even for unifying receiver devices
* we can just use 0xff as device index.
*
* If there is a special need like for G900, we can pass a
* udev prop RATBAG_HIDPP20_INDEX.
* If there is a special need like for G900, we can add this in the
* device data file.
*/
dev = hidpp20_device_new(&base, device_idx);
if (!dev) {

View File

@@ -114,20 +114,10 @@ hidpp10_dpi_table_get_min_dpi(struct hidpp10_device *dev)
int
hidpp10_build_dpi_table_from_list(struct hidpp10_device *dev,
const char *str_list)
const struct dpi_list *list)
{
_cleanup_(dpi_list_freep) struct dpi_list *list;
size_t i;
/*
* str_list is in the form:
* "0;200;400;600;800;1000;1200"
*/
list = dpi_list_from_string(str_list);
if (!list)
goto err;
if (list->nentries + 0x80 - 1> 0xff)
goto err;
@@ -149,19 +139,10 @@ err:
int
hidpp10_build_dpi_table_from_dpi_info(struct hidpp10_device *dev,
const char *str_dpi)
const struct dpi_range *range)
{
unsigned raw_max, i;
_cleanup_(freep) struct dpi_range *range = NULL;
/*
* str_list is in the form:
* "MIN:MAX@STEP"
*/
range = dpi_range_from_string(str_dpi);
if (!range)
return -EINVAL;
raw_max = (range->max - range->min) / range->step;
if (raw_max > 0xff)

View File

@@ -33,6 +33,9 @@
#include "hidpp-generic.h"
struct dpi_list;
struct dpi_range;
/* FIXME: that's what my G500s supports, but only pages 3-5 are valid.
* 0 is zeroed, 1 and 2 are garbage, all above 6 is garbage */
#define HIDPP10_NUM_PROFILES 5
@@ -88,7 +91,7 @@ hidpp10_device_destroy(struct hidpp10_device *dev);
*/
int
hidpp10_build_dpi_table_from_list(struct hidpp10_device *dev,
const char *str_list);
const struct dpi_list *list);
/**
* Builds the table of dpi for the device from the given dpi description.
@@ -103,7 +106,7 @@ hidpp10_build_dpi_table_from_list(struct hidpp10_device *dev,
*/
int
hidpp10_build_dpi_table_from_dpi_info(struct hidpp10_device *dev,
const char *str_dpi);
const struct dpi_range *range);
/**
* Returns the lowest dpi stored in the dpi mapping table generated by

405
src/libratbag-data.c Normal file
View File

@@ -0,0 +1,405 @@
/*
* Copyright © 2017 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.
*/
#include "config.h"
#include <linux/input.h>
#include <assert.h>
#include <stdlib.h>
#include <glib.h>
#include "libratbag.h"
#include "libratbag-private.h"
#include "libratbag-data.h"
#define GROUP_DEVICE "Device"
DEFINE_TRIVIAL_CLEANUP_FUNC(GKeyFile *, g_key_file_free);
DEFINE_TRIVIAL_CLEANUP_FUNC(GError *, g_error_free);
DEFINE_TRIVIAL_CLEANUP_FUNC(char **, g_strfreev);
enum driver {
NONE = 0,
HIDPP10,
HIDPP20,
ROCCAT,
ETEKCITY,
GSKILL,
LOGITECH_G300
};
struct data_hidpp20 {
int index;
};
struct data_hidpp10 {
int index;
int profile_count;
char *profile_type;
struct dpi_list *dpi_list;
struct dpi_range *dpi_range;
};
struct ratbag_device_data {
int refcount;
char *name;
char *driver;
char *svg;
enum driver drivertype;
union {
struct data_hidpp20 hidpp20;
struct data_hidpp10 hidpp10;
};
};
static void
init_data_hidpp10(struct ratbag *ratbag,
GKeyFile *keyfile,
struct ratbag_device_data *data)
{
const char *group = "Driver/hidpp10";
char *profile_type;
GError *error = NULL;
_cleanup_(freep) char *str = NULL;
int num;
data->hidpp10.index = -1;
data->hidpp10.profile_count = -1;
data->hidpp10.profile_type = NULL;
num = g_key_file_get_integer(keyfile, group, "DeviceIndex", &error);
if (num != 0 || !error)
data->hidpp10.index = num;
if (error)
g_error_free(error);
num = g_key_file_get_integer(keyfile, group, "Profiles", &error);
if (num > 0 || !error)
data->hidpp10.profile_count = num;
if (error)
g_error_free(error);
profile_type = g_key_file_get_string(keyfile, group, "ProfileType", NULL);
if (profile_type)
data->hidpp10.profile_type = profile_type;
str = g_key_file_get_string(keyfile, group, "DpiRange", NULL);
if (str) {
data->hidpp10.dpi_range = dpi_range_from_string(str);
} else {
str = g_key_file_get_string(keyfile, group, "DpiList", NULL);
if (str)
data->hidpp10.dpi_list = dpi_list_from_string(str);
}
}
static void
init_data_hidpp20(struct ratbag *ratbag,
GKeyFile *keyfile,
struct ratbag_device_data *data)
{
const char *group = "Driver/hidpp20";
GError *error = NULL;
int num;
data->hidpp20.index = -1;
num = g_key_file_get_integer(keyfile, group, "DeviceIndex", &error);
if (num != 0 || !error)
data->hidpp20.index = num;
if (error)
g_error_free(error);
}
static const struct driver_map {
enum driver map;
const char *driver;
void (*init)(struct ratbag *ratbag,
GKeyFile *keyfile,
struct ratbag_device_data *data);
} driver_map[] = {
{ HIDPP10, "hidpp10", init_data_hidpp10 },
{ HIDPP20, "hidpp20", init_data_hidpp20 },
{ ROCCAT, "roccat", NULL },
{ ETEKCITY, "etekcity", NULL},
{ GSKILL, "gskill", NULL },
{ LOGITECH_G300, "logitech_g300", NULL},
};
const char *
ratbag_device_data_get_driver(const struct ratbag_device_data *data)
{
return data->driver;
}
const char *
ratbag_device_data_get_name(const struct ratbag_device_data *data)
{
return data->name;
}
const char *
ratbag_device_data_get_svg(const struct ratbag_device_data *data)
{
return data->svg;
}
struct ratbag_device_data *
ratbag_device_data_ref(struct ratbag_device_data *data)
{
data->refcount++;
return data;
}
static void
ratbag_device_data_destroy(struct ratbag_device_data *data)
{
switch (data->drivertype) {
case HIDPP10:
free(data->hidpp10.profile_type);
break;
default:
break;
}
free(data->name);
free(data->driver);
free(data->svg);
free(data);
}
struct ratbag_device_data *
ratbag_device_data_unref(struct ratbag_device_data *data)
{
if (data == NULL)
return NULL;
assert(data->refcount > 0);
data->refcount--;
if (data->refcount == 0)
ratbag_device_data_destroy(data);
return NULL;
}
DEFINE_TRIVIAL_CLEANUP_FUNC(struct ratbag_device_data *, ratbag_device_data_unref);
static bool
match(const struct input_id *id, char **strv)
{
const char *bus;
char str[64];
int i = 0;
switch (id->bustype) {
case BUS_USB: bus = "usb"; break;
case BUS_BLUETOOTH: bus = "bluetooth"; break;
default:
return false;
}
snprintf(str, sizeof(str), "%s:%04x:%04x", bus, id->vendor, id->product);
while (strv[i]) {
if (streq(strv[i], str))
return true;
i++;
}
return false;
}
static bool
file_data_matches(struct ratbag *ratbag,
const char *path, const struct input_id *id,
struct ratbag_device_data **data_out)
{
_cleanup_(g_key_file_freep) GKeyFile *keyfile = NULL;
_cleanup_(g_error_freep) GError *error = NULL;
_cleanup_(g_strfreevp) char **strv = NULL;
_cleanup_(ratbag_device_data_unrefp) struct ratbag_device_data *data = NULL;
int rc;
keyfile = g_key_file_new();
rc = g_key_file_load_from_file(keyfile, path, G_KEY_FILE_NONE, &error);
if (!rc) {
log_error(ratbag, "Failed to parse keyfile %s: %s\n", path, error->message);
return false;
}
strv = g_key_file_get_string_list(keyfile, GROUP_DEVICE, "DeviceMatch", NULL, NULL);
if (!strv) {
log_error(ratbag, "Missing DeviceMatch in %s\n", basename(path));
return false;
}
if (!match(id, strv))
return false;
data = zalloc(sizeof(*data));
data->refcount = 1;
data->name = g_key_file_get_string(keyfile, GROUP_DEVICE, "Name", NULL);
if (!data->name) {
return false;
}
data->driver = g_key_file_get_string(keyfile, GROUP_DEVICE, "Driver", NULL);
if (!data->driver) {
log_error(ratbag, "Missing Driver in %s\n", basename(path));
return false;
} else {
const struct driver_map *map;
data->drivertype = NONE;
ARRAY_FOR_EACH(driver_map, map) {
if (streq(map->driver, data->driver)) {
data->drivertype = map->map;
if (map->init)
map->init(ratbag, keyfile, data);
break;
}
}
if (data->drivertype == NONE) {
log_error(ratbag, "Unknown driver %s in %s\n",
data->driver, basename(path));
return false;
}
}
data->svg = g_key_file_get_string(keyfile, GROUP_DEVICE, "Svg", NULL);
*data_out = data;
data = NULL;
return true;
}
static int
filter_device_files(const struct dirent *entry)
{
const char *SUFFIX = ".device";
const char *name = entry->d_name;
int len, slen;
if (!name || name[0] == '.')
return 0;
len = strlen(name);
slen = strlen(SUFFIX);
if (len <= slen)
return 0;
return streq(&name[len - slen], SUFFIX);
}
struct ratbag_device_data *
ratbag_device_data_new_for_id(struct ratbag *ratbag, const struct input_id *id)
{
struct ratbag_device_data *data = NULL;
struct dirent **files;
int n, nfiles;
n = scandir(LIBRATBAG_DATA_DIR, &files, filter_device_files, alphasort);
if (n <= 0) {
log_error(ratbag, "Unable to locate device files: %s\n",
n == 0 ? "No files found" : strerror(errno));
return NULL;
}
nfiles = n;
while(n--) {
_cleanup_(freep) char *file = NULL;
int rc;
rc = xasprintf(&file, "%s/%s", LIBRATBAG_DATA_DIR, files[n]->d_name);
if (rc == -1)
goto out;
if (file_data_matches(ratbag, file, id, &data))
goto out;
}
out:
while(nfiles--)
free(files[nfiles]);
free(files);
return data;
}
/* HID++ 1.0 */
int
ratbag_device_data_hidpp10_get_index(const struct ratbag_device_data *data)
{
assert(data->drivertype == HIDPP10);
return data->hidpp10.index;
}
int
ratbag_device_data_hidpp10_get_profile_count(const struct ratbag_device_data *data)
{
assert(data->drivertype == HIDPP10);
return data->hidpp10.profile_count;
}
const char *
ratbag_device_data_hidpp10_get_profile_type(const struct ratbag_device_data *data)
{
assert(data->drivertype == HIDPP10);
return data->hidpp10.profile_type;
}
struct dpi_list *
ratbag_device_data_hidpp10_get_dpi_list(const struct ratbag_device_data *data)
{
assert(data->drivertype == HIDPP10);
return data->hidpp10.dpi_list;
}
struct dpi_range *
ratbag_device_data_hidpp10_get_dpi_range(const struct ratbag_device_data *data)
{
assert(data->drivertype == HIDPP10);
return data->hidpp10.dpi_range;
}
/* HID++ 2.0 */
int
ratbag_device_data_hidpp20_get_index(const struct ratbag_device_data *data)
{
assert(data->drivertype == HIDPP20);
return data->hidpp20.index;
}

74
src/libratbag-data.h Normal file
View File

@@ -0,0 +1,74 @@
/*
* Copyright © 2017 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.
*/
#pragma once
struct ratbag_device_data;
struct ratbag_device_data *
ratbag_device_data_new_for_id(struct ratbag *ratbag, const struct input_id *id);
struct ratbag_device_data *
ratbag_device_data_unref(struct ratbag_device_data *data);
struct ratbag_device_data *
ratbag_device_data_ref(struct ratbag_device_data *data);
const char *
ratbag_device_data_get_driver(const struct ratbag_device_data *data);
const char *
ratbag_device_data_get_name(const struct ratbag_device_data *data);
const char *
ratbag_device_data_get_svg(const struct ratbag_device_data *data);
/* HID++ 1.0 */
/**
* @return The device index or -1 if not set
*/
int
ratbag_device_data_hidpp10_get_index(const struct ratbag_device_data *data);
const char *
ratbag_device_data_hidpp10_get_profile_type(const struct ratbag_device_data *data);
/**
* @return The profile count index or -1 if not set
*/
int
ratbag_device_data_hidpp10_get_profile_count(const struct ratbag_device_data *data);
struct dpi_list *
ratbag_device_data_hidpp10_get_dpi_list(const struct ratbag_device_data *data);
struct dpi_range *
ratbag_device_data_hidpp10_get_dpi_range(const struct ratbag_device_data *data);
/* HID++ 2.0 */
/**
* @return The device index or -1 if not set
*/
int
ratbag_device_data_hidpp20_get_index(const struct ratbag_device_data *data);

View File

@@ -90,6 +90,7 @@ struct ratbag_device {
struct input_id ids;
struct ratbag_driver *driver;
struct ratbag *ratbag;
struct ratbag_device_data *data;
unsigned long capabilities[NLONGS(MAX_CAP)];
unsigned num_profiles;

View File

@@ -37,6 +37,7 @@
#include "usb-ids.h"
#include "libratbag-private.h"
#include "libratbag-util.h"
#include "libratbag-data.h"
static enum ratbag_error_code
error_code(enum ratbag_error_code code)
@@ -180,6 +181,7 @@ ratbag_device_new(struct ratbag *ratbag, struct udev_device *udev_device,
device->refcount = 1;
device->udev_device = udev_device_ref(udev_device);
device->ids = *id;
device->data = ratbag_device_data_new_for_id(ratbag, id);
list_init(&device->profiles);
list_insert(&ratbag->devices, &device->link);
@@ -217,6 +219,7 @@ ratbag_device_destroy(struct ratbag_device *device)
list_remove(&device->link);
ratbag_unref(device->ratbag);
ratbag_device_data_unref(device->data);
free(device->name);
free(device);
}
@@ -358,7 +361,7 @@ ratbag_assign_driver(struct ratbag_device *device,
const char *driver_name;
if (!test_device) {
driver_name = udev_prop_value(device->udev_device, "RATBAG_DRIVER");
driver_name = ratbag_device_data_get_driver(device->data);
} else {
driver_name = "test_driver";
}
@@ -433,7 +436,7 @@ ratbag_device_new_from_udev_device(struct ratbag *ratbag,
goto out_err;
device = ratbag_device_new(ratbag, udev_device, name, &id);
if (!device)
if (!device || !device->data)
goto out_err;
if (!ratbag_assign_driver(device, &device->ids, NULL))
@@ -483,9 +486,10 @@ ratbag_device_get_name(const struct ratbag_device* device)
LIBRATBAG_EXPORT const char *
ratbag_device_get_svg_name(const struct ratbag_device* device)
{
const char *name;
const char *name = NULL;
name = udev_prop_value(device->udev_device, "RATBAG_SVG");
if (device->data)
name = ratbag_device_data_get_svg(device->data);
if (!name && device->driver->get_svg_name)
name = device->driver->get_svg_name(device);