|
|
/* |
|
|
// Copyright (c) 2014 Intel Corporation |
|
|
// |
|
|
// 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. |
|
|
*/ |
|
|
#include <poll.h> |
|
|
#include <sys/socket.h> |
|
|
#include <sys/un.h> |
|
|
#include <sys/queue.h> |
|
|
#include <linux/netlink.h> |
|
|
#include <sys/types.h> |
|
|
#include <unistd.h> |
|
|
#include <DrmConfig.h> |
|
|
#include <HwcTrace.h> |
|
|
#include <UeventObserver.h> |
|
|
|
|
|
namespace android { |
|
|
namespace intel { |
|
|
|
|
|
UeventObserver::UeventObserver() |
|
|
: mUeventFd(-1), |
|
|
mExitRDFd(-1), |
|
|
mExitWDFd(-1), |
|
|
mListeners() |
|
|
{ |
|
|
} |
|
|
|
|
|
UeventObserver::~UeventObserver() |
|
|
{ |
|
|
deinitialize(); |
|
|
} |
|
|
|
|
|
bool UeventObserver::initialize() |
|
|
{ |
|
|
mListeners.clear(); |
|
|
|
|
|
if (mUeventFd != -1) { |
|
|
return true; |
|
|
} |
|
|
|
|
|
mThread = new UeventObserverThread(this); |
|
|
if (!mThread.get()) { |
|
|
ETRACE("failed to create uevent observer thread"); |
|
|
return false; |
|
|
} |
|
|
|
|
|
// init uevent socket |
|
|
struct sockaddr_nl addr; |
|
|
// set the socket receive buffer to 64K |
|
|
// NOTE: this is only called for once |
|
|
int sz = 64 * 1024; |
|
|
|
|
|
memset(&addr, 0, sizeof(addr)); |
|
|
addr.nl_family = AF_NETLINK; |
|
|
addr.nl_pid = pthread_self() | getpid(); |
|
|
addr.nl_groups = 0xffffffff; |
|
|
|
|
|
mUeventFd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT); |
|
|
if (mUeventFd < 0) { |
|
|
DEINIT_AND_RETURN_FALSE("failed to create uevent socket"); |
|
|
} |
|
|
|
|
|
if (setsockopt(mUeventFd, SOL_SOCKET, SO_RCVBUFFORCE, &sz, sizeof(sz))) { |
|
|
WTRACE("setsockopt() failed"); |
|
|
//return false; |
|
|
} |
|
|
|
|
|
if (bind(mUeventFd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { |
|
|
DEINIT_AND_RETURN_FALSE("failed to bind scoket"); |
|
|
return false; |
|
|
} |
|
|
|
|
|
memset(mUeventMessage, 0, UEVENT_MSG_LEN); |
|
|
|
|
|
int exitFds[2]; |
|
|
if (pipe(exitFds) < 0) { |
|
|
ETRACE("failed to make pipe"); |
|
|
deinitialize(); |
|
|
return false; |
|
|
} |
|
|
mExitRDFd = exitFds[0]; |
|
|
mExitWDFd = exitFds[1]; |
|
|
|
|
|
return true; |
|
|
} |
|
|
|
|
|
void UeventObserver::deinitialize() |
|
|
{ |
|
|
if (mUeventFd != -1) { |
|
|
if (mExitWDFd != -1) { |
|
|
close(mExitWDFd); |
|
|
mExitWDFd = -1; |
|
|
} |
|
|
close(mUeventFd); |
|
|
mUeventFd = -1; |
|
|
} |
|
|
|
|
|
if (mThread.get()) { |
|
|
mThread->requestExitAndWait(); |
|
|
mThread = NULL; |
|
|
} |
|
|
|
|
|
while (!mListeners.isEmpty()) { |
|
|
UeventListener *listener = mListeners.valueAt(0); |
|
|
mListeners.removeItemsAt(0); |
|
|
delete listener; |
|
|
} |
|
|
} |
|
|
|
|
|
void UeventObserver::start() |
|
|
{ |
|
|
if (mThread.get()) { |
|
|
mThread->run("UeventObserver", PRIORITY_URGENT_DISPLAY); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
void UeventObserver::registerListener(const char *event, UeventListenerFunc func, void *data) |
|
|
{ |
|
|
if (!event || !func) { |
|
|
ETRACE("invalid event string or listener to register"); |
|
|
return; |
|
|
} |
|
|
|
|
|
String8 key(event); |
|
|
if (mListeners.indexOfKey(key) >= 0) { |
|
|
ETRACE("listener for uevent %s exists", event); |
|
|
return; |
|
|
} |
|
|
|
|
|
UeventListener *listener = new UeventListener; |
|
|
if (!listener) { |
|
|
ETRACE("failed to create Uevent Listener"); |
|
|
return; |
|
|
} |
|
|
listener->func = func; |
|
|
listener->data = data; |
|
|
|
|
|
mListeners.add(key, listener); |
|
|
} |
|
|
|
|
|
bool UeventObserver::threadLoop() |
|
|
{ |
|
|
if (mUeventFd == -1) { |
|
|
ETRACE("invalid uEvent file descriptor"); |
|
|
return false; |
|
|
} |
|
|
|
|
|
struct pollfd fds[2]; |
|
|
int nr; |
|
|
|
|
|
fds[0].fd = mUeventFd; |
|
|
fds[0].events = POLLIN; |
|
|
fds[0].revents = 0; |
|
|
fds[1].fd = mExitRDFd; |
|
|
fds[1].events = POLLIN; |
|
|
fds[1].revents = 0; |
|
|
nr = poll(fds, 2, -1); |
|
|
|
|
|
if (nr > 0 && fds[0].revents == POLLIN) { |
|
|
int count = recv(mUeventFd, mUeventMessage, UEVENT_MSG_LEN - 2, 0); |
|
|
if (count > 0) { |
|
|
onUevent(); |
|
|
} |
|
|
} else if (fds[1].revents) { |
|
|
close(mExitRDFd); |
|
|
mExitRDFd = -1; |
|
|
ITRACE("exiting wait"); |
|
|
return false; |
|
|
} |
|
|
// always looping |
|
|
return true; |
|
|
} |
|
|
|
|
|
void UeventObserver::onUevent() |
|
|
{ |
|
|
char *msg = mUeventMessage; |
|
|
const char *envelope = DrmConfig::getUeventEnvelope(); |
|
|
if (strncmp(msg, envelope, strlen(envelope)) != 0) |
|
|
return; |
|
|
|
|
|
msg += strlen(msg) + 1; |
|
|
|
|
|
UeventListener *listener; |
|
|
String8 key; |
|
|
while (*msg) { |
|
|
key = String8(msg); |
|
|
if (mListeners.indexOfKey(key) >= 0) { |
|
|
DTRACE("received Uevent: %s", msg); |
|
|
listener = mListeners.valueFor(key); |
|
|
if (listener) { |
|
|
listener->func(listener->data); |
|
|
} else { |
|
|
ETRACE("no listener for uevent %s", msg); |
|
|
} |
|
|
} |
|
|
msg += strlen(msg) + 1; |
|
|
} |
|
|
} |
|
|
|
|
|
} // namespace intel |
|
|
} // namespace android |
|
|
|
|
|
|