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 
71     /** @brief Get the current event state. */
72     auto fetch(unsigned int type, unsigned int code)
73     {
74         int val;
75         auto rc = libevdev_fetch_event_value(evdev.get(), type, code, &val);
76         if (!rc)
77         {
78             log<level::ERR>("Error in call to libevdev_fetch_event_value",
79                             entry("TYPE=%d", type), entry("CODE=%d", code));
80             elog<InternalFailure>();
81         }
82 
83         return val;
84     }
85 
86     /** @brief Get the next event. */
87     auto next()
88     {
89         struct input_event ev;
90         while (true)
91         {
92             auto rc = libevdev_next_event(evdev.get(),
93                                           LIBEVDEV_READ_FLAG_NORMAL, &ev);
94             if (rc < 0)
95             {
96                 log<level::ERR>("Error in call to libevdev_next_event",
97                                 entry("RC=%d", rc));
98                 elog<InternalFailure>();
99             }
100 
101             if (ev.type == EV_SYN && ev.code == SYN_REPORT)
102                 continue;
103 
104             break;
105         }
106         return std::make_tuple(ev.type, ev.code, ev.value);
107     }
108 
109   private:
110     EvDevPtr get()
111     {
112         return evdev.get();
113     }
114 
115     details::EvDev evdev;
116 };
117 
118 inline auto newFromFD(int fd)
119 {
120     using InternalFailure =
121         sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
122 
123     EvDevPtr dev = nullptr;
124     auto rc = libevdev_new_from_fd(fd, &dev);
125 
126     if (rc)
127     {
128         log<level::ERR>("Error in call to libevdev_new_from_fd",
129                         entry("RC=%d", rc), entry("FD=%d", fd));
130         elog<InternalFailure>();
131     }
132 
133     return EvDev(dev);
134 }
135 } // namespace evdev
136 } // namespace evdevpp
137