quirks: allow for parsing multiple products

This allows for a slight optimization of the quirks parser: where
multiple devices from the same vendor require the same quirk, allow
for multiple product matches in the form:

MatchProduct=0x0001;0x0002;

This is stored as a fixed-sized zero-terminated array - a product ID of
zero isn't something we need to worry about in real situations.

Closes #879

Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1038>
This commit is contained in:
Peter Hutterer 2024-08-30 14:48:45 +10:00 committed by Marge Bot
parent ea7ad8d25c
commit f159abdc57
2 changed files with 78 additions and 10 deletions

View file

@ -141,7 +141,7 @@ struct match {
char *uniq;
enum bustype bus;
uint32_t vendor;
uint32_t product;
uint32_t product[64]; /* zero-terminated */
uint32_t version;
char *dmi; /* dmi modalias with preceding "dmi:" */
@ -537,6 +537,14 @@ parse_hex(const char *value, unsigned int *parsed)
*parsed <= 0xFFFF;
}
static int
strv_parse_hex(const char *str, size_t index, void *data)
{
unsigned int *product = data;
return !parse_hex(str, &product[index]); /* 0 for success */
}
/**
* Parse a MatchFooBar=banana line.
*
@ -592,13 +600,18 @@ parse_match(struct quirks_context *ctx,
s->match.vendor = vendor;
} else if (streq(key, "MatchProduct")) {
unsigned int product;
unsigned int product[ARRAY_LENGTH(s->match.product)] = {0};
const size_t max = ARRAY_LENGTH(s->match.product) - 1;
check_set_bit(s, M_PID);
if (!parse_hex(value, &product))
size_t nelems = 0;
char **strs = strv_from_string(value, ";", &nelems);
int rc = strv_for_each_n((const char**)strs, max, strv_parse_hex, product);
strv_free(strs);
if (rc != 0)
goto out;
s->match.product = product;
check_set_bit(s, M_PID);
memcpy(s->match.product, product, sizeof(product));
} else if (streq(key, "MatchVersion")) {
unsigned int version;
@ -1325,7 +1338,8 @@ match_fill_bus_vid_pid(struct match *m,
if (sscanf(str, "%x/%x/%x/%x", &bus, &vendor, &product, &version) != 4)
return;
m->product = product;
m->product[0] = product;
m->product[1] = 0;
m->vendor = vendor;
m->version = version;
m->bits |= M_PID|M_VID|M_VERSION;
@ -1545,8 +1559,19 @@ quirk_match_section(struct quirks_context *ctx,
matched_flags |= flag;
break;
case M_PID:
if (m->product == s->match.product)
matched_flags |= flag;
ARRAY_FOR_EACH(m->product, mi) {
if (*mi == 0 || matched_flags & flag)
break;
ARRAY_FOR_EACH(s->match.product, si) {
if (*si == 0)
break;
if (*mi == *si) {
matched_flags |= flag;
break;
}
}
}
break;
case M_VERSION:
if (m->version == s->match.version)

View file

@ -581,11 +581,11 @@ START_TEST(quirks_parse_product)
struct quirks_context *ctx;
const char quirks_file[] =
"[Section name]\n"
"MatchProduct=0x0000\n"
"MatchProduct=0x12AB\n"
"ModelAppleTouchpad=1\n"
"\n"
"[Section name]\n"
"MatchProduct=0x0001\n"
"MatchProduct=0x0001;0x1234;0xABCD\n"
"ModelAppleTouchpad=1\n"
"\n"
"[Section name]\n"
@ -604,6 +604,48 @@ START_TEST(quirks_parse_product)
}
END_TEST
START_TEST(quirks_parse_product_too_many)
{
struct quirks_context *ctx;
const char prologue[] =
"[Section name]\n"
"MatchProduct=0x12AB\n"
"ModelAppleTouchpad=1\n"
"\n"
"[Section name]\n"
"MatchProduct=";
const char epilogue[] = "\n"
"ModelAppleTouchpad=1\n"
"\n"
"[Section name]\n"
"MatchProduct=0x2343\n"
"ModelAppleTouchpad=1\n";
char matches[4096] = {0};
for (int i = 0; i < 128; i++) {
int len = strlen(matches);
int remaining = sizeof(matches) - len;
snprintf(&matches[len], remaining, "0x%04X", i);
}
char *quirks_file = NULL;
xasprintf(&quirks_file, "%s%s%s", prologue, matches, epilogue);
struct data_dir dd = make_data_dir(quirks_file);
free(quirks_file);
/* This test will only blow up in valgrind/asan */
ctx = quirks_init_subsystem(dd.dirname,
NULL,
log_handler,
NULL,
QLOG_CUSTOM_LOG_PRIORITIES);
ck_assert(ctx == NULL);
cleanup_data_dir(dd);
}
END_TEST
START_TEST(quirks_parse_product_invalid)
{
struct quirks_context *ctx;
@ -1590,6 +1632,7 @@ TEST_COLLECTION(quirks)
litest_add_deviceless(quirks_parse_vendor);
litest_add_deviceless(quirks_parse_vendor_invalid);
litest_add_deviceless(quirks_parse_product);
litest_add_deviceless(quirks_parse_product_too_many);
litest_add_deviceless(quirks_parse_product_invalid);
litest_add_deviceless(quirks_parse_version);
litest_add_deviceless(quirks_parse_version_invalid);