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 #include "config.h"
25
26 /* This has the hallmarks of a library to make it re-usable from the tests
27 * and from the list-quirks tool. It doesn't have all of the features from a
28 * library you'd expect though
29 */
30
31 #undef NDEBUG /* You don't get to disable asserts here */
32 #include <assert.h>
33 #include <stdlib.h>
34 #include <libudev.h>
35 #include <dirent.h>
36 #include <fnmatch.h>
37 #include <libgen.h>
38
39 #include "libinput-versionsort.h"
40 #include "libinput-util.h"
41
42 #include "quirks.h"
43
44 /* Custom logging so we can have detailed output for the tool but minimal
45 * logging for libinput itself. */
46 #define qlog_debug(ctx_, ...) quirk_log_msg((ctx_), QLOG_NOISE, __VA_ARGS__)
47 #define qlog_info(ctx_, ...) quirk_log_msg((ctx_), QLOG_INFO, __VA_ARGS__)
48 #define qlog_error(ctx_, ...) quirk_log_msg((ctx_), QLOG_ERROR, __VA_ARGS__)
49 #define qlog_parser(ctx_, ...) quirk_log_msg((ctx_), QLOG_PARSER_ERROR, __VA_ARGS__)
50
51 enum property_type {
52 PT_UINT,
53 PT_INT,
54 PT_STRING,
55 PT_BOOL,
56 PT_DIMENSION,
57 PT_RANGE,
58 PT_DOUBLE,
59 PT_TUPLES,
60 };
61
62 /**
63 * Generic value holder for the property types we support. The type
64 * identifies which value in the union is defined and we expect callers to
65 * already know which type yields which value.
66 */
67 struct property {
68 size_t refcount;
69 struct list link; /* struct sections.properties */
70
71 enum quirk id;
72 enum property_type type;
73 union {
74 bool b;
75 uint32_t u;
76 int32_t i;
77 char *s;
78 double d;
79 struct quirk_dimensions dim;
80 struct quirk_range range;
81 struct quirk_tuples tuples;
82 } value;
83 };
84
85 enum match_flags {
86 M_NAME = bit(0),
87 M_BUS = bit(1),
88 M_VID = bit(2),
89 M_PID = bit(3),
90 M_DMI = bit(4),
91 M_UDEV_TYPE = bit(5),
92 M_DT = bit(6),
93 M_VERSION = bit(7),
94
95 M_LAST = M_VERSION,
96 };
97
98 enum bustype {
99 BT_UNKNOWN,
100 BT_USB,
101 BT_BLUETOOTH,
102 BT_PS2,
103 BT_RMI,
104 BT_I2C,
105 };
106
107 enum udev_type {
108 UDEV_MOUSE = bit(1),
109 UDEV_POINTINGSTICK = bit(2),
110 UDEV_TOUCHPAD = bit(3),
111 UDEV_TABLET = bit(4),
112 UDEV_TABLET_PAD = bit(5),
113 UDEV_JOYSTICK = bit(6),
114 UDEV_KEYBOARD = bit(7),
115 };
116
117 /**
118 * Contains the combined set of matches for one section or the values for
119 * one device.
120 *
121 * bits defines which fields are set, the rest is zero.
122 */
123 struct match {
124 uint32_t bits;
125
126 char *name;
127 enum bustype bus;
128 uint32_t vendor;
129 uint32_t product;
130 uint32_t version;
131
132 char *dmi; /* dmi modalias with preceding "dmi:" */
133
134 /* We can have more than one type set, so this is a bitfield */
135 uint32_t udev_type;
136
137 char *dt; /* device tree compatible (first) string */
138 };
139
140 /**
141 * Represents one section in the .quirks file.
142 */
143 struct section {
144 struct list link;
145
146 bool has_match; /* to check for empty sections */
147 bool has_property; /* to check for empty sections */
148
149 char *name; /* the [Section Name] */
150 struct match match;
151 struct list properties;
152 };
153
154 /**
155 * The struct returned to the caller. It contains the
156 * properties for a given device.
157 */
158 struct quirks {
159 size_t refcount;
160 struct list link; /* struct quirks_context.quirks */
161
162 /* These are not ref'd, just a collection of pointers */
163 struct property **properties;
164 size_t nproperties;
165 };
166
167 /**
168 * Quirk matching context, initialized once with quirks_init_subsystem()
169 */
170 struct quirks_context {
171 size_t refcount;
172
173 libinput_log_handler log_handler;
174 enum quirks_log_type log_type;
175 struct libinput *libinput; /* for logging */
176
177 char *dmi;
178 char *dt;
179
180 struct list sections;
181
182 /* list of quirks handed to libinput, just for bookkeeping */
183 struct list quirks;
184 };
185
186 LIBINPUT_ATTRIBUTE_PRINTF(3, 0)
187 static inline void
quirk_log_msg_va(struct quirks_context * ctx,enum quirks_log_priorities priority,const char * format,va_list args)188 quirk_log_msg_va(struct quirks_context *ctx,
189 enum quirks_log_priorities priority,
190 const char *format,
191 va_list args)
192 {
193 switch (priority) {
194 /* We don't use this if we're logging through libinput */
195 default:
196 case QLOG_NOISE:
197 case QLOG_PARSER_ERROR:
198 if (ctx->log_type == QLOG_LIBINPUT_LOGGING)
199 return;
200 break;
201 case QLOG_DEBUG: /* These map straight to libinput priorities */
202 case QLOG_INFO:
203 case QLOG_ERROR:
204 break;
205 }
206
207 ctx->log_handler(ctx->libinput,
208 (enum libinput_log_priority)priority,
209 format,
210 args);
211 }
212
213 LIBINPUT_ATTRIBUTE_PRINTF(3, 4)
214 static inline void
quirk_log_msg(struct quirks_context * ctx,enum quirks_log_priorities priority,const char * format,...)215 quirk_log_msg(struct quirks_context *ctx,
216 enum quirks_log_priorities priority,
217 const char *format,
218 ...)
219 {
220 va_list args;
221
222 va_start(args, format);
223 quirk_log_msg_va(ctx, priority, format, args);
224 va_end(args);
225
226 }
227
228 const char *
quirk_get_name(enum quirk q)229 quirk_get_name(enum quirk q)
230 {
231 switch(q) {
232 case QUIRK_MODEL_ALPS_SERIAL_TOUCHPAD: return "ModelALPSSerialTouchpad";
233 case QUIRK_MODEL_APPLE_TOUCHPAD: return "ModelAppleTouchpad";
234 case QUIRK_MODEL_APPLE_TOUCHPAD_ONEBUTTON: return "ModelAppleTouchpadOneButton";
235 case QUIRK_MODEL_BOUNCING_KEYS: return "ModelBouncingKeys";
236 case QUIRK_MODEL_CHROMEBOOK: return "ModelChromebook";
237 case QUIRK_MODEL_CLEVO_W740SU: return "ModelClevoW740SU";
238 case QUIRK_MODEL_HP_PAVILION_DM4_TOUCHPAD: return "ModelHPPavilionDM4Touchpad";
239 case QUIRK_MODEL_HP_STREAM11_TOUCHPAD: return "ModelHPStream11Touchpad";
240 case QUIRK_MODEL_HP_ZBOOK_STUDIO_G3: return "ModelHPZBookStudioG3";
241 case QUIRK_MODEL_INVERT_HORIZONTAL_SCROLLING: return "ModelInvertHorizontalScrolling";
242 case QUIRK_MODEL_LENOVO_L380_TOUCHPAD: return "ModelLenovoL380Touchpad";
243 case QUIRK_MODEL_LENOVO_SCROLLPOINT: return "ModelLenovoScrollPoint";
244 case QUIRK_MODEL_LENOVO_T450_TOUCHPAD: return "ModelLenovoT450Touchpad";
245 case QUIRK_MODEL_LENOVO_T480S_TOUCHPAD: return "ModelLenovoT480sTouchpad";
246 case QUIRK_MODEL_LENOVO_T490S_TOUCHPAD: return "ModelLenovoT490sTouchpad";
247 case QUIRK_MODEL_LENOVO_X1GEN6_TOUCHPAD: return "ModelLenovoX1Gen6Touchpad";
248 case QUIRK_MODEL_LENOVO_X230: return "ModelLenovoX230";
249 case QUIRK_MODEL_SYNAPTICS_SERIAL_TOUCHPAD: return "ModelSynapticsSerialTouchpad";
250 case QUIRK_MODEL_SYSTEM76_BONOBO: return "ModelSystem76Bonobo";
251 case QUIRK_MODEL_SYSTEM76_GALAGO: return "ModelSystem76Galago";
252 case QUIRK_MODEL_SYSTEM76_KUDU: return "ModelSystem76Kudu";
253 case QUIRK_MODEL_TABLET_MODE_NO_SUSPEND: return "ModelTabletModeNoSuspend";
254 case QUIRK_MODEL_TABLET_MODE_SWITCH_UNRELIABLE: return "ModelTabletModeSwitchUnreliable";
255 case QUIRK_MODEL_TOUCHPAD_VISIBLE_MARKER: return "ModelTouchpadVisibleMarker";
256 case QUIRK_MODEL_TRACKBALL: return "ModelTrackball";
257 case QUIRK_MODEL_WACOM_TOUCHPAD: return "ModelWacomTouchpad";
258 case QUIRK_MODEL_DELL_CANVAS_TOTEM: return "ModelDellCanvasTotem";
259
260 case QUIRK_ATTR_SIZE_HINT: return "AttrSizeHint";
261 case QUIRK_ATTR_TOUCH_SIZE_RANGE: return "AttrTouchSizeRange";
262 case QUIRK_ATTR_PALM_SIZE_THRESHOLD: return "AttrPalmSizeThreshold";
263 case QUIRK_ATTR_LID_SWITCH_RELIABILITY: return "AttrLidSwitchReliability";
264 case QUIRK_ATTR_KEYBOARD_INTEGRATION: return "AttrKeyboardIntegration";
265 case QUIRK_ATTR_TRACKPOINT_INTEGRATION: return "AttrPointingStickIntegration";
266 case QUIRK_ATTR_TPKBCOMBO_LAYOUT: return "AttrTPKComboLayout";
267 case QUIRK_ATTR_PRESSURE_RANGE: return "AttrPressureRange";
268 case QUIRK_ATTR_PALM_PRESSURE_THRESHOLD: return "AttrPalmPressureThreshold";
269 case QUIRK_ATTR_RESOLUTION_HINT: return "AttrResolutionHint";
270 case QUIRK_ATTR_TRACKPOINT_MULTIPLIER: return "AttrTrackpointMultiplier";
271 case QUIRK_ATTR_THUMB_PRESSURE_THRESHOLD: return "AttrThumbPressureThreshold";
272 case QUIRK_ATTR_USE_VELOCITY_AVERAGING: return "AttrUseVelocityAveraging";
273 case QUIRK_ATTR_THUMB_SIZE_THRESHOLD: return "AttrThumbSizeThreshold";
274 case QUIRK_ATTR_MSC_TIMESTAMP: return "AttrMscTimestamp";
275 case QUIRK_ATTR_EVENT_CODE_DISABLE: return "AttrEventCodeDisable";
276 default:
277 abort();
278 }
279 }
280
281 static inline const char *
matchflagname(enum match_flags f)282 matchflagname(enum match_flags f)
283 {
284 switch(f) {
285 case M_NAME: return "MatchName"; break;
286 case M_BUS: return "MatchBus"; break;
287 case M_VID: return "MatchVendor"; break;
288 case M_PID: return "MatchProduct"; break;
289 case M_VERSION: return "MatchVersion"; break;
290 case M_DMI: return "MatchDMIModalias"; break;
291 case M_UDEV_TYPE: return "MatchUdevType"; break;
292 case M_DT: return "MatchDeviceTree"; break;
293 default:
294 abort();
295 }
296 }
297
298 static inline struct property *
property_new(void)299 property_new(void)
300 {
301 struct property *p;
302
303 p = zalloc(sizeof *p);
304 p->refcount = 1;
305 list_init(&p->link);
306
307 return p;
308 }
309
310 static inline struct property *
property_ref(struct property * p)311 property_ref(struct property *p)
312 {
313 assert(p->refcount > 0);
314 p->refcount++;
315 return p;
316 }
317
318 static inline struct property *
property_unref(struct property * p)319 property_unref(struct property *p)
320 {
321 /* Note: we don't cleanup here, that is a separate call so we
322 can abort if we haven't cleaned up correctly. */
323 assert(p->refcount > 0);
324 p->refcount--;
325
326 return NULL;
327 }
328
329 /* Separate call so we can verify that the caller unrefs the property
330 * before shutting down the subsystem.
331 */
332 static inline void
property_cleanup(struct property * p)333 property_cleanup(struct property *p)
334 {
335 /* If we get here, the quirks must've been removed already */
336 property_unref(p);
337 assert(p->refcount == 0);
338
339 list_remove(&p->link);
340 if (p->type == PT_STRING)
341 free(p->value.s);
342 free(p);
343 }
344
345 /**
346 * Return the dmi modalias from the udev device.
347 */
348 static inline char *
init_dmi(void)349 init_dmi(void)
350 {
351 struct udev *udev;
352 struct udev_device *udev_device;
353 const char *modalias = NULL;
354 char *copy = NULL;
355 const char *syspath = "/sys/devices/virtual/dmi/id";
356
357 if (getenv("LIBINPUT_RUNNING_TEST_SUITE"))
358 return safe_strdup("dmi:");
359
360 udev = udev_new();
361 if (!udev)
362 return NULL;
363
364 udev_device = udev_device_new_from_syspath(udev, syspath);
365 if (udev_device)
366 modalias = udev_device_get_property_value(udev_device,
367 "MODALIAS");
368
369 /* Not sure whether this could ever really fail, if so we should
370 * open the sysfs file directly. But then udev wouldn't have failed,
371 * so... */
372 if (!modalias)
373 modalias = "dmi:*";
374
375 copy = safe_strdup(modalias);
376
377 udev_device_unref(udev_device);
378 udev_unref(udev);
379
380 return copy;
381 }
382
383 /**
384 * Return the dt compatible string
385 */
386 static inline char *
init_dt(void)387 init_dt(void)
388 {
389 char compatible[1024];
390 char *copy = NULL;
391 const char *syspath = "/sys/firmware/devicetree/base/compatible";
392 FILE *fp;
393
394 if (getenv("LIBINPUT_RUNNING_TEST_SUITE"))
395 return safe_strdup("");
396
397 fp = fopen(syspath, "r");
398 if (!fp)
399 return NULL;
400
401 /* devicetree/base/compatible has multiple null-terminated entries
402 but we only care about the first one here, so strdup is enough */
403 if (fgets(compatible, sizeof(compatible), fp)) {
404 copy = safe_strdup(compatible);
405 }
406
407 fclose(fp);
408
409 return copy;
410 }
411
412 static inline struct section *
section_new(const char * path,const char * name)413 section_new(const char *path, const char *name)
414 {
415 struct section *s = zalloc(sizeof(*s));
416
417 char *path_dup = safe_strdup(path);
418 xasprintf(&s->name, "%s (%s)", name, basename(path_dup));
419 free(path_dup);
420 list_init(&s->link);
421 list_init(&s->properties);
422
423 return s;
424 }
425
426 static inline void
section_destroy(struct section * s)427 section_destroy(struct section *s)
428 {
429 struct property *p, *tmp;
430
431 free(s->name);
432 free(s->match.name);
433 free(s->match.dmi);
434 free(s->match.dt);
435
436 list_for_each_safe(p, tmp, &s->properties, link)
437 property_cleanup(p);
438
439 assert(list_empty(&s->properties));
440
441 list_remove(&s->link);
442 free(s);
443 }
444
445 static inline bool
parse_hex(const char * value,unsigned int * parsed)446 parse_hex(const char *value, unsigned int *parsed)
447 {
448 return strneq(value, "0x", 2) &&
449 safe_atou_base(value, parsed, 16) &&
450 strspn(value, "0123456789xABCDEF") == strlen(value) &&
451 *parsed <= 0xFFFF;
452 }
453
454 /**
455 * Parse a MatchFooBar=banana line.
456 *
457 * @param section The section struct to be filled in
458 * @param key The MatchFooBar part of the line
459 * @param value The banana part of the line.
460 *
461 * @return true on success, false otherwise.
462 */
463 static bool
parse_match(struct quirks_context * ctx,struct section * s,const char * key,const char * value)464 parse_match(struct quirks_context *ctx,
465 struct section *s,
466 const char *key,
467 const char *value)
468 {
469 int rc = false;
470
471 #define check_set_bit(s_, bit_) { \
472 if ((s_)->match.bits & (bit_)) goto out; \
473 (s_)->match.bits |= (bit_); \
474 }
475
476 assert(strlen(value) >= 1);
477
478 if (streq(key, "MatchName")) {
479 check_set_bit(s, M_NAME);
480 s->match.name = safe_strdup(value);
481 } else if (streq(key, "MatchBus")) {
482 check_set_bit(s, M_BUS);
483 if (streq(value, "usb"))
484 s->match.bus = BT_USB;
485 else if (streq(value, "bluetooth"))
486 s->match.bus = BT_BLUETOOTH;
487 else if (streq(value, "ps2"))
488 s->match.bus = BT_PS2;
489 else if (streq(value, "rmi"))
490 s->match.bus = BT_RMI;
491 else if (streq(value, "i2c"))
492 s->match.bus = BT_I2C;
493 else
494 goto out;
495 } else if (streq(key, "MatchVendor")) {
496 unsigned int vendor;
497
498 check_set_bit(s, M_VID);
499 if (!parse_hex(value, &vendor))
500 goto out;
501
502 s->match.vendor = vendor;
503 } else if (streq(key, "MatchProduct")) {
504 unsigned int product;
505
506 check_set_bit(s, M_PID);
507 if (!parse_hex(value, &product))
508 goto out;
509
510 s->match.product = product;
511 } else if (streq(key, "MatchVersion")) {
512 unsigned int version;
513
514 check_set_bit(s, M_VERSION);
515 if (!parse_hex(value, &version))
516 goto out;
517
518 s->match.version = version;
519 } else if (streq(key, "MatchDMIModalias")) {
520 check_set_bit(s, M_DMI);
521 if (!strneq(value, "dmi:", 4)) {
522 qlog_parser(ctx,
523 "%s: MatchDMIModalias must start with 'dmi:'\n",
524 s->name);
525 goto out;
526 }
527 s->match.dmi = safe_strdup(value);
528 } else if (streq(key, "MatchUdevType")) {
529 check_set_bit(s, M_UDEV_TYPE);
530 if (streq(value, "touchpad"))
531 s->match.udev_type = UDEV_TOUCHPAD;
532 else if (streq(value, "mouse"))
533 s->match.udev_type = UDEV_MOUSE;
534 else if (streq(value, "pointingstick"))
535 s->match.udev_type = UDEV_POINTINGSTICK;
536 else if (streq(value, "keyboard"))
537 s->match.udev_type = UDEV_KEYBOARD;
538 else if (streq(value, "joystick"))
539 s->match.udev_type = UDEV_JOYSTICK;
540 else if (streq(value, "tablet"))
541 s->match.udev_type = UDEV_TABLET;
542 else if (streq(value, "tablet-pad"))
543 s->match.udev_type = UDEV_TABLET_PAD;
544 else
545 goto out;
546 } else if (streq(key, "MatchDeviceTree")) {
547 check_set_bit(s, M_DT);
548 s->match.dt = safe_strdup(value);
549 } else {
550 qlog_error(ctx, "Unknown match key '%s'\n", key);
551 goto out;
552 }
553
554 #undef check_set_bit
555 s->has_match = true;
556 rc = true;
557 out:
558 return rc;
559 }
560
561 /**
562 * Parse a ModelFooBar=1 line.
563 *
564 * @param section The section struct to be filled in
565 * @param key The ModelFooBar part of the line
566 * @param value The value after the =, must be 1 or 0.
567 *
568 * @return true on success, false otherwise.
569 */
570 static bool
parse_model(struct quirks_context * ctx,struct section * s,const char * key,const char * value)571 parse_model(struct quirks_context *ctx,
572 struct section *s,
573 const char *key,
574 const char *value)
575 {
576 bool b;
577 enum quirk q = QUIRK_MODEL_ALPS_SERIAL_TOUCHPAD;
578
579 assert(strneq(key, "Model", 5));
580
581 if (streq(value, "1"))
582 b = true;
583 else if (streq(value, "0"))
584 b = false;
585 else
586 return false;
587
588 do {
589 if (streq(key, quirk_get_name(q))) {
590 struct property *p = property_new();
591 p->id = q,
592 p->type = PT_BOOL;
593 p->value.b = b;
594 list_append(&s->properties, &p->link);
595 s->has_property = true;
596 return true;
597 }
598 } while (++q < _QUIRK_LAST_MODEL_QUIRK_);
599
600 qlog_error(ctx, "Unknown key %s in %s\n", key, s->name);
601
602 return false;
603 }
604
605 /**
606 * Parse a AttrFooBar=banana line.
607 *
608 * @param section The section struct to be filled in
609 * @param key The AttrFooBar part of the line
610 * @param value The banana part of the line.
611 *
612 * Value parsing depends on the attribute type.
613 *
614 * @return true on success, false otherwise.
615 */
616 static inline bool
parse_attr(struct quirks_context * ctx,struct section * s,const char * key,const char * value)617 parse_attr(struct quirks_context *ctx,
618 struct section *s,
619 const char *key,
620 const char *value)
621 {
622 struct property *p = property_new();
623 bool rc = false;
624 struct quirk_dimensions dim;
625 struct quirk_range range;
626 unsigned int v;
627 bool b;
628 double d;
629
630 if (streq(key, quirk_get_name(QUIRK_ATTR_SIZE_HINT))) {
631 p->id = QUIRK_ATTR_SIZE_HINT;
632 if (!parse_dimension_property(value, &dim.x, &dim.y))
633 goto out;
634 p->type = PT_DIMENSION;
635 p->value.dim = dim;
636 rc = true;
637 } else if (streq(key, quirk_get_name(QUIRK_ATTR_TOUCH_SIZE_RANGE))) {
638 p->id = QUIRK_ATTR_TOUCH_SIZE_RANGE;
639 if (!parse_range_property(value, &range.upper, &range.lower))
640 goto out;
641 p->type = PT_RANGE;
642 p->value.range = range;
643 rc = true;
644 } else if (streq(key, quirk_get_name(QUIRK_ATTR_PALM_SIZE_THRESHOLD))) {
645 p->id = QUIRK_ATTR_PALM_SIZE_THRESHOLD;
646 if (!safe_atou(value, &v))
647 goto out;
648 p->type = PT_UINT;
649 p->value.u = v;
650 rc = true;
651 } else if (streq(key, quirk_get_name(QUIRK_ATTR_LID_SWITCH_RELIABILITY))) {
652 p->id = QUIRK_ATTR_LID_SWITCH_RELIABILITY;
653 if (!streq(value, "reliable") &&
654 !streq(value, "write_open"))
655 goto out;
656 p->type = PT_STRING;
657 p->value.s = safe_strdup(value);
658 rc = true;
659 } else if (streq(key, quirk_get_name(QUIRK_ATTR_KEYBOARD_INTEGRATION))) {
660 p->id = QUIRK_ATTR_KEYBOARD_INTEGRATION;
661 if (!streq(value, "internal") && !streq(value, "external"))
662 goto out;
663 p->type = PT_STRING;
664 p->value.s = safe_strdup(value);
665 rc = true;
666 } else if (streq(key, quirk_get_name(QUIRK_ATTR_TRACKPOINT_INTEGRATION))) {
667 p->id = QUIRK_ATTR_TRACKPOINT_INTEGRATION;
668 if (!streq(value, "internal") && !streq(value, "external"))
669 goto out;
670 p->type = PT_STRING;
671 p->value.s = safe_strdup(value);
672 rc = true;
673 } else if (streq(key, quirk_get_name(QUIRK_ATTR_TPKBCOMBO_LAYOUT))) {
674 p->id = QUIRK_ATTR_TPKBCOMBO_LAYOUT;
675 if (!streq(value, "below"))
676 goto out;
677 p->type = PT_STRING;
678 p->value.s = safe_strdup(value);
679 rc = true;
680 } else if (streq(key, quirk_get_name(QUIRK_ATTR_PRESSURE_RANGE))) {
681 p->id = QUIRK_ATTR_PRESSURE_RANGE;
682 if (!parse_range_property(value, &range.upper, &range.lower))
683 goto out;
684 p->type = PT_RANGE;
685 p->value.range = range;
686 rc = true;
687 } else if (streq(key, quirk_get_name(QUIRK_ATTR_PALM_PRESSURE_THRESHOLD))) {
688 p->id = QUIRK_ATTR_PALM_PRESSURE_THRESHOLD;
689 if (!safe_atou(value, &v))
690 goto out;
691 p->type = PT_UINT;
692 p->value.u = v;
693 rc = true;
694 } else if (streq(key, quirk_get_name(QUIRK_ATTR_RESOLUTION_HINT))) {
695 p->id = QUIRK_ATTR_RESOLUTION_HINT;
696 if (!parse_dimension_property(value, &dim.x, &dim.y))
697 goto out;
698 p->type = PT_DIMENSION;
699 p->value.dim = dim;
700 rc = true;
701 } else if (streq(key, quirk_get_name(QUIRK_ATTR_TRACKPOINT_MULTIPLIER))) {
702 p->id = QUIRK_ATTR_TRACKPOINT_MULTIPLIER;
703 if (!safe_atod(value, &d))
704 goto out;
705 p->type = PT_DOUBLE;
706 p->value.d = d;
707 rc = true;
708 } else if (streq(key, quirk_get_name(QUIRK_ATTR_USE_VELOCITY_AVERAGING))) {
709 p->id = QUIRK_ATTR_USE_VELOCITY_AVERAGING;
710 if (streq(value, "1"))
711 b = true;
712 else if (streq(value, "0"))
713 b = false;
714 else
715 goto out;
716 p->type = PT_BOOL;
717 p->value.b = b;
718 rc = true;
719 } else if (streq(key, quirk_get_name(QUIRK_ATTR_THUMB_PRESSURE_THRESHOLD))) {
720 p->id = QUIRK_ATTR_THUMB_PRESSURE_THRESHOLD;
721 if (!safe_atou(value, &v))
722 goto out;
723 p->type = PT_UINT;
724 p->value.u = v;
725 rc = true;
726 } else if (streq(key, quirk_get_name(QUIRK_ATTR_THUMB_SIZE_THRESHOLD))) {
727 p->id = QUIRK_ATTR_THUMB_SIZE_THRESHOLD;
728 if (!safe_atou(value, &v))
729 goto out;
730 p->type = PT_UINT;
731 p->value.u = v;
732 rc = true;
733 } else if (streq(key, quirk_get_name(QUIRK_ATTR_MSC_TIMESTAMP))) {
734 p->id = QUIRK_ATTR_MSC_TIMESTAMP;
735 if (!streq(value, "watch"))
736 goto out;
737 p->type = PT_STRING;
738 p->value.s = safe_strdup(value);
739 rc = true;
740 } else if (streq(key, quirk_get_name(QUIRK_ATTR_EVENT_CODE_DISABLE))) {
741 struct input_event events[32];
742 size_t nevents = ARRAY_LENGTH(events);
743 p->id = QUIRK_ATTR_EVENT_CODE_DISABLE;
744 if (!parse_evcode_property(value, events, &nevents) ||
745 nevents == 0)
746 goto out;
747
748 for (size_t i = 0; i < nevents; i++) {
749 p->value.tuples.tuples[i].first = events[i].type;
750 p->value.tuples.tuples[i].second = events[i].code;
751 }
752 p->value.tuples.ntuples = nevents;
753 p->type = PT_TUPLES;
754
755 rc = true;
756 } else {
757 qlog_error(ctx, "Unknown key %s in %s\n", key, s->name);
758 }
759 out:
760 if (rc) {
761 list_append(&s->properties, &p->link);
762 s->has_property = true;
763 } else {
764 property_cleanup(p);
765 }
766 return rc;
767 }
768
769 /**
770 * Parse a single line, expected to be in the format Key=value. Anything
771 * else will be rejected with a failure.
772 *
773 * Our data files can only have Match, Model and Attr, so let's check for
774 * those too.
775 */
776 static bool
parse_value_line(struct quirks_context * ctx,struct section * s,const char * line)777 parse_value_line(struct quirks_context *ctx, struct section *s, const char *line)
778 {
779 char **strv;
780 const char *key, *value;
781 bool rc = false;
782
783 strv = strv_from_string(line, "=");
784 if (strv[0] == NULL || strv[1] == NULL || strv[2] != NULL) {
785 goto out;
786 }
787
788
789 key = strv[0];
790 value = strv[1];
791 if (strlen(key) == 0 || strlen(value) == 0)
792 goto out;
793
794 /* Whatever the value is, it's not supposed to be in quotes */
795 if (value[0] == '"' || value[0] == '\'')
796 goto out;
797
798 if (strneq(key, "Match", 5))
799 rc = parse_match(ctx, s, key, value);
800 else if (strneq(key, "Model", 5))
801 rc = parse_model(ctx, s, key, value);
802 else if (strneq(key, "Attr", 4))
803 rc = parse_attr(ctx, s, key, value);
804 else
805 qlog_error(ctx, "Unknown value prefix %s\n", line);
806 out:
807 strv_free(strv);
808 return rc;
809 }
810
811 static inline bool
parse_file(struct quirks_context * ctx,const char * path)812 parse_file(struct quirks_context *ctx, const char *path)
813 {
814 enum state {
815 STATE_SECTION,
816 STATE_MATCH,
817 STATE_MATCH_OR_VALUE,
818 STATE_VALUE_OR_SECTION,
819 STATE_ANY,
820 };
821 FILE *fp;
822 char line[512];
823 bool rc = false;
824 enum state state = STATE_SECTION;
825 struct section *section = NULL;
826 int lineno = -1;
827
828 qlog_debug(ctx, "%s\n", path);
829
830 /* Not using open_restricted here, if we can't access
831 * our own data files, our installation is screwed up.
832 */
833 fp = fopen(path, "r");
834 if (!fp) {
835 /* If the file doesn't exist that's fine. Only way this can
836 * happen is for the custom override file, all others are
837 * provided by scandir so they do exist. Short of races we
838 * don't care about. */
839 if (errno == ENOENT)
840 return true;
841
842 qlog_error(ctx, "%s: failed to open file\n", path);
843 goto out;
844 }
845
846 while (fgets(line, sizeof(line), fp)) {
847 char *comment;
848
849 lineno++;
850
851 comment = strstr(line, "#");
852 if (comment) {
853 /* comment points to # but we need to remove the
854 * preceding whitespaces too */
855 comment--;
856 while (comment >= line) {
857 if (*comment != ' ' && *comment != '\t')
858 break;
859 comment--;
860 }
861 *(comment + 1) = '\0';
862 } else { /* strip the trailing newline */
863 comment = strstr(line, "\n");
864 if (comment)
865 *comment = '\0';
866 }
867 if (strlen(line) == 0)
868 continue;
869
870 /* We don't use quotes for strings, so we really don't want
871 * erroneous trailing whitespaces */
872 switch (line[strlen(line) - 1]) {
873 case ' ':
874 case '\t':
875 qlog_parser(ctx,
876 "%s:%d: Trailing whitespace '%s'\n",
877 path, lineno, line);
878 goto out;
879 }
880
881 switch (line[0]) {
882 case '\0':
883 case '\n':
884 case '#':
885 break;
886 /* white space not allowed */
887 case ' ':
888 case '\t':
889 qlog_parser(ctx, "%s:%d: Preceding whitespace '%s'\n",
890 path, lineno, line);
891 goto out;
892 /* section title */
893 case '[':
894 if (line[strlen(line) - 1] != ']') {
895 qlog_parser(ctx, "%s:%d: Closing ] missing '%s'\n",
896 path, lineno, line);
897 goto out;
898 }
899
900 if (state != STATE_SECTION &&
901 state != STATE_VALUE_OR_SECTION) {
902 qlog_parser(ctx, "%s:%d: expected section before %s\n",
903 path, lineno, line);
904 goto out;
905 }
906 if (section &&
907 (!section->has_match || !section->has_property)) {
908 qlog_parser(ctx, "%s:%d: previous section %s was empty\n",
909 path, lineno, section->name);
910 goto out; /* Previous section was empty */
911 }
912
913 state = STATE_MATCH;
914 section = section_new(path, line);
915 list_append(&ctx->sections, §ion->link);
916 break;
917 default:
918 /* entries must start with A-Z */
919 if (line[0] < 'A' && line[0] > 'Z') {
920 qlog_parser(ctx, "%s:%d: Unexpected line %s\n",
921 path, lineno, line);
922 goto out;
923 }
924 switch (state) {
925 case STATE_SECTION:
926 qlog_parser(ctx, "%s:%d: expected [Section], got %s\n",
927 path, lineno, line);
928 goto out;
929 case STATE_MATCH:
930 if (!strneq(line, "Match", 5)) {
931 qlog_parser(ctx, "%s:%d: expected MatchFoo=bar, have %s\n",
932 path, lineno, line);
933 goto out;
934 }
935 state = STATE_MATCH_OR_VALUE;
936 break;
937 case STATE_MATCH_OR_VALUE:
938 if (!strneq(line, "Match", 5))
939 state = STATE_VALUE_OR_SECTION;
940 break;
941 case STATE_VALUE_OR_SECTION:
942 if (strneq(line, "Match", 5)) {
943 qlog_parser(ctx, "%s:%d: expected value or [Section], have %s\n",
944 path, lineno, line);
945 goto out;
946 }
947 break;
948 case STATE_ANY:
949 break;
950 }
951
952 if (!parse_value_line(ctx, section, line)) {
953 qlog_parser(ctx, "%s:%d: failed to parse %s\n",
954 path, lineno, line);
955 goto out;
956 }
957 break;
958 }
959 }
960
961 if (!section) {
962 qlog_parser(ctx, "%s: is an empty file\n", path);
963 goto out;
964 }
965
966 if ((!section->has_match || !section->has_property)) {
967 qlog_parser(ctx, "%s:%d: previous section %s was empty\n",
968 path, lineno, section->name);
969 goto out; /* Previous section was empty */
970 }
971
972 rc = true;
973 out:
974 if (fp)
975 fclose(fp);
976
977 return rc;
978 }
979
980 static int
is_data_file(const struct dirent * dir)981 is_data_file(const struct dirent *dir) {
982 return strendswith(dir->d_name, ".quirks");
983 }
984
985 static inline bool
parse_files(struct quirks_context * ctx,const char * data_path)986 parse_files(struct quirks_context *ctx, const char *data_path)
987 {
988 struct dirent **namelist;
989 int ndev = -1;
990 int idx = 0;
991
992 ndev = scandir(data_path, &namelist, is_data_file, versionsort);
993 if (ndev <= 0) {
994 qlog_error(ctx,
995 "%s: failed to find data files\n",
996 data_path);
997 return false;
998 }
999
1000 for (idx = 0; idx < ndev; idx++) {
1001 char path[PATH_MAX];
1002
1003 snprintf(path,
1004 sizeof(path),
1005 "%s/%s",
1006 data_path,
1007 namelist[idx]->d_name);
1008
1009 if (!parse_file(ctx, path))
1010 break;
1011 }
1012
1013 for (int i = 0; i < ndev; i++)
1014 free(namelist[i]);
1015 free(namelist);
1016
1017 return idx == ndev;
1018 }
1019
1020 struct quirks_context *
quirks_init_subsystem(const char * data_path,const char * override_file,libinput_log_handler log_handler,struct libinput * libinput,enum quirks_log_type log_type)1021 quirks_init_subsystem(const char *data_path,
1022 const char *override_file,
1023 libinput_log_handler log_handler,
1024 struct libinput *libinput,
1025 enum quirks_log_type log_type)
1026 {
1027 struct quirks_context *ctx = zalloc(sizeof *ctx);
1028
1029 assert(data_path);
1030
1031 ctx->refcount = 1;
1032 ctx->log_handler = log_handler;
1033 ctx->log_type = log_type;
1034 ctx->libinput = libinput;
1035 list_init(&ctx->quirks);
1036 list_init(&ctx->sections);
1037
1038 qlog_debug(ctx, "%s is data root\n", data_path);
1039
1040 ctx->dmi = init_dmi();
1041 ctx->dt = init_dt();
1042 if (!ctx->dmi && !ctx->dt)
1043 goto error;
1044
1045 if (!parse_files(ctx, data_path))
1046 goto error;
1047
1048 if (override_file && !parse_file(ctx, override_file))
1049 goto error;
1050
1051 return ctx;
1052
1053 error:
1054 quirks_context_unref(ctx);
1055 return NULL;
1056 }
1057
1058 struct quirks_context *
quirks_context_ref(struct quirks_context * ctx)1059 quirks_context_ref(struct quirks_context *ctx)
1060 {
1061 assert(ctx->refcount > 0);
1062 ctx->refcount++;
1063
1064 return ctx;
1065 }
1066
1067 struct quirks_context *
quirks_context_unref(struct quirks_context * ctx)1068 quirks_context_unref(struct quirks_context *ctx)
1069 {
1070 struct section *s, *tmp;
1071
1072 if (!ctx)
1073 return NULL;
1074
1075 assert(ctx->refcount >= 1);
1076 ctx->refcount--;
1077
1078 if (ctx->refcount > 0)
1079 return NULL;
1080
1081 /* Caller needs to clean up before calling this */
1082 assert(list_empty(&ctx->quirks));
1083
1084 list_for_each_safe(s, tmp, &ctx->sections, link) {
1085 section_destroy(s);
1086 }
1087
1088 free(ctx->dmi);
1089 free(ctx->dt);
1090 free(ctx);
1091
1092 return NULL;
1093 }
1094
1095 static struct quirks *
quirks_new(void)1096 quirks_new(void)
1097 {
1098 struct quirks *q;
1099
1100 q = zalloc(sizeof *q);
1101 q->refcount = 1;
1102 q->nproperties = 0;
1103 list_init(&q->link);
1104
1105 return q;
1106 }
1107
1108 struct quirks *
quirks_unref(struct quirks * q)1109 quirks_unref(struct quirks *q)
1110 {
1111 if (!q)
1112 return NULL;
1113
1114 /* We don't really refcount, but might
1115 * as well have the API in place */
1116 assert(q->refcount == 1);
1117
1118 for (size_t i = 0; i < q->nproperties; i++) {
1119 property_unref(q->properties[i]);
1120 }
1121
1122 list_remove(&q->link);
1123 free(q->properties);
1124 free(q);
1125
1126 return NULL;
1127 }
1128
1129 /**
1130 * Searches for the udev property on this device and its parent devices.
1131 *
1132 * @return the value of the property or NULL
1133 */
1134 static const char *
udev_prop(struct udev_device * device,const char * prop)1135 udev_prop(struct udev_device *device, const char *prop)
1136 {
1137 struct udev_device *d = device;
1138 const char *value = NULL;
1139
1140 do {
1141 value = udev_device_get_property_value(d, prop);
1142 d = udev_device_get_parent(d);
1143 } while (value == NULL && d != NULL);
1144
1145 return value;
1146 }
1147
1148 static inline void
match_fill_name(struct match * m,struct udev_device * device)1149 match_fill_name(struct match *m,
1150 struct udev_device *device)
1151 {
1152 const char *str = udev_prop(device, "NAME");
1153 size_t slen;
1154
1155 if (!str)
1156 return;
1157
1158 /* udev NAME is in quotes, strip them */
1159 if (str[0] == '"')
1160 str++;
1161
1162 m->name = safe_strdup(str);
1163 slen = strlen(m->name);
1164 if (slen > 1 &&
1165 m->name[slen - 1] == '"')
1166 m->name[slen - 1] = '\0';
1167
1168 m->bits |= M_NAME;
1169 }
1170
1171 static inline void
match_fill_bus_vid_pid(struct match * m,struct udev_device * device)1172 match_fill_bus_vid_pid(struct match *m,
1173 struct udev_device *device)
1174 {
1175 const char *str;
1176 unsigned int product, vendor, bus, version;
1177
1178 str = udev_prop(device, "PRODUCT");
1179 if (!str)
1180 return;
1181
1182 /* ID_VENDOR_ID/ID_PRODUCT_ID/ID_BUS aren't filled in for virtual
1183 * devices so we have to resort to PRODUCT */
1184 if (sscanf(str, "%x/%x/%x/%x", &bus, &vendor, &product, &version) != 4)
1185 return;
1186
1187 m->product = product;
1188 m->vendor = vendor;
1189 m->version = version;
1190 m->bits |= M_PID|M_VID|M_VERSION;
1191 switch (bus) {
1192 case BUS_USB:
1193 m->bus = BT_USB;
1194 m->bits |= M_BUS;
1195 break;
1196 case BUS_BLUETOOTH:
1197 m->bus = BT_BLUETOOTH;
1198 m->bits |= M_BUS;
1199 break;
1200 case BUS_I8042:
1201 m->bus = BT_PS2;
1202 m->bits |= M_BUS;
1203 break;
1204 case BUS_RMI:
1205 m->bus = BT_RMI;
1206 m->bits |= M_BUS;
1207 break;
1208 case BUS_I2C:
1209 m->bus = BT_I2C;
1210 m->bits |= M_BUS;
1211 break;
1212 default:
1213 break;
1214 }
1215 }
1216
1217 static inline void
match_fill_udev_type(struct match * m,struct udev_device * device)1218 match_fill_udev_type(struct match *m,
1219 struct udev_device *device)
1220 {
1221 struct ut_map {
1222 const char *prop;
1223 enum udev_type flag;
1224 } mappings[] = {
1225 { "ID_INPUT_MOUSE", UDEV_MOUSE },
1226 { "ID_INPUT_POINTINGSTICK", UDEV_POINTINGSTICK },
1227 { "ID_INPUT_TOUCHPAD", UDEV_TOUCHPAD },
1228 { "ID_INPUT_TABLET", UDEV_TABLET },
1229 { "ID_INPUT_TABLET_PAD", UDEV_TABLET_PAD },
1230 { "ID_INPUT_JOYSTICK", UDEV_JOYSTICK },
1231 { "ID_INPUT_KEYBOARD", UDEV_KEYBOARD },
1232 { "ID_INPUT_KEY", UDEV_KEYBOARD },
1233 };
1234 struct ut_map *map;
1235
1236 ARRAY_FOR_EACH(mappings, map) {
1237 if (udev_prop(device, map->prop))
1238 m->udev_type |= map->flag;
1239 }
1240 m->bits |= M_UDEV_TYPE;
1241 }
1242
1243 static inline void
match_fill_dmi_dt(struct match * m,char * dmi,char * dt)1244 match_fill_dmi_dt(struct match *m, char *dmi, char *dt)
1245 {
1246 if (dmi) {
1247 m->dmi = dmi;
1248 m->bits |= M_DMI;
1249 }
1250
1251 if (dt) {
1252 m->dt = dt;
1253 m->bits |= M_DT;
1254 }
1255 }
1256
1257 static struct match *
match_new(struct udev_device * device,char * dmi,char * dt)1258 match_new(struct udev_device *device,
1259 char *dmi, char *dt)
1260 {
1261 struct match *m = zalloc(sizeof *m);
1262
1263 match_fill_name(m, device);
1264 match_fill_bus_vid_pid(m, device);
1265 match_fill_dmi_dt(m, dmi, dt);
1266 match_fill_udev_type(m, device);
1267 return m;
1268 }
1269
1270 static void
match_free(struct match * m)1271 match_free(struct match *m)
1272 {
1273 /* dmi and dt are global */
1274 free(m->name);
1275 free(m);
1276 }
1277
1278 static void
quirk_apply_section(struct quirks_context * ctx,struct quirks * q,const struct section * s)1279 quirk_apply_section(struct quirks_context *ctx,
1280 struct quirks *q,
1281 const struct section *s)
1282 {
1283 struct property *p;
1284 size_t nprops = 0;
1285 void *tmp;
1286
1287 list_for_each(p, &s->properties, link) {
1288 nprops++;
1289 }
1290
1291 nprops += q->nproperties;
1292 tmp = realloc(q->properties, nprops * sizeof(p));
1293 if (!tmp)
1294 return;
1295
1296 q->properties = tmp;
1297 list_for_each(p, &s->properties, link) {
1298 qlog_debug(ctx, "property added: %s from %s\n",
1299 quirk_get_name(p->id), s->name);
1300
1301 q->properties[q->nproperties++] = property_ref(p);
1302 }
1303 }
1304
1305 static bool
quirk_match_section(struct quirks_context * ctx,struct quirks * q,struct section * s,struct match * m,struct udev_device * device)1306 quirk_match_section(struct quirks_context *ctx,
1307 struct quirks *q,
1308 struct section *s,
1309 struct match *m,
1310 struct udev_device *device)
1311 {
1312 uint32_t matched_flags = 0x0;
1313
1314 for (uint32_t flag = 0x1; flag <= M_LAST; flag <<= 1) {
1315 uint32_t prev_matched_flags = matched_flags;
1316 /* section doesn't have this bit set, continue */
1317 if ((s->match.bits & flag) == 0)
1318 continue;
1319
1320 /* Couldn't fill in this bit for the match, so we
1321 * do not match on it */
1322 if ((m->bits & flag) == 0) {
1323 qlog_debug(ctx,
1324 "%s wants %s but we don't have that\n",
1325 s->name, matchflagname(flag));
1326 continue;
1327 }
1328
1329 /* now check the actual matching bit */
1330 switch (flag) {
1331 case M_NAME:
1332 if (fnmatch(s->match.name, m->name, 0) == 0)
1333 matched_flags |= flag;
1334 break;
1335 case M_BUS:
1336 if (m->bus == s->match.bus)
1337 matched_flags |= flag;
1338 break;
1339 case M_VID:
1340 if (m->vendor == s->match.vendor)
1341 matched_flags |= flag;
1342 break;
1343 case M_PID:
1344 if (m->product == s->match.product)
1345 matched_flags |= flag;
1346 break;
1347 case M_VERSION:
1348 if (m->version == s->match.version)
1349 matched_flags |= flag;
1350 break;
1351 case M_DMI:
1352 if (fnmatch(s->match.dmi, m->dmi, 0) == 0)
1353 matched_flags |= flag;
1354 break;
1355 case M_DT:
1356 if (fnmatch(s->match.dt, m->dt, 0) == 0)
1357 matched_flags |= flag;
1358 break;
1359 case M_UDEV_TYPE:
1360 if (s->match.udev_type & m->udev_type)
1361 matched_flags |= flag;
1362 break;
1363 default:
1364 abort();
1365 }
1366
1367 if (prev_matched_flags != matched_flags) {
1368 qlog_debug(ctx,
1369 "%s matches for %s\n",
1370 s->name,
1371 matchflagname(flag));
1372 }
1373 }
1374
1375 if (s->match.bits == matched_flags) {
1376 qlog_debug(ctx, "%s is full match\n", s->name);
1377 quirk_apply_section(ctx, q, s);
1378 }
1379
1380 return true;
1381 }
1382
1383 struct quirks *
quirks_fetch_for_device(struct quirks_context * ctx,struct udev_device * udev_device)1384 quirks_fetch_for_device(struct quirks_context *ctx,
1385 struct udev_device *udev_device)
1386 {
1387 struct quirks *q = NULL;
1388 struct section *s;
1389 struct match *m;
1390
1391 if (!ctx)
1392 return NULL;
1393
1394 qlog_debug(ctx, "%s: fetching quirks\n",
1395 udev_device_get_devnode(udev_device));
1396
1397 q = quirks_new();
1398
1399 m = match_new(udev_device, ctx->dmi, ctx->dt);
1400
1401 list_for_each(s, &ctx->sections, link) {
1402 quirk_match_section(ctx, q, s, m, udev_device);
1403 }
1404
1405 match_free(m);
1406
1407 if (q->nproperties == 0) {
1408 quirks_unref(q);
1409 return NULL;
1410 }
1411
1412 list_insert(&ctx->quirks, &q->link);
1413
1414 return q;
1415 }
1416
1417
1418 static inline struct property *
quirk_find_prop(struct quirks * q,enum quirk which)1419 quirk_find_prop(struct quirks *q, enum quirk which)
1420 {
1421 /* Run backwards to only handle the last one assigned */
1422 for (ssize_t i = q->nproperties - 1; i >= 0; i--) {
1423 struct property *p = q->properties[i];
1424 if (p->id == which)
1425 return p;
1426 }
1427
1428 return NULL;
1429 }
1430
1431 bool
quirks_has_quirk(struct quirks * q,enum quirk which)1432 quirks_has_quirk(struct quirks *q, enum quirk which)
1433 {
1434 return quirk_find_prop(q, which) != NULL;
1435 }
1436
1437 bool
quirks_get_int32(struct quirks * q,enum quirk which,int32_t * val)1438 quirks_get_int32(struct quirks *q, enum quirk which, int32_t *val)
1439 {
1440 struct property *p;
1441
1442 if (!q)
1443 return false;
1444
1445 p = quirk_find_prop(q, which);
1446 if (!p)
1447 return false;
1448
1449 assert(p->type == PT_INT);
1450 *val = p->value.i;
1451
1452 return true;
1453 }
1454
1455 bool
quirks_get_uint32(struct quirks * q,enum quirk which,uint32_t * val)1456 quirks_get_uint32(struct quirks *q, enum quirk which, uint32_t *val)
1457 {
1458 struct property *p;
1459
1460 if (!q)
1461 return false;
1462
1463 p = quirk_find_prop(q, which);
1464 if (!p)
1465 return false;
1466
1467 assert(p->type == PT_UINT);
1468 *val = p->value.u;
1469
1470 return true;
1471 }
1472
1473 bool
quirks_get_double(struct quirks * q,enum quirk which,double * val)1474 quirks_get_double(struct quirks *q, enum quirk which, double *val)
1475 {
1476 struct property *p;
1477
1478 if (!q)
1479 return false;
1480
1481 p = quirk_find_prop(q, which);
1482 if (!p)
1483 return false;
1484
1485 assert(p->type == PT_DOUBLE);
1486 *val = p->value.d;
1487
1488 return true;
1489 }
1490
1491 bool
quirks_get_string(struct quirks * q,enum quirk which,char ** val)1492 quirks_get_string(struct quirks *q, enum quirk which, char **val)
1493 {
1494 struct property *p;
1495
1496 if (!q)
1497 return false;
1498
1499 p = quirk_find_prop(q, which);
1500 if (!p)
1501 return false;
1502
1503 assert(p->type == PT_STRING);
1504 *val = p->value.s;
1505
1506 return true;
1507 }
1508
1509 bool
quirks_get_bool(struct quirks * q,enum quirk which,bool * val)1510 quirks_get_bool(struct quirks *q, enum quirk which, bool *val)
1511 {
1512 struct property *p;
1513
1514 if (!q)
1515 return false;
1516
1517 p = quirk_find_prop(q, which);
1518 if (!p)
1519 return false;
1520
1521 assert(p->type == PT_BOOL);
1522 *val = p->value.b;
1523
1524 return true;
1525 }
1526
1527 bool
quirks_get_dimensions(struct quirks * q,enum quirk which,struct quirk_dimensions * val)1528 quirks_get_dimensions(struct quirks *q,
1529 enum quirk which,
1530 struct quirk_dimensions *val)
1531 {
1532 struct property *p;
1533
1534 if (!q)
1535 return false;
1536
1537 p = quirk_find_prop(q, which);
1538 if (!p)
1539 return false;
1540
1541 assert(p->type == PT_DIMENSION);
1542 *val = p->value.dim;
1543
1544 return true;
1545 }
1546
1547 bool
quirks_get_range(struct quirks * q,enum quirk which,struct quirk_range * val)1548 quirks_get_range(struct quirks *q,
1549 enum quirk which,
1550 struct quirk_range *val)
1551 {
1552 struct property *p;
1553
1554 if (!q)
1555 return false;
1556
1557 p = quirk_find_prop(q, which);
1558 if (!p)
1559 return false;
1560
1561 assert(p->type == PT_RANGE);
1562 *val = p->value.range;
1563
1564 return true;
1565 }
1566
1567 bool
quirks_get_tuples(struct quirks * q,enum quirk which,const struct quirk_tuples ** tuples)1568 quirks_get_tuples(struct quirks *q,
1569 enum quirk which,
1570 const struct quirk_tuples **tuples)
1571 {
1572 struct property *p;
1573
1574 if (!q)
1575 return false;
1576
1577 p = quirk_find_prop(q, which);
1578 if (!p)
1579 return false;
1580
1581 assert(p->type == PT_TUPLES);
1582 *tuples = &p->value.tuples;
1583
1584 return true;
1585 }
1586