blob: 8f5e6b684c538b192c03ab40529836d61a4d2157 [file] [log] [blame]
/**
* 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);
}
}