blob: 5f69c7b8bce653dd647023814baf67f3f6db6193 [file] [log] [blame]
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define LOG_TAG "hwc-drm-event-listener"
#include "drmeventlistener.h"
#include "drmdevice.h"
#include <drm/samsung_drm.h>
#include <assert.h>
#include <errno.h>
#include <linux/netlink.h>
#include <sys/socket.h>
#include <hardware/hardware.h>
#include <hardware/hwcomposer.h>
#include <log/log.h>
#include <xf86drm.h>
namespace android {
DrmEventListener::DrmEventListener(DrmDevice *drm)
: Worker("drm-event-listener", HAL_PRIORITY_URGENT_DISPLAY), drm_(drm) {
}
DrmEventListener::~DrmEventListener() {
Exit();
}
int DrmEventListener::Init() {
struct epoll_event ev;
char buffer[1024];
/* Open User Event File Descriptor */
uevent_fd_.Set(socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT));
if (uevent_fd_.get() < 0) {
ALOGE("Failed to open uevent socket: %s", strerror(errno));
return uevent_fd_.get();
}
struct sockaddr_nl addr;
memset(&addr, 0, sizeof(addr));
addr.nl_family = AF_NETLINK;
addr.nl_pid = 0;
addr.nl_groups = 0xFFFFFFFF;
int ret = bind(uevent_fd_.get(), (struct sockaddr *)&addr, sizeof(addr));
if (ret) {
ALOGE("Failed to bind uevent socket: %s", strerror(errno));
return -errno;
}
/* Open TUI Event File Descriptor */
tuievent_fd_.Set(open(kTUIStatusPath, O_RDONLY));
if (tuievent_fd_.get() < 0) {
ALOGE("Failed to open sysfs(%s) for TUI event: %s", kTUIStatusPath, strerror(errno));
} else {
/* Read garbage data once */
pread(tuievent_fd_.get(), &buffer, sizeof(buffer), 0);
ev.events = EPOLLPRI;
ev.data.fd = tuievent_fd_.get();
if (epoll_ctl(epoll_fd_.get(), EPOLL_CTL_ADD, tuievent_fd_.get(), &ev) < 0)
ALOGE("Failed to add tui fd into epoll: %s", strerror(errno));
}
/* Set EPoll*/
epoll_fd_.Set(epoll_create(maxFds));
if (epoll_fd_.get() < 0) {
ALOGE("Failed to create epoll: %s", strerror(errno));
return epoll_fd_.get();
}
ev.events = EPOLLIN;
ev.data.fd = uevent_fd_.get();
if (epoll_ctl(epoll_fd_.get(), EPOLL_CTL_ADD, uevent_fd_.get(), &ev) < 0) {
ALOGE("Failed to add uevent fd into epoll: %s", strerror(errno));
return -errno;
}
ev.events = EPOLLIN;
ev.data.fd = drm_->fd();
if (epoll_ctl(epoll_fd_.get(), EPOLL_CTL_ADD, drm_->fd(), &ev) < 0) {
ALOGE("Failed to add drm fd into epoll: %s", strerror(errno));
return -errno;
}
return InitWorker();
}
void DrmEventListener::RegisterHotplugHandler(DrmEventHandler *handler) {
assert(!hotplug_handler_);
hotplug_handler_.reset(handler);
}
void DrmEventListener::UnRegisterHotplugHandler(DrmEventHandler *handler) {
if (handler == hotplug_handler_.get())
hotplug_handler_ = NULL;
}
void DrmEventListener::RegisterHistogramHandler(DrmHistogramEventHandler *handler) {
assert(!histogram_handler_);
histogram_handler_.reset(handler);
}
void DrmEventListener::UnRegisterHistogramHandler(DrmHistogramEventHandler *handler) {
if (handler == histogram_handler_.get()) histogram_handler_ = NULL;
}
void DrmEventListener::RegisterTUIHandler(DrmTUIEventHandler *handler) {
if (tui_handler_) {
ALOGE("TUI handler was already registered");
return;
}
tui_handler_.reset(handler);
}
void DrmEventListener::UnRegisterTUIHandler(DrmTUIEventHandler *handler) {
if (handler == tui_handler_.get())
tui_handler_ = NULL;
}
bool DrmEventListener::IsDrmInTUI() {
char buffer[1024];
int ret;
if (tuievent_fd_.get() >= 0) {
ret = pread(tuievent_fd_.get(), &buffer, sizeof(buffer), 0);
if (ret == 0) {
return false;
} else if (ret < 0) {
ALOGE("Got error reading TUI event %s", strerror(errno));
return false;
}
return atoi(buffer) == 1 ? true : false;
}
return false;
}
void DrmEventListener::FlipHandler(int /* fd */, unsigned int /* sequence */,
unsigned int tv_sec, unsigned int tv_usec,
void *user_data) {
DrmEventHandler *handler = (DrmEventHandler *)user_data;
if (!handler)
return;
handler->HandleEvent((uint64_t)tv_sec * 1000 * 1000 + tv_usec);
delete handler;
}
void DrmEventListener::UEventHandler() {
char buffer[1024];
int ret;
struct timespec ts;
uint64_t timestamp = 0;
ret = clock_gettime(CLOCK_MONOTONIC, &ts);
if (!ret)
timestamp = ts.tv_sec * 1000 * 1000 * 1000 + ts.tv_nsec;
else
ALOGE("Failed to get monotonic clock on hotplug %d", ret);
ret = read(uevent_fd_.get(), &buffer, sizeof(buffer));
if (ret == 0) {
return;
} else if (ret < 0) {
ALOGE("Got error reading uevent %d", ret);
return;
}
bool drm_event = false, hotplug_event = false;
for (int i = 0; i < ret;) {
char *event = buffer + i;
if (!strcmp(event, "DEVTYPE=drm_minor")) {
drm_event = true;
} else if (!strcmp(event, "HOTPLUG=1")) {
hotplug_event = true;
}
i += strlen(event) + 1;
}
if (drm_event && hotplug_event) {
if (!hotplug_handler_)
return;
hotplug_handler_->HandleEvent(timestamp);
}
}
void DrmEventListener::DRMEventHandler() {
char buffer[1024];
int len, i;
struct drm_event *e;
struct drm_event_vblank *vblank;
struct exynos_drm_histogram_event *histo;
void *user_data;
len = read(drm_->fd(), &buffer, sizeof(buffer));
if (len == 0) return;
if (len < (int)sizeof(*e)) return;
i = 0;
while (i < len) {
e = (struct drm_event *)(buffer + i);
switch (e->type) {
case EXYNOS_DRM_HISTOGRAM_EVENT:
if (histogram_handler_) {
histo = (struct exynos_drm_histogram_event *)e;
histogram_handler_->HandleHistogramEvent((void *)&(histo->bins));
}
break;
case DRM_EVENT_FLIP_COMPLETE:
vblank = (struct drm_event_vblank *)e;
user_data = (void *)(unsigned long)(vblank->user_data);
FlipHandler(drm_->fd(), vblank->sequence, vblank->tv_sec, vblank->tv_usec,
user_data);
break;
case DRM_EVENT_VBLANK:
case DRM_EVENT_CRTC_SEQUENCE:
/* These DRM events are not handled */
break;
default:
break;
}
i += e->length;
}
return;
}
void DrmEventListener::TUIEventHandler() {
if (!tui_handler_) {
ALOGE("%s:: tui event handler is not valid", __func__);
return;
}
tui_handler_->HandleTUIEvent();
}
void DrmEventListener::Routine() {
struct epoll_event events[maxFds];
int nfds, n;
do {
nfds = epoll_wait(epoll_fd_.get(), events, maxFds, -1);
} while (nfds <= 0);
for (n = 0; n < nfds; n++) {
if (events[n].events & EPOLLIN) {
if (events[n].data.fd == uevent_fd_.get()) {
UEventHandler();
} else if (events[n].data.fd == drm_->fd()) {
DRMEventHandler();
}
} else if (events[n].events & EPOLLPRI) {
if (tuievent_fd_.get() >= 0 && events[n].data.fd == tuievent_fd_.get()) {
TUIEventHandler();
}
}
}
}
} // namespace android