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, §ion->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