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