327 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			327 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C++
		
	
	
	
| /*
 | |
|  * Copyright (C) 2018 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 "libpixelusb"
 | |
| 
 | |
| #include "include/pixelusb/UsbGadgetCommon.h"
 | |
| 
 | |
| namespace android {
 | |
| namespace hardware {
 | |
| namespace google {
 | |
| namespace pixel {
 | |
| namespace usb {
 | |
| 
 | |
| static volatile bool gadgetPullup;
 | |
| 
 | |
| MonitorFfs::MonitorFfs(const char *const gadget, const char *const extconTypecState,
 | |
|                        const char *const usbGadgetState)
 | |
|     : mWatchFd(),
 | |
|       mEndpointList(),
 | |
|       mLock(),
 | |
|       mCv(),
 | |
|       mLockFd(),
 | |
|       mCurrentUsbFunctionsApplied(false),
 | |
|       mMonitor(),
 | |
|       mCallback(NULL),
 | |
|       mPayload(NULL),
 | |
|       mGadgetName(gadget),
 | |
|       mExtconTypecState(extconTypecState),
 | |
|       mUsbGadgetState(usbGadgetState),
 | |
|       mMonitorRunning(false) {
 | |
|     unique_fd eventFd(eventfd(0, 0));
 | |
|     if (eventFd == -1) {
 | |
|         ALOGE("mEventFd failed to create %d", errno);
 | |
|         abort();
 | |
|     }
 | |
| 
 | |
|     unique_fd epollFd(epoll_create(2));
 | |
|     if (epollFd == -1) {
 | |
|         ALOGE("mEpollFd failed to create %d", errno);
 | |
|         abort();
 | |
|     }
 | |
| 
 | |
|     unique_fd inotifyFd(inotify_init());
 | |
|     if (inotifyFd < 0) {
 | |
|         ALOGE("inotify init failed");
 | |
|         abort();
 | |
|     }
 | |
| 
 | |
|     if (addEpollFd(epollFd, inotifyFd) == -1)
 | |
|         abort();
 | |
| 
 | |
|     if (addEpollFd(epollFd, eventFd) == -1)
 | |
|         abort();
 | |
| 
 | |
|     mEpollFd = move(epollFd);
 | |
|     mInotifyFd = move(inotifyFd);
 | |
|     mEventFd = move(eventFd);
 | |
|     gadgetPullup = false;
 | |
| }
 | |
| 
 | |
| static void displayInotifyEvent(struct inotify_event *i) {
 | |
|     ALOGE("    wd =%2d; ", i->wd);
 | |
|     if (i->cookie > 0)
 | |
|         ALOGE("cookie =%4d; ", i->cookie);
 | |
| 
 | |
|     ALOGE("mask = ");
 | |
|     if (i->mask & IN_ACCESS)
 | |
|         ALOGE("IN_ACCESS ");
 | |
|     if (i->mask & IN_ATTRIB)
 | |
|         ALOGE("IN_ATTRIB ");
 | |
|     if (i->mask & IN_CLOSE_NOWRITE)
 | |
|         ALOGE("IN_CLOSE_NOWRITE ");
 | |
|     if (i->mask & IN_CLOSE_WRITE)
 | |
|         ALOGE("IN_CLOSE_WRITE ");
 | |
|     if (i->mask & IN_CREATE)
 | |
|         ALOGE("IN_CREATE ");
 | |
|     if (i->mask & IN_DELETE)
 | |
|         ALOGE("IN_DELETE ");
 | |
|     if (i->mask & IN_DELETE_SELF)
 | |
|         ALOGE("IN_DELETE_SELF ");
 | |
|     if (i->mask & IN_IGNORED)
 | |
|         ALOGE("IN_IGNORED ");
 | |
|     if (i->mask & IN_ISDIR)
 | |
|         ALOGE("IN_ISDIR ");
 | |
|     if (i->mask & IN_MODIFY)
 | |
|         ALOGE("IN_MODIFY ");
 | |
|     if (i->mask & IN_MOVE_SELF)
 | |
|         ALOGE("IN_MOVE_SELF ");
 | |
|     if (i->mask & IN_MOVED_FROM)
 | |
|         ALOGE("IN_MOVED_FROM ");
 | |
|     if (i->mask & IN_MOVED_TO)
 | |
|         ALOGE("IN_MOVED_TO ");
 | |
|     if (i->mask & IN_OPEN)
 | |
|         ALOGE("IN_OPEN ");
 | |
|     if (i->mask & IN_Q_OVERFLOW)
 | |
|         ALOGE("IN_Q_OVERFLOW ");
 | |
|     if (i->mask & IN_UNMOUNT)
 | |
|         ALOGE("IN_UNMOUNT ");
 | |
|     ALOGE("\n");
 | |
| 
 | |
|     if (i->len > 0)
 | |
|         ALOGE("        name = %s\n", i->name);
 | |
| }
 | |
| 
 | |
| void updateState(const char *typecState, const char *usbGadgetState, std::string &typec_state,
 | |
|                  std::string &usb_gadget_state) {
 | |
|     if (ReadFileToString(typecState, &typec_state))
 | |
|         typec_state = Trim(typec_state);
 | |
|     else
 | |
|         typec_state = TYPEC_GADGET_MODE_ENABLE;
 | |
| 
 | |
|     if (ReadFileToString(usbGadgetState, &usb_gadget_state))
 | |
|         usb_gadget_state = Trim(usb_gadget_state);
 | |
|     else
 | |
|         usb_gadget_state = "";
 | |
| }
 | |
| 
 | |
| void *MonitorFfs::startMonitorFd(void *param) {
 | |
|     MonitorFfs *monitorFfs = (MonitorFfs *)param;
 | |
|     char buf[kBufferSize];
 | |
|     bool writeUdc = true, stopMonitor = false;
 | |
|     struct epoll_event events[kEpollEvents];
 | |
|     steady_clock::time_point disconnect;
 | |
|     std::string typec_state, usb_gadget_state;
 | |
| 
 | |
|     bool descriptorWritten = true;
 | |
|     for (int i = 0; i < static_cast<int>(monitorFfs->mEndpointList.size()); i++) {
 | |
|         if (access(monitorFfs->mEndpointList.at(i).c_str(), R_OK)) {
 | |
|             descriptorWritten = false;
 | |
|             break;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     updateState(monitorFfs->mExtconTypecState, monitorFfs->mUsbGadgetState, typec_state,
 | |
|                 usb_gadget_state);
 | |
| 
 | |
|     // notify here if the endpoints are already present.
 | |
|     if (typec_state == TYPEC_GADGET_MODE_DISABLE && usb_gadget_state == GADGET_ACTIVATE) {
 | |
|         gadgetPullup = false;
 | |
|         ALOGI("pending GADGET pulled up due to USB disconnected");
 | |
|     } else if (descriptorWritten) {
 | |
|         usleep(kPullUpDelay);
 | |
|         if (!!WriteStringToFile(monitorFfs->mGadgetName, PULLUP_PATH)) {
 | |
|             lock_guard<mutex> lock(monitorFfs->mLock);
 | |
|             monitorFfs->mCurrentUsbFunctionsApplied = true;
 | |
|             monitorFfs->mCallback(monitorFfs->mCurrentUsbFunctionsApplied, monitorFfs->mPayload);
 | |
|             gadgetPullup = true;
 | |
|             writeUdc = false;
 | |
|             ALOGI("GADGET pulled up");
 | |
|             monitorFfs->mCv.notify_all();
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     while (!stopMonitor) {
 | |
|         int nrEvents = epoll_wait(monitorFfs->mEpollFd, events, kEpollEvents, -1);
 | |
| 
 | |
|         if (nrEvents <= 0) {
 | |
|             ALOGE("epoll wait did not return descriptor number");
 | |
|             continue;
 | |
|         }
 | |
| 
 | |
|         for (int i = 0; i < nrEvents; i++) {
 | |
|             ALOGV("event=%u on fd=%d\n", events[i].events, events[i].data.fd);
 | |
| 
 | |
|             if (events[i].data.fd == monitorFfs->mInotifyFd) {
 | |
|                 // Process all of the events in buffer returned by read().
 | |
|                 int numRead = read(monitorFfs->mInotifyFd, buf, kBufferSize);
 | |
|                 for (char *p = buf; p < buf + numRead;) {
 | |
|                     struct inotify_event *event = (struct inotify_event *)p;
 | |
|                     if (kDebug)
 | |
|                         displayInotifyEvent(event);
 | |
| 
 | |
|                     p += sizeof(struct inotify_event) + event->len;
 | |
| 
 | |
|                     bool descriptorPresent = true;
 | |
|                     for (int j = 0; j < static_cast<int>(monitorFfs->mEndpointList.size()); j++) {
 | |
|                         if (access(monitorFfs->mEndpointList.at(j).c_str(), R_OK)) {
 | |
|                             if (kDebug)
 | |
|                                 ALOGI("%s absent", monitorFfs->mEndpointList.at(j).c_str());
 | |
|                             descriptorPresent = false;
 | |
|                             break;
 | |
|                         }
 | |
|                     }
 | |
| 
 | |
|                     updateState(monitorFfs->mExtconTypecState, monitorFfs->mUsbGadgetState,
 | |
|                                 typec_state, usb_gadget_state);
 | |
| 
 | |
|                     if (typec_state == TYPEC_GADGET_MODE_DISABLE &&
 | |
|                         usb_gadget_state == GADGET_ACTIVATE) {
 | |
|                         gadgetPullup = false;
 | |
|                         ALOGI("pending GADGET pulled up due to USB disconnected");
 | |
|                     } else if (!descriptorPresent && !writeUdc) {
 | |
|                         if (kDebug)
 | |
|                             ALOGI("endpoints not up");
 | |
|                         writeUdc = true;
 | |
|                         disconnect = std::chrono::steady_clock::now();
 | |
|                     } else if (descriptorPresent && writeUdc) {
 | |
|                         steady_clock::time_point temp = steady_clock::now();
 | |
| 
 | |
|                         if (std::chrono::duration_cast<microseconds>(temp - disconnect).count() <
 | |
|                             kPullUpDelay)
 | |
|                             usleep(kPullUpDelay);
 | |
| 
 | |
|                         if (!!WriteStringToFile(monitorFfs->mGadgetName, PULLUP_PATH)) {
 | |
|                             lock_guard<mutex> lock(monitorFfs->mLock);
 | |
|                             monitorFfs->mCurrentUsbFunctionsApplied = true;
 | |
|                             monitorFfs->mCallback(monitorFfs->mCurrentUsbFunctionsApplied,
 | |
|                                                   monitorFfs->mPayload);
 | |
|                             ALOGI("GADGET pulled up");
 | |
|                             writeUdc = false;
 | |
|                             gadgetPullup = true;
 | |
|                             // notify the main thread to signal userspace.
 | |
|                             monitorFfs->mCv.notify_all();
 | |
|                         }
 | |
|                     }
 | |
|                 }
 | |
|             } else {
 | |
|                 uint64_t flag;
 | |
|                 read(monitorFfs->mEventFd, &flag, sizeof(flag));
 | |
|                 if (flag == 100) {
 | |
|                     stopMonitor = true;
 | |
|                     break;
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|     }
 | |
|     return NULL;
 | |
| }
 | |
| 
 | |
| void MonitorFfs::reset() {
 | |
|     lock_guard<mutex> lock(mLockFd);
 | |
|     uint64_t flag = 100;
 | |
|     unsigned long ret;
 | |
| 
 | |
|     if (mMonitorRunning) {
 | |
|         // Stop the monitor thread by writing into signal fd.
 | |
|         ret = TEMP_FAILURE_RETRY(write(mEventFd, &flag, sizeof(flag)));
 | |
|         if (ret < 0)
 | |
|             ALOGE("Error writing eventfd errno=%d", errno);
 | |
| 
 | |
|         ALOGI("mMonitor signalled to exit");
 | |
|         mMonitor->join();
 | |
|         ALOGI("mMonitor destroyed");
 | |
|         mMonitorRunning = false;
 | |
|     }
 | |
| 
 | |
|     for (std::vector<int>::size_type i = 0; i != mWatchFd.size(); i++)
 | |
|         inotify_rm_watch(mInotifyFd, mWatchFd[i]);
 | |
| 
 | |
|     mEndpointList.clear();
 | |
|     gadgetPullup = false;
 | |
|     mCallback = NULL;
 | |
|     mPayload = NULL;
 | |
| }
 | |
| 
 | |
| bool MonitorFfs::startMonitor() {
 | |
|     mMonitor = unique_ptr<thread>(new thread(this->startMonitorFd, this));
 | |
|     mMonitorRunning = true;
 | |
|     return true;
 | |
| }
 | |
| 
 | |
| bool MonitorFfs::isMonitorRunning() {
 | |
|     return mMonitorRunning;
 | |
| }
 | |
| 
 | |
| bool MonitorFfs::waitForPullUp(int timeout_ms) {
 | |
|     std::unique_lock<std::mutex> lk(mLock);
 | |
| 
 | |
|     if (gadgetPullup)
 | |
|         return true;
 | |
| 
 | |
|     if (mCv.wait_for(lk, timeout_ms * 1ms, [] { return gadgetPullup; })) {
 | |
|         ALOGI("monitorFfs signalled true");
 | |
|         return true;
 | |
|     } else {
 | |
|         ALOGI("monitorFfs signalled error");
 | |
|         // continue monitoring as the descriptors might be written at a later
 | |
|         // point.
 | |
|         return false;
 | |
|     }
 | |
| }
 | |
| 
 | |
| bool MonitorFfs::addInotifyFd(string fd) {
 | |
|     lock_guard<mutex> lock(mLockFd);
 | |
|     int wfd;
 | |
| 
 | |
|     wfd = inotify_add_watch(mInotifyFd, fd.c_str(), IN_ALL_EVENTS);
 | |
|     if (wfd == -1)
 | |
|         return false;
 | |
|     else
 | |
|         mWatchFd.push_back(wfd);
 | |
| 
 | |
|     return true;
 | |
| }
 | |
| 
 | |
| void MonitorFfs::addEndPoint(string ep) {
 | |
|     lock_guard<mutex> lock(mLockFd);
 | |
| 
 | |
|     mEndpointList.push_back(ep);
 | |
| }
 | |
| 
 | |
| void MonitorFfs::registerFunctionsAppliedCallback(void (*callback)(bool functionsApplied,
 | |
|                                                                    void *payload),
 | |
|                                                   void *payload) {
 | |
|     mCallback = callback;
 | |
|     mPayload = payload;
 | |
| }
 | |
| 
 | |
| }  // namespace usb
 | |
| }  // namespace pixel
 | |
| }  // namespace google
 | |
| }  // namespace hardware
 | |
| }  // namespace android
 |