1 /*
2 * Copyright (c) 2023-2024 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 <fstream>
17
18 #include <unistd.h>
19
20 #include <libudev.h>
21 #include <linux/input.h>
22
23 #include "mmi_log.h"
24
25 #undef MMI_LOG_DOMAIN
26 #define MMI_LOG_DOMAIN MMI_LOG_SERVER
27 #undef MMI_LOG_TAG
28 #define MMI_LOG_TAG "MmiLibudev"
29
30 using namespace std::literals;
31 namespace {
32 constexpr int UTIL_PATH_SIZE { 1024 };
33 constexpr int UTIL_LINE_SIZE { 16384 };
34
StartsWith(std::string_view str,std::string_view prefix)35 bool StartsWith(std::string_view str, std::string_view prefix)
36 {
37 return str.size() >= prefix.size() && str.substr(0, prefix.size()) == prefix;
38 }
39
ChopTail(std::string_view & str,char sep)40 bool ChopTail(std::string_view &str, char sep)
41 {
42 auto pos = str.rfind(sep);
43 if (pos == std::string_view::npos) {
44 return false;
45 }
46 str.remove_suffix(str.size() - pos);
47 return true;
48 }
49
ResolveSymLink(const std::string & syspath)50 std::string ResolveSymLink(const std::string &syspath)
51 {
52 constexpr auto backStr = "../"sv;
53 char linkTarget[UTIL_PATH_SIZE];
54
55 ssize_t len = readlink(syspath.c_str(), linkTarget, sizeof(linkTarget));
56 if (len <= 0 || len == static_cast<ssize_t>(sizeof(linkTarget))) {
57 return syspath;
58 }
59
60 std::string_view tail{ linkTarget, len };
61 int32_t back = 0;
62 for (; StartsWith(tail, backStr); back++) {
63 tail.remove_prefix(backStr.size());
64 }
65
66 std::string_view base = syspath;
67 for (int32_t i = 0; i <= back; i++) {
68 if (!ChopTail(base, '/')) {
69 return syspath;
70 }
71 }
72
73 return std::string{ base }.append("/").append(tail);
74 }
75
GetLinkValue(const std::string & slink,const std::string & syspath)76 std::optional<std::string> GetLinkValue(const std::string &slink, const std::string &syspath)
77 {
78 auto path = syspath + "/" + slink;
79
80 char target[UTIL_PATH_SIZE];
81 ssize_t len = readlink(path.c_str(), target, sizeof(target));
82 if (len <= 0 || len == static_cast<ssize_t>(sizeof(target))) {
83 MMI_HILOGE("Failed to read link");
84 return std::nullopt;
85 }
86
87 std::string_view result{ target, len };
88 auto pos = result.rfind('/');
89 if (pos == std::string_view::npos) {
90 MMI_HILOGE("Failed to get link value");
91 return std::nullopt;
92 }
93 return std::string{ result.substr(pos + 1) };
94 }
95
96 class BitVector {
97 public:
98 // This type depends on kernel definition
99 using val_t = unsigned long;
100
101 // Input string is hexadecimal 64-bit numbers separated by spaces with high bit number first
BitVector(const std::string & str)102 explicit BitVector(const std::string &str)
103 {
104 std::istringstream ss{ str };
105 ss >> std::hex;
106 std::copy(std::istream_iterator<val_t>(ss), std::istream_iterator<val_t>(), std::back_inserter(bits_));
107 // Since numbers in string starts with high number we need to reverse vector to count numbers from low to high
108 std::reverse(bits_.begin(), bits_.end());
109 }
110
CheckBit(size_t idx) const111 [[nodiscard]] bool CheckBit(size_t idx) const
112 {
113 auto vidx = idx / (sizeof(val_t) * CHAR_BIT);
114 auto bidx = idx % (sizeof(val_t) * CHAR_BIT);
115 if (vidx >= bits_.size()) {
116 return false;
117 }
118 return (bits_[vidx] & (1ULL << bidx)) != 0;
119 }
120
121 private:
122 std::vector<val_t> bits_;
123 };
124 } // namespace
125
126 struct udev {};
127
128 typedef std::map<std::string, std::vector<std::string>> Propertys;
129 typedef std::map<std::string, Propertys> AllPropertys;
130 struct udev_device {
131 public:
132 // Not copyable and not movable
133 udev_device(udev_device &) = delete;
134 udev_device(udev_device &&) = delete;
135 udev_device &operator = (udev_device &) = delete;
136 udev_device &operator = (udev_device &&) = delete;
137
NewFromSyspathudev_device138 static udev_device *NewFromSyspath(const std::string &syspathParam)
139 {
140 // path starts in sys
141 if (!StartsWith(syspathParam, "/sys/") || syspathParam.back() == '/') {
142 errno = EINVAL;
143 return nullptr;
144 }
145
146 // resolve possible symlink to real path
147 std::string path = ResolveSymLink(syspathParam);
148 if (StartsWith(path, "/sys/devices/")) {
149 // all "devices" require a "uevent" file
150 struct stat statbuf;
151 std::string filename = path + "/uevent";
152 if (stat(filename.c_str(), &statbuf) != 0) {
153 return nullptr;
154 }
155 } else {
156 return nullptr;
157 }
158
159 auto *inst = new udev_device;
160 inst->SetSyspath(std::move(path));
161
162 return inst;
163 }
164
NewFromDevnumudev_device165 static udev_device *NewFromDevnum(char type, dev_t devnum)
166 {
167 const char *typeStr = nullptr;
168
169 if (type == 'b') {
170 typeStr = "block";
171 } else if (type == 'c') {
172 typeStr = "char";
173 } else {
174 MMI_HILOGE("Param invalid");
175 errno = EINVAL;
176 return nullptr;
177 }
178
179 // use /sys/dev/{block,char}/<maj>:<min> link
180 auto majStr = std::to_string(major(devnum));
181 auto minStr = std::to_string(minor(devnum));
182 return NewFromSyspath("/sys/dev/"s + typeStr + "/" + majStr + ":" + minStr);
183 }
184
AddPropertysudev_device185 static void AddPropertys(char type, dev_t devnum, const char *devnode)
186 {
187 const char *typeStr = nullptr;
188
189 if (type == 'b') {
190 typeStr = "block";
191 } else if (type == 'c') {
192 typeStr = "char";
193 } else {
194 MMI_HILOGE("Param invalid");
195 errno = EINVAL;
196 return;
197 }
198
199 // use /sys/dev/{block,char}/<maj>:<min> link
200 auto majStr = std::to_string(major(devnum));
201 auto minStr = std::to_string(minor(devnum));
202 std::string syspathParam = "/sys/dev/"s + typeStr + "/" + majStr + ":" + minStr;
203 Propertys propertys;
204 if (IsFromSyspath(syspathParam)) {
205 AddProperty(syspathParam, propertys);
206 }
207
208 while (true) {
209 std::string_view path = syspathParam;
210 if (!ChopTail(path, '/')) {
211 break;
212 }
213
214 syspathParam = std::string {path};
215
216 if (IsFromSyspath(syspathParam)) {
217 AddProperty(syspathParam, propertys);
218 }
219 }
220 std::lock_guard<std::mutex> lock(mutex_);
221 allPropertys_[devnode] = propertys;
222 }
223
IsFromSyspathudev_device224 static bool IsFromSyspath(std::string& syspathParam)
225 {
226 // path starts in sys
227 if (!StartsWith(syspathParam, "/sys/") || syspathParam.back() == '/') {
228 errno = EINVAL;
229 return false;
230 }
231
232 // resolve possible symlink to real path
233 std::string path = ResolveSymLink(syspathParam);
234 if (StartsWith(path, "/sys/devices/")) {
235 // all "devices" require a "uevent" file
236 struct stat statbuf;
237 std::string filename = path + "/uevent";
238 if (stat(filename.c_str(), &statbuf) != 0) {
239 return false;
240 }
241 } else {
242 return false;
243 }
244 syspathParam = path;
245 return true;
246 }
247
AddPropertyudev_device248 static void AddProperty(const std::string& path, Propertys& propertys)
249 {
250 auto filename = path + "/uevent";
251 char realPath[PATH_MAX] = {};
252 CHKPV(realpath(filename.c_str(), realPath));
253 std::ifstream f(realPath, std::ios_base::in);
254 if (!f.is_open()) {
255 MMI_HILOGE("ReadUeventFile(): path:%{private}s, error:%{public}s", realPath, std::strerror(errno));
256 return;
257 }
258
259 char line[UTIL_LINE_SIZE];
260 while (f.getline(line, sizeof(line))) {
261 propertys[filename].push_back(line);
262 }
263 }
264
RemovePropertyudev_device265 static void RemoveProperty(const char *devnode)
266 {
267 std::lock_guard<std::mutex> lock(mutex_);
268 allPropertys_.erase(devnode);
269 }
270
RecordDevNodeudev_device271 static void RecordDevNode(const char *devnode)
272 {
273 devnode_ = devnode;
274 }
275
Refudev_device276 void Ref()
277 {
278 refcount++;
279 }
280
Unrefudev_device281 void Unref()
282 {
283 if (--refcount <= 0) {
284 delete this;
285 }
286 }
287
GetParentudev_device288 udev_device *GetParent()
289 {
290 if (!parentDevice_.has_value()) {
291 parentDevice_ = NewFromChild(this);
292 }
293 return *parentDevice_;
294 }
295
GetSyspathudev_device296 const std::string &GetSyspath() const
297 {
298 return syspath;
299 }
300
GetSysnameudev_device301 const std::string &GetSysname() const
302 {
303 return sysname;
304 }
305
GetDevnodeudev_device306 const std::string &GetDevnode()
307 {
308 return GetProperty("DEVNAME");
309 }
310
IsInitializedudev_device311 bool IsInitialized()
312 {
313 if (!ueventLoaded) {
314 ReadUeventFile();
315 }
316 return ueventLoaded;
317 }
318
GetParentWithSubsystemudev_device319 udev_device *GetParentWithSubsystem(const std::string &subsystem)
320 {
321 udev_device *parent = GetParent();
322 while (parent != nullptr) {
323 auto parentSubsystem = parent->GetSubsystem();
324 if (parentSubsystem.has_value() && parentSubsystem.value() == subsystem) {
325 break;
326 }
327 parent = parent->GetParent();
328 }
329
330 if (parent == nullptr) {
331 errno = ENOENT;
332 }
333 return parent;
334 }
335
HasPropertyudev_device336 bool HasProperty(const std::string &key)
337 {
338 if (!ueventLoaded) {
339 ReadUeventFile();
340 }
341 return property_.find(key) != property_.end();
342 }
343
GetPropertyudev_device344 const std::string &GetProperty(const std::string &key)
345 {
346 if (!ueventLoaded) {
347 ReadUeventFile();
348 }
349 return property_[key];
350 }
351
352 private:
353 udev_device() = default;
354
~udev_deviceudev_device355 ~udev_device()
356 {
357 if (parentDevice_.has_value() && parentDevice_.value() != nullptr) {
358 parentDevice_.value()->Unref();
359 }
360 }
361
NewFromChildudev_device362 static udev_device *NewFromChild(udev_device *child)
363 {
364 std::string_view path = child->GetSyspath();
365
366 while (true) {
367 if (!ChopTail(path, '/')) {
368 break;
369 }
370 udev_device *parent = NewFromSyspath(std::string{ path });
371 if (parent != nullptr) {
372 return parent;
373 }
374 }
375
376 return nullptr;
377 }
378
SetSyspathudev_device379 void SetSyspath(std::string newSyspath)
380 {
381 syspath = std::move(newSyspath);
382
383 AddProperty("DEVPATH", syspath.substr(0, "/sys"sv.size()));
384
385 auto pos = syspath.rfind('/');
386 if (pos == std::string::npos) {
387 return;
388 }
389 sysname = syspath.substr(pos + 1);
390
391 // some devices have '!' in their name, change that to '/'
392 for (char &c : sysname) {
393 if (c == '!') {
394 c = '/';
395 }
396 }
397 }
398
AddPropertyFromStringudev_device399 void AddPropertyFromString(const std::string &line)
400 {
401 auto pos = line.find('=');
402 if (pos == std::string::npos) {
403 return;
404 }
405 std::string key = line.substr(0, pos);
406 if (key == "DEVNAME") {
407 SetDevnode(line.substr(pos + 1));
408 return;
409 }
410 AddProperty(std::move(key), line.substr(pos + 1));
411 }
412
ReadUeventFileudev_device413 void ReadUeventFile()
414 {
415 if (ueventLoaded) {
416 return;
417 }
418
419 auto filename = syspath + "/uevent";
420 char realPath[PATH_MAX] = {};
421 CHKPV(realpath(filename.c_str(), realPath));
422 std::ifstream f(realPath, std::ios_base::in);
423 if (!f.is_open()) {
424 MMI_HILOGE("ReadUeventFile(): path:%{private}s, error:%{public}s", realPath, std::strerror(errno));
425 return;
426 }
427 ueventLoaded = true;
428
429 std::lock_guard<std::mutex> lock(mutex_);
430 auto propertys = allPropertys_[devnode_];
431 auto lines = propertys[filename];
432 for (auto it = lines.begin(); it != lines.end(); it++) {
433 AddPropertyFromString(*it);
434 }
435 CheckInputProperties();
436 }
437
CheckAcceludev_device438 bool CheckAccel(const BitVector &ev, const BitVector &abs, const BitVector &prop)
439 {
440 bool hasKeys = ev.CheckBit(EV_KEY);
441 bool has3dCoordinates = abs.CheckBit(ABS_X) && abs.CheckBit(ABS_Y) && abs.CheckBit(ABS_Z);
442 bool isAccelerometer = prop.CheckBit(INPUT_PROP_ACCELEROMETER);
443
444 if (!hasKeys && has3dCoordinates) {
445 isAccelerometer = true;
446 }
447
448 if (isAccelerometer) {
449 SetInputProperty("ID_INPUT_ACCELEROMETER");
450 }
451 return isAccelerometer;
452 }
453
HasJoystickAxesOrButtonsudev_device454 bool HasJoystickAxesOrButtons(const BitVector &abs, const BitVector &key)
455 {
456 bool hasJoystickAxesOrButtons = false;
457 // Some mouses have so much buttons that they overflow in joystick range, ignore them
458 if (!key.CheckBit(BTN_JOYSTICK - 1)) {
459 for (int32_t button = BTN_JOYSTICK; button < BTN_DIGI && !hasJoystickAxesOrButtons; button++) {
460 hasJoystickAxesOrButtons = key.CheckBit(button);
461 }
462 for (int32_t button = BTN_TRIGGER_HAPPY1; button <= BTN_TRIGGER_HAPPY40 && !hasJoystickAxesOrButtons;
463 button++) {
464 hasJoystickAxesOrButtons = key.CheckBit(button);
465 }
466 for (int32_t button = BTN_DPAD_UP; button <= BTN_DPAD_RIGHT && !hasJoystickAxesOrButtons; button++) {
467 hasJoystickAxesOrButtons = key.CheckBit(button);
468 }
469 }
470 for (int32_t axis = ABS_RX; axis < ABS_PRESSURE && !hasJoystickAxesOrButtons; axis++) {
471 hasJoystickAxesOrButtons = abs.CheckBit(axis);
472 }
473 return hasJoystickAxesOrButtons;
474 }
475
CheckPointingStickudev_device476 bool CheckPointingStick(const BitVector &prop)
477 {
478 if (prop.CheckBit(INPUT_PROP_POINTING_STICK)) {
479 SetInputProperty("ID_INPUT_POINTINGSTICK");
480 return true;
481 }
482 return false;
483 }
484
CheckAndSetPropudev_device485 void CheckAndSetProp(std::string prop, const bool &flag)
486 {
487 if (flag) {
488 SetInputProperty(prop);
489 MMI_HILOGD("The device has prop with %{public}s", prop.c_str());
490 }
491 }
492
CheckMouseButtonudev_device493 void CheckMouseButton(const BitVector &key, bool &flag)
494 {
495 for (int32_t button = BTN_MOUSE; button < BTN_JOYSTICK && !flag; button++) {
496 flag = key.CheckBit(button);
497 }
498 }
499
UpdateProByKeyudev_device500 void UpdateProByKey(const BitVector &key, const bool &isDirect, bool &probablyTablet, bool &probablyTouchpad,
501 bool &probablyTouchscreen)
502 {
503 probablyTablet = key.CheckBit(BTN_STYLUS) || key.CheckBit(BTN_TOOL_PEN);
504 probablyTouchpad = key.CheckBit(BTN_TOOL_FINGER) && !key.CheckBit(BTN_TOOL_PEN) && !isDirect;
505 probablyTouchscreen = key.CheckBit(BTN_TOUCH) && isDirect;
506 }
507
CheckMtCoordinatesudev_device508 bool CheckMtCoordinates(const BitVector &abs)
509 {
510 bool hasMtCoordinates = abs.CheckBit(ABS_MT_POSITION_X) && abs.CheckBit(ABS_MT_POSITION_Y);
511 /* unset hasMtCoordinates if devices claims to have all abs axis */
512 if (hasMtCoordinates && abs.CheckBit(ABS_MT_SLOT) && abs.CheckBit(ABS_MT_SLOT - 1)) {
513 hasMtCoordinates = false;
514 }
515 return hasMtCoordinates;
516 }
517
UpdateProByStatusudev_device518 void UpdateProByStatus(const bool &isMouse, const bool &isTouchpad, const bool &isTouchscreen,
519 const bool &isJoystick, const bool &isTablet)
520 {
521 CheckAndSetProp("ID_INPUT_MOUSE", isMouse);
522 CheckAndSetProp("ID_INPUT_TOUCHPAD", isTouchpad);
523 CheckAndSetProp("ID_INPUT_TOUCHSCREEN", isTouchscreen);
524 CheckAndSetProp("ID_INPUT_JOYSTICK", isJoystick);
525 CheckAndSetProp("ID_INPUT_TABLET", isTablet);
526 }
527
CheckPointersudev_device528 bool CheckPointers(const BitVector &ev, const BitVector &abs, const BitVector &key, const BitVector &rel,
529 const BitVector &prop)
530 {
531 bool isDirect = prop.CheckBit(INPUT_PROP_DIRECT);
532 bool hasAbsCoordinates = abs.CheckBit(ABS_X) && abs.CheckBit(ABS_Y);
533 bool hasRelCoordinates = ev.CheckBit(EV_REL) && rel.CheckBit(REL_X) && rel.CheckBit(REL_Y);
534 bool hasMtCoordinates = CheckMtCoordinates(abs);
535
536 bool hasMouseButton = false;
537 CheckMouseButton(key, hasMouseButton);
538
539 bool probablyTablet;
540 bool probablyTouchpad;
541 bool probablyTouchscreen;
542 UpdateProByKey(key, isDirect, probablyTablet, probablyTouchpad, probablyTouchscreen);
543 bool probablyJoystick = HasJoystickAxesOrButtons(abs, key);
544
545 bool isTablet = false;
546 bool isMouse = false;
547 bool isTouchpad = false;
548 bool isTouchscreen = false;
549 bool isJoystick = false;
550 if (hasAbsCoordinates) {
551 if (probablyTablet) {
552 isTablet = true;
553 } else if (probablyTouchpad) {
554 isTouchpad = true;
555 } else if (hasMouseButton) {
556 /* This path is taken by VMware's USB mouse, which has
557 * absolute axes, but no touch/pressure button. */
558 isMouse = true;
559 } else if (probablyTouchscreen) {
560 isTouchscreen = true;
561 } else {
562 isJoystick = probablyJoystick;
563 }
564 } else {
565 isJoystick = probablyJoystick;
566 }
567
568 if (hasMtCoordinates) {
569 if (probablyTablet) {
570 isTablet = true;
571 } else if (probablyTouchpad) {
572 isTouchpad = true;
573 } else if (probablyTouchscreen) {
574 isTouchscreen = true;
575 }
576 }
577
578 /* mouse buttons and no axis */
579 if (!isTablet && !isTouchpad && !isJoystick && hasMouseButton && (hasRelCoordinates || !hasAbsCoordinates)) {
580 isMouse = true;
581 }
582
583 UpdateProByStatus(isMouse, isTouchpad, isTouchscreen, isJoystick, isTablet);
584
585 return isTablet || isMouse || isTouchpad || isTouchscreen || isJoystick || CheckPointingStick(prop);
586 }
587
CheckKeysudev_device588 bool CheckKeys(const BitVector &ev, const BitVector &key)
589 {
590 if (!ev.CheckBit(EV_KEY)) {
591 return false;
592 }
593
594 /* only consider KEY_* here, not BTN_* */
595 bool found = false;
596 for (int32_t i = 0; i < BTN_MISC && !found; ++i) {
597 found = key.CheckBit(i);
598 }
599 /* If there are no keys in the lower block, check the higher blocks */
600 for (int32_t i = KEY_OK; i < BTN_DPAD_UP && !found; ++i) {
601 found = key.CheckBit(i);
602 }
603 for (int32_t i = KEY_ALS_TOGGLE; i < BTN_TRIGGER_HAPPY && !found; ++i) {
604 found = key.CheckBit(i);
605 }
606
607 if (found) {
608 SetInputProperty("ID_INPUT_KEY");
609 }
610
611 /* the first 32 bits are ESC, numbers, and Q to D; if we have all of
612 * those, consider it a full keyboard; do not test KEY_RESERVED, though */
613 bool isKeyboard = true;
614 for (int32_t i = KEY_ESC; i < KEY_D && isKeyboard; i++) {
615 isKeyboard = key.CheckBit(i);
616 }
617 if (isKeyboard) {
618 SetInputProperty("ID_INPUT_KEYBOARD");
619 }
620
621 return found || isKeyboard;
622 }
623
SetInputPropertyudev_device624 void SetInputProperty(std::string prop)
625 {
626 AddProperty("ID_INPUT", "1");
627 AddProperty(std::move(prop), "1");
628 }
629
CheckInputPropertiesudev_device630 void CheckInputProperties()
631 {
632 BitVector ev{ GetProperty("EV") };
633 BitVector abs{ GetProperty("ABS") };
634 BitVector key{ GetProperty("KEY") };
635 BitVector rel{ GetProperty("REL") };
636 BitVector prop{ GetProperty("PROP") };
637
638 bool isPointer = CheckAccel(ev, abs, prop) || CheckPointers(ev, abs, key, rel, prop);
639 bool isKey = CheckKeys(ev, key);
640 /* Some evdev nodes have only a scrollwheel */
641 if (!isPointer && !isKey && ev.CheckBit(EV_REL) && (rel.CheckBit(REL_WHEEL) || rel.CheckBit(REL_HWHEEL))) {
642 SetInputProperty("ID_INPUT_KEY");
643 }
644 if (ev.CheckBit(EV_SW)) {
645 SetInputProperty("ID_INPUT_SWITCH");
646 }
647 }
648
SetDevnodeudev_device649 void SetDevnode(std::string newDevnode)
650 {
651 if (newDevnode[0] != '/') {
652 newDevnode = "/dev/" + newDevnode;
653 }
654 AddProperty("DEVNAME", std::move(newDevnode));
655 }
656
AddPropertyudev_device657 void AddProperty(std::string key, std::string value)
658 {
659 property_[std::move(key)] = std::move(value);
660 }
661
GetSubsystemudev_device662 std::optional<std::string> GetSubsystem()
663 {
664 if (!subsystem_.has_value()) {
665 auto res = GetLinkValue("subsystem", syspath);
666 // read "subsystem" link
667 if (res.has_value()) {
668 SetSubsystem(std::move(*res));
669 return subsystem_;
670 }
671 subsystem_ = "";
672 }
673 return subsystem_;
674 }
675
SetSubsystemudev_device676 void SetSubsystem(std::string newSubsystem)
677 {
678 subsystem_ = newSubsystem;
679 AddProperty("SUBSYSTEM", std::move(newSubsystem));
680 }
681
682 private:
683 int refcount = 1;
684 std::string syspath;
685 std::string sysname;
686
687 std::optional<udev_device *> parentDevice_;
688 std::optional<std::string> subsystem_;
689
690 bool ueventLoaded = false;
691 std::unordered_map<std::string, std::string> property_;
692 static AllPropertys allPropertys_;
693 static std::mutex mutex_;
694 static std::string devnode_;
695 };
696 AllPropertys udev_device::allPropertys_;
697 std::mutex udev_device::mutex_;
698 std::string udev_device::devnode_;
699
700 // C-style interface
701
udev_new(void)702 udev *udev_new(void)
703 {
704 static udev instance{};
705 return &instance;
706 }
707
udev_unref(udev * udev)708 udev *udev_unref([[maybe_unused]] udev *udev)
709 {
710 return nullptr;
711 }
712
udev_device_ref(udev_device * device)713 udev_device *udev_device_ref(udev_device *device)
714 {
715 CHKPP(device);
716 device->Ref();
717 return device;
718 }
719
udev_device_unref(udev_device * device)720 udev_device *udev_device_unref(udev_device *device)
721 {
722 CHKPP(device);
723 device->Unref();
724 return nullptr;
725 }
726
udev_device_get_udev(udev_device * device)727 udev *udev_device_get_udev(udev_device *device)
728 {
729 CHKPP(device);
730 return udev_new();
731 }
732
udev_device_new_from_syspath(udev * udev,const char * syspath)733 udev_device *udev_device_new_from_syspath(udev *udev, const char *syspath)
734 {
735 if (udev == nullptr || syspath == nullptr) {
736 errno = EINVAL;
737 return nullptr;
738 }
739 return udev_device::NewFromSyspath(syspath);
740 }
741
udev_device_new_from_devnum(udev * udev,char type,dev_t devnum)742 udev_device *udev_device_new_from_devnum(udev *udev, char type, dev_t devnum)
743 {
744 if (udev == nullptr) {
745 errno = EINVAL;
746 return nullptr;
747 }
748 return udev_device::NewFromDevnum(type, devnum);
749 }
750
udev_device_get_parent(udev_device * device)751 udev_device *udev_device_get_parent(udev_device *device)
752 {
753 if (device == nullptr) {
754 errno = EINVAL;
755 return nullptr;
756 }
757 return device->GetParent();
758 }
759
udev_device_get_parent_with_subsystem_devtype(udev_device * device,const char * subsystem,const char * devtype)760 udev_device *udev_device_get_parent_with_subsystem_devtype(udev_device *device, const char *subsystem,
761 const char *devtype)
762 {
763 CHKPP(device);
764 if (subsystem == nullptr) {
765 errno = EINVAL;
766 return nullptr;
767 }
768 // Searching with specific devtype is not supported, since not used by libinput
769 CHKPP(devtype);
770 return device->GetParentWithSubsystem(subsystem);
771 }
772
udev_device_get_syspath(udev_device * device)773 const char *udev_device_get_syspath(udev_device *device)
774 {
775 CHKPP(device);
776 return device->GetSyspath().c_str();
777 }
778
udev_device_get_sysname(udev_device * device)779 const char *udev_device_get_sysname(udev_device *device)
780 {
781 CHKPP(device);
782 return device->GetSysname().c_str();
783 }
784
udev_device_get_devnode(udev_device * device)785 const char *udev_device_get_devnode(udev_device *device)
786 {
787 CHKPP(device);
788 return device->GetDevnode().c_str();
789 }
790
udev_device_get_is_initialized(udev_device * device)791 int udev_device_get_is_initialized(udev_device *device)
792 {
793 return (device != nullptr) ? static_cast<int>(device->IsInitialized()) : -1;
794 }
795
udev_device_get_property_value(udev_device * device,const char * key)796 const char *udev_device_get_property_value(udev_device *device, const char *key)
797 {
798 CHKPP(device);
799 CHKPP(key);
800 std::string skey{ key };
801 if (!device->HasProperty(key)) {
802 return nullptr;
803 }
804 return device->GetProperty(key).c_str();
805 }
806
udev_device_property_add(char type,const char * devnode)807 bool udev_device_property_add(char type, const char *devnode)
808 {
809 struct stat st;
810 if (stat(devnode, &st) < 0) {
811 MMI_HILOGW("udev_device_property_add stat failed, devnode:%{public}s", devnode);
812 return false;
813 }
814 udev_device::AddPropertys(type, st.st_rdev, devnode);
815 return true;
816 }
817
udev_device_property_remove(const char * devnode)818 void udev_device_property_remove(const char *devnode)
819 {
820 udev_device::RemoveProperty(devnode);
821 }
822
udev_device_record_devnode(const char * devnode)823 void udev_device_record_devnode(const char *devnode)
824 {
825 udev_device::RecordDevNode(devnode);
826 }