1 /*
2 * Copyright (c) 2023 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include <algorithm>
17 #include <cerrno>
18 #include <climits>
19 #include <cstring>
20 #include <fstream>
21 #include <iostream>
22 #include <iterator>
23 #include <optional>
24 #include <sstream>
25 #include <string>
26 #include <unordered_map>
27 #include <vector>
28
29 #include <libudev.h>
30 #include <linux/input.h>
31 #include <unistd.h>
32
33 #include "mmi_log.h"
34
35 using namespace std::literals;
36
37 namespace {
38 constexpr OHOS::HiviewDFX::HiLogLabel LABEL = { LOG_CORE, OHOS::MMI::MMI_LOG_DOMAIN, "MmiLibudev" };
39
40 constexpr int UTIL_PATH_SIZE = 1024;
41 constexpr int UTIL_LINE_SIZE = 16384;
42
StartsWith(std::string_view str,std::string_view prefix)43 bool StartsWith(std::string_view str, std::string_view prefix)
44 {
45 return str.size() >= prefix.size() && str.substr(0, prefix.size()) == prefix;
46 }
47
ChopTail(std::string_view & str,char sep)48 bool ChopTail(std::string_view &str, char sep)
49 {
50 auto pos = str.rfind(sep);
51 if (pos == std::string_view::npos) {
52 return false;
53 }
54 str.remove_suffix(str.size() - pos);
55 return true;
56 }
57
ResolveSymLink(const std::string & syspath)58 std::string ResolveSymLink(const std::string &syspath)
59 {
60 constexpr auto backStr = "../"sv;
61 char linkTarget[UTIL_PATH_SIZE];
62
63 ssize_t len = readlink(syspath.c_str(), linkTarget, sizeof(linkTarget));
64 if (len <= 0 || len == static_cast<ssize_t>(sizeof(linkTarget))) {
65 return syspath;
66 }
67
68 std::string_view tail{ linkTarget, len };
69 int back = 0;
70 for (; StartsWith(tail, backStr); back++) {
71 tail.remove_prefix(backStr.size());
72 }
73
74 std::string_view base = syspath;
75 for (int i = 0; i <= back; i++) {
76 if (!ChopTail(base, '/')) {
77 return syspath;
78 }
79 }
80
81 return std::string{ base }.append("/").append(tail);
82 }
83
GetLinkValue(const std::string & slink,const std::string & syspath)84 std::optional<std::string> GetLinkValue(const std::string &slink, const std::string &syspath)
85 {
86 auto path = syspath + "/" + slink;
87
88 char target[UTIL_PATH_SIZE];
89 ssize_t len = readlink(path.c_str(), target, sizeof(target));
90 if (len <= 0 || len == static_cast<ssize_t>(sizeof(target))) {
91 return std::nullopt;
92 }
93
94 std::string_view result{ target, len };
95 auto pos = result.rfind('/');
96 if (pos == std::string_view::npos) {
97 return std::nullopt;
98 }
99 return std::string{ result.substr(pos + 1) };
100 }
101
102 class BitVector {
103 public:
104 // This type depends on kernel definition
105 using val_t = unsigned long;
106
107 // Input string is hexadecimal 64-bit numbers separated by spaces with high bit number first
BitVector(const std::string & str)108 explicit BitVector(const std::string &str)
109 {
110 std::istringstream ss{ str };
111 ss >> std::hex;
112 std::copy(std::istream_iterator<val_t>(ss), std::istream_iterator<val_t>(), std::back_inserter(bits_));
113 // Since numbers in string starts with high number we need to reverse vector to count numbers from low to high
114 std::reverse(bits_.begin(), bits_.end());
115 }
116
CheckBit(size_t idx) const117 [[nodiscard]] bool CheckBit(size_t idx) const
118 {
119 auto vidx = idx / (sizeof(val_t) * CHAR_BIT);
120 auto bidx = idx % (sizeof(val_t) * CHAR_BIT);
121 if (vidx >= bits_.size()) {
122 return false;
123 }
124 return (bits_[vidx] & (1ULL << bidx)) != 0;
125 }
126
127 private:
128 std::vector<val_t> bits_;
129 };
130 } // namespace
131
132 struct udev {};
133
134 struct udev_device {
135 public:
136 // Not copyable and not movable
137 udev_device(udev_device &) = delete;
138 udev_device(udev_device &&) = delete;
139 udev_device &operator = (udev_device &) = delete;
140 udev_device &operator = (udev_device &&) = delete;
141
NewFromSyspathudev_device142 static udev_device *NewFromSyspath(const std::string &syspathParam)
143 {
144 CALL_DEBUG_ENTER;
145 // path starts in sys
146 if (!StartsWith(syspathParam, "/sys/") || syspathParam.back() == '/') {
147 errno = EINVAL;
148 return nullptr;
149 }
150
151 // resolve possible symlink to real path
152 std::string path = ResolveSymLink(syspathParam);
153 if (StartsWith(path, "/sys/devices/")) {
154 // all "devices" require a "uevent" file
155 struct stat statbuf;
156 std::string filename = path + "/uevent";
157 if (stat(filename.c_str(), &statbuf) != 0) {
158 return nullptr;
159 }
160 } else {
161 return nullptr;
162 }
163
164 auto *inst = new udev_device;
165 inst->SetSyspath(std::move(path));
166
167 return inst;
168 }
169
NewFromDevnumudev_device170 static udev_device *NewFromDevnum(char type, dev_t devnum)
171 {
172 CALL_DEBUG_ENTER;
173 const char *typeStr = nullptr;
174
175 if (type == 'b') {
176 typeStr = "block";
177 } else if (type == 'c') {
178 typeStr = "char";
179 } else {
180 errno = EINVAL;
181 return nullptr;
182 }
183
184 // use /sys/dev/{block,char}/<maj>:<min> link
185 auto majStr = std::to_string(major(devnum));
186 auto minStr = std::to_string(minor(devnum));
187 return NewFromSyspath("/sys/dev/"s + typeStr + "/" + majStr + ":" + minStr);
188 }
189
Refudev_device190 void Ref()
191 {
192 refcount++;
193 }
194
Unrefudev_device195 void Unref()
196 {
197 if (--refcount <= 0) {
198 delete this;
199 }
200 }
201
GetParentudev_device202 udev_device *GetParent()
203 {
204 if (!parentDevice_.has_value()) {
205 parentDevice_ = NewFromChild(this);
206 }
207 return *parentDevice_;
208 }
209
GetSyspathudev_device210 const std::string &GetSyspath() const
211 {
212 return syspath;
213 }
214
GetSysnameudev_device215 const std::string &GetSysname() const
216 {
217 return sysname;
218 }
219
GetDevnodeudev_device220 const std::string &GetDevnode()
221 {
222 return GetProperty("DEVNAME");
223 }
224
IsInitializedudev_device225 bool IsInitialized()
226 {
227 if (!ueventLoaded) {
228 ReadUeventFile();
229 }
230 return ueventLoaded;
231 }
232
GetParentWithSubsystemudev_device233 udev_device *GetParentWithSubsystem(const std::string &subsystem)
234 {
235 CALL_DEBUG_ENTER;
236 udev_device *parent = GetParent();
237 while (parent != nullptr) {
238 auto parentSubsystem = parent->GetSubsystem();
239 if (parentSubsystem.has_value() && parentSubsystem.value() == subsystem) {
240 break;
241 }
242 parent = parent->GetParent();
243 }
244
245 if (parent == nullptr) {
246 errno = ENOENT;
247 }
248 return parent;
249 }
250
HasPropertyudev_device251 bool HasProperty(const std::string &key)
252 {
253 if (!ueventLoaded) {
254 ReadUeventFile();
255 }
256 return property_.find(key) != property_.end();
257 }
258
GetPropertyudev_device259 const std::string &GetProperty(const std::string &key)
260 {
261 if (!ueventLoaded) {
262 ReadUeventFile();
263 }
264 return property_[key];
265 }
266
267 private:
268 udev_device() = default;
269
~udev_deviceudev_device270 ~udev_device()
271 {
272 if (parentDevice_.has_value() && parentDevice_.value() != nullptr) {
273 parentDevice_.value()->Unref();
274 }
275 }
276
NewFromChildudev_device277 static udev_device *NewFromChild(udev_device *child)
278 {
279 CALL_DEBUG_ENTER;
280 std::string_view path = child->GetSyspath();
281
282 while (true) {
283 if (!ChopTail(path, '/')) {
284 break;
285 }
286 udev_device *parent = NewFromSyspath(std::string{ path });
287 if (parent != nullptr) {
288 return parent;
289 }
290 }
291
292 return nullptr;
293 }
294
SetSyspathudev_device295 void SetSyspath(std::string newSyspath)
296 {
297 CALL_DEBUG_ENTER;
298 syspath = std::move(newSyspath);
299
300 AddProperty("DEVPATH", syspath.substr(0, "/sys"sv.size()));
301
302 auto pos = syspath.rfind('/');
303 if (pos == std::string::npos) {
304 return;
305 }
306 sysname = syspath.substr(pos + 1);
307
308 // some devices have '!' in their name, change that to '/'
309 for (char &c : sysname) {
310 if (c == '!') {
311 c = '/';
312 }
313 }
314 }
315
AddPropertyFromStringudev_device316 void AddPropertyFromString(const std::string &line)
317 {
318 auto pos = line.find('=');
319 if (pos == std::string::npos) {
320 return;
321 }
322 std::string key = line.substr(0, pos);
323 if (key == "DEVNAME") {
324 SetDevnode(line.substr(pos + 1));
325 return;
326 }
327 AddProperty(std::move(key), line.substr(pos + 1));
328 }
329
ReadUeventFileudev_device330 void ReadUeventFile()
331 {
332 CALL_DEBUG_ENTER;
333 if (ueventLoaded) {
334 return;
335 }
336
337 auto filename = syspath + "/uevent";
338 std::ifstream f(filename, std::ios_base::in);
339 if (!f.is_open()) {
340 MMI_HILOGE("ReadUeventFile(): path: %{public}s, error: %{public}s", filename.c_str(), std::strerror(errno));
341 return;
342 }
343 ueventLoaded = true;
344
345 char line[UTIL_LINE_SIZE];
346 while (f.getline(line, sizeof(line))) {
347 AddPropertyFromString(line);
348 }
349
350 CheckInputProperties();
351 }
352
CheckAcceludev_device353 bool CheckAccel(const BitVector &ev, const BitVector &abs, const BitVector &prop)
354 {
355 bool hasKeys = ev.CheckBit(EV_KEY);
356 bool has3dCoordinates = abs.CheckBit(ABS_X) && abs.CheckBit(ABS_Y) && abs.CheckBit(ABS_Z);
357 bool isAccelerometer = prop.CheckBit(INPUT_PROP_ACCELEROMETER);
358
359 if (!hasKeys && has3dCoordinates) {
360 isAccelerometer = true;
361 }
362
363 if (isAccelerometer) {
364 SetInputProperty("ID_INPUT_ACCELEROMETER");
365 }
366 return isAccelerometer;
367 }
368
HasJoystickAxesOrButtonsudev_device369 bool HasJoystickAxesOrButtons(const BitVector &abs, const BitVector &key)
370 {
371 bool hasJoystickAxesOrButtons = false;
372 // Some mouses have so much buttons that they overflow in joystick range, ignore them
373 if (!key.CheckBit(BTN_JOYSTICK - 1)) {
374 for (int button = BTN_JOYSTICK; button < BTN_DIGI && !hasJoystickAxesOrButtons; button++) {
375 hasJoystickAxesOrButtons = key.CheckBit(button);
376 }
377 for (int button = BTN_TRIGGER_HAPPY1; button <= BTN_TRIGGER_HAPPY40 && !hasJoystickAxesOrButtons;
378 button++) {
379 hasJoystickAxesOrButtons = key.CheckBit(button);
380 }
381 for (int button = BTN_DPAD_UP; button <= BTN_DPAD_RIGHT && !hasJoystickAxesOrButtons; button++) {
382 hasJoystickAxesOrButtons = key.CheckBit(button);
383 }
384 }
385 for (int axis = ABS_RX; axis < ABS_PRESSURE && !hasJoystickAxesOrButtons; axis++) {
386 hasJoystickAxesOrButtons = abs.CheckBit(axis);
387 }
388 return hasJoystickAxesOrButtons;
389 }
390
CheckPointingStickudev_device391 bool CheckPointingStick(const BitVector &prop)
392 {
393 if (prop.CheckBit(INPUT_PROP_POINTING_STICK)) {
394 SetInputProperty("ID_INPUT_POINTINGSTICK");
395 return true;
396 }
397 return false;
398 }
399
CheckAndSetPropudev_device400 void CheckAndSetProp(std::string prop, const bool &flag)
401 {
402 if (flag) {
403 SetInputProperty(prop);
404 MMI_HILOGD("device has prop with %{public}s", prop.c_str());
405 }
406 }
407
CheckMouseButtonudev_device408 void CheckMouseButton(const BitVector &key, bool &flag)
409 {
410 for (int button = BTN_MOUSE; button < BTN_JOYSTICK && !flag; button++) {
411 flag = key.CheckBit(button);
412 }
413 }
414
UpdateProByKeyudev_device415 void UpdateProByKey(const BitVector &key, const bool &isDirect, bool &probablyTablet, bool &probablyTouchpad,
416 bool &probablyTouchscreen)
417 {
418 probablyTablet = key.CheckBit(BTN_STYLUS) || key.CheckBit(BTN_TOOL_PEN);
419 probablyTouchpad = key.CheckBit(BTN_TOOL_FINGER) && !key.CheckBit(BTN_TOOL_PEN) && !isDirect;
420 probablyTouchscreen = key.CheckBit(BTN_TOUCH) && isDirect;
421 }
422
CheckMtCoordinatesudev_device423 bool CheckMtCoordinates(const BitVector &abs)
424 {
425 bool hasMtCoordinates = abs.CheckBit(ABS_MT_POSITION_X) && abs.CheckBit(ABS_MT_POSITION_Y);
426 /* unset hasMtCoordinates if devices claims to have all abs axis */
427 if (hasMtCoordinates && abs.CheckBit(ABS_MT_SLOT) && abs.CheckBit(ABS_MT_SLOT - 1)) {
428 hasMtCoordinates = false;
429 }
430 return hasMtCoordinates;
431 }
432
UpdateProByStatusudev_device433 void UpdateProByStatus(const bool &isMouse, const bool &isTouchpad, const bool &isTouchscreen,
434 const bool &isJoystick, const bool &isTablet)
435 {
436 CheckAndSetProp("ID_INPUT_MOUSE", isMouse);
437 CheckAndSetProp("ID_INPUT_TOUCHPAD", isTouchpad);
438 CheckAndSetProp("ID_INPUT_TOUCHSCREEN", isTouchscreen);
439 CheckAndSetProp("ID_INPUT_JOYSTICK", isJoystick);
440 CheckAndSetProp("ID_INPUT_TABLET", isTablet);
441 }
442
CheckPointersudev_device443 bool CheckPointers(const BitVector &ev, const BitVector &abs, const BitVector &key, const BitVector &rel,
444 const BitVector &prop)
445 {
446 bool isDirect = prop.CheckBit(INPUT_PROP_DIRECT);
447 bool hasAbsCoordinates = abs.CheckBit(ABS_X) && abs.CheckBit(ABS_Y);
448 bool hasRelCoordinates = ev.CheckBit(EV_REL) && rel.CheckBit(REL_X) && rel.CheckBit(REL_Y);
449 bool hasMtCoordinates = CheckMtCoordinates(abs);
450
451 bool hasMouseButton = false;
452 CheckMouseButton(key, hasMouseButton);
453
454 bool probablyTablet;
455 bool probablyTouchpad;
456 bool probablyTouchscreen;
457 UpdateProByKey(key, isDirect, probablyTablet, probablyTouchpad, probablyTouchscreen);
458 bool probablyJoystick = HasJoystickAxesOrButtons(abs, key);
459
460 bool isTablet = false;
461 bool isMouse = false;
462 bool isTouchpad = false;
463 bool isTouchscreen = false;
464 bool isJoystick = false;
465 if (hasAbsCoordinates) {
466 if (probablyTablet) {
467 isTablet = true;
468 } else if (probablyTouchpad) {
469 isTouchpad = true;
470 } else if (hasMouseButton) {
471 /* This path is taken by VMware's USB mouse, which has
472 * absolute axes, but no touch/pressure button. */
473 isMouse = true;
474 } else if (probablyTouchscreen) {
475 isTouchscreen = true;
476 } else {
477 isJoystick = probablyJoystick;
478 }
479 } else {
480 isJoystick = probablyJoystick;
481 }
482
483 if (hasMtCoordinates) {
484 if (probablyTablet) {
485 isTablet = true;
486 } else if (probablyTouchpad) {
487 isTouchpad = true;
488 } else if (probablyTouchscreen) {
489 isTouchscreen = true;
490 }
491 }
492
493 /* mouse buttons and no axis */
494 if (!isTablet && !isTouchpad && !isJoystick && hasMouseButton && (hasRelCoordinates || !hasAbsCoordinates)) {
495 isMouse = true;
496 }
497
498 UpdateProByStatus(isMouse, isTouchpad, isTouchscreen, isJoystick, isTablet);
499
500 return isTablet || isMouse || isTouchpad || isTouchscreen || isJoystick || CheckPointingStick(prop);
501 }
502
CheckKeysudev_device503 bool CheckKeys(const BitVector &ev, const BitVector &key)
504 {
505 if (!ev.CheckBit(EV_KEY)) {
506 return false;
507 }
508
509 /* only consider KEY_* here, not BTN_* */
510 bool found = false;
511 for (int i = 0; i < BTN_MISC && !found; ++i) {
512 found = key.CheckBit(i);
513 }
514 /* If there are no keys in the lower block, check the higher blocks */
515 for (int i = KEY_OK; i < BTN_DPAD_UP && !found; ++i) {
516 found = key.CheckBit(i);
517 }
518 for (int i = KEY_ALS_TOGGLE; i < BTN_TRIGGER_HAPPY && !found; ++i) {
519 found = key.CheckBit(i);
520 }
521
522 if (found) {
523 SetInputProperty("ID_INPUT_KEY");
524 }
525
526 /* the first 32 bits are ESC, numbers, and Q to D; if we have all of
527 * those, consider it a full keyboard; do not test KEY_RESERVED, though */
528 bool isKeyboard = true;
529 for (int i = KEY_ESC; i < KEY_D && isKeyboard; i++) {
530 isKeyboard = key.CheckBit(i);
531 }
532 if (isKeyboard) {
533 SetInputProperty("ID_INPUT_KEYBOARD");
534 }
535
536 return found || isKeyboard;
537 }
538
SetInputPropertyudev_device539 void SetInputProperty(std::string prop)
540 {
541 AddProperty("ID_INPUT", "1");
542 AddProperty(std::move(prop), "1");
543 }
544
CheckInputPropertiesudev_device545 void CheckInputProperties()
546 {
547 CALL_DEBUG_ENTER;
548 BitVector ev{ GetProperty("EV") };
549 BitVector abs{ GetProperty("ABS") };
550 BitVector key{ GetProperty("KEY") };
551 BitVector rel{ GetProperty("REL") };
552 BitVector prop{ GetProperty("PROP") };
553
554 bool isPointer = CheckAccel(ev, abs, prop) || CheckPointers(ev, abs, key, rel, prop);
555 bool isKey = CheckKeys(ev, key);
556 /* Some evdev nodes have only a scrollwheel */
557 if (!isPointer && !isKey && ev.CheckBit(EV_REL) && (rel.CheckBit(REL_WHEEL) || rel.CheckBit(REL_HWHEEL))) {
558 SetInputProperty("ID_INPUT_KEY");
559 }
560 if (ev.CheckBit(EV_SW)) {
561 SetInputProperty("ID_INPUT_SWITCH");
562 }
563 }
564
SetDevnodeudev_device565 void SetDevnode(std::string newDevnode)
566 {
567 if (newDevnode[0] != '/') {
568 newDevnode = "/dev/" + newDevnode;
569 }
570 AddProperty("DEVNAME", std::move(newDevnode));
571 }
572
AddPropertyudev_device573 void AddProperty(std::string key, std::string value)
574 {
575 property_[std::move(key)] = std::move(value);
576 }
577
GetSubsystemudev_device578 std::optional<std::string> GetSubsystem()
579 {
580 CALL_DEBUG_ENTER;
581 if (!subsystem_.has_value()) {
582 auto res = GetLinkValue("subsystem", syspath);
583 // read "subsystem" link
584 if (res.has_value()) {
585 SetSubsystem(std::move(*res));
586 return subsystem_;
587 }
588 subsystem_ = "";
589 }
590 return subsystem_;
591 }
592
SetSubsystemudev_device593 void SetSubsystem(std::string newSubsystem)
594 {
595 subsystem_ = newSubsystem;
596 AddProperty("SUBSYSTEM", std::move(newSubsystem));
597 }
598
599 private:
600 int refcount = 1;
601 std::string syspath;
602 std::string sysname;
603
604 std::optional<udev_device *> parentDevice_;
605 std::optional<std::string> subsystem_;
606
607 bool ueventLoaded = false;
608 std::unordered_map<std::string, std::string> property_;
609 };
610
611 // C-style interface
612
udev_new(void)613 udev *udev_new(void)
614 {
615 static udev instance{};
616 return &instance;
617 }
618
udev_unref(udev * udev)619 udev *udev_unref([[maybe_unused]] udev *udev)
620 {
621 return nullptr;
622 }
623
udev_device_ref(udev_device * device)624 udev_device *udev_device_ref(udev_device *device)
625 {
626 if (device == nullptr) {
627 return nullptr;
628 }
629 device->Ref();
630 return device;
631 }
632
udev_device_unref(udev_device * device)633 udev_device *udev_device_unref(udev_device *device)
634 {
635 if (device == nullptr) {
636 return nullptr;
637 }
638 device->Unref();
639 return nullptr;
640 }
641
udev_device_get_udev(udev_device * device)642 udev *udev_device_get_udev(udev_device *device)
643 {
644 if (device == nullptr) {
645 return nullptr;
646 }
647 return udev_new();
648 }
649
udev_device_new_from_syspath(udev * udev,const char * syspath)650 udev_device *udev_device_new_from_syspath(udev *udev, const char *syspath)
651 {
652 if (udev == nullptr || syspath == nullptr) {
653 errno = EINVAL;
654 return nullptr;
655 }
656 return udev_device::NewFromSyspath(syspath);
657 }
658
udev_device_new_from_devnum(udev * udev,char type,dev_t devnum)659 udev_device *udev_device_new_from_devnum(udev *udev, char type, dev_t devnum)
660 {
661 if (udev == nullptr) {
662 errno = EINVAL;
663 return nullptr;
664 }
665 return udev_device::NewFromDevnum(type, devnum);
666 }
667
udev_device_get_parent(udev_device * device)668 udev_device *udev_device_get_parent(udev_device *device)
669 {
670 if (device == nullptr) {
671 errno = EINVAL;
672 return nullptr;
673 }
674 return device->GetParent();
675 }
676
udev_device_get_parent_with_subsystem_devtype(udev_device * device,const char * subsystem,const char * devtype)677 udev_device *udev_device_get_parent_with_subsystem_devtype(udev_device *device, const char *subsystem,
678 const char *devtype)
679 {
680 if (device == nullptr) {
681 return nullptr;
682 }
683 if (subsystem == nullptr) {
684 errno = EINVAL;
685 return nullptr;
686 }
687 // Searching with specific devtype is not supported, since not used by libinput
688 if (devtype != nullptr) {
689 return nullptr;
690 }
691 return device->GetParentWithSubsystem(subsystem);
692 }
693
udev_device_get_syspath(udev_device * device)694 const char *udev_device_get_syspath(udev_device *device)
695 {
696 if (device == nullptr) {
697 return nullptr;
698 }
699 return device->GetSyspath().c_str();
700 }
701
udev_device_get_sysname(udev_device * device)702 const char *udev_device_get_sysname(udev_device *device)
703 {
704 if (device == nullptr) {
705 return nullptr;
706 }
707 return device->GetSysname().c_str();
708 }
709
udev_device_get_devnode(udev_device * device)710 const char *udev_device_get_devnode(udev_device *device)
711 {
712 if (device == nullptr) {
713 return nullptr;
714 }
715 return device->GetDevnode().c_str();
716 }
717
udev_device_get_is_initialized(udev_device * device)718 int udev_device_get_is_initialized(udev_device *device)
719 {
720 return (device != nullptr) ? static_cast<int>(device->IsInitialized()) : -1;
721 }
722
udev_device_get_property_value(udev_device * device,const char * key)723 const char *udev_device_get_property_value(udev_device *device, const char *key)
724 {
725 if (device == nullptr || key == nullptr) {
726 return nullptr;
727 }
728 std::string skey{ key };
729 if (!device->HasProperty(key)) {
730 return nullptr;
731 }
732 return device->GetProperty(key).c_str();
733 }
734