| /* |
| * Copyright (c) 2015 - 2016, The Linux Foundation. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are |
| * met: |
| * * Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * * Redistributions in binary form must reproduce the above |
| * copyright notice, this list of conditions and the following |
| * disclaimer in the documentation and/or other materials provided |
| * with the distribution. |
| * * Neither the name of The Linux Foundation nor the names of its |
| * contributors may be used to endorse or promote products derived |
| * from this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED |
| * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF |
| * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT |
| * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS |
| * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR |
| * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, |
| * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE |
| * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN |
| * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include <errno.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <math.h> |
| #include <fcntl.h> |
| #include <sys/types.h> |
| #include <sys/resource.h> |
| #include <sys/prctl.h> |
| #include <utils/debug.h> |
| #include <utils/sys.h> |
| #include <pthread.h> |
| #include <algorithm> |
| #include <vector> |
| #include <map> |
| #include <utility> |
| |
| #include "hw_events.h" |
| |
| #define __CLASS__ "HWEvents" |
| |
| namespace sdm { |
| |
| DisplayError HWEventsInterface::Create(int fb_num, HWEventHandler *event_handler, |
| std::vector<const char *> *event_list, |
| HWEventsInterface **intf) { |
| DisplayError error = kErrorNone; |
| HWEvents *hw_events = NULL; |
| |
| hw_events = new HWEvents(); |
| error = hw_events->Init(fb_num, event_handler, event_list); |
| if (error != kErrorNone) { |
| delete hw_events; |
| } else { |
| *intf = hw_events; |
| } |
| |
| return error; |
| } |
| |
| DisplayError HWEventsInterface::Destroy(HWEventsInterface *intf) { |
| HWEvents *hw_events = static_cast<HWEvents *>(intf); |
| |
| if (hw_events) { |
| hw_events->Deinit(); |
| delete hw_events; |
| } |
| |
| return kErrorNone; |
| } |
| |
| pollfd HWEvents::InitializePollFd(HWEventData *event_data) { |
| char node_path[kMaxStringLength] = {0}; |
| char data[kMaxStringLength] = {0}; |
| pollfd poll_fd = {0}; |
| poll_fd.fd = -1; |
| |
| if (!strncmp(event_data->event_name, "thread_exit", strlen("thread_exit"))) { |
| // Create an eventfd to be used to unblock the poll system call when |
| // a thread is exiting. |
| poll_fd.fd = Sys::eventfd_(0, 0); |
| poll_fd.events |= POLLIN; |
| exit_fd_ = poll_fd.fd; |
| } else { |
| snprintf(node_path, sizeof(node_path), "%s%d/%s", fb_path_, fb_num_, event_data->event_name); |
| poll_fd.fd = Sys::open_(node_path, O_RDONLY); |
| poll_fd.events |= POLLPRI | POLLERR; |
| } |
| |
| if (poll_fd.fd < 0) { |
| DLOGW("open failed for display=%d event=%s, error=%s", fb_num_, event_data->event_name, |
| strerror(errno)); |
| return poll_fd; |
| } |
| |
| // Read once on all fds to clear data on all fds. |
| Sys::pread_(poll_fd.fd, data , kMaxStringLength, 0); |
| |
| return poll_fd; |
| } |
| |
| DisplayError HWEvents::SetEventParser(const char *event_name, HWEventData *event_data) { |
| DisplayError error = kErrorNone; |
| |
| if (!strncmp(event_name, "vsync_event", strlen("vsync_event"))) { |
| event_data->event_parser = &HWEvents::HandleVSync; |
| } else if (!strncmp(event_name, "show_blank_event", strlen("show_blank_event"))) { |
| event_data->event_parser = &HWEvents::HandleBlank; |
| } else if (!strncmp(event_name, "idle_notify", strlen("idle_notify"))) { |
| event_data->event_parser = &HWEvents::HandleIdleTimeout; |
| } else if (!strncmp(event_name, "msm_fb_thermal_level", strlen("msm_fb_thermal_level"))) { |
| event_data->event_parser = &HWEvents::HandleThermal; |
| } else if (!strncmp(event_name, "cec/rd_msg", strlen("cec/rd_msg"))) { |
| event_data->event_parser = &HWEvents::HandleCECMessage; |
| } else if (!strncmp(event_name, "thread_exit", strlen("thread_exit"))) { |
| event_data->event_parser = &HWEvents::HandleThreadExit; |
| } else { |
| error = kErrorParameters; |
| } |
| |
| return error; |
| } |
| |
| void HWEvents::PopulateHWEventData() { |
| for (uint32_t i = 0; i < event_list_->size(); i++) { |
| const char *event_name = event_list_->at(i); |
| HWEventData event_data; |
| event_data.event_name = event_name; |
| SetEventParser(event_name, &event_data); |
| poll_fds_[i] = InitializePollFd(&event_data); |
| event_data_list_.push_back(event_data); |
| } |
| } |
| |
| DisplayError HWEvents::Init(int fb_num, HWEventHandler *event_handler, |
| vector<const char *> *event_list) { |
| if (!event_handler) |
| return kErrorParameters; |
| |
| event_handler_ = event_handler; |
| fb_num_ = fb_num; |
| event_list_ = event_list; |
| poll_fds_.resize(event_list_->size()); |
| event_thread_name_ += " - " + std::to_string(fb_num_); |
| |
| PopulateHWEventData(); |
| |
| if (pthread_create(&event_thread_, NULL, &DisplayEventThread, this) < 0) { |
| DLOGE("Failed to start %s, error = %s", event_thread_name_.c_str()); |
| return kErrorResources; |
| } |
| |
| return kErrorNone; |
| } |
| |
| DisplayError HWEvents::Deinit() { |
| exit_threads_ = true; |
| Sys::pthread_cancel_(event_thread_); |
| |
| uint64_t exit_value = 1; |
| ssize_t write_size = Sys::write_(exit_fd_, &exit_value, sizeof(uint64_t)); |
| if (write_size != sizeof(uint64_t)) |
| DLOGW("Error triggering exit_fd_ (%d). write size = %d, error = %s", exit_fd_, write_size, |
| strerror(errno)); |
| |
| pthread_join(event_thread_, NULL); |
| |
| for (uint32_t i = 0; i < event_list_->size(); i++) { |
| Sys::close_(poll_fds_[i].fd); |
| poll_fds_[i].fd = -1; |
| } |
| |
| return kErrorNone; |
| } |
| |
| void* HWEvents::DisplayEventThread(void *context) { |
| if (context) { |
| return reinterpret_cast<HWEvents *>(context)->DisplayEventHandler(); |
| } |
| |
| return NULL; |
| } |
| |
| void* HWEvents::DisplayEventHandler() { |
| char data[kMaxStringLength] = {0}; |
| |
| prctl(PR_SET_NAME, event_thread_name_.c_str(), 0, 0, 0); |
| setpriority(PRIO_PROCESS, 0, kThreadPriorityUrgent); |
| |
| while (!exit_threads_) { |
| int error = Sys::poll_(poll_fds_.data(), UINT32(event_list_->size()), -1); |
| |
| if (error <= 0) { |
| DLOGW("poll failed. error = %s", strerror(errno)); |
| continue; |
| } |
| |
| for (uint32_t event = 0; event < event_list_->size(); event++) { |
| pollfd &poll_fd = poll_fds_[event]; |
| |
| if (!strncmp(event_list_->at(event), "thread_exit", strlen("thread_exit"))) { |
| if ((poll_fd.revents & POLLIN) && (Sys::read_(poll_fd.fd, data, kMaxStringLength) > 0)) { |
| (this->*(event_data_list_[event]).event_parser)(data); |
| } |
| } else { |
| if ((poll_fd.revents & POLLPRI) && |
| (Sys::pread_(poll_fd.fd, data, kMaxStringLength, 0) > 0)) { |
| (this->*(event_data_list_[event]).event_parser)(data); |
| } |
| } |
| } |
| } |
| |
| pthread_exit(0); |
| |
| return NULL; |
| } |
| |
| void HWEvents::HandleVSync(char *data) { |
| int64_t timestamp = 0; |
| if (!strncmp(data, "VSYNC=", strlen("VSYNC="))) { |
| timestamp = strtoll(data + strlen("VSYNC="), NULL, 0); |
| } |
| |
| event_handler_->VSync(timestamp); |
| } |
| |
| void HWEvents::HandleIdleTimeout(char *data) { |
| event_handler_->IdleTimeout(); |
| } |
| |
| void HWEvents::HandleThermal(char *data) { |
| int64_t thermal_level = 0; |
| if (!strncmp(data, "thermal_level=", strlen("thermal_level="))) { |
| thermal_level = strtoll(data + strlen("thermal_level="), NULL, 0); |
| } |
| |
| DLOGI("Received thermal notification with thermal level = %d", thermal_level); |
| |
| event_handler_->ThermalEvent(thermal_level); |
| } |
| |
| void HWEvents::HandleCECMessage(char *data) { |
| event_handler_->CECMessage(data); |
| } |
| |
| } // namespace sdm |
| |