• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 }