1 #pragma once
2 
3 #include <fcntl.h>
4 #include <libevdev/libevdev.h>
5 #include <memory>
6 #include <phosphor-logging/elog.hpp>
7 #include <phosphor-logging/elog-errors.hpp>
8 #include <string>
9 #include <tuple>
10 #include <unistd.h>
11 #include <xyz/openbmc_project/Common/error.hpp>
12 
13 namespace evdevpp
14 {
15 namespace evdev
16 {
17 
18 using EvDevPtr = libevdev*;
19 
20 namespace details
21 {
22 
23 /** @brief unique_ptr functor to release an evdev reference. */
24 struct EvDevDeleter
25 {
26     void operator()(libevdev* ptr) const
27     {
28         deleter(ptr);
29     }
30 
31     decltype(&libevdev_free) deleter = libevdev_free;
32 };
33 
34 /* @brief Alias evdev to a unique_ptr type for auto-release. */
35 using EvDev = std::unique_ptr<libevdev, EvDevDeleter>;
36 
37 } // namespace details
38 
39 using namespace phosphor::logging;
40 /** @class EvDev
41  *  @brief Provides C++ bindings to the libevdev C API.
42  */
43 class EvDev
44 {
45     private:
46         using InternalFailure = sdbusplus::xyz::openbmc_project::Common::
47             Error::InternalFailure;
48 
49     public:
50         /* Define all of the basic class operations:
51          *     Not allowed:
52          *         - Default constructor to avoid nullptrs.
53          *         - Copy operations due to internal unique_ptr.
54          *     Allowed:
55          *         - Move operations.
56          *         - Destructor.
57          */
58         EvDev() = delete;
59         EvDev(const EvDev&) = delete;
60         EvDev& operator=(const EvDev&) = delete;
61         EvDev(EvDev&&) = default;
62         EvDev& operator=(EvDev&&) = default;
63         ~EvDev() = default;
64 
65         /** @brief Conversion constructor from evdev. */
66         explicit EvDev(EvDevPtr ptr) : evdev(ptr) {}
67 
68         /** @brief Get the current event state. */
69         auto fetch(unsigned int type, unsigned int code)
70         {
71             int val;
72             auto rc = libevdev_fetch_event_value(
73                     evdev.get(), type, code, &val);
74             if (!rc)
75             {
76                 log<level::ERR>("Error in call to libevdev_fetch_event_value",
77                         entry("TYPE=%d", type),
78                         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(
92                         evdev.get(), 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 = sdbusplus::xyz::openbmc_project::Common::
120         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),
129                 entry("FD=%d", fd));
130         elog<InternalFailure>();
131     }
132 
133     return EvDev(dev);
134 }
135 } // namespace evdev
136 } // namespace evdevpp
137