1 /*
2 * Copyright © 2018 Red Hat, Inc.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 * DEALINGS IN THE SOFTWARE.
22 */
23
24 /* This has the hallmarks of a library to make it re-usable from the tests
25 * and from the list-quirks tool. It doesn't have all of the features from a
26 * library you'd expect though
27 */
28
29 #include <sys/types.h>
30 #include <dev/evdev/input.h>
31
32 #undef NDEBUG /* You don't get to disable asserts here */
33 #include <assert.h>
34 #include <dirent.h>
35 #include <errno.h>
36 #include <fnmatch.h>
37 #include <kenv.h>
38 #include <libgen.h>
39 #include <limits.h>
40 #include <stdarg.h>
41 #include <stdbool.h>
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <string.h>
45
46 #include "quirks.h"
47 #include "util.h"
48 #include "util-list.h"
49
50
51 /* Custom logging so we can have detailed output for the tool but minimal
52 * logging for moused itself. */
53 #define qlog_debug(ctx_, ...) quirk_log_msg((ctx_), QLOG_NOISE, __VA_ARGS__)
54 #define qlog_info(ctx_, ...) quirk_log_msg((ctx_), QLOG_INFO, __VA_ARGS__)
55 #define qlog_error(ctx_, ...) quirk_log_msg((ctx_), QLOG_ERROR, __VA_ARGS__)
56 #define qlog_parser(ctx_, ...) quirk_log_msg((ctx_), QLOG_PARSER_ERROR, __VA_ARGS__)
57
58 enum property_type {
59 PT_UINT,
60 PT_INT,
61 PT_STRING,
62 PT_BOOL,
63 PT_DIMENSION,
64 PT_RANGE,
65 PT_DOUBLE,
66 PT_TUPLES,
67 PT_UINT_ARRAY,
68 };
69
70 struct quirk_array {
71 union {
72 uint32_t u[32];
73 } data;
74 size_t nelements;
75 };
76
77 /**
78 * Generic value holder for the property types we support. The type
79 * identifies which value in the union is defined and we expect callers to
80 * already know which type yields which value.
81 */
82 struct property {
83 size_t refcount;
84 struct list link; /* struct sections.properties */
85
86 enum quirk id;
87 enum property_type type;
88 union {
89 bool b;
90 uint32_t u;
91 int32_t i;
92 char *s;
93 double d;
94 struct quirk_dimensions dim;
95 struct quirk_range range;
96 struct quirk_tuples tuples;
97 struct quirk_array array;
98 } value;
99 };
100
101 enum match_flags {
102 M_NAME = bit(0),
103 M_BUS = bit(1),
104 M_VID = bit(2),
105 M_PID = bit(3),
106 M_DMI = bit(4),
107 M_UDEV_TYPE = bit(5),
108 M_DT = bit(6),
109 M_VERSION = bit(7),
110 M_UNIQ = bit(8),
111
112 M_LAST = M_UNIQ,
113 };
114
115 enum bustype {
116 BT_UNKNOWN,
117 BT_USB,
118 BT_BLUETOOTH,
119 BT_PS2,
120 BT_RMI,
121 BT_I2C,
122 BT_SPI,
123 };
124
125 enum udev_type {
126 UDEV_MOUSE = bit(1),
127 UDEV_POINTINGSTICK = bit(2),
128 UDEV_TOUCHPAD = bit(3),
129 UDEV_TABLET = bit(4),
130 UDEV_TABLET_PAD = bit(5),
131 UDEV_JOYSTICK = bit(6),
132 UDEV_KEYBOARD = bit(7),
133 };
134
135 /**
136 * Contains the combined set of matches for one section or the values for
137 * one device.
138 *
139 * bits defines which fields are set, the rest is zero.
140 */
141 struct match {
142 uint32_t bits;
143
144 char *name;
145 char *uniq;
146 enum bustype bus;
147 uint32_t vendor;
148 uint32_t product[64]; /* zero-terminated */
149 uint32_t version;
150
151 char *dmi; /* dmi modalias with preceding "dmi:" */
152
153 /* We can have more than one type set, so this is a bitfield */
154 uint32_t udev_type;
155
156 char *dt; /* device tree compatible (first) string */
157 };
158
159 /**
160 * Represents one section in the .quirks file.
161 */
162 struct section {
163 struct list link;
164
165 bool has_match; /* to check for empty sections */
166 bool has_property; /* to check for empty sections */
167
168 char *name; /* the [Section Name] */
169 struct match match;
170 struct list properties;
171 };
172
173 /**
174 * The struct returned to the caller. It contains the
175 * properties for a given device.
176 */
177 struct quirks {
178 size_t refcount;
179 struct list link; /* struct quirks_context.quirks */
180
181 /* These are not ref'd, just a collection of pointers */
182 struct property **properties;
183 size_t nproperties;
184
185 /* Special properties for AttrEventCode and AttrInputCode, these are
186 * owned by us, not the section */
187 struct list floating_properties;
188 };
189
190 /**
191 * Quirk matching context, initialized once with quirks_init_subsystem()
192 */
193 struct quirks_context {
194 size_t refcount;
195
196 moused_log_handler *log_handler;
197 enum quirks_log_type log_type;
198
199 char *dmi;
200 char *dt;
201
202 struct list sections;
203
204 /* list of quirks handed to moused, just for bookkeeping */
205 struct list quirks;
206 };
207
208 MOUSED_ATTRIBUTE_PRINTF(3, 0)
209 static inline void
quirk_log_msg_va(struct quirks_context * ctx,enum quirks_log_priorities priority,const char * format,va_list args)210 quirk_log_msg_va(struct quirks_context *ctx,
211 enum quirks_log_priorities priority,
212 const char *format,
213 va_list args)
214 {
215 switch (priority) {
216 /* We don't use this if we're logging through syslog */
217 default:
218 case QLOG_NOISE:
219 case QLOG_PARSER_ERROR:
220 if (ctx->log_type == QLOG_MOUSED_LOGGING)
221 return;
222 break;
223 case QLOG_DEBUG: /* These map straight to syslog priorities */
224 case QLOG_INFO:
225 case QLOG_ERROR:
226 break;
227 }
228
229 ctx->log_handler(priority,
230 0,
231 format,
232 args);
233 }
234
235 MOUSED_ATTRIBUTE_PRINTF(3, 4)
236 static inline void
quirk_log_msg(struct quirks_context * ctx,enum quirks_log_priorities priority,const char * format,...)237 quirk_log_msg(struct quirks_context *ctx,
238 enum quirks_log_priorities priority,
239 const char *format,
240 ...)
241 {
242 va_list args;
243
244 va_start(args, format);
245 quirk_log_msg_va(ctx, priority, format, args);
246 va_end(args);
247
248 }
249
250 const char *
quirk_get_name(enum quirk q)251 quirk_get_name(enum quirk q)
252 {
253 switch(q) {
254 case QUIRK_MODEL_ALPS_SERIAL_TOUCHPAD: return "ModelALPSSerialTouchpad";
255 case QUIRK_MODEL_APPLE_TOUCHPAD: return "ModelAppleTouchpad";
256 case QUIRK_MODEL_APPLE_TOUCHPAD_ONEBUTTON: return "ModelAppleTouchpadOneButton";
257 case QUIRK_MODEL_BOUNCING_KEYS: return "ModelBouncingKeys";
258 case QUIRK_MODEL_CHROMEBOOK: return "ModelChromebook";
259 case QUIRK_MODEL_CLEVO_W740SU: return "ModelClevoW740SU";
260 case QUIRK_MODEL_DELL_CANVAS_TOTEM: return "ModelDellCanvasTotem";
261 case QUIRK_MODEL_HP_PAVILION_DM4_TOUCHPAD: return "ModelHPPavilionDM4Touchpad";
262 case QUIRK_MODEL_HP_ZBOOK_STUDIO_G3: return "ModelHPZBookStudioG3";
263 case QUIRK_MODEL_INVERT_HORIZONTAL_SCROLLING: return "ModelInvertHorizontalScrolling";
264 case QUIRK_MODEL_LENOVO_SCROLLPOINT: return "ModelLenovoScrollPoint";
265 case QUIRK_MODEL_LENOVO_T450_TOUCHPAD: return "ModelLenovoT450Touchpad";
266 case QUIRK_MODEL_LENOVO_X1GEN6_TOUCHPAD: return "ModelLenovoX1Gen6Touchpad";
267 case QUIRK_MODEL_LENOVO_X230: return "ModelLenovoX230";
268 case QUIRK_MODEL_SYNAPTICS_SERIAL_TOUCHPAD: return "ModelSynapticsSerialTouchpad";
269 case QUIRK_MODEL_SYSTEM76_BONOBO: return "ModelSystem76Bonobo";
270 case QUIRK_MODEL_SYSTEM76_GALAGO: return "ModelSystem76Galago";
271 case QUIRK_MODEL_SYSTEM76_KUDU: return "ModelSystem76Kudu";
272 case QUIRK_MODEL_TABLET_MODE_NO_SUSPEND: return "ModelTabletModeNoSuspend";
273 case QUIRK_MODEL_TABLET_MODE_SWITCH_UNRELIABLE: return "ModelTabletModeSwitchUnreliable";
274 case QUIRK_MODEL_TOUCHPAD_VISIBLE_MARKER: return "ModelTouchpadVisibleMarker";
275 case QUIRK_MODEL_TOUCHPAD_PHANTOM_CLICKS: return "ModelTouchpadPhantomClicks";
276 case QUIRK_MODEL_TRACKBALL: return "ModelTrackball";
277 case QUIRK_MODEL_WACOM_TOUCHPAD: return "ModelWacomTouchpad";
278 case QUIRK_MODEL_PRESSURE_PAD: return "ModelPressurePad";
279
280 case QUIRK_ATTR_SIZE_HINT: return "AttrSizeHint";
281 case QUIRK_ATTR_TOUCH_SIZE_RANGE: return "AttrTouchSizeRange";
282 case QUIRK_ATTR_PALM_SIZE_THRESHOLD: return "AttrPalmSizeThreshold";
283 case QUIRK_ATTR_LID_SWITCH_RELIABILITY: return "AttrLidSwitchReliability";
284 case QUIRK_ATTR_KEYBOARD_INTEGRATION: return "AttrKeyboardIntegration";
285 case QUIRK_ATTR_TRACKPOINT_INTEGRATION: return "AttrPointingStickIntegration";
286 case QUIRK_ATTR_TPKBCOMBO_LAYOUT: return "AttrTPKComboLayout";
287 case QUIRK_ATTR_PRESSURE_RANGE: return "AttrPressureRange";
288 case QUIRK_ATTR_PALM_PRESSURE_THRESHOLD: return "AttrPalmPressureThreshold";
289 case QUIRK_ATTR_RESOLUTION_HINT: return "AttrResolutionHint";
290 case QUIRK_ATTR_TRACKPOINT_MULTIPLIER: return "AttrTrackpointMultiplier";
291 case QUIRK_ATTR_THUMB_PRESSURE_THRESHOLD: return "AttrThumbPressureThreshold";
292 case QUIRK_ATTR_USE_VELOCITY_AVERAGING: return "AttrUseVelocityAveraging";
293 case QUIRK_ATTR_TABLET_SMOOTHING: return "AttrTabletSmoothing";
294 case QUIRK_ATTR_THUMB_SIZE_THRESHOLD: return "AttrThumbSizeThreshold";
295 case QUIRK_ATTR_MSC_TIMESTAMP: return "AttrMscTimestamp";
296 case QUIRK_ATTR_EVENT_CODE: return "AttrEventCode";
297 case QUIRK_ATTR_INPUT_PROP: return "AttrInputProp";
298
299 case MOUSED_GRAB_DEVICE: return "MousedGrabDevice";
300 case MOUSED_IGNORE_DEVICE: return "MousedIgnoreDevice";
301
302 case MOUSED_CLICK_THRESHOLD: return "MousedClickThreshold";
303 case MOUSED_DRIFT_TERMINATE: return "MousedDriftTerminate";
304 case MOUSED_DRIFT_DISTANCE: return "MousedDriftDistance";
305 case MOUSED_DRIFT_TIME: return "MousedDriftTime";
306 case MOUSED_DRIFT_AFTER: return "MousedDriftAfter";
307 case MOUSED_EMULATE_THIRD_BUTTON: return "MousedEmulateThirdButton";
308 case MOUSED_EMULATE_THIRD_BUTTON_TIMEOUT: return "MousedEmulateThirdButtonTimeout";
309 case MOUSED_EXPONENTIAL_ACCEL: return "MousedExponentialAccel";
310 case MOUSED_EXPONENTIAL_OFFSET: return "MousedExponentialOffset";
311 case MOUSED_LINEAR_ACCEL_X: return "MousedLinearAccelX";
312 case MOUSED_LINEAR_ACCEL_Y: return "MousedLinearAccelY";
313 case MOUSED_LINEAR_ACCEL_Z: return "MousedLinearAccelZ";
314 case MOUSED_MAP_Z_AXIS: return "MousedMapZAxis";
315 case MOUSED_VIRTUAL_SCROLL_ENABLE: return "MousedVirtualScrollEnable";
316 case MOUSED_HOR_VIRTUAL_SCROLL_ENABLE: return "MousedHorVirtualScrollEnable";
317 case MOUSED_VIRTUAL_SCROLL_SPEED: return "MousedVirtualScrollSpeed";
318 case MOUSED_VIRTUAL_SCROLL_THRESHOLD: return "MousedVirtualScrollThreshold";
319 case MOUSED_WMODE: return "MousedWMode";
320
321 case MOUSED_TWO_FINGER_SCROLL: return "MousedTwoFingerScroll";
322 case MOUSED_NATURAL_SCROLL: return "MousedNaturalScroll";
323 case MOUSED_THREE_FINGER_DRAG: return "MousedThreeFingerDrag";
324 case MOUSED_SOFTBUTTON2_X: return "MousedSoftButton2X";
325 case MOUSED_SOFTBUTTON3_X: return "MousedSoftButton3X";
326 case MOUSED_SOFTBUTTONS_Y: return "MousedSoftButtonsY";
327 case MOUSED_TAP_TIMEOUT: return "MousedTapTimeout";
328 case MOUSED_TAP_PRESSURE_THRESHOLD: return "MousedTapPressureThreshold";
329 case MOUSED_TAP_MAX_DELTA: return "MousedTapMaxDelta";
330 case MOUSED_TAPHOLD_TIMEOUT: return "MousedTapholdTimeout";
331 case MOUSED_VSCROLL_MIN_DELTA: return "MousedVScrollMinDelta";
332 case MOUSED_VSCROLL_HOR_AREA: return "MousedVScrollHorArea";
333 case MOUSED_VSCROLL_VER_AREA: return "MousedVScrollVerArea";
334
335
336 default:
337 abort();
338 }
339 }
340
341 static inline const char *
matchflagname(enum match_flags f)342 matchflagname(enum match_flags f)
343 {
344 switch(f) {
345 case M_NAME: return "MatchName"; break;
346 case M_BUS: return "MatchBus"; break;
347 case M_VID: return "MatchVendor"; break;
348 case M_PID: return "MatchProduct"; break;
349 case M_VERSION: return "MatchVersion"; break;
350 case M_DMI: return "MatchDMIModalias"; break;
351 case M_UDEV_TYPE: return "MatchDevType"; break;
352 case M_DT: return "MatchDeviceTree"; break;
353 case M_UNIQ: return "MatchUniq"; break;
354 default:
355 abort();
356 }
357 }
358
359 static inline struct property *
property_new(void)360 property_new(void)
361 {
362 struct property *p;
363
364 p = zalloc(sizeof *p);
365 p->refcount = 1;
366 list_init(&p->link);
367
368 return p;
369 }
370
371 static inline struct property *
property_ref(struct property * p)372 property_ref(struct property *p)
373 {
374 assert(p->refcount > 0);
375 p->refcount++;
376 return p;
377 }
378
379 static inline struct property *
property_unref(struct property * p)380 property_unref(struct property *p)
381 {
382 /* Note: we don't cleanup here, that is a separate call so we
383 can abort if we haven't cleaned up correctly. */
384 assert(p->refcount > 0);
385 p->refcount--;
386
387 return NULL;
388 }
389
390 /* Separate call so we can verify that the caller unrefs the property
391 * before shutting down the subsystem.
392 */
393 static inline void
property_cleanup(struct property * p)394 property_cleanup(struct property *p)
395 {
396 /* If we get here, the quirks must've been removed already */
397 property_unref(p);
398 assert(p->refcount == 0);
399
400 list_remove(&p->link);
401 if (p->type == PT_STRING)
402 free(p->value.s);
403 free(p);
404 }
405
406 /**
407 * Return the system DMI info in modalias format.
408 */
409 static inline char *
init_dmi(void)410 init_dmi(void)
411 {
412 #define LEN (KENV_MVALLEN + 1)
413 char *modalias;
414 char bios_vendor[LEN], bios_version[LEN], bios_date[LEN];
415 char sys_vendor[LEN], product_name[LEN], product_version[LEN];
416 char board_vendor[LEN], board_name[LEN], board_version[LEN];
417 char chassis_vendor[LEN], chassis_type[LEN], chassis_version[LEN];
418 int chassis_type_num = 0x2;
419
420 kenv(KENV_GET, "smbios.bios.vendor", bios_vendor, LEN);
421 kenv(KENV_GET, "smbios.bios.version", bios_version, LEN);
422 kenv(KENV_GET, "smbios.bios.reldate", bios_date, LEN);
423 kenv(KENV_GET, "smbios.system.maker", sys_vendor, LEN);
424 kenv(KENV_GET, "smbios.system.product", product_name, LEN);
425 kenv(KENV_GET, "smbios.system.version", product_version, LEN);
426 kenv(KENV_GET, "smbios.planar.maker", board_vendor, LEN);
427 kenv(KENV_GET, "smbios.planar.product", board_name, LEN);
428 kenv(KENV_GET, "smbios.planar.version", board_version, LEN);
429 kenv(KENV_GET, "smbios.chassis.vendor", chassis_vendor, LEN);
430 kenv(KENV_GET, "smbios.chassis.type", chassis_type, LEN);
431 kenv(KENV_GET, "smbios.chassis.version", chassis_version, LEN);
432 #undef LEN
433
434 if (strcmp(chassis_type, "Desktop") == 0)
435 chassis_type_num = 0x3;
436 else if (strcmp(chassis_type, "Portable") == 0)
437 chassis_type_num = 0x8;
438 else if (strcmp(chassis_type, "Laptop") == 0)
439 chassis_type_num = 0x9;
440 else if (strcmp(chassis_type, "Notebook") == 0)
441 chassis_type_num = 0xA;
442 else if (strcmp(chassis_type, "Tablet") == 0)
443 chassis_type_num = 0x1E;
444 else if (strcmp(chassis_type, "Convertible") == 0)
445 chassis_type_num = 0x1F;
446 else if (strcmp(chassis_type, "Detachable") == 0)
447 chassis_type_num = 0x20;
448
449 xasprintf(&modalias,
450 "dmi:bvn%s:bvr%s:bd%s:svn%s:pn%s:pvr%s:rvn%s:rn%s:rvr%s:cvn%s:ct%d:cvr%s:",
451 bios_vendor, bios_version, bios_date, sys_vendor, product_name,
452 product_version, board_vendor, board_name, board_version, chassis_vendor,
453 chassis_type_num, chassis_version);
454
455 return modalias;
456 }
457
458 /**
459 * Return the dt compatible string
460 */
461 static inline char *
init_dt(void)462 init_dt(void)
463 {
464 char compatible[1024];
465 char *copy = NULL;
466 const char *syspath = "/sys/firmware/devicetree/base/compatible";
467 FILE *fp;
468
469 if (getenv("LIBINPUT_RUNNING_TEST_SUITE"))
470 return safe_strdup("");
471
472 fp = fopen(syspath, "r");
473 if (!fp)
474 return NULL;
475
476 /* devicetree/base/compatible has multiple null-terminated entries
477 but we only care about the first one here, so strdup is enough */
478 if (fgets(compatible, sizeof(compatible), fp)) {
479 copy = safe_strdup(compatible);
480 }
481
482 fclose(fp);
483
484 return copy;
485 }
486
487 static inline struct section *
section_new(const char * path,const char * name)488 section_new(const char *path, const char *name)
489 {
490 struct section *s = zalloc(sizeof(*s));
491
492 char *path_dup = safe_strdup(path);
493 xasprintf(&s->name, "%s (%s)", name, basename(path_dup));
494 free(path_dup);
495 list_init(&s->link);
496 list_init(&s->properties);
497
498 return s;
499 }
500
501 static inline void
section_destroy(struct section * s)502 section_destroy(struct section *s)
503 {
504 struct property *p;
505
506 free(s->name);
507 free(s->match.name);
508 free(s->match.uniq);
509 free(s->match.dmi);
510 free(s->match.dt);
511
512 list_for_each_safe(p, &s->properties, link)
513 property_cleanup(p);
514
515 assert(list_empty(&s->properties));
516
517 list_remove(&s->link);
518 free(s);
519 }
520
521 static inline bool
parse_hex(const char * value,unsigned int * parsed)522 parse_hex(const char *value, unsigned int *parsed)
523 {
524 return strstartswith(value, "0x") &&
525 safe_atou_base(value, parsed, 16) &&
526 strspn(value, "0123456789xABCDEF") == strlen(value) &&
527 *parsed <= 0xFFFF;
528 }
529
530 static int
strv_parse_hex(const char * str,size_t index,void * data)531 strv_parse_hex(const char *str, size_t index, void *data)
532 {
533 unsigned int *product = data;
534
535 return !parse_hex(str, &product[index]); /* 0 for success */
536 }
537
538 /**
539 * Parse a MatchFooBar=banana line.
540 *
541 * @param section The section struct to be filled in
542 * @param key The MatchFooBar part of the line
543 * @param value The banana part of the line.
544 *
545 * @return true on success, false otherwise.
546 */
547 static bool
parse_match(struct quirks_context * ctx,struct section * s,const char * key,const char * value)548 parse_match(struct quirks_context *ctx,
549 struct section *s,
550 const char *key,
551 const char *value)
552 {
553 int rc = false;
554
555 #define check_set_bit(s_, bit_) { \
556 if ((s_)->match.bits & (bit_)) goto out; \
557 (s_)->match.bits |= (bit_); \
558 }
559
560 assert(strlen(value) >= 1);
561
562 if (streq(key, "MatchName")) {
563 check_set_bit(s, M_NAME);
564 s->match.name = safe_strdup(value);
565 } else if (streq(key, "MatchUniq")) {
566 check_set_bit(s, M_UNIQ);
567 s->match.uniq = safe_strdup(value);
568 } else if (streq(key, "MatchBus")) {
569 check_set_bit(s, M_BUS);
570 if (streq(value, "usb"))
571 s->match.bus = BT_USB;
572 else if (streq(value, "bluetooth"))
573 s->match.bus = BT_BLUETOOTH;
574 else if (streq(value, "ps2"))
575 s->match.bus = BT_PS2;
576 else if (streq(value, "rmi"))
577 s->match.bus = BT_RMI;
578 else if (streq(value, "i2c"))
579 s->match.bus = BT_I2C;
580 else if (streq(value, "spi"))
581 s->match.bus = BT_SPI;
582 else
583 goto out;
584 } else if (streq(key, "MatchVendor")) {
585 unsigned int vendor;
586
587 check_set_bit(s, M_VID);
588 if (!parse_hex(value, &vendor))
589 goto out;
590
591 s->match.vendor = vendor;
592 } else if (streq(key, "MatchProduct")) {
593 unsigned int product[ARRAY_LENGTH(s->match.product)] = {0};
594 const size_t max = ARRAY_LENGTH(s->match.product) - 1;
595
596 size_t nelems = 0;
597 char **strs = strv_from_string(value, ";", &nelems);
598 int rc = strv_for_each_n((const char**)strs, max, strv_parse_hex, product);
599 strv_free(strs);
600 if (rc != 0)
601 goto out;
602
603 check_set_bit(s, M_PID);
604 memcpy(s->match.product, product, sizeof(product));
605 } else if (streq(key, "MatchVersion")) {
606 unsigned int version;
607
608 check_set_bit(s, M_VERSION);
609 if (!parse_hex(value, &version))
610 goto out;
611
612 s->match.version = version;
613 } else if (streq(key, "MatchDMIModalias")) {
614 check_set_bit(s, M_DMI);
615 if (!strstartswith(value, "dmi:")) {
616 qlog_parser(ctx,
617 "%s: MatchDMIModalias must start with 'dmi:'\n",
618 s->name);
619 goto out;
620 }
621 s->match.dmi = safe_strdup(value);
622 } else if (streq(key, "MatchUdevType") || streq(key, "MatchDevType")) {
623 check_set_bit(s, M_UDEV_TYPE);
624 if (streq(value, "touchpad"))
625 s->match.udev_type = UDEV_TOUCHPAD;
626 else if (streq(value, "mouse"))
627 s->match.udev_type = UDEV_MOUSE;
628 else if (streq(value, "pointingstick"))
629 s->match.udev_type = UDEV_POINTINGSTICK;
630 else if (streq(value, "keyboard"))
631 s->match.udev_type = UDEV_KEYBOARD;
632 else if (streq(value, "joystick"))
633 s->match.udev_type = UDEV_JOYSTICK;
634 else if (streq(value, "tablet"))
635 s->match.udev_type = UDEV_TABLET;
636 else if (streq(value, "tablet-pad"))
637 s->match.udev_type = UDEV_TABLET_PAD;
638 else
639 goto out;
640 } else if (streq(key, "MatchDeviceTree")) {
641 check_set_bit(s, M_DT);
642 s->match.dt = safe_strdup(value);
643 } else {
644 qlog_error(ctx, "Unknown match key '%s'\n", key);
645 goto out;
646 }
647
648 #undef check_set_bit
649 s->has_match = true;
650 rc = true;
651 out:
652 return rc;
653 }
654
655 /**
656 * Parse a ModelFooBar=1 line.
657 *
658 * @param section The section struct to be filled in
659 * @param key The ModelFooBar part of the line
660 * @param value The value after the =, must be 1 or 0.
661 *
662 * @return true on success, false otherwise.
663 */
664 static bool
parse_model(struct quirks_context * ctx,struct section * s,const char * key,const char * value)665 parse_model(struct quirks_context *ctx,
666 struct section *s,
667 const char *key,
668 const char *value)
669 {
670 bool b;
671 enum quirk q = QUIRK_MODEL_ALPS_SERIAL_TOUCHPAD;
672
673 assert(strstartswith(key, "Model"));
674
675 if (!parse_boolean_property(value, &b))
676 return false;
677
678 do {
679 if (streq(key, quirk_get_name(q))) {
680 struct property *p = property_new();
681 p->id = q,
682 p->type = PT_BOOL;
683 p->value.b = b;
684 list_append(&s->properties, &p->link);
685 s->has_property = true;
686 return true;
687 }
688 } while (++q < _QUIRK_LAST_MODEL_QUIRK_);
689
690 qlog_error(ctx, "Unknown key %s in %s\n", key, s->name);
691
692 return false;
693 }
694
695 /**
696 * Parse a AttrFooBar=banana line.
697 *
698 * @param section The section struct to be filled in
699 * @param key The AttrFooBar part of the line
700 * @param value The banana part of the line.
701 *
702 * Value parsing depends on the attribute type.
703 *
704 * @return true on success, false otherwise.
705 */
706 static inline bool
parse_attr(struct quirks_context * ctx,struct section * s,const char * key,const char * value)707 parse_attr(struct quirks_context *ctx,
708 struct section *s,
709 const char *key,
710 const char *value)
711 {
712 struct property *p = property_new();
713 bool rc = false;
714 struct quirk_dimensions dim;
715 struct quirk_range range;
716 unsigned int v;
717 bool b;
718 double d;
719
720 if (streq(key, quirk_get_name(QUIRK_ATTR_SIZE_HINT))) {
721 p->id = QUIRK_ATTR_SIZE_HINT;
722 if (!parse_dimension_property(value, &dim.x, &dim.y))
723 goto out;
724 p->type = PT_DIMENSION;
725 p->value.dim = dim;
726 rc = true;
727 } else if (streq(key, quirk_get_name(QUIRK_ATTR_TOUCH_SIZE_RANGE))) {
728 p->id = QUIRK_ATTR_TOUCH_SIZE_RANGE;
729 if (!parse_range_property(value, &range.upper, &range.lower))
730 goto out;
731 p->type = PT_RANGE;
732 p->value.range = range;
733 rc = true;
734 } else if (streq(key, quirk_get_name(QUIRK_ATTR_PALM_SIZE_THRESHOLD))) {
735 p->id = QUIRK_ATTR_PALM_SIZE_THRESHOLD;
736 if (!safe_atou(value, &v))
737 goto out;
738 p->type = PT_UINT;
739 p->value.u = v;
740 rc = true;
741 } else if (streq(key, quirk_get_name(QUIRK_ATTR_LID_SWITCH_RELIABILITY))) {
742 p->id = QUIRK_ATTR_LID_SWITCH_RELIABILITY;
743 if (!streq(value, "reliable") &&
744 !streq(value, "write_open") &&
745 !streq(value, "unreliable"))
746 goto out;
747 p->type = PT_STRING;
748 p->value.s = safe_strdup(value);
749 rc = true;
750 } else if (streq(key, quirk_get_name(QUIRK_ATTR_KEYBOARD_INTEGRATION))) {
751 p->id = QUIRK_ATTR_KEYBOARD_INTEGRATION;
752 if (!streq(value, "internal") && !streq(value, "external"))
753 goto out;
754 p->type = PT_STRING;
755 p->value.s = safe_strdup(value);
756 rc = true;
757 } else if (streq(key, quirk_get_name(QUIRK_ATTR_TRACKPOINT_INTEGRATION))) {
758 p->id = QUIRK_ATTR_TRACKPOINT_INTEGRATION;
759 if (!streq(value, "internal") && !streq(value, "external"))
760 goto out;
761 p->type = PT_STRING;
762 p->value.s = safe_strdup(value);
763 rc = true;
764 } else if (streq(key, quirk_get_name(QUIRK_ATTR_TPKBCOMBO_LAYOUT))) {
765 p->id = QUIRK_ATTR_TPKBCOMBO_LAYOUT;
766 if (!streq(value, "below"))
767 goto out;
768 p->type = PT_STRING;
769 p->value.s = safe_strdup(value);
770 rc = true;
771 } else if (streq(key, quirk_get_name(QUIRK_ATTR_PRESSURE_RANGE))) {
772 p->id = QUIRK_ATTR_PRESSURE_RANGE;
773 if (!parse_range_property(value, &range.upper, &range.lower))
774 goto out;
775 p->type = PT_RANGE;
776 p->value.range = range;
777 rc = true;
778 } else if (streq(key, quirk_get_name(QUIRK_ATTR_PALM_PRESSURE_THRESHOLD))) {
779 p->id = QUIRK_ATTR_PALM_PRESSURE_THRESHOLD;
780 if (!safe_atou(value, &v))
781 goto out;
782 p->type = PT_UINT;
783 p->value.u = v;
784 rc = true;
785 } else if (streq(key, quirk_get_name(QUIRK_ATTR_RESOLUTION_HINT))) {
786 p->id = QUIRK_ATTR_RESOLUTION_HINT;
787 if (!parse_dimension_property(value, &dim.x, &dim.y))
788 goto out;
789 p->type = PT_DIMENSION;
790 p->value.dim = dim;
791 rc = true;
792 } else if (streq(key, quirk_get_name(QUIRK_ATTR_TRACKPOINT_MULTIPLIER))) {
793 p->id = QUIRK_ATTR_TRACKPOINT_MULTIPLIER;
794 if (!safe_atod(value, &d))
795 goto out;
796 p->type = PT_DOUBLE;
797 p->value.d = d;
798 rc = true;
799 } else if (streq(key, quirk_get_name(QUIRK_ATTR_USE_VELOCITY_AVERAGING))) {
800 p->id = QUIRK_ATTR_USE_VELOCITY_AVERAGING;
801 if (!parse_boolean_property(value, &b))
802 goto out;
803 p->type = PT_BOOL;
804 p->value.b = b;
805 rc = true;
806 } else if (streq(key, quirk_get_name(QUIRK_ATTR_TABLET_SMOOTHING))) {
807 p->id = QUIRK_ATTR_TABLET_SMOOTHING;
808 if (!parse_boolean_property(value, &b))
809 goto out;
810 p->type = PT_BOOL;
811 p->value.b = b;
812 rc = true;
813 } else if (streq(key, quirk_get_name(QUIRK_ATTR_THUMB_PRESSURE_THRESHOLD))) {
814 p->id = QUIRK_ATTR_THUMB_PRESSURE_THRESHOLD;
815 if (!safe_atou(value, &v))
816 goto out;
817 p->type = PT_UINT;
818 p->value.u = v;
819 rc = true;
820 } else if (streq(key, quirk_get_name(QUIRK_ATTR_THUMB_SIZE_THRESHOLD))) {
821 p->id = QUIRK_ATTR_THUMB_SIZE_THRESHOLD;
822 if (!safe_atou(value, &v))
823 goto out;
824 p->type = PT_UINT;
825 p->value.u = v;
826 rc = true;
827 } else if (streq(key, quirk_get_name(QUIRK_ATTR_MSC_TIMESTAMP))) {
828 p->id = QUIRK_ATTR_MSC_TIMESTAMP;
829 if (!streq(value, "watch"))
830 goto out;
831 p->type = PT_STRING;
832 p->value.s = safe_strdup(value);
833 rc = true;
834 } else if (streq(key, quirk_get_name(QUIRK_ATTR_EVENT_CODE))) {
835 struct input_event events[32];
836 size_t nevents = ARRAY_LENGTH(events);
837
838 p->id = QUIRK_ATTR_EVENT_CODE;
839
840 if (!parse_evcode_property(value, events, &nevents) ||
841 nevents == 0)
842 goto out;
843
844 for (size_t i = 0; i < nevents; i++) {
845 p->value.tuples.tuples[i].first = events[i].type;
846 p->value.tuples.tuples[i].second = events[i].code;
847 p->value.tuples.tuples[i].third = events[i].value;
848 }
849 p->value.tuples.ntuples = nevents;
850 p->type = PT_TUPLES;
851
852 rc = true;
853 } else if (streq(key, quirk_get_name(QUIRK_ATTR_INPUT_PROP))) {
854 struct input_prop props[INPUT_PROP_CNT];
855 size_t nprops = ARRAY_LENGTH(props);
856
857 p->id = QUIRK_ATTR_INPUT_PROP;
858
859 if (!parse_input_prop_property(value, props, &nprops) ||
860 nprops == 0)
861 goto out;
862
863 for (size_t i = 0; i < nprops; i++) {
864 p->value.tuples.tuples[i].first = props[i].prop;
865 p->value.tuples.tuples[i].second = props[i].enabled;
866 }
867
868 rc = true;
869 } else {
870 qlog_error(ctx, "Unknown key %s in %s\n", key, s->name);
871 }
872 out:
873 if (rc) {
874 list_append(&s->properties, &p->link);
875 s->has_property = true;
876 } else {
877 property_cleanup(p);
878 }
879 return rc;
880 }
881
882 /**
883 * Parse a MousedFooBar=banana line.
884 *
885 * @param section The section struct to be filled in
886 * @param key The MousedFooBar part of the line
887 * @param value The banana part of the line.
888 *
889 * Value parsing depends on the attribute type.
890 *
891 * @return true on success, false otherwise.
892 */
893 static inline bool
parse_moused(struct quirks_context * ctx,struct section * s,const char * key,const char * value)894 parse_moused(struct quirks_context *ctx,
895 struct section *s,
896 const char *key,
897 const char *value)
898 {
899 struct property *p = property_new();
900 bool rc = false;
901 struct quirk_dimensions dim;
902 struct quirk_range range;
903 unsigned int v;
904 int i;
905 bool b;
906 double d;
907
908 if (streq(key, quirk_get_name(MOUSED_GRAB_DEVICE))) {
909 p->id = MOUSED_GRAB_DEVICE;
910 if (!parse_boolean_property(value, &b))
911 goto out;
912 p->type = PT_BOOL;
913 p->value.b = b;
914 rc = true;
915 } else if (streq(key, quirk_get_name(MOUSED_IGNORE_DEVICE))) {
916 p->id = MOUSED_IGNORE_DEVICE;
917 if (!parse_boolean_property(value, &b))
918 goto out;
919 p->type = PT_BOOL;
920 p->value.b = b;
921 rc = true;
922 } else if (streq(key, quirk_get_name(MOUSED_CLICK_THRESHOLD))) {
923 p->id = MOUSED_CLICK_THRESHOLD;
924 if (!safe_atou(value, &v))
925 goto out;
926 p->type = PT_UINT;
927 p->value.u = v;
928 rc = true;
929 } else if (streq(key, quirk_get_name(MOUSED_DRIFT_TERMINATE))) {
930 p->id = MOUSED_DRIFT_TERMINATE;
931 if (!parse_boolean_property(value, &b))
932 goto out;
933 p->type = PT_BOOL;
934 p->value.b = b;
935 rc = true;
936 } else if (streq(key, quirk_get_name(MOUSED_DRIFT_DISTANCE))) {
937 p->id = MOUSED_DRIFT_DISTANCE;
938 if (!safe_atou(value, &v))
939 goto out;
940 p->type = PT_UINT;
941 p->value.u = v;
942 rc = true;
943 } else if (streq(key, quirk_get_name(MOUSED_DRIFT_TIME))) {
944 p->id = MOUSED_DRIFT_TIME;
945 if (!safe_atou(value, &v))
946 goto out;
947 p->type = PT_UINT;
948 p->value.u = v;
949 rc = true;
950 } else if (streq(key, quirk_get_name(MOUSED_DRIFT_AFTER))) {
951 p->id = MOUSED_DRIFT_AFTER;
952 if (!safe_atou(value, &v))
953 goto out;
954 p->type = PT_UINT;
955 p->value.u = v;
956 rc = true;
957 } else if (streq(key, quirk_get_name(MOUSED_EMULATE_THIRD_BUTTON))) {
958 p->id = MOUSED_EMULATE_THIRD_BUTTON;
959 if (!parse_boolean_property(value, &b))
960 goto out;
961 p->type = PT_BOOL;
962 p->value.b = b;
963 rc = true;
964 } else if (streq(key, quirk_get_name(MOUSED_EMULATE_THIRD_BUTTON_TIMEOUT))) {
965 p->id = MOUSED_EMULATE_THIRD_BUTTON_TIMEOUT;
966 if (!safe_atou(value, &v))
967 goto out;
968 p->type = PT_UINT;
969 p->value.u = v;
970 rc = true;
971 } else if (streq(key, quirk_get_name(MOUSED_EXPONENTIAL_ACCEL))) {
972 p->id = MOUSED_EXPONENTIAL_ACCEL;
973 if (!safe_atod(value, &d))
974 goto out;
975 p->type = PT_DOUBLE;
976 p->value.d = d;
977 rc = true;
978 } else if (streq(key, quirk_get_name(MOUSED_EXPONENTIAL_OFFSET))) {
979 p->id = MOUSED_EXPONENTIAL_OFFSET;
980 if (!safe_atod(value, &d))
981 goto out;
982 p->type = PT_DOUBLE;
983 p->value.d = d;
984 rc = true;
985 } else if (streq(key, quirk_get_name(MOUSED_LINEAR_ACCEL_X))) {
986 p->id = MOUSED_LINEAR_ACCEL_X;
987 if (!safe_atod(value, &d))
988 goto out;
989 p->type = PT_DOUBLE;
990 p->value.d = d;
991 rc = true;
992 } else if (streq(key, quirk_get_name(MOUSED_LINEAR_ACCEL_Y))) {
993 p->id = MOUSED_LINEAR_ACCEL_Y;
994 if (!safe_atod(value, &d))
995 goto out;
996 p->type = PT_DOUBLE;
997 p->value.d = d;
998 rc = true;
999 } else if (streq(key, quirk_get_name(MOUSED_LINEAR_ACCEL_Z))) {
1000 p->id = MOUSED_LINEAR_ACCEL_Z;
1001 if (!safe_atod(value, &d))
1002 goto out;
1003 p->type = PT_DOUBLE;
1004 p->value.d = d;
1005 rc = true;
1006 } else if (streq(key, quirk_get_name(MOUSED_MAP_Z_AXIS))) {
1007 } else if (streq(key, quirk_get_name(MOUSED_VIRTUAL_SCROLL_ENABLE))) {
1008 p->id = MOUSED_VIRTUAL_SCROLL_ENABLE;
1009 if (!parse_boolean_property(value, &b))
1010 goto out;
1011 p->type = PT_BOOL;
1012 p->value.b = b;
1013 rc = true;
1014 } else if (streq(key, quirk_get_name(MOUSED_HOR_VIRTUAL_SCROLL_ENABLE))) {
1015 p->id = MOUSED_HOR_VIRTUAL_SCROLL_ENABLE;
1016 if (!parse_boolean_property(value, &b))
1017 goto out;
1018 p->type = PT_BOOL;
1019 p->value.b = b;
1020 rc = true;
1021 } else if (streq(key, quirk_get_name(MOUSED_VIRTUAL_SCROLL_SPEED))) {
1022 p->id = MOUSED_VIRTUAL_SCROLL_SPEED;
1023 if (!safe_atou(value, &v))
1024 goto out;
1025 p->type = PT_UINT;
1026 p->value.u = v;
1027 rc = true;
1028 } else if (streq(key, quirk_get_name(MOUSED_VIRTUAL_SCROLL_THRESHOLD))) {
1029 p->id = MOUSED_VIRTUAL_SCROLL_THRESHOLD;
1030 if (!safe_atou(value, &v))
1031 goto out;
1032 p->type = PT_UINT;
1033 p->value.u = v;
1034 rc = true;
1035 } else if (streq(key, quirk_get_name(MOUSED_WMODE))) {
1036 p->id = MOUSED_WMODE;
1037 if (!safe_atou(value, &v))
1038 goto out;
1039 p->type = PT_UINT;
1040 p->value.u = v;
1041 rc = true;
1042 } else if (streq(key, quirk_get_name(MOUSED_TWO_FINGER_SCROLL))) {
1043 p->id = MOUSED_TWO_FINGER_SCROLL;
1044 if (!parse_boolean_property(value, &b))
1045 goto out;
1046 p->type = PT_BOOL;
1047 p->value.b = b;
1048 rc = true;
1049 } else if (streq(key, quirk_get_name(MOUSED_NATURAL_SCROLL))) {
1050 p->id = MOUSED_NATURAL_SCROLL;
1051 if (!parse_boolean_property(value, &b))
1052 goto out;
1053 p->type = PT_BOOL;
1054 p->value.b = b;
1055 rc = true;
1056 } else if (streq(key, quirk_get_name(MOUSED_THREE_FINGER_DRAG))) {
1057 p->id = MOUSED_THREE_FINGER_DRAG;
1058 if (!parse_boolean_property(value, &b))
1059 goto out;
1060 p->type = PT_BOOL;
1061 p->value.b = b;
1062 rc = true;
1063 } else if (streq(key, quirk_get_name(MOUSED_SOFTBUTTON2_X))) {
1064 p->id = MOUSED_SOFTBUTTON2_X;
1065 if (!safe_atou(value, &v))
1066 goto out;
1067 p->type = PT_UINT;
1068 p->value.u = v;
1069 rc = true;
1070 } else if (streq(key, quirk_get_name(MOUSED_SOFTBUTTON3_X))) {
1071 p->id = MOUSED_SOFTBUTTON3_X;
1072 if (!safe_atou(value, &v))
1073 goto out;
1074 p->type = PT_UINT;
1075 p->value.u = v;
1076 rc = true;
1077 } else if (streq(key, quirk_get_name(MOUSED_SOFTBUTTONS_Y))) {
1078 p->id = MOUSED_SOFTBUTTONS_Y;
1079 if (!safe_atoi(value, &i))
1080 goto out;
1081 p->type = PT_INT;
1082 p->value.i = i;
1083 rc = true;
1084 } else if (streq(key, quirk_get_name(MOUSED_TAP_TIMEOUT))) {
1085 p->id = MOUSED_TAP_TIMEOUT;
1086 if (!safe_atou(value, &v))
1087 goto out;
1088 p->type = PT_UINT;
1089 p->value.u = v;
1090 rc = true;
1091 } else if (streq(key, quirk_get_name(MOUSED_TAP_PRESSURE_THRESHOLD))) {
1092 p->id = MOUSED_TAP_PRESSURE_THRESHOLD;
1093 if (!safe_atou(value, &v))
1094 goto out;
1095 p->type = PT_UINT;
1096 p->value.u = v;
1097 rc = true;
1098 } else if (streq(key, quirk_get_name(MOUSED_TAP_MAX_DELTA))) {
1099 p->id = MOUSED_TAP_MAX_DELTA;
1100 if (!safe_atod(value, &d))
1101 goto out;
1102 p->type = PT_DOUBLE;
1103 p->value.d = d;
1104 rc = true;
1105 } else if (streq(key, quirk_get_name(MOUSED_TAPHOLD_TIMEOUT))) {
1106 p->id = MOUSED_TAPHOLD_TIMEOUT;
1107 if (!safe_atou(value, &v))
1108 goto out;
1109 p->type = PT_UINT;
1110 p->value.u = v;
1111 rc = true;
1112 } else if (streq(key, quirk_get_name(MOUSED_VSCROLL_MIN_DELTA))) {
1113 p->id = MOUSED_VSCROLL_MIN_DELTA;
1114 if (!safe_atod(value, &d))
1115 goto out;
1116 p->type = PT_DOUBLE;
1117 p->value.d = d;
1118 rc = true;
1119 } else if (streq(key, quirk_get_name(MOUSED_VSCROLL_HOR_AREA))) {
1120 p->id = MOUSED_VSCROLL_HOR_AREA;
1121 if (!safe_atod(value, &d))
1122 goto out;
1123 p->type = PT_DOUBLE;
1124 p->value.d = d;
1125 rc = true;
1126 } else if (streq(key, quirk_get_name(MOUSED_VSCROLL_VER_AREA))) {
1127 p->id = MOUSED_VSCROLL_VER_AREA;
1128 if (!safe_atod(value, &d))
1129 goto out;
1130 p->type = PT_DOUBLE;
1131 p->value.d = d;
1132 rc = true;
1133 } else {
1134 qlog_error(ctx, "Unknown key %s in %s\n", key, s->name);
1135 }
1136 out:
1137 if (rc) {
1138 list_append(&s->properties, &p->link);
1139 s->has_property = true;
1140 } else {
1141 property_cleanup(p);
1142 }
1143 return rc;
1144 }
1145
1146 /**
1147 * Parse a single line, expected to be in the format Key=value. Anything
1148 * else will be rejected with a failure.
1149 *
1150 * Our data files can only have Match, Model and Attr, so let's check for
1151 * those too.
1152 */
1153 static bool
parse_value_line(struct quirks_context * ctx,struct section * s,const char * line)1154 parse_value_line(struct quirks_context *ctx, struct section *s, const char *line)
1155 {
1156 bool rc = false;
1157
1158 size_t nelem;
1159 char **strv = strv_from_string(line, "=", &nelem);
1160 if (!strv || nelem != 2)
1161 goto out;
1162
1163 const char *key = strv[0];
1164 const char *value = strv[1];
1165 if (strlen(key) == 0 || strlen(value) == 0)
1166 goto out;
1167
1168 /* Whatever the value is, it's not supposed to be in quotes */
1169 if (value[0] == '"' || value[0] == '\'')
1170 goto out;
1171
1172 if (strstartswith(key, "Match"))
1173 rc = parse_match(ctx, s, key, value);
1174 else if (strstartswith(key, "Model"))
1175 rc = parse_model(ctx, s, key, value);
1176 else if (strstartswith(key, "Attr"))
1177 rc = parse_attr(ctx, s, key, value);
1178 else if (strstartswith(key, "Moused"))
1179 rc = parse_moused(ctx, s, key, value);
1180 else
1181 qlog_error(ctx, "Unknown value prefix %s\n", line);
1182 out:
1183 strv_free(strv);
1184 return rc;
1185 }
1186
1187 static inline bool
parse_file(struct quirks_context * ctx,const char * path)1188 parse_file(struct quirks_context *ctx, const char *path)
1189 {
1190 enum state {
1191 STATE_SECTION,
1192 STATE_MATCH,
1193 STATE_MATCH_OR_VALUE,
1194 STATE_VALUE_OR_SECTION,
1195 STATE_ANY,
1196 };
1197 FILE *fp;
1198 char line[512];
1199 bool rc = false;
1200 enum state state = STATE_SECTION;
1201 struct section *section = NULL;
1202 int lineno = -1;
1203
1204 qlog_debug(ctx, "%s\n", path);
1205
1206 /* Not using open_restricted here, if we can't access
1207 * our own data files, our installation is screwed up.
1208 */
1209 fp = fopen(path, "r");
1210 if (!fp) {
1211 /* If the file doesn't exist that's fine. Only way this can
1212 * happen is for the custom override file, all others are
1213 * provided by scandir so they do exist. Short of races we
1214 * don't care about. */
1215 if (errno == ENOENT)
1216 return true;
1217
1218 qlog_error(ctx, "%s: failed to open file\n", path);
1219 goto out;
1220 }
1221
1222 while (fgets(line, sizeof(line), fp)) {
1223 char *comment;
1224
1225 lineno++;
1226
1227 comment = strstr(line, "#");
1228 if (comment) {
1229 /* comment points to # but we need to remove the
1230 * preceding whitespaces too */
1231 comment--;
1232 while (comment >= line) {
1233 if (*comment != ' ' && *comment != '\t')
1234 break;
1235 comment--;
1236 }
1237 *(comment + 1) = '\0';
1238 } else { /* strip the trailing newline */
1239 comment = strstr(line, "\n");
1240 if (comment)
1241 *comment = '\0';
1242 }
1243 if (strlen(line) == 0)
1244 continue;
1245
1246 /* We don't use quotes for strings, so we really don't want
1247 * erroneous trailing whitespaces */
1248 switch (line[strlen(line) - 1]) {
1249 case ' ':
1250 case '\t':
1251 qlog_parser(ctx,
1252 "%s:%d: Trailing whitespace '%s'\n",
1253 path, lineno, line);
1254 goto out;
1255 }
1256
1257 switch (line[0]) {
1258 case '\0':
1259 case '\n':
1260 case '#':
1261 break;
1262 /* white space not allowed */
1263 case ' ':
1264 case '\t':
1265 qlog_parser(ctx, "%s:%d: Preceding whitespace '%s'\n",
1266 path, lineno, line);
1267 goto out;
1268 /* section title */
1269 case '[':
1270 if (line[strlen(line) - 1] != ']') {
1271 qlog_parser(ctx, "%s:%d: Closing ] missing '%s'\n",
1272 path, lineno, line);
1273 goto out;
1274 }
1275
1276 if (state != STATE_SECTION &&
1277 state != STATE_VALUE_OR_SECTION) {
1278 qlog_parser(ctx, "%s:%d: expected section before %s\n",
1279 path, lineno, line);
1280 goto out;
1281 }
1282 if (section &&
1283 (!section->has_match || !section->has_property)) {
1284 qlog_parser(ctx, "%s:%d: previous section %s was empty\n",
1285 path, lineno, section->name);
1286 goto out; /* Previous section was empty */
1287 }
1288
1289 state = STATE_MATCH;
1290 section = section_new(path, line);
1291 list_append(&ctx->sections, §ion->link);
1292 break;
1293 default:
1294 /* entries must start with A-Z */
1295 if (line[0] < 'A' || line[0] > 'Z') {
1296 qlog_parser(ctx, "%s:%d: Unexpected line %s\n",
1297 path, lineno, line);
1298 goto out;
1299 }
1300 switch (state) {
1301 case STATE_SECTION:
1302 qlog_parser(ctx, "%s:%d: expected [Section], got %s\n",
1303 path, lineno, line);
1304 goto out;
1305 case STATE_MATCH:
1306 if (!strstartswith(line, "Match")) {
1307 qlog_parser(ctx, "%s:%d: expected MatchFoo=bar, have %s\n",
1308 path, lineno, line);
1309 goto out;
1310 }
1311 state = STATE_MATCH_OR_VALUE;
1312 break;
1313 case STATE_MATCH_OR_VALUE:
1314 if (!strstartswith(line, "Match"))
1315 state = STATE_VALUE_OR_SECTION;
1316 break;
1317 case STATE_VALUE_OR_SECTION:
1318 if (strstartswith(line, "Match")) {
1319 qlog_parser(ctx, "%s:%d: expected value or [Section], have %s\n",
1320 path, lineno, line);
1321 goto out;
1322 }
1323 break;
1324 case STATE_ANY:
1325 break;
1326 }
1327
1328 if (!parse_value_line(ctx, section, line)) {
1329 qlog_parser(ctx, "%s:%d: failed to parse %s\n",
1330 path, lineno, line);
1331 goto out;
1332 }
1333 break;
1334 }
1335 }
1336
1337 if (!section) {
1338 qlog_parser(ctx, "%s: is an empty file\n", path);
1339 goto out;
1340 }
1341
1342 if ((!section->has_match || !section->has_property)) {
1343 qlog_parser(ctx, "%s:%d: previous section %s was empty\n",
1344 path, lineno, section->name);
1345 goto out; /* Previous section was empty */
1346 }
1347
1348 rc = true;
1349 out:
1350 if (fp)
1351 fclose(fp);
1352
1353 return rc;
1354 }
1355
1356 static int
is_data_file(const struct dirent * dir)1357 is_data_file(const struct dirent *dir) {
1358 return strendswith(dir->d_name, ".quirks");
1359 }
1360
1361 static inline bool
parse_files(struct quirks_context * ctx,const char * data_path)1362 parse_files(struct quirks_context *ctx, const char *data_path)
1363 {
1364 struct dirent **namelist;
1365 int ndev = -1;
1366 int idx = 0;
1367
1368 ndev = scandir(data_path, &namelist, is_data_file, versionsort);
1369 if (ndev <= 0) {
1370 qlog_error(ctx,
1371 "%s: failed to find data files\n",
1372 data_path);
1373 return false;
1374 }
1375
1376 for (idx = 0; idx < ndev; idx++) {
1377 char path[PATH_MAX];
1378
1379 snprintf(path,
1380 sizeof(path),
1381 "%s/%s",
1382 data_path,
1383 namelist[idx]->d_name);
1384
1385 if (!parse_file(ctx, path))
1386 break;
1387 }
1388
1389 for (int i = 0; i < ndev; i++)
1390 free(namelist[i]);
1391 free(namelist);
1392
1393 return idx == ndev;
1394 }
1395
1396 struct quirks_context *
quirks_init_subsystem(const char * data_path,const char * override_file,moused_log_handler log_handler,enum quirks_log_type log_type)1397 quirks_init_subsystem(const char *data_path,
1398 const char *override_file,
1399 moused_log_handler log_handler,
1400 enum quirks_log_type log_type)
1401 {
1402 _unref_(quirks_context) *ctx = zalloc(sizeof *ctx);
1403
1404 assert(data_path);
1405
1406 ctx->refcount = 1;
1407 ctx->log_handler = log_handler;
1408 ctx->log_type = log_type;
1409 list_init(&ctx->quirks);
1410 list_init(&ctx->sections);
1411
1412 qlog_debug(ctx, "%s is data root\n", data_path);
1413
1414 ctx->dmi = init_dmi();
1415 ctx->dt = init_dt();
1416 if (!ctx->dmi && !ctx->dt)
1417 return NULL;
1418
1419 if (!parse_files(ctx, data_path))
1420 return NULL;
1421
1422 if (override_file && !parse_file(ctx, override_file))
1423 return NULL;
1424
1425 return steal(&ctx);
1426 }
1427
1428 struct quirks_context *
quirks_context_ref(struct quirks_context * ctx)1429 quirks_context_ref(struct quirks_context *ctx)
1430 {
1431 assert(ctx->refcount > 0);
1432 ctx->refcount++;
1433
1434 return ctx;
1435 }
1436
1437 struct quirks_context *
quirks_context_unref(struct quirks_context * ctx)1438 quirks_context_unref(struct quirks_context *ctx)
1439 {
1440 struct section *s;
1441
1442 if (!ctx)
1443 return NULL;
1444
1445 assert(ctx->refcount >= 1);
1446 ctx->refcount--;
1447
1448 if (ctx->refcount > 0)
1449 return NULL;
1450
1451 /* Caller needs to clean up before calling this */
1452 assert(list_empty(&ctx->quirks));
1453
1454 list_for_each_safe(s, &ctx->sections, link) {
1455 section_destroy(s);
1456 }
1457
1458 free(ctx->dmi);
1459 free(ctx->dt);
1460 free(ctx);
1461
1462 return NULL;
1463 }
1464
1465 static struct quirks *
quirks_new(void)1466 quirks_new(void)
1467 {
1468 struct quirks *q;
1469
1470 q = zalloc(sizeof *q);
1471 q->refcount = 1;
1472 q->nproperties = 0;
1473 list_init(&q->link);
1474 list_init(&q->floating_properties);
1475
1476 return q;
1477 }
1478
1479 struct quirks *
quirks_unref(struct quirks * q)1480 quirks_unref(struct quirks *q)
1481 {
1482 if (!q)
1483 return NULL;
1484
1485 /* We don't really refcount, but might
1486 * as well have the API in place */
1487 assert(q->refcount == 1);
1488
1489 for (size_t i = 0; i < q->nproperties; i++) {
1490 property_unref(q->properties[i]);
1491 }
1492
1493 /* Floating properties are owned by our quirks context, need to be
1494 * cleaned up here */
1495 struct property *p;
1496 list_for_each_safe(p, &q->floating_properties, link) {
1497 property_cleanup(p);
1498 }
1499
1500 list_remove(&q->link);
1501 free(q->properties);
1502 free(q);
1503
1504 return NULL;
1505 }
1506
1507 static inline void
match_fill_name(struct match * m,struct device * device)1508 match_fill_name(struct match *m,
1509 struct device *device)
1510 {
1511 if (device->name[0] == 0)
1512 return;
1513
1514 m->name = safe_strdup(device->name);
1515
1516 m->bits |= M_NAME;
1517 }
1518
1519 static inline void
match_fill_uniq(struct match * m,struct device * device)1520 match_fill_uniq(struct match *m,
1521 struct device *device)
1522 {
1523 if (device->uniq[0] == 0)
1524 return;
1525
1526 m->uniq = safe_strdup(device->uniq);
1527
1528 m->bits |= M_UNIQ;
1529 }
1530
1531 static inline void
match_fill_bus_vid_pid(struct match * m,struct device * device)1532 match_fill_bus_vid_pid(struct match *m,
1533 struct device *device)
1534 {
1535 m->product[0] = device->id.product;
1536 m->product[1] = 0;
1537 m->vendor = device->id.vendor;
1538 m->version = device->id.version;
1539 m->bits |= M_PID|M_VID|M_VERSION;
1540 switch (device->id.bustype) {
1541 case BUS_USB:
1542 m->bus = BT_USB;
1543 m->bits |= M_BUS;
1544 break;
1545 case BUS_BLUETOOTH:
1546 m->bus = BT_BLUETOOTH;
1547 m->bits |= M_BUS;
1548 break;
1549 case BUS_I8042:
1550 m->bus = BT_PS2;
1551 m->bits |= M_BUS;
1552 break;
1553 case BUS_RMI:
1554 m->bus = BT_RMI;
1555 m->bits |= M_BUS;
1556 break;
1557 case BUS_I2C:
1558 m->bus = BT_I2C;
1559 m->bits |= M_BUS;
1560 break;
1561 case BUS_SPI:
1562 m->bus = BT_SPI;
1563 m->bits |= M_BUS;
1564 break;
1565 default:
1566 break;
1567 }
1568 }
1569
1570 static inline void
match_fill_udev_type(struct match * m,struct device * device)1571 match_fill_udev_type(struct match *m,
1572 struct device *device)
1573 {
1574 switch (device->type) {
1575 case DEVICE_TYPE_MOUSE:
1576 m->udev_type |= UDEV_MOUSE;
1577 break;
1578 case DEVICE_TYPE_POINTINGSTICK:
1579 m->udev_type |= UDEV_MOUSE | UDEV_POINTINGSTICK;
1580 break;
1581 case DEVICE_TYPE_TOUCHPAD:
1582 m->udev_type |= UDEV_TOUCHPAD;
1583 break;
1584 case DEVICE_TYPE_TABLET:
1585 m->udev_type |= UDEV_TABLET;
1586 break;
1587 case DEVICE_TYPE_TABLET_PAD:
1588 m->udev_type |= UDEV_TABLET_PAD;
1589 break;
1590 case DEVICE_TYPE_KEYBOARD:
1591 m->udev_type |= UDEV_KEYBOARD;
1592 break;
1593 case DEVICE_TYPE_JOYSTICK:
1594 m->udev_type |= UDEV_JOYSTICK;
1595 break;
1596 default:
1597 break;
1598 }
1599 m->bits |= M_UDEV_TYPE;
1600 }
1601
1602 static inline void
match_fill_dmi_dt(struct match * m,char * dmi,char * dt)1603 match_fill_dmi_dt(struct match *m, char *dmi, char *dt)
1604 {
1605 if (dmi) {
1606 m->dmi = dmi;
1607 m->bits |= M_DMI;
1608 }
1609
1610 if (dt) {
1611 m->dt = dt;
1612 m->bits |= M_DT;
1613 }
1614 }
1615
1616 static struct match *
match_new(struct device * device,char * dmi,char * dt)1617 match_new(struct device *device,
1618 char *dmi, char *dt)
1619 {
1620 struct match *m = zalloc(sizeof *m);
1621
1622 match_fill_name(m, device);
1623 match_fill_uniq(m, device);
1624 match_fill_bus_vid_pid(m, device);
1625 match_fill_dmi_dt(m, dmi, dt);
1626 match_fill_udev_type(m, device);
1627 return m;
1628 }
1629
1630 static void
match_free(struct match * m)1631 match_free(struct match *m)
1632 {
1633 /* dmi and dt are global */
1634 free(m->name);
1635 free(m->uniq);
1636 free(m);
1637 }
1638
1639 static void
quirk_merge_event_codes(struct quirks_context * ctx,struct quirks * q,const struct property * property)1640 quirk_merge_event_codes(struct quirks_context *ctx,
1641 struct quirks *q,
1642 const struct property *property)
1643 {
1644 for (size_t i = 0; i < q->nproperties; i++) {
1645 struct property *p = q->properties[i];
1646
1647 if (p->id != property->id)
1648 continue;
1649
1650 /* We have a duplicated property, merge in with ours */
1651 size_t offset = p->value.tuples.ntuples;
1652 size_t max = ARRAY_LENGTH(p->value.tuples.tuples);
1653 for (size_t j = 0; j < property->value.tuples.ntuples; j++) {
1654 if (offset + j >= max)
1655 break;
1656 p->value.tuples.tuples[offset + j] = property->value.tuples.tuples[j];
1657 p->value.tuples.ntuples++;
1658 }
1659 return;
1660 }
1661
1662 /* First time we add AttrEventCode: create a new property.
1663 * Unlike the other properties, this one isn't part of a section, it belongs
1664 * to the quirks */
1665 struct property *newprop = property_new();
1666 newprop->id = property->id;
1667 newprop->type = property->type;
1668 newprop->value.tuples = property->value.tuples;
1669 /* Caller responsible for pre-allocating space */
1670 q->properties[q->nproperties++] = property_ref(newprop);
1671 list_append(&q->floating_properties, &newprop->link);
1672 }
1673
1674 static void
quirk_apply_section(struct quirks_context * ctx,struct quirks * q,const struct section * s)1675 quirk_apply_section(struct quirks_context *ctx,
1676 struct quirks *q,
1677 const struct section *s)
1678 {
1679 struct property *p;
1680 size_t nprops = 0;
1681 void *tmp;
1682
1683 list_for_each(p, &s->properties, link) {
1684 nprops++;
1685 }
1686
1687 nprops += q->nproperties;
1688 tmp = realloc(q->properties, nprops * sizeof(p));
1689 if (!tmp)
1690 return;
1691
1692 q->properties = tmp;
1693 list_for_each(p, &s->properties, link) {
1694 qlog_debug(ctx, "property added: %s from %s\n",
1695 quirk_get_name(p->id), s->name);
1696
1697 /* All quirks but AttrEventCode and AttrInputProp
1698 * simply overwrite each other, so we can just append the
1699 * matching property and, later when checking the quirk, pick
1700 * the last one in the array.
1701 *
1702 * The event codes/input props are special because they're lists
1703 * that may *partially* override each other, e.g. a section may
1704 * enable BTN_LEFT and BTN_RIGHT but a later section may disable
1705 * only BTN_RIGHT. This should result in BTN_LEFT force-enabled
1706 * and BTN_RIGHT force-disabled.
1707 *
1708 * To hack around this, those are the only ones where only ever
1709 * have one struct property in the list (not owned by a section)
1710 * and we simply merge any extra sections onto that.
1711 */
1712 if (p->id == QUIRK_ATTR_EVENT_CODE ||
1713 p->id == QUIRK_ATTR_INPUT_PROP)
1714 quirk_merge_event_codes(ctx, q, p);
1715 else
1716 q->properties[q->nproperties++] = property_ref(p);
1717 }
1718 }
1719
1720 static bool
quirk_match_section(struct quirks_context * ctx,struct quirks * q,struct section * s,struct match * m,struct device * device)1721 quirk_match_section(struct quirks_context *ctx,
1722 struct quirks *q,
1723 struct section *s,
1724 struct match *m,
1725 struct device *device)
1726 {
1727 uint32_t matched_flags = 0x0;
1728
1729 for (uint32_t flag = 0x1; flag <= M_LAST; flag <<= 1) {
1730 uint32_t prev_matched_flags = matched_flags;
1731 /* section doesn't have this bit set, continue */
1732 if ((s->match.bits & flag) == 0)
1733 continue;
1734
1735 /* Couldn't fill in this bit for the match, so we
1736 * do not match on it */
1737 if ((m->bits & flag) == 0) {
1738 qlog_debug(ctx,
1739 "%s wants %s but we don't have that\n",
1740 s->name, matchflagname(flag));
1741 continue;
1742 }
1743
1744 /* now check the actual matching bit */
1745 switch (flag) {
1746 case M_NAME:
1747 if (fnmatch(s->match.name, m->name, 0) == 0)
1748 matched_flags |= flag;
1749 break;
1750 case M_UNIQ:
1751 if (fnmatch(s->match.uniq, m->uniq, 0) == 0)
1752 matched_flags |= flag;
1753 break;
1754 case M_BUS:
1755 if (m->bus == s->match.bus)
1756 matched_flags |= flag;
1757 break;
1758 case M_VID:
1759 if (m->vendor == s->match.vendor)
1760 matched_flags |= flag;
1761 break;
1762 case M_PID:
1763 ARRAY_FOR_EACH(m->product, mi) {
1764 if (*mi == 0 || matched_flags & flag)
1765 break;
1766
1767 ARRAY_FOR_EACH(s->match.product, si) {
1768 if (*si == 0)
1769 break;
1770 if (*mi == *si) {
1771 matched_flags |= flag;
1772 break;
1773 }
1774 }
1775 }
1776 break;
1777 case M_VERSION:
1778 if (m->version == s->match.version)
1779 matched_flags |= flag;
1780 break;
1781 case M_DMI:
1782 if (fnmatch(s->match.dmi, m->dmi, 0) == 0)
1783 matched_flags |= flag;
1784 break;
1785 case M_DT:
1786 if (fnmatch(s->match.dt, m->dt, 0) == 0)
1787 matched_flags |= flag;
1788 break;
1789 case M_UDEV_TYPE:
1790 if (s->match.udev_type & m->udev_type)
1791 matched_flags |= flag;
1792 break;
1793 default:
1794 abort();
1795 }
1796
1797 if (prev_matched_flags != matched_flags) {
1798 qlog_debug(ctx,
1799 "%s matches for %s\n",
1800 s->name,
1801 matchflagname(flag));
1802 }
1803 }
1804
1805 if (s->match.bits == matched_flags) {
1806 qlog_debug(ctx, "%s is full match\n", s->name);
1807 quirk_apply_section(ctx, q, s);
1808 }
1809
1810 return true;
1811 }
1812
1813 struct quirks *
quirks_fetch_for_device(struct quirks_context * ctx,struct device * device)1814 quirks_fetch_for_device(struct quirks_context *ctx,
1815 struct device *device)
1816 {
1817 struct section *s;
1818 struct match *m;
1819
1820 if (!ctx)
1821 return NULL;
1822
1823 qlog_debug(ctx, "%s: fetching quirks\n", device->path);
1824
1825 _unref_(quirks) *q = quirks_new();
1826
1827 m = match_new(device, ctx->dmi, ctx->dt);
1828
1829 list_for_each(s, &ctx->sections, link) {
1830 quirk_match_section(ctx, q, s, m, device);
1831 }
1832
1833 match_free(m);
1834
1835 if (q->nproperties == 0) {
1836 return NULL;
1837 }
1838
1839 list_insert(&ctx->quirks, &q->link);
1840
1841 return steal(&q);
1842 }
1843
1844 static inline struct property *
quirk_find_prop(struct quirks * q,enum quirk which)1845 quirk_find_prop(struct quirks *q, enum quirk which)
1846 {
1847 /* Run backwards to only handle the last one assigned */
1848 for (ssize_t i = q->nproperties - 1; i >= 0; i--) {
1849 struct property *p = q->properties[i];
1850 if (p->id == which)
1851 return p;
1852 }
1853
1854 return NULL;
1855 }
1856
1857 bool
quirks_has_quirk(struct quirks * q,enum quirk which)1858 quirks_has_quirk(struct quirks *q, enum quirk which)
1859 {
1860 return quirk_find_prop(q, which) != NULL;
1861 }
1862
1863 bool
quirks_get_int32(struct quirks * q,enum quirk which,int32_t * val)1864 quirks_get_int32(struct quirks *q, enum quirk which, int32_t *val)
1865 {
1866 struct property *p;
1867
1868 if (!q)
1869 return false;
1870
1871 p = quirk_find_prop(q, which);
1872 if (!p)
1873 return false;
1874
1875 assert(p->type == PT_INT);
1876 *val = p->value.i;
1877
1878 return true;
1879 }
1880
1881 bool
quirks_get_uint32(struct quirks * q,enum quirk which,uint32_t * val)1882 quirks_get_uint32(struct quirks *q, enum quirk which, uint32_t *val)
1883 {
1884 struct property *p;
1885
1886 if (!q)
1887 return false;
1888
1889 p = quirk_find_prop(q, which);
1890 if (!p)
1891 return false;
1892
1893 assert(p->type == PT_UINT);
1894 *val = p->value.u;
1895
1896 return true;
1897 }
1898
1899 bool
quirks_get_double(struct quirks * q,enum quirk which,double * val)1900 quirks_get_double(struct quirks *q, enum quirk which, double *val)
1901 {
1902 struct property *p;
1903
1904 if (!q)
1905 return false;
1906
1907 p = quirk_find_prop(q, which);
1908 if (!p)
1909 return false;
1910
1911 assert(p->type == PT_DOUBLE);
1912 *val = p->value.d;
1913
1914 return true;
1915 }
1916
1917 bool
quirks_get_string(struct quirks * q,enum quirk which,char ** val)1918 quirks_get_string(struct quirks *q, enum quirk which, char **val)
1919 {
1920 struct property *p;
1921
1922 if (!q)
1923 return false;
1924
1925 p = quirk_find_prop(q, which);
1926 if (!p)
1927 return false;
1928
1929 assert(p->type == PT_STRING);
1930 *val = p->value.s;
1931
1932 return true;
1933 }
1934
1935 bool
quirks_get_bool(struct quirks * q,enum quirk which,bool * val)1936 quirks_get_bool(struct quirks *q, enum quirk which, bool *val)
1937 {
1938 struct property *p;
1939
1940 if (!q)
1941 return false;
1942
1943 p = quirk_find_prop(q, which);
1944 if (!p)
1945 return false;
1946
1947 assert(p->type == PT_BOOL);
1948 *val = p->value.b;
1949
1950 return true;
1951 }
1952
1953 bool
quirks_get_dimensions(struct quirks * q,enum quirk which,struct quirk_dimensions * val)1954 quirks_get_dimensions(struct quirks *q,
1955 enum quirk which,
1956 struct quirk_dimensions *val)
1957 {
1958 struct property *p;
1959
1960 if (!q)
1961 return false;
1962
1963 p = quirk_find_prop(q, which);
1964 if (!p)
1965 return false;
1966
1967 assert(p->type == PT_DIMENSION);
1968 *val = p->value.dim;
1969
1970 return true;
1971 }
1972
1973 bool
quirks_get_range(struct quirks * q,enum quirk which,struct quirk_range * val)1974 quirks_get_range(struct quirks *q,
1975 enum quirk which,
1976 struct quirk_range *val)
1977 {
1978 struct property *p;
1979
1980 if (!q)
1981 return false;
1982
1983 p = quirk_find_prop(q, which);
1984 if (!p)
1985 return false;
1986
1987 assert(p->type == PT_RANGE);
1988 *val = p->value.range;
1989
1990 return true;
1991 }
1992
1993 bool
quirks_get_tuples(struct quirks * q,enum quirk which,const struct quirk_tuples ** tuples)1994 quirks_get_tuples(struct quirks *q,
1995 enum quirk which,
1996 const struct quirk_tuples **tuples)
1997 {
1998 struct property *p;
1999
2000 if (!q)
2001 return false;
2002
2003 p = quirk_find_prop(q, which);
2004 if (!p)
2005 return false;
2006
2007 assert(p->type == PT_TUPLES);
2008 *tuples = &p->value.tuples;
2009
2010 return true;
2011 }
2012
2013 bool
quirks_get_uint32_array(struct quirks * q,enum quirk which,const uint32_t ** array,size_t * nelements)2014 quirks_get_uint32_array(struct quirks *q,
2015 enum quirk which,
2016 const uint32_t **array,
2017 size_t *nelements)
2018 {
2019 struct property *p;
2020
2021 if (!q)
2022 return false;
2023
2024 p = quirk_find_prop(q, which);
2025 if (!p)
2026 return false;
2027
2028 assert(p->type == PT_UINT_ARRAY);
2029 *array = p->value.array.data.u;
2030 *nelements = p->value.array.nelements;
2031
2032 return true;
2033 }
2034