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