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