• 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         if (parentDevice_.value() != nullptr) {
294             return *parentDevice_;
295         }
296         return nullptr;
297     }
298 
GetSyspathudev_device299     const std::string &GetSyspath() const
300     {
301         return syspath;
302     }
303 
GetSysnameudev_device304     const std::string &GetSysname() const
305     {
306         return sysname;
307     }
308 
GetDevnodeudev_device309     const std::string &GetDevnode()
310     {
311         return GetProperty("DEVNAME");
312     }
313 
IsInitializedudev_device314     bool IsInitialized()
315     {
316         if (!ueventLoaded) {
317             ReadUeventFile();
318         }
319         return ueventLoaded;
320     }
321 
GetParentWithSubsystemudev_device322     udev_device *GetParentWithSubsystem(const std::string &subsystem)
323     {
324         udev_device *parent = GetParent();
325         while (parent != nullptr) {
326             auto parentSubsystem = parent->GetSubsystem();
327             if (parentSubsystem.has_value() && parentSubsystem.value() == subsystem) {
328                 break;
329             }
330             parent = parent->GetParent();
331         }
332 
333         if (parent == nullptr) {
334             errno = ENOENT;
335         }
336         return parent;
337     }
338 
HasPropertyudev_device339     bool HasProperty(const std::string &key)
340     {
341         if (!ueventLoaded) {
342             ReadUeventFile();
343         }
344         return property_.find(key) != property_.end();
345     }
346 
GetPropertyudev_device347     const std::string &GetProperty(const std::string &key)
348     {
349         if (!ueventLoaded) {
350             ReadUeventFile();
351         }
352         return property_[key];
353     }
354 
355 private:
356     udev_device() = default;
357 
~udev_deviceudev_device358     ~udev_device()
359     {
360         if (parentDevice_.has_value() && parentDevice_.value() != nullptr) {
361             parentDevice_.value()->Unref();
362         }
363     }
364 
NewFromChildudev_device365     static udev_device *NewFromChild(udev_device *child)
366     {
367         std::string_view path = child->GetSyspath();
368 
369         while (true) {
370             if (!ChopTail(path, '/')) {
371                 break;
372             }
373             udev_device *parent = NewFromSyspath(std::string{ path });
374             if (parent != nullptr) {
375                 return parent;
376             }
377         }
378 
379         return nullptr;
380     }
381 
SetSyspathudev_device382     void SetSyspath(std::string newSyspath)
383     {
384         syspath = std::move(newSyspath);
385 
386         AddProperty("DEVPATH", syspath.substr(0, "/sys"sv.size()));
387 
388         auto pos = syspath.rfind('/');
389         if (pos == std::string::npos) {
390             return;
391         }
392         sysname = syspath.substr(pos + 1);
393 
394         // some devices have '!' in their name, change that to '/'
395         for (char &c : sysname) {
396             if (c == '!') {
397                 c = '/';
398             }
399         }
400     }
401 
AddPropertyFromStringudev_device402     void AddPropertyFromString(const std::string &line)
403     {
404         auto pos = line.find('=');
405         if (pos == std::string::npos) {
406             return;
407         }
408         std::string key = line.substr(0, pos);
409         if (key == "DEVNAME") {
410             SetDevnode(line.substr(pos + 1));
411             return;
412         }
413         AddProperty(std::move(key), line.substr(pos + 1));
414     }
415 
ReadUeventFileudev_device416     void ReadUeventFile()
417     {
418         if (ueventLoaded) {
419             return;
420         }
421 
422         auto filename = syspath + "/uevent";
423         ueventLoaded = true;
424 
425         std::lock_guard<std::mutex> lock(mutex_);
426         auto propertys = allPropertys_[devnode_];
427         auto lines = propertys[filename];
428         for (auto it = lines.begin(); it != lines.end(); it++) {
429             AddPropertyFromString(*it);
430         }
431         CheckInputProperties();
432     }
433 
CheckAcceludev_device434     bool CheckAccel(const BitVector &ev, const BitVector &abs, const BitVector &prop)
435     {
436         bool hasKeys = ev.CheckBit(EV_KEY);
437         bool has3dCoordinates = abs.CheckBit(ABS_X) && abs.CheckBit(ABS_Y) && abs.CheckBit(ABS_Z);
438         bool isAccelerometer = prop.CheckBit(INPUT_PROP_ACCELEROMETER);
439 
440         if (!hasKeys && has3dCoordinates) {
441             isAccelerometer = true;
442         }
443 
444         if (isAccelerometer) {
445             SetInputProperty("ID_INPUT_ACCELEROMETER");
446         }
447         return isAccelerometer;
448     }
449 
HasJoystickAxesOrButtonsudev_device450     bool HasJoystickAxesOrButtons(const BitVector &abs, const BitVector &key)
451     {
452         bool hasJoystickAxesOrButtons = false;
453         // Some mouses have so much buttons that they overflow in joystick range, ignore them
454         if (!key.CheckBit(BTN_JOYSTICK - 1)) {
455             for (int32_t button = BTN_JOYSTICK; button < BTN_DIGI && !hasJoystickAxesOrButtons; button++) {
456                 hasJoystickAxesOrButtons = key.CheckBit(button);
457             }
458             for (int32_t button = BTN_TRIGGER_HAPPY1; button <= BTN_TRIGGER_HAPPY40 && !hasJoystickAxesOrButtons;
459                 button++) {
460                 hasJoystickAxesOrButtons = key.CheckBit(button);
461             }
462             for (int32_t button = BTN_DPAD_UP; button <= BTN_DPAD_RIGHT && !hasJoystickAxesOrButtons; button++) {
463                 hasJoystickAxesOrButtons = key.CheckBit(button);
464             }
465         }
466         for (int32_t axis = ABS_RX; axis < ABS_PRESSURE && !hasJoystickAxesOrButtons; axis++) {
467             hasJoystickAxesOrButtons = abs.CheckBit(axis);
468         }
469         return hasJoystickAxesOrButtons;
470     }
471 
CheckPointingStickudev_device472     bool CheckPointingStick(const BitVector &prop)
473     {
474         if (prop.CheckBit(INPUT_PROP_POINTING_STICK)) {
475             SetInputProperty("ID_INPUT_POINTINGSTICK");
476             return true;
477         }
478         return false;
479     }
480 
CheckAndSetPropudev_device481     void CheckAndSetProp(std::string prop, const bool &flag)
482     {
483         if (flag) {
484             SetInputProperty(prop);
485             MMI_HILOGD("The device has prop with %{public}s", prop.c_str());
486         }
487     }
488 
CheckMouseButtonudev_device489     void CheckMouseButton(const BitVector &key, bool &flag)
490     {
491         for (int32_t button = BTN_MOUSE; button < BTN_JOYSTICK && !flag; button++) {
492             flag = key.CheckBit(button);
493         }
494     }
495 
UpdateProByKeyudev_device496     void UpdateProByKey(const BitVector &key, const bool &isDirect, bool &probablyTablet, bool &probablyTouchpad,
497         bool &probablyTouchscreen)
498     {
499         probablyTablet = key.CheckBit(BTN_STYLUS) || key.CheckBit(BTN_TOOL_PEN);
500         probablyTouchpad = key.CheckBit(BTN_TOOL_FINGER) && !key.CheckBit(BTN_TOOL_PEN) && !isDirect;
501         probablyTouchscreen = key.CheckBit(BTN_TOUCH) && isDirect;
502     }
503 
CheckMtCoordinatesudev_device504     bool CheckMtCoordinates(const BitVector &abs)
505     {
506         bool hasMtCoordinates = abs.CheckBit(ABS_MT_POSITION_X) && abs.CheckBit(ABS_MT_POSITION_Y);
507         /* unset hasMtCoordinates if devices claims to have all abs axis */
508         if (hasMtCoordinates && abs.CheckBit(ABS_MT_SLOT) && abs.CheckBit(ABS_MT_SLOT - 1)) {
509             hasMtCoordinates = false;
510         }
511         return hasMtCoordinates;
512     }
513 
UpdateProByStatusudev_device514     void UpdateProByStatus(const bool &isMouse, const bool &isTouchpad, const bool &isTouchscreen,
515         const bool &isJoystick, const bool &isTablet)
516     {
517         CheckAndSetProp("ID_INPUT_MOUSE", isMouse);
518         CheckAndSetProp("ID_INPUT_TOUCHPAD", isTouchpad);
519         CheckAndSetProp("ID_INPUT_TOUCHSCREEN", isTouchscreen);
520         CheckAndSetProp("ID_INPUT_JOYSTICK", isJoystick);
521         CheckAndSetProp("ID_INPUT_TABLET", isTablet);
522     }
523 
CheckPointersudev_device524     bool CheckPointers(const BitVector &ev, const BitVector &abs, const BitVector &key, const BitVector &rel,
525         const BitVector &prop)
526     {
527         bool isDirect = prop.CheckBit(INPUT_PROP_DIRECT);
528         bool hasAbsCoordinates = abs.CheckBit(ABS_X) && abs.CheckBit(ABS_Y);
529         bool hasRelCoordinates = ev.CheckBit(EV_REL) && rel.CheckBit(REL_X) && rel.CheckBit(REL_Y);
530         bool hasMtCoordinates = CheckMtCoordinates(abs);
531 
532         bool hasMouseButton = false;
533         CheckMouseButton(key, hasMouseButton);
534 
535         bool probablyTablet;
536         bool probablyTouchpad;
537         bool probablyTouchscreen;
538         UpdateProByKey(key, isDirect, probablyTablet, probablyTouchpad, probablyTouchscreen);
539         bool probablyJoystick = HasJoystickAxesOrButtons(abs, key);
540 
541         bool isTablet = false;
542         bool isMouse = false;
543         bool isTouchpad = false;
544         bool isTouchscreen = false;
545         bool isJoystick = false;
546         if (hasAbsCoordinates) {
547             if (probablyTablet) {
548                 isTablet = true;
549             } else if (probablyTouchpad) {
550                 isTouchpad = true;
551             } else if (hasMouseButton) {
552                 /* This path is taken by VMware's USB mouse, which has
553                  * absolute axes, but no touch/pressure button. */
554                 isMouse = true;
555             } else if (probablyTouchscreen) {
556                 isTouchscreen = true;
557             } else {
558                 isJoystick = probablyJoystick;
559             }
560         } else {
561             isJoystick = probablyJoystick;
562         }
563 
564         if (hasMtCoordinates) {
565             if (probablyTablet) {
566                 isTablet = true;
567             } else if (probablyTouchpad) {
568                 isTouchpad = true;
569             } else if (probablyTouchscreen) {
570                 isTouchscreen = true;
571             }
572         }
573 
574         /* mouse buttons and no axis */
575         if (!isTablet && !isTouchpad && !isJoystick && hasMouseButton && (hasRelCoordinates || !hasAbsCoordinates)) {
576             isMouse = true;
577         }
578 
579         UpdateProByStatus(isMouse, isTouchpad, isTouchscreen, isJoystick, isTablet);
580 
581         return isTablet || isMouse || isTouchpad || isTouchscreen || isJoystick || CheckPointingStick(prop);
582     }
583 
CheckKeysudev_device584     bool CheckKeys(const BitVector &ev, const BitVector &key)
585     {
586         if (!ev.CheckBit(EV_KEY)) {
587             return false;
588         }
589 
590         /* only consider KEY_* here, not BTN_* */
591         bool found = false;
592         for (int32_t i = 0; i < BTN_MISC && !found; ++i) {
593             found = key.CheckBit(i);
594         }
595         /* If there are no keys in the lower block, check the higher blocks */
596         for (int32_t i = KEY_OK; i < BTN_DPAD_UP && !found; ++i) {
597             found = key.CheckBit(i);
598         }
599         for (int32_t i = KEY_ALS_TOGGLE; i < BTN_TRIGGER_HAPPY && !found; ++i) {
600             found = key.CheckBit(i);
601         }
602 
603         if (found) {
604             SetInputProperty("ID_INPUT_KEY");
605         }
606 
607         /* the first 32 bits are ESC, numbers, and Q to D; if we have all of
608          * those, consider it a full keyboard; do not test KEY_RESERVED, though */
609         bool isKeyboard = true;
610         for (int32_t i = KEY_ESC; i < KEY_D && isKeyboard; i++) {
611             isKeyboard = key.CheckBit(i);
612         }
613         if (isKeyboard) {
614             SetInputProperty("ID_INPUT_KEYBOARD");
615         }
616 
617         return found || isKeyboard;
618     }
619 
SetInputPropertyudev_device620     void SetInputProperty(std::string prop)
621     {
622         AddProperty("ID_INPUT", "1");
623         AddProperty(std::move(prop), "1");
624     }
625 
CheckInputPropertiesudev_device626     void CheckInputProperties()
627     {
628         BitVector ev{ GetProperty("EV") };
629         BitVector abs{ GetProperty("ABS") };
630         BitVector key{ GetProperty("KEY") };
631         BitVector rel{ GetProperty("REL") };
632         BitVector prop{ GetProperty("PROP") };
633 
634         bool isPointer = CheckAccel(ev, abs, prop) || CheckPointers(ev, abs, key, rel, prop);
635         bool isKey = CheckKeys(ev, key);
636         /* Some evdev nodes have only a scrollwheel */
637         if (!isPointer && !isKey && ev.CheckBit(EV_REL) && (rel.CheckBit(REL_WHEEL) || rel.CheckBit(REL_HWHEEL))) {
638             SetInputProperty("ID_INPUT_KEY");
639         }
640         if (ev.CheckBit(EV_SW)) {
641             SetInputProperty("ID_INPUT_SWITCH");
642         }
643     }
644 
SetDevnodeudev_device645     void SetDevnode(std::string newDevnode)
646     {
647         if (newDevnode[0] != '/') {
648             newDevnode = "/dev/" + newDevnode;
649         }
650         AddProperty("DEVNAME", std::move(newDevnode));
651     }
652 
AddPropertyudev_device653     void AddProperty(std::string key, std::string value)
654     {
655         property_[std::move(key)] = std::move(value);
656     }
657 
GetSubsystemudev_device658     std::optional<std::string> GetSubsystem()
659     {
660         if (!subsystem_.has_value()) {
661             auto res = GetLinkValue("subsystem", syspath);
662             // read "subsystem" link
663             if (res.has_value()) {
664                 SetSubsystem(std::move(*res));
665                 return subsystem_;
666             }
667             subsystem_ = "";
668         }
669         return subsystem_;
670     }
671 
SetSubsystemudev_device672     void SetSubsystem(std::string newSubsystem)
673     {
674         subsystem_ = newSubsystem;
675         AddProperty("SUBSYSTEM", std::move(newSubsystem));
676     }
677 
678 private:
679     int refcount = 1;
680     std::string syspath;
681     std::string sysname;
682 
683     std::optional<udev_device *> parentDevice_;
684     std::optional<std::string> subsystem_;
685 
686     bool ueventLoaded = false;
687     std::unordered_map<std::string, std::string> property_;
688     static AllPropertys allPropertys_;
689     static std::mutex mutex_;
690     static std::string devnode_;
691 };
692 AllPropertys udev_device::allPropertys_;
693 std::mutex udev_device::mutex_;
694 std::string udev_device::devnode_;
695 
696 // C-style interface
697 
udev_new(void)698 udev *udev_new(void)
699 {
700     static udev instance{};
701     return &instance;
702 }
703 
udev_unref(udev * udev)704 udev *udev_unref([[maybe_unused]] udev *udev)
705 {
706     return nullptr;
707 }
708 
udev_device_ref(udev_device * device)709 udev_device *udev_device_ref(udev_device *device)
710 {
711     CHKPP(device);
712     device->Ref();
713     return device;
714 }
715 
udev_device_unref(udev_device * device)716 udev_device *udev_device_unref(udev_device *device)
717 {
718     CHKPP(device);
719     device->Unref();
720     return nullptr;
721 }
722 
udev_device_get_udev(udev_device * device)723 udev *udev_device_get_udev(udev_device *device)
724 {
725     CHKPP(device);
726     return udev_new();
727 }
728 
udev_device_new_from_syspath(udev * udev,const char * syspath)729 udev_device *udev_device_new_from_syspath(udev *udev, const char *syspath)
730 {
731     if (udev == nullptr || syspath == nullptr) {
732         errno = EINVAL;
733         return nullptr;
734     }
735     return udev_device::NewFromSyspath(syspath);
736 }
737 
udev_device_new_from_devnum(udev * udev,char type,dev_t devnum)738 udev_device *udev_device_new_from_devnum(udev *udev, char type, dev_t devnum)
739 {
740     if (udev == nullptr) {
741         errno = EINVAL;
742         return nullptr;
743     }
744     return udev_device::NewFromDevnum(type, devnum);
745 }
746 
udev_device_get_parent(udev_device * device)747 udev_device *udev_device_get_parent(udev_device *device)
748 {
749     if (device == nullptr) {
750         errno = EINVAL;
751         return nullptr;
752     }
753     return device->GetParent();
754 }
755 
udev_device_get_parent_with_subsystem_devtype(udev_device * device,const char * subsystem,const char * devtype)756 udev_device *udev_device_get_parent_with_subsystem_devtype(udev_device *device, const char *subsystem,
757     const char *devtype)
758 {
759     CHKPP(device);
760     if (subsystem == nullptr) {
761         errno = EINVAL;
762         return nullptr;
763     }
764     // Searching with specific devtype is not supported, since not used by libinput
765     CHKPP(devtype);
766     return device->GetParentWithSubsystem(subsystem);
767 }
768 
udev_device_get_syspath(udev_device * device)769 const char *udev_device_get_syspath(udev_device *device)
770 {
771     CHKPP(device);
772     return device->GetSyspath().c_str();
773 }
774 
udev_device_get_sysname(udev_device * device)775 const char *udev_device_get_sysname(udev_device *device)
776 {
777     CHKPP(device);
778     return device->GetSysname().c_str();
779 }
780 
udev_device_get_devnode(udev_device * device)781 const char *udev_device_get_devnode(udev_device *device)
782 {
783     CHKPP(device);
784     return device->GetDevnode().c_str();
785 }
786 
udev_device_get_is_initialized(udev_device * device)787 int udev_device_get_is_initialized(udev_device *device)
788 {
789     return (device != nullptr) ? static_cast<int>(device->IsInitialized()) : -1;
790 }
791 
udev_device_get_property_value(udev_device * device,const char * key)792 const char *udev_device_get_property_value(udev_device *device, const char *key)
793 {
794     CHKPP(device);
795     CHKPP(key);
796     std::string skey{ key };
797     if (!device->HasProperty(key)) {
798         return nullptr;
799     }
800     return device->GetProperty(key).c_str();
801 }
802 
udev_device_property_add(char type,const char * devnode)803 bool udev_device_property_add(char type, const char *devnode)
804 {
805     struct stat st;
806     if (stat(devnode, &st) < 0) {
807         MMI_HILOGW("udev_device_property_add stat failed, devnode:%{public}s", devnode);
808         return false;
809     }
810     udev_device::AddPropertys(type, st.st_rdev, devnode);
811     return true;
812 }
813 
udev_device_property_remove(const char * devnode)814 void udev_device_property_remove(const char *devnode)
815 {
816     udev_device::RemoveProperty(devnode);
817 }
818 
udev_device_record_devnode(const char * devnode)819 void udev_device_record_devnode(const char *devnode)
820 {
821     udev_device::RecordDevNode(devnode);
822 }