1 #pragma once
2 
3 #include <fcntl.h>
4 #include <libevdev/libevdev.h>
5 #include <unistd.h>
6 
7 #include <phosphor-logging/elog-errors.hpp>
8 #include <phosphor-logging/elog.hpp>
9 #include <xyz/openbmc_project/Common/error.hpp>
10 
11 #include <memory>
12 #include <string>
13 #include <tuple>
14 
15 namespace evdevpp
16 {
17 namespace evdev
18 {
19 
20 using EvDevPtr = libevdev*;
21 
22 namespace details
23 {
24 
25 /** @brief unique_ptr functor to release an evdev reference. */
26 struct EvDevDeleter
27 {
28     void operator()(libevdev* ptr) const
29     {
30         deleter(ptr);
31     }
32 
33     decltype(&libevdev_free) deleter = libevdev_free;
34 };
35 
36 /* @brief Alias evdev to a unique_ptr type for auto-release. */
37 using EvDev = std::unique_ptr<libevdev, EvDevDeleter>;
38 
39 } // namespace details
40 
41 using namespace phosphor::logging;
42 /** @class EvDev
43  *  @brief Provides C++ bindings to the libevdev C API.
44  */
45 class EvDev
46 {
47   private:
48     using InternalFailure =
49         sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
50 
51   public:
52     /* Define all of the basic class operations:
53      *     Not allowed:
54      *         - Default constructor to avoid nullptrs.
55      *         - Copy operations due to internal unique_ptr.
56      *     Allowed:
57      *         - Move operations.
58      *         - Destructor.
59      */
60     EvDev() = delete;
61     EvDev(const EvDev&) = delete;
62     EvDev& operator=(const EvDev&) = delete;
63     EvDev(EvDev&&) = default;
64     EvDev& operator=(EvDev&&) = default;
65     ~EvDev() = default;
66 
67     /** @brief Conversion constructor from evdev. */
68     explicit EvDev(EvDevPtr ptr) : evdev(ptr) {}
69 
70     /** @brief Get the current event state. */
71     auto fetch(unsigned int type, unsigned int code)
72     {
73         int val;
74         auto rc = libevdev_fetch_event_value(evdev.get(), type, code, &val);
75         if (!rc)
76         {
77             log<level::ERR>("Error in call to libevdev_fetch_event_value",
78                             entry("TYPE=%d", type), entry("CODE=%d", code));
79             elog<InternalFailure>();
80         }
81 
82         return val;
83     }
84 
85     /** @brief Get the next event. */
86     auto next()
87     {
88         struct input_event ev;
89         while (true)
90         {
91             auto rc = libevdev_next_event(evdev.get(),
92                                           LIBEVDEV_READ_FLAG_NORMAL, &ev);
93             if (rc < 0)
94             {
95                 log<level::ERR>("Error in call to libevdev_next_event",
96                                 entry("RC=%d", rc));
97                 elog<InternalFailure>();
98             }
99 
100             if (ev.type == EV_SYN && ev.code == SYN_REPORT)
101                 continue;
102 
103             break;
104         }
105         return std::make_tuple(ev.type, ev.code, ev.value);
106     }
107 
108   private:
109     EvDevPtr get()
110     {
111         return evdev.get();
112     }
113 
114     details::EvDev evdev;
115 };
116 
117 inline auto newFromFD(int fd)
118 {
119     using InternalFailure =
120         sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
121 
122     EvDevPtr dev = nullptr;
123     auto rc = libevdev_new_from_fd(fd, &dev);
124 
125     if (rc)
126     {
127         log<level::ERR>("Error in call to libevdev_new_from_fd",
128                         entry("RC=%d", rc), entry("FD=%d", fd));
129         elog<InternalFailure>();
130     }
131 
132     return EvDev(dev);
133 }
134 } // namespace evdev
135 } // namespace evdevpp
136