xref: /aosp_15_r20/external/drm_hwcomposer/utils/UEvent.h (revision 0a9764fe0a15e71ebbeb85e87e10990c23aab47f)
1*0a9764feSAndroid Build Coastguard Worker /*
2*0a9764feSAndroid Build Coastguard Worker  * Copyright (C) 2022 The Android Open Source Project
3*0a9764feSAndroid Build Coastguard Worker  *
4*0a9764feSAndroid Build Coastguard Worker  * Licensed under the Apache License, Version 2.0 (the "License");
5*0a9764feSAndroid Build Coastguard Worker  * you may not use this file except in compliance with the License.
6*0a9764feSAndroid Build Coastguard Worker  * You may obtain a copy of the License at
7*0a9764feSAndroid Build Coastguard Worker  *
8*0a9764feSAndroid Build Coastguard Worker  *      http://www.apache.org/licenses/LICENSE-2.0
9*0a9764feSAndroid Build Coastguard Worker  *
10*0a9764feSAndroid Build Coastguard Worker  * Unless required by applicable law or agreed to in writing, software
11*0a9764feSAndroid Build Coastguard Worker  * distributed under the License is distributed on an "AS IS" BASIS,
12*0a9764feSAndroid Build Coastguard Worker  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*0a9764feSAndroid Build Coastguard Worker  * See the License for the specific language governing permissions and
14*0a9764feSAndroid Build Coastguard Worker  * limitations under the License.
15*0a9764feSAndroid Build Coastguard Worker  */
16*0a9764feSAndroid Build Coastguard Worker 
17*0a9764feSAndroid Build Coastguard Worker #pragma once
18*0a9764feSAndroid Build Coastguard Worker 
19*0a9764feSAndroid Build Coastguard Worker #include <linux/netlink.h>
20*0a9764feSAndroid Build Coastguard Worker #include <poll.h>
21*0a9764feSAndroid Build Coastguard Worker #include <sys/eventfd.h>
22*0a9764feSAndroid Build Coastguard Worker #include <sys/socket.h>
23*0a9764feSAndroid Build Coastguard Worker 
24*0a9764feSAndroid Build Coastguard Worker #include <cerrno>
25*0a9764feSAndroid Build Coastguard Worker #include <memory>
26*0a9764feSAndroid Build Coastguard Worker #include <optional>
27*0a9764feSAndroid Build Coastguard Worker #include <string>
28*0a9764feSAndroid Build Coastguard Worker 
29*0a9764feSAndroid Build Coastguard Worker #include "fd.h"
30*0a9764feSAndroid Build Coastguard Worker #include "log.h"
31*0a9764feSAndroid Build Coastguard Worker 
32*0a9764feSAndroid Build Coastguard Worker namespace android {
33*0a9764feSAndroid Build Coastguard Worker 
34*0a9764feSAndroid Build Coastguard Worker class UEvent {
35*0a9764feSAndroid Build Coastguard Worker  public:
36*0a9764feSAndroid Build Coastguard Worker   static auto CreateInstance() -> std::unique_ptr<UEvent> {
37*0a9764feSAndroid Build Coastguard Worker     auto fd = MakeUniqueFd(
38*0a9764feSAndroid Build Coastguard Worker         socket(PF_NETLINK, SOCK_DGRAM | SOCK_CLOEXEC, NETLINK_KOBJECT_UEVENT));
39*0a9764feSAndroid Build Coastguard Worker 
40*0a9764feSAndroid Build Coastguard Worker     if (!fd) {
41*0a9764feSAndroid Build Coastguard Worker       ALOGE("Failed to open uevent socket: errno=%i", errno);
42*0a9764feSAndroid Build Coastguard Worker       return {};
43*0a9764feSAndroid Build Coastguard Worker     }
44*0a9764feSAndroid Build Coastguard Worker 
45*0a9764feSAndroid Build Coastguard Worker     struct sockaddr_nl addr {};
46*0a9764feSAndroid Build Coastguard Worker     addr.nl_family = AF_NETLINK;
47*0a9764feSAndroid Build Coastguard Worker     addr.nl_pid = 0;
48*0a9764feSAndroid Build Coastguard Worker     addr.nl_groups = UINT32_MAX;
49*0a9764feSAndroid Build Coastguard Worker 
50*0a9764feSAndroid Build Coastguard Worker     // NOLINTNEXTLINE(cppcoreguidelines-pro-type-cstyle-cast)
51*0a9764feSAndroid Build Coastguard Worker     const int ret = bind(*fd, (struct sockaddr *)&addr, sizeof(addr));
52*0a9764feSAndroid Build Coastguard Worker     if (ret != 0) {
53*0a9764feSAndroid Build Coastguard Worker       ALOGE("Failed to bind uevent socket: errno=%i", errno);
54*0a9764feSAndroid Build Coastguard Worker       return {};
55*0a9764feSAndroid Build Coastguard Worker     }
56*0a9764feSAndroid Build Coastguard Worker 
57*0a9764feSAndroid Build Coastguard Worker     auto stop_event_fd = MakeUniqueFd(eventfd(0, EFD_CLOEXEC));
58*0a9764feSAndroid Build Coastguard Worker     if (!stop_event_fd) {
59*0a9764feSAndroid Build Coastguard Worker       ALOGE("Failed to create eventfd: errno=%i", errno);
60*0a9764feSAndroid Build Coastguard Worker       return {};
61*0a9764feSAndroid Build Coastguard Worker     }
62*0a9764feSAndroid Build Coastguard Worker 
63*0a9764feSAndroid Build Coastguard Worker     return std::unique_ptr<UEvent>(new UEvent(fd, stop_event_fd));
64*0a9764feSAndroid Build Coastguard Worker   }
65*0a9764feSAndroid Build Coastguard Worker 
66*0a9764feSAndroid Build Coastguard Worker   auto ReadNext() -> std::optional<std::string> {
67*0a9764feSAndroid Build Coastguard Worker     constexpr int kUEventBufferSize = 1024;
68*0a9764feSAndroid Build Coastguard Worker     char buffer[kUEventBufferSize];
69*0a9764feSAndroid Build Coastguard Worker 
70*0a9764feSAndroid Build Coastguard Worker     if (!WaitForData()) {
71*0a9764feSAndroid Build Coastguard Worker       return {};
72*0a9764feSAndroid Build Coastguard Worker     }
73*0a9764feSAndroid Build Coastguard Worker 
74*0a9764feSAndroid Build Coastguard Worker     ssize_t ret = 0;
75*0a9764feSAndroid Build Coastguard Worker     ret = read(*fd_, &buffer, sizeof(buffer));
76*0a9764feSAndroid Build Coastguard Worker     if (ret == 0)
77*0a9764feSAndroid Build Coastguard Worker       return {};
78*0a9764feSAndroid Build Coastguard Worker 
79*0a9764feSAndroid Build Coastguard Worker     if (ret < 0) {
80*0a9764feSAndroid Build Coastguard Worker       ALOGE("Got error reading uevent %zd", ret);
81*0a9764feSAndroid Build Coastguard Worker       return {};
82*0a9764feSAndroid Build Coastguard Worker     }
83*0a9764feSAndroid Build Coastguard Worker 
84*0a9764feSAndroid Build Coastguard Worker     for (int i = 0; i < ret - 1; i++) {
85*0a9764feSAndroid Build Coastguard Worker       if (buffer[i] == '\0') {
86*0a9764feSAndroid Build Coastguard Worker         buffer[i] = '\n';
87*0a9764feSAndroid Build Coastguard Worker       }
88*0a9764feSAndroid Build Coastguard Worker     }
89*0a9764feSAndroid Build Coastguard Worker 
90*0a9764feSAndroid Build Coastguard Worker     return std::string(buffer);
91*0a9764feSAndroid Build Coastguard Worker   }
92*0a9764feSAndroid Build Coastguard Worker 
Stop()93*0a9764feSAndroid Build Coastguard Worker   void Stop() {
94*0a9764feSAndroid Build Coastguard Worker     // Increment the eventfd by writing 1. All subsequent calls to ReadNext will
95*0a9764feSAndroid Build Coastguard Worker     // return false.
96*0a9764feSAndroid Build Coastguard Worker     const uint64_t value = 1;
97*0a9764feSAndroid Build Coastguard Worker     const ssize_t ret = write(*stop_event_fd_, &value, sizeof(value));
98*0a9764feSAndroid Build Coastguard Worker     if (ret == -1) {
99*0a9764feSAndroid Build Coastguard Worker       ALOGE("Error writing to eventfd. errno: %d", errno);
100*0a9764feSAndroid Build Coastguard Worker     } else if (ret != sizeof(value)) {
101*0a9764feSAndroid Build Coastguard Worker       ALOGE("Wrote fewer bytes to eventfd than expected: %zd vs %zd", ret,
102*0a9764feSAndroid Build Coastguard Worker             sizeof(value));
103*0a9764feSAndroid Build Coastguard Worker     }
104*0a9764feSAndroid Build Coastguard Worker   }
105*0a9764feSAndroid Build Coastguard Worker 
106*0a9764feSAndroid Build Coastguard Worker  private:
107*0a9764feSAndroid Build Coastguard Worker   enum { kFdIdx = 0, kStopEventFdIdx, kNumFds };
108*0a9764feSAndroid Build Coastguard Worker 
UEvent(UniqueFd & fd,UniqueFd & stop_event_fd)109*0a9764feSAndroid Build Coastguard Worker   UEvent(UniqueFd &fd, UniqueFd &stop_event_fd)
110*0a9764feSAndroid Build Coastguard Worker       : fd_(std::move(fd)), stop_event_fd_(std::move(stop_event_fd)) {};
111*0a9764feSAndroid Build Coastguard Worker 
112*0a9764feSAndroid Build Coastguard Worker   // Returns true if there is data to be read off of fd_.
WaitForData()113*0a9764feSAndroid Build Coastguard Worker   bool WaitForData() {
114*0a9764feSAndroid Build Coastguard Worker     struct pollfd poll_fds[kNumFds];
115*0a9764feSAndroid Build Coastguard Worker     poll_fds[kFdIdx].fd = *fd_;
116*0a9764feSAndroid Build Coastguard Worker     poll_fds[kFdIdx].events = POLLIN;
117*0a9764feSAndroid Build Coastguard Worker     poll_fds[kStopEventFdIdx].fd = *stop_event_fd_;
118*0a9764feSAndroid Build Coastguard Worker     poll_fds[kStopEventFdIdx].events = POLLIN;
119*0a9764feSAndroid Build Coastguard Worker 
120*0a9764feSAndroid Build Coastguard Worker     const int ret = poll(poll_fds, kNumFds, -1);
121*0a9764feSAndroid Build Coastguard Worker     if (ret == 0) {
122*0a9764feSAndroid Build Coastguard Worker       // Timeout shouldn't happen, but return here anyways.
123*0a9764feSAndroid Build Coastguard Worker       ALOGE("Timed out polling uevent.");
124*0a9764feSAndroid Build Coastguard Worker       return false;
125*0a9764feSAndroid Build Coastguard Worker     }
126*0a9764feSAndroid Build Coastguard Worker     if (ret < 1) {
127*0a9764feSAndroid Build Coastguard Worker       ALOGE("Error polling uevent. errno: %d", errno);
128*0a9764feSAndroid Build Coastguard Worker       return false;
129*0a9764feSAndroid Build Coastguard Worker     }
130*0a9764feSAndroid Build Coastguard Worker 
131*0a9764feSAndroid Build Coastguard Worker     if ((poll_fds[kStopEventFdIdx].revents & POLLIN) != 0) {
132*0a9764feSAndroid Build Coastguard Worker       // Stop event has been signalled. Return without reading from the fd to
133*0a9764feSAndroid Build Coastguard Worker       // ensure that this fd stays in a readable state.
134*0a9764feSAndroid Build Coastguard Worker       ALOGI("Stop event signalled.");
135*0a9764feSAndroid Build Coastguard Worker       return false;
136*0a9764feSAndroid Build Coastguard Worker     }
137*0a9764feSAndroid Build Coastguard Worker 
138*0a9764feSAndroid Build Coastguard Worker     // Return true if there is data to read.
139*0a9764feSAndroid Build Coastguard Worker     return (poll_fds[kFdIdx].revents & POLLIN) != 0;
140*0a9764feSAndroid Build Coastguard Worker   }
141*0a9764feSAndroid Build Coastguard Worker 
142*0a9764feSAndroid Build Coastguard Worker   UniqueFd fd_;
143*0a9764feSAndroid Build Coastguard Worker   UniqueFd stop_event_fd_;
144*0a9764feSAndroid Build Coastguard Worker };
145*0a9764feSAndroid Build Coastguard Worker 
146*0a9764feSAndroid Build Coastguard Worker }  // namespace android
147