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