blob: 744501cf0a794b7228bc76bb8a6cce262d6bce88 [file] [log] [blame]
/* Copyright 2015 The Android Open Source Project
**
** This software is licensed under the terms of the GNU General Public
** License version 2, as published by the Free Software Foundation, and
** may be copied, distributed, and modified under those terms.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
*/
#include "android-qemu2-glue/emulation/android_pipe_device.h"
#include "android/base/Compiler.h"
#include "android/base/Log.h"
#include "android/emulation/AndroidPipe.h"
#include "android/emulation/android_pipe_common.h"
#include "android/emulation/android_pipe_device.h"
#include "android/emulation/VmLock.h"
#include "android/utils/stream.h"
#include "android-qemu2-glue/utils/stream.h"
#include <assert.h>
#include <memory>
extern "C" {
#include "hw/misc/goldfish_pipe.h"
} // extern "C"
// Technical note: This file contains the glue code used between the
// generic AndroidPipe service implementation, and the QEMU2-specific
// goldfish_pipe virtual device. More specifically:
//
// The host service pipe expects a device implementation that will
// implement the callbacks in AndroidHwPipeFuncs. These are injected
// into the service by calling android_pipe_set_hw_funcs().
//
// The virtual device expects a service implementation that will
// implement the callbacks in GoldfishPIpeServiceOps. These are injected
// into the device through goldfish_pipe_set_service_ops().
//
// qemu_android_pipe_init() is used to inject all callbacks at setup time
// and initialize the threading mode of AndroidPipe (see below).
//
namespace {
// Convenience class used to wrap a QEMUFile inside a ::Stream instance
// which will be usable by the generic AndroidPipe service.
class ScopedStream {
public:
explicit ScopedStream(QEMUFile* file)
: mStream(stream_from_qemufile(file)) {}
~ScopedStream() { close(); }
void close() {
if (mStream) {
stream_free(mStream);
mStream = nullptr;
}
}
::Stream* get() const { return mStream; }
DISALLOW_COPY_ASSIGN_AND_MOVE(ScopedStream);
private:
::Stream* mStream;
};
} // namespace
// These callbacks are called from the virtual device into the pipe service.
static const GoldfishPipeServiceOps goldfish_pipe_service_ops = {
// guest_open()
[](GoldfishHwPipe* hwPipe) -> GoldfishHostPipe* {
return static_cast<GoldfishHostPipe*>(
android_pipe_guest_open(hwPipe));
},
// guest_load()
[](QEMUFile* file,
GoldfishHwPipe* hwPipe,
char* force_close) -> GoldfishHostPipe* {
ScopedStream stream(file);
return static_cast<GoldfishHostPipe*>(
android_pipe_guest_load(stream.get(), hwPipe, force_close));
},
// guest_close()
[](GoldfishHostPipe* hostPipe) { android_pipe_guest_close(hostPipe); },
// guest_save()
[](GoldfishHostPipe* hostPipe, QEMUFile* file) {
ScopedStream stream(file);
android_pipe_guest_save(hostPipe, stream.get());
},
// guest_poll()
[](GoldfishHostPipe* hostPipe) {
static_assert((int)GOLDFISH_PIPE_POLL_IN == (int)PIPE_POLL_IN,
"invalid POLL_IN values");
static_assert((int)GOLDFISH_PIPE_POLL_OUT == (int)PIPE_POLL_OUT,
"invalid POLL_OUT values");
static_assert((int)GOLDFISH_PIPE_POLL_HUP == (int)PIPE_POLL_HUP,
"invalid POLL_HUP values");
return static_cast<GoldfishPipePollFlags>(
android_pipe_guest_poll(hostPipe));
},
// guest_recv()
[](GoldfishHostPipe* hostPipe,
GoldfishPipeBuffer* buffers,
int numBuffers) -> int {
// NOTE: Assumes that AndroidPipeBuffer and GoldfishPipeBuffer
// have exactly the same layout.
static_assert(
sizeof(AndroidPipeBuffer) == sizeof(GoldfishPipeBuffer),
"Invalid PipeBuffer sizes");
static_assert(offsetof(AndroidPipeBuffer, data) ==
offsetof(GoldfishPipeBuffer, data),
"Invalid PipeBuffer::data offsets");
static_assert(offsetof(AndroidPipeBuffer, size) ==
offsetof(GoldfishPipeBuffer, size),
"Invalid PipeBuffer::size offsets");
return android_pipe_guest_recv(
hostPipe, reinterpret_cast<AndroidPipeBuffer*>(buffers),
numBuffers);
},
// guest_send()
[](GoldfishHostPipe* hostPipe,
const GoldfishPipeBuffer* buffers,
int numBuffers) -> int {
return android_pipe_guest_send(
hostPipe,
reinterpret_cast<const AndroidPipeBuffer*>(buffers),
numBuffers);
},
// guest_wake_on()
[](GoldfishHostPipe* hostPipe, GoldfishPipeWakeFlags wakeFlags) {
android_pipe_guest_wake_on(hostPipe, static_cast<int>(wakeFlags));
},
};
// These callbacks are called from the pipe service into the virtual device.
static const AndroidPipeHwFuncs android_pipe_hw_funcs = {
// resetPipe()
[](void* hwPipe, void* hostPipe) {
goldfish_pipe_reset(static_cast<GoldfishHwPipe*>(hwPipe),
static_cast<GoldfishHostPipe*>(hostPipe));
},
// closeFromHost()
[](void* hwPipe) {
goldfish_pipe_close_from_host(static_cast<GoldfishHwPipe*>(hwPipe));
},
// signalWake()
[](void* hwPipe, unsigned flags) {
static_assert(
(int)GOLDFISH_PIPE_WAKE_CLOSED == (int)PIPE_WAKE_CLOSED,
"Invalid PIPE_WAKE_CLOSED values");
static_assert((int)GOLDFISH_PIPE_WAKE_READ == (int)PIPE_WAKE_READ,
"Invalid PIPE_WAKE_READ values");
static_assert((int)GOLDFISH_PIPE_WAKE_WRITE == (int)PIPE_WAKE_WRITE,
"Invalid PIPE_WAKE_WRITE values");
goldfish_pipe_signal_wake(
static_cast<GoldfishHwPipe*>(hwPipe),
static_cast<GoldfishPipeWakeFlags>(flags));
},
};
bool qemu_android_pipe_init(android::VmLock* vmLock) {
goldfish_pipe_set_service_ops(&goldfish_pipe_service_ops);
android_pipe_set_hw_funcs(&android_pipe_hw_funcs);
android::AndroidPipe::initThreading(vmLock);
return true;
}