/* // 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 <common/utils/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()) { ELOGTRACE("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))) { WLOGTRACE("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) { ELOGTRACE("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) { ELOGTRACE("invalid event string or listener to register"); return; } String8 key(event); if (mListeners.indexOfKey(key) >= 0) { ELOGTRACE("listener for uevent %s exists", event); return; } UeventListener *listener = new UeventListener; if (!listener) { ELOGTRACE("failed to create Uevent Listener"); return; } listener->func = func; listener->data = data; mListeners.add(key, listener); } bool UeventObserver::threadLoop() { if (mUeventFd == -1) { ELOGTRACE("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; ILOGTRACE("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) { DLOGTRACE("received Uevent: %s", msg); listener = mListeners.valueFor(key); if (listener) { listener->func(listener->data); } else { ELOGTRACE("no listener for uevent %s", msg); } } msg += strlen(msg) + 1; } } } // namespace intel } // namespace android