1 /*
2  * Copyright (C) 2016 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #define LOG_TAG "hwc-drm-event-listener"
18 
19 #include "drmeventlistener.h"
20 #include "drmdevice.h"
21 #include <drm/samsung_drm.h>
22 
23 #include <assert.h>
24 #include <errno.h>
25 #include <linux/netlink.h>
26 #include <sys/socket.h>
27 
28 #include <hardware/hardware.h>
29 #include <hardware/hwcomposer.h>
30 #include <log/log.h>
31 #include <xf86drm.h>
32 
33 namespace android {
34 
DrmEventListener(DrmDevice * drm)35 DrmEventListener::DrmEventListener(DrmDevice *drm)
36     : Worker("drm-event-listener", HAL_PRIORITY_URGENT_DISPLAY), drm_(drm) {
37 }
38 
~DrmEventListener()39 DrmEventListener::~DrmEventListener() {
40     Exit();
41 }
42 
Init()43 int DrmEventListener::Init() {
44   struct epoll_event ev;
45   char buffer[1024];
46 
47   /* Open User Event File Descriptor */
48   uevent_fd_.Set(socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT));
49   if (uevent_fd_.get() < 0) {
50     ALOGE("Failed to open uevent socket: %s", strerror(errno));
51     return uevent_fd_.get();
52   }
53 
54   struct sockaddr_nl addr;
55   memset(&addr, 0, sizeof(addr));
56   addr.nl_family = AF_NETLINK;
57   addr.nl_pid = 0;
58   addr.nl_groups = 0xFFFFFFFF;
59 
60   int ret = bind(uevent_fd_.get(), (struct sockaddr *)&addr, sizeof(addr));
61   if (ret) {
62     ALOGE("Failed to bind uevent socket: %s", strerror(errno));
63     return -errno;
64   }
65 
66   /* Open TUI Event File Descriptor */
67   tuievent_fd_.Set(open(kTUIStatusPath, O_RDONLY));
68   if (tuievent_fd_.get() < 0) {
69     ALOGE("Failed to open sysfs(%s) for TUI event: %s", kTUIStatusPath, strerror(errno));
70     return tuievent_fd_.get();
71   }
72 
73   /* Read garbage data once */
74   pread(tuievent_fd_.get(), &buffer, sizeof(buffer), 0);
75 
76   /* Set EPoll*/
77   epoll_fd_.Set(epoll_create(maxFds));
78   if (epoll_fd_.get() < 0) {
79     ALOGE("Failed to create epoll: %s", strerror(errno));
80     return epoll_fd_.get();
81   }
82 
83   ev.events = EPOLLIN;
84   ev.data.fd = uevent_fd_.get();
85   if (epoll_ctl(epoll_fd_.get(), EPOLL_CTL_ADD, uevent_fd_.get(), &ev) < 0) {
86     ALOGE("Failed to add uevent fd into epoll: %s", strerror(errno));
87     return -errno;
88   }
89 
90   ev.events = EPOLLIN;
91   ev.data.fd = drm_->fd();
92   if (epoll_ctl(epoll_fd_.get(), EPOLL_CTL_ADD, drm_->fd(), &ev) < 0) {
93     ALOGE("Failed to add drm fd into epoll: %s", strerror(errno));
94     return -errno;
95   }
96 
97   ev.events = EPOLLPRI;
98   ev.data.fd = tuievent_fd_.get();
99   if (epoll_ctl(epoll_fd_.get(), EPOLL_CTL_ADD, tuievent_fd_.get(), &ev) < 0) {
100     ALOGE("Failed to add tui fd into epoll: %s", strerror(errno));
101     return -errno;
102   }
103 
104   return InitWorker();
105 }
106 
RegisterHotplugHandler(DrmEventHandler * handler)107 void DrmEventListener::RegisterHotplugHandler(DrmEventHandler *handler) {
108   assert(!hotplug_handler_);
109   hotplug_handler_.reset(handler);
110 }
111 
UnRegisterHotplugHandler(DrmEventHandler * handler)112 void DrmEventListener::UnRegisterHotplugHandler(DrmEventHandler *handler) {
113   if (handler == hotplug_handler_.get())
114     hotplug_handler_ = NULL;
115 }
116 
RegisterTUIHandler(DrmTUIEventHandler * handler)117 void DrmEventListener::RegisterTUIHandler(DrmTUIEventHandler *handler) {
118   if (tui_handler_) {
119     ALOGE("TUI handler was already registered");
120     return;
121   }
122   tui_handler_.reset(handler);
123 }
124 
UnRegisterTUIHandler(DrmTUIEventHandler * handler)125 void DrmEventListener::UnRegisterTUIHandler(DrmTUIEventHandler *handler) {
126   if (handler == tui_handler_.get())
127     tui_handler_ = NULL;
128 }
129 
IsDrmInTUI()130 bool DrmEventListener::IsDrmInTUI() {
131   char buffer[1024];
132   int ret;
133 
134   ret = pread(tuievent_fd_.get(), &buffer, sizeof(buffer), 0);
135   if (ret == 0) {
136     return false;
137   } else if (ret < 0) {
138     ALOGE("Got error reading TUI event %s", strerror(errno));
139     return false;
140   }
141 
142   return atoi(buffer) == 1 ? true : false;
143 }
144 
FlipHandler(int,unsigned int,unsigned int tv_sec,unsigned int tv_usec,void * user_data)145 void DrmEventListener::FlipHandler(int /* fd */, unsigned int /* sequence */,
146                                    unsigned int tv_sec, unsigned int tv_usec,
147                                    void *user_data) {
148   DrmEventHandler *handler = (DrmEventHandler *)user_data;
149   if (!handler)
150     return;
151 
152   handler->HandleEvent((uint64_t)tv_sec * 1000 * 1000 + tv_usec);
153   delete handler;
154 }
155 
UEventHandler()156 void DrmEventListener::UEventHandler() {
157   char buffer[1024];
158   int ret;
159 
160   struct timespec ts;
161   uint64_t timestamp = 0;
162   ret = clock_gettime(CLOCK_MONOTONIC, &ts);
163   if (!ret)
164     timestamp = ts.tv_sec * 1000 * 1000 * 1000 + ts.tv_nsec;
165   else
166     ALOGE("Failed to get monotonic clock on hotplug %d", ret);
167 
168   ret = read(uevent_fd_.get(), &buffer, sizeof(buffer));
169   if (ret == 0) {
170       return;
171   } else if (ret < 0) {
172     ALOGE("Got error reading uevent %d", ret);
173     return;
174   }
175 
176   bool drm_event = false, hotplug_event = false;
177   for (int i = 0; i < ret;) {
178     char *event = buffer + i;
179     if (!strcmp(event, "DEVTYPE=drm_minor")) {
180       drm_event = true;
181     } else if (!strcmp(event, "HOTPLUG=1")) {
182       hotplug_event = true;
183     }
184 
185     i += strlen(event) + 1;
186   }
187 
188   if (drm_event && hotplug_event) {
189     if (!hotplug_handler_)
190       return;
191 
192     hotplug_handler_->HandleEvent(timestamp);
193   }
194 }
195 
TUIEventHandler()196 void DrmEventListener::TUIEventHandler() {
197   if (!tui_handler_) {
198     ALOGE("%s:: tui event handler is not valid", __func__);
199     return;
200   }
201 
202   tui_handler_->HandleTUIEvent();
203 }
204 
Routine()205 void DrmEventListener::Routine() {
206   struct epoll_event events[maxFds];
207   int nfds, n;
208 
209   do {
210     nfds = epoll_wait(epoll_fd_.get(), events, maxFds, -1);
211   } while (nfds <= 0);
212 
213   for (n = 0; n < nfds; n++) {
214     if (events[n].events & EPOLLIN) {
215       if (events[n].data.fd == uevent_fd_.get()) {
216         UEventHandler();
217       } else if (events[n].data.fd == drm_->fd()) {
218         drmEventContext event_context =
219             {.version = 2,
220              .vblank_handler = NULL,
221              .page_flip_handler = DrmEventListener::FlipHandler};
222         drmHandleEvent(drm_->fd(), &event_context);
223       }
224     } else if (events[n].events & EPOLLPRI) {
225       if (events[n].data.fd == tuievent_fd_.get()) {
226         TUIEventHandler();
227       }
228     }
229   }
230 }
231 }  // namespace android
232