| /** |
| * Copyright (C) ARM Limited 2010-2014. All rights reserved. |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License version 2 as |
| * published by the Free Software Foundation. |
| */ |
| |
| #include "ExternalSource.h" |
| |
| #include <fcntl.h> |
| #include <sys/prctl.h> |
| #include <unistd.h> |
| |
| #include "Logging.h" |
| #include "OlySocket.h" |
| #include "SessionData.h" |
| |
| static const char MALI_VIDEO[] = "\0mali-video"; |
| static const char MALI_VIDEO_STARTUP[] = "\0mali-video-startup"; |
| static const char MALI_VIDEO_V1[] = "MALI_VIDEO 1\n"; |
| static const char MALI_GRAPHICS[] = "\0mali_thirdparty_server"; |
| static const char MALI_GRAPHICS_STARTUP[] = "\0mali_thirdparty_client"; |
| static const char MALI_GRAPHICS_V1[] = "MALI_GRAPHICS 1\n"; |
| |
| static bool setNonblock(const int fd) { |
| int flags; |
| |
| flags = fcntl(fd, F_GETFL); |
| if (flags < 0) { |
| logg->logMessage("fcntl getfl failed"); |
| return false; |
| } |
| |
| if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) != 0) { |
| logg->logMessage("fcntl setfl failed"); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| ExternalSource::ExternalSource(sem_t *senderSem) : mBuffer(0, FRAME_EXTERNAL, 128*1024, senderSem), mMonitor(), mMveStartupUds(MALI_VIDEO_STARTUP, sizeof(MALI_VIDEO_STARTUP)), mMaliStartupUds(MALI_GRAPHICS_STARTUP, sizeof(MALI_GRAPHICS_STARTUP)), mAnnotate(8083), mInterruptFd(-1), mMaliUds(-1), mMveUds(-1) { |
| sem_init(&mBufferSem, 0, 0); |
| } |
| |
| ExternalSource::~ExternalSource() { |
| } |
| |
| void ExternalSource::waitFor(const int bytes) { |
| while (mBuffer.bytesAvailable() <= bytes) { |
| sem_wait(&mBufferSem); |
| } |
| } |
| |
| void ExternalSource::configureConnection(const int fd, const char *const handshake, size_t size) { |
| if (!setNonblock(fd)) { |
| logg->logError(__FILE__, __LINE__, "Unable to set nonblock on fh"); |
| handleException(); |
| } |
| |
| if (!mMonitor.add(fd)) { |
| logg->logError(__FILE__, __LINE__, "Unable to add fh to monitor"); |
| handleException(); |
| } |
| |
| // Write the handshake to the circular buffer |
| waitFor(Buffer::MAXSIZE_PACK32 + size - 1); |
| mBuffer.packInt(fd); |
| mBuffer.writeBytes(handshake, size - 1); |
| mBuffer.commit(1); |
| } |
| |
| bool ExternalSource::connectMali() { |
| mMaliUds = OlySocket::connect(MALI_GRAPHICS, sizeof(MALI_GRAPHICS)); |
| if (mMaliUds < 0) { |
| return false; |
| } |
| |
| configureConnection(mMaliUds, MALI_GRAPHICS_V1, sizeof(MALI_GRAPHICS_V1)); |
| |
| return true; |
| } |
| |
| bool ExternalSource::connectMve() { |
| if (!gSessionData->maliVideo.countersEnabled()) { |
| return true; |
| } |
| |
| mMveUds = OlySocket::connect(MALI_VIDEO, sizeof(MALI_VIDEO)); |
| if (mMveUds < 0) { |
| return false; |
| } |
| |
| if (!gSessionData->maliVideo.start(mMveUds)) { |
| return false; |
| } |
| |
| configureConnection(mMveUds, MALI_VIDEO_V1, sizeof(MALI_VIDEO_V1)); |
| |
| return true; |
| } |
| |
| bool ExternalSource::prepare() { |
| if (!mMonitor.init() || |
| !setNonblock(mMveStartupUds.getFd()) || !mMonitor.add(mMveStartupUds.getFd()) || |
| !setNonblock(mMaliStartupUds.getFd()) || !mMonitor.add(mMaliStartupUds.getFd()) || |
| !setNonblock(mAnnotate.getFd()) || !mMonitor.add(mAnnotate.getFd()) || |
| false) { |
| return false; |
| } |
| |
| connectMali(); |
| connectMve(); |
| |
| return true; |
| } |
| |
| void ExternalSource::run() { |
| int pipefd[2]; |
| |
| prctl(PR_SET_NAME, (unsigned long)&"gatord-external", 0, 0, 0); |
| |
| if (pipe_cloexec(pipefd) != 0) { |
| logg->logError(__FILE__, __LINE__, "pipe failed"); |
| handleException(); |
| } |
| mInterruptFd = pipefd[1]; |
| |
| if (!mMonitor.add(pipefd[0])) { |
| logg->logError(__FILE__, __LINE__, "Monitor::add failed"); |
| handleException(); |
| } |
| |
| // Notify annotate clients to retry connecting to gatord |
| gSessionData->annotateListener.signal(); |
| |
| while (gSessionData->mSessionIsActive) { |
| struct epoll_event events[16]; |
| // Clear any pending sem posts |
| while (sem_trywait(&mBufferSem) == 0); |
| int ready = mMonitor.wait(events, ARRAY_LENGTH(events), -1); |
| if (ready < 0) { |
| logg->logError(__FILE__, __LINE__, "Monitor::wait failed"); |
| handleException(); |
| } |
| |
| const uint64_t currTime = getTime(); |
| |
| for (int i = 0; i < ready; ++i) { |
| const int fd = events[i].data.fd; |
| if (fd == mMveStartupUds.getFd()) { |
| // Mali Video Engine says it's alive |
| int client = mMveStartupUds.acceptConnection(); |
| // Don't read from this connection, establish a new connection to Mali-V500 |
| close(client); |
| if (!connectMve()) { |
| logg->logError(__FILE__, __LINE__, "Unable to configure incoming Mali video connection"); |
| handleException(); |
| } |
| } else if (fd == mMaliStartupUds.getFd()) { |
| // Mali Graphics says it's alive |
| int client = mMaliStartupUds.acceptConnection(); |
| // Don't read from this connection, establish a new connection to Mali Graphics |
| close(client); |
| if (!connectMali()) { |
| logg->logError(__FILE__, __LINE__, "Unable to configure incoming Mali graphics connection"); |
| handleException(); |
| } |
| } else if (fd == mAnnotate.getFd()) { |
| int client = mAnnotate.acceptConnection(); |
| if (!setNonblock(client) || !mMonitor.add(client)) { |
| logg->logError(__FILE__, __LINE__, "Unable to set socket options on incoming annotation connection"); |
| handleException(); |
| } |
| } else if (fd == pipefd[0]) { |
| // Means interrupt has been called and mSessionIsActive should be reread |
| } else { |
| /* This can result in some starvation if there are multiple |
| * threads which are annotating heavily, but it is not |
| * recommended that threads annotate that much as it can also |
| * starve out the gator data. |
| */ |
| while (gSessionData->mSessionIsActive) { |
| // Wait until there is enough room for the fd, two headers and two ints |
| waitFor(7*Buffer::MAXSIZE_PACK32 + 2*sizeof(uint32_t)); |
| mBuffer.packInt(fd); |
| const int contiguous = mBuffer.contiguousSpaceAvailable(); |
| const int bytes = read(fd, mBuffer.getWritePos(), contiguous); |
| if (bytes < 0) { |
| if (errno == EAGAIN) { |
| // Nothing left to read |
| mBuffer.commit(currTime); |
| break; |
| } |
| // Something else failed, close the socket |
| mBuffer.commit(currTime); |
| mBuffer.packInt(-1); |
| mBuffer.packInt(fd); |
| mBuffer.commit(currTime); |
| close(fd); |
| break; |
| } else if (bytes == 0) { |
| // The other side is closed |
| mBuffer.commit(currTime); |
| mBuffer.packInt(-1); |
| mBuffer.packInt(fd); |
| mBuffer.commit(currTime); |
| close(fd); |
| break; |
| } |
| |
| mBuffer.advanceWrite(bytes); |
| mBuffer.commit(currTime); |
| |
| // Short reads also mean nothing is left to read |
| if (bytes < contiguous) { |
| break; |
| } |
| } |
| } |
| } |
| } |
| |
| mBuffer.setDone(); |
| |
| if (mMveUds >= 0) { |
| gSessionData->maliVideo.stop(mMveUds); |
| } |
| |
| mInterruptFd = -1; |
| close(pipefd[0]); |
| close(pipefd[1]); |
| } |
| |
| void ExternalSource::interrupt() { |
| if (mInterruptFd >= 0) { |
| int8_t c = 0; |
| // Write to the pipe to wake the monitor which will cause mSessionIsActive to be reread |
| if (::write(mInterruptFd, &c, sizeof(c)) != sizeof(c)) { |
| logg->logError(__FILE__, __LINE__, "write failed"); |
| handleException(); |
| } |
| } |
| } |
| |
| bool ExternalSource::isDone() { |
| return mBuffer.isDone(); |
| } |
| |
| void ExternalSource::write(Sender *sender) { |
| // Don't send external data until the summary packet is sent so that monotonic delta is available |
| if (!gSessionData->mSentSummary) { |
| return; |
| } |
| if (!mBuffer.isDone()) { |
| mBuffer.write(sender); |
| sem_post(&mBufferSem); |
| } |
| } |