aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristoffer Dall <christoffer.dall@linaro.org>2014-06-18 16:03:22 +0100
committerPeter Maydell <peter.maydell@linaro.org>2014-06-23 16:33:30 +0100
commit072294657bd3c6a9ac58cbb7351198a00c489613 (patch)
treead62a939a7deb426126d858147214a930f50a832
parent2d2141145538d9d1879fc991d0ec669922b7ce1f (diff)
downloadqemu-arm-072294657bd3c6a9ac58cbb7351198a00c489613.tar.gz
android_adb: add Android ADB backend
The Android adbd daemon running inside Android VMs on the emulator expect to be able to talk to a backend named "qemud:adb" (for historical reasons). The backend will carry out a handshake: 1. Listen for connections from the host adb server on adb_port = <5555 + (2 * emulator instance id)>. 2. Connect to the local adb server on port 5037 if it's available. 3. Listen for and accept the beginning of the handshake with the adbd daemon (adbd sends 'accept' over the write). 4. After the adb backend detects a connection on <adb_port>, it sends back an "ok" string to adbd. 5. The adb backend will not send any more data over the pipe until it recognizes a 'start' request, which means the adbd is ready to receive data from the host ADB server. Once the above process is complete, the adb backend will consume everything it receives on the socket connected on <adb_port> and send it over the adb pipe and will consume everything it receives over the adb pipe and send it on the socket connected on <adb_port>. Signed-off-by: Christoffer Dall <christoffer.dall@linaro.org> [AJB: Bunch of clean-up/re-factoring to GIOChannel code] Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
-rw-r--r--hw/misc/Makefile.objs1
-rw-r--r--hw/misc/android_adb.c771
-rw-r--r--include/hw/misc/android_pipe.h3
3 files changed, 775 insertions, 0 deletions
diff --git a/hw/misc/Makefile.objs b/hw/misc/Makefile.objs
index 814a0f80fb..f97ecb6c57 100644
--- a/hw/misc/Makefile.objs
+++ b/hw/misc/Makefile.objs
@@ -17,6 +17,7 @@ common-obj-$(CONFIG_ARM11SCU) += arm11scu.o
obj-$(CONFIG_ANDROID_PIPE) += android_pipe.o
obj-$(CONFIG_ANDROID_PIPE) += android_pipe_test.o
obj-$(CONFIG_ANDROID_PIPE) += android_adb_dbg.o
+obj-$(CONFIG_ANDROID_PIPE) += android_adb.o
# PKUnity SoC devices
common-obj-$(CONFIG_PUV3) += puv3_pm.o
diff --git a/hw/misc/android_adb.c b/hw/misc/android_adb.c
new file mode 100644
index 0000000000..68da548a0f
--- /dev/null
+++ b/hw/misc/android_adb.c
@@ -0,0 +1,771 @@
+/*
+ * ARM Android emulator adb backend
+ *
+ * Copyright (c) 2014 Linaro Limited
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Handle connections to the qemud:adb pipe from Android guests and route
+ * traffic over this pipe to the host adb server connecting to the qemu
+ * process on a tcp socket.
+ */
+
+#include <glib.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include "qemu-common.h"
+#include "qemu/config-file.h"
+#include "qemu/main-loop.h"
+#include "qapi/error.h"
+#include "qemu/module.h"
+#include "qemu/sockets.h"
+
+#include "hw/misc/android_pipe.h"
+
+//#define DEBUG_ADB
+
+#ifdef DEBUG_ADB
+#define DPRINTF(fmt, ...) \
+do { fprintf(stderr, "adb: " fmt , ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(fmt, ...) do {} while(0)
+#endif
+
+#define PIPE_QUEUE_LEN 16
+#define HANDSHAKE_MAXLEN 128
+#define ADB_BUFFER_LEN 4096
+
+#define ADB_SERVER_PORT 5037
+
+/* 'accept' request from adbd */
+static const char _accept_req[] = "accept";
+/* 'start' request from adbd */
+static const char _start_req[] = "start";
+/* handshake reply to adbd */
+static const char _ok_resp[] = "ok";
+static const char _ko_resp[] = "ko";
+
+
+static bool pipe_backend_initialized = false;
+
+enum adb_connect_state {
+ ADB_CONNECTION_STATE_UNCONNECTED = 0,
+ ADB_CONNECTION_STATE_ACCEPT,
+ ADB_CONNECTION_STATE_CONNECTED,
+};
+
+typedef struct {
+ void* hwpipe;
+ enum adb_connect_state state;
+ GIOChannel *chan; /* I/O channel to adb server */
+ unsigned flags;
+
+ /* TODO: Make sure access to thes buffers is
+ * synchronized through the io accessor
+ * functions in QEMU or some other
+ * mechanism. */
+ char out_buffer[ADB_BUFFER_LEN];
+ char *out_next;
+ unsigned out_cnt;
+
+} adb_pipe;
+
+/*
+ * This structure keeps track of the adb-server connection state (that
+ * is HOST adb-server <-> QEMU connections). We only expect to see one
+ * active connection at a time as data is multiplexed over the pipe.
+ */
+
+typedef struct {
+ GIOChannel *listen_chan; /* listener/connect socket */
+ GIOChannel *chan; /* actual comms socket */
+ /* these cache the read/write state for when wakeon is called */
+ gboolean data_in; /* have we seen data? */
+ gboolean data_out; /* can we output data? */
+ adb_pipe *adb_pipes[PIPE_QUEUE_LEN];
+ adb_pipe *connected_pipe;
+} adb_backend_state;
+
+static adb_backend_state adb_state;
+
+static void adb_reply(adb_pipe *apipe, const char *reply);
+
+
+/****************************************************************************
+ * ADB Server Interface
+ */
+
+static QemuOpts* adb_server_config(void) {
+ QemuOpts *socket_opts;
+
+ socket_opts = qemu_opts_create(&socket_optslist, NULL, 0,
+ &error_abort);
+
+ if (!qemu_opt_get(socket_opts, "host")) {
+ qemu_opt_set(socket_opts, "host", "localhost");
+ }
+
+ if (!qemu_opt_get(socket_opts, "port")) {
+ qemu_opt_set_number(socket_opts, "port", ADB_SERVER_PORT);
+ }
+
+ return socket_opts;
+}
+
+static void adb_server_notify(int adb_port) {
+ Error *local_err = NULL;
+ QemuOpts *socket_opts = adb_server_config();
+ int sock = inet_connect_opts(socket_opts, &local_err, NULL, NULL);
+ size_t len;
+ gchar *message,*handshake;
+
+ if (sock >= 0) {
+ socket_set_nodelay(sock);
+ }
+
+ /* Failed to establish connection */
+ if (sock < 0) {
+ fprintf(stderr,"%s: Failed to establish connection to ADB server\n",
+ __func__);
+ return;
+ }
+
+ message = g_strdup_printf("host:emulator:%d", adb_port);
+ handshake = g_strdup_printf("%04x%s", (int) strlen(message), message);
+ len = strlen(handshake);
+
+ if (send_all(sock, handshake, len) != len) {
+ fprintf(stderr,"%s: error sending string:%s\n", __func__, handshake);
+ }
+
+ closesocket(sock);
+
+ g_free(message);
+ g_free(handshake);
+ return;
+}
+
+/* TODO: Needs a common implementation with the likes of qemu-char.c */
+static GIOChannel *io_channel_from_socket(int fd)
+{
+ GIOChannel *chan;
+
+ if (fd == -1) {
+ return NULL;
+ }
+
+#ifdef _WIN32
+ chan = g_io_channel_win32_new_socket(fd);
+#else
+ chan = g_io_channel_unix_new(fd);
+#endif
+
+ g_io_channel_set_encoding(chan, NULL, NULL);
+ g_io_channel_set_buffered(chan, FALSE);
+
+ return chan;
+}
+static gboolean tcp_adb_accept(GIOChannel *channel, GIOCondition cond,
+ void *opaque);
+
+/* Close a connection to the server.
+**
+** We need to ensure we clean-up any connection state and re-enable
+** the watch on the listen socket so new connections can be created.
+*/
+static void tcp_adb_server_close(adb_backend_state *bs)
+{
+ g_assert(bs->chan);
+ g_assert(bs->listen_chan);
+
+ /* clean-up connected pipes */
+ if (bs->connected_pipe) {
+ DPRINTF("%s: closing connected pipe\n", __func__);
+ android_pipe_close(bs->connected_pipe->hwpipe);
+ bs->connected_pipe->chan = NULL;
+ bs->connected_pipe = NULL;
+ }
+
+ /* wait for new connections */
+ g_io_add_watch(bs->listen_chan, G_IO_IN, tcp_adb_accept, bs);
+
+ /* finally close down this socket */
+ g_io_channel_shutdown(bs->chan, FALSE, NULL);
+ g_io_channel_unref(bs->chan);
+ bs->chan = NULL;
+}
+
+/*
+ * This handles state changes on the server socket. We don't directly
+ * start receiving or sending data here but we do need to ensure that
+ * the pipe guest wakes up so it can start reading data.
+ *
+ * This is a one-shot wake up - the watch needs re-adding whenever
+ * things go quite so we'll wake up again when needed.
+ */
+static gboolean tcp_adb_server_data(GIOChannel *channel, GIOCondition cond,
+ void *opaque)
+{
+ adb_backend_state *bs = (adb_backend_state *) opaque;
+ if (cond & G_IO_IN) {
+ bs->data_in = TRUE;
+ if (bs->connected_pipe && bs->connected_pipe->flags & PIPE_WAKE_READ) {
+ DPRINTF("%s: waking up pipe for incomming data\n", __func__);
+ android_pipe_wake(bs->connected_pipe->hwpipe, PIPE_WAKE_READ);
+ }
+ }
+
+ if (cond & G_IO_OUT) {
+ bs->data_out = TRUE;
+ if (bs->connected_pipe && bs->connected_pipe->flags & PIPE_WAKE_WRITE) {
+ DPRINTF("%s: waking up pipe for now able to write\n", __func__);
+ android_pipe_wake(bs->connected_pipe->hwpipe, PIPE_WAKE_WRITE);
+ }
+ }
+
+ if ((cond & G_IO_ERR) ||
+ (cond & G_IO_HUP)) {
+ DPRINTF("%s: error %d - closing server connectio\n", __func__, cond);
+ tcp_adb_server_close(bs);
+ }
+
+ /* Done, we must re-add watch next time we are waiting for data */
+ return FALSE;
+}
+
+static gboolean tcp_adb_connect(adb_backend_state *bs, int fd)
+{
+ if (bs->chan) {
+ DPRINTF("%s: existing connection %p, fail connect!\n",
+ __func__, bs->chan);
+ return FALSE;
+ } else {
+ DPRINTF("%s: in-coming connection on %d\n", __func__, fd);
+
+ qemu_set_nonblock(fd);
+ bs->chan = io_channel_from_socket(fd);
+ g_io_add_watch(bs->chan, G_IO_IN|G_IO_ERR|G_IO_HUP, tcp_adb_server_data, bs);
+
+ /* If we don't have a pipe to use for the tcp backend, then find one in
+ * the accept state. Note, this can happen, for example, if the previous
+ * connected pipe was closed for some reason. Also note that this becomes
+ * sort of random which pipe we select, but there doesn't seem to be any
+ * clearly defined semantics about the ordering here. A proper fifo may
+ * be a better data structure for this.
+ */
+ if (!bs->connected_pipe) {
+ int i;
+ for (i = 0; i < PIPE_QUEUE_LEN; i++) {
+ if (bs->adb_pipes[i] &&
+ bs->adb_pipes[i]->state == ADB_CONNECTION_STATE_ACCEPT) {
+ bs->connected_pipe = bs->adb_pipes[i];
+ }
+ }
+ }
+
+ /* Tell the adbd that the adb server has conected and that we're ready to
+ * receive the start package */
+ if (bs->connected_pipe) {
+ DPRINTF("Incoming TCP connection on already accepted pipe, connect\n");
+ adb_pipe *apipe = bs->connected_pipe;
+ apipe->chan = bs->chan;
+ if (apipe->out_next) {
+ fprintf(stderr, "Pending reply on non-connected pipe, error\n");
+ abort();
+ }
+ adb_reply(apipe, _ok_resp);
+ }
+
+ return TRUE;
+ }
+}
+
+/* Accept incoming connections. If the connect succeeds and we create
+ * a connection we return FALSE to take the listen socket out of the
+ * polling loop. We need to re-add the watch if/when the connection
+ * dies so another connection can be created.
+ */
+static gboolean tcp_adb_accept(GIOChannel *channel, GIOCondition cond,
+ void *opaque)
+{
+ adb_backend_state *bs = opaque;
+ struct sockaddr_in saddr;
+ struct sockaddr *addr;
+ socklen_t len;
+ int fd;
+
+ for(;;) {
+ len = sizeof(saddr);
+ addr = (struct sockaddr *)&saddr;
+ fd = qemu_accept(g_io_channel_unix_get_fd(bs->listen_chan), addr, &len);
+ if (fd < 0 && errno != EINTR) {
+ DPRINTF("%s: failed to accept %d/%d\n", __func__, fd, errno);
+ return FALSE;
+ } else if (fd >= 0) {
+ break;
+ }
+ }
+
+ if (tcp_adb_connect(bs, fd)) {
+ return FALSE;
+ } else {
+ return TRUE;
+ }
+}
+
+static bool adb_server_listen_incoming(int port)
+{
+ adb_backend_state *bs = &adb_state;
+ char *host_port;
+ Error *err = NULL;
+ int fd;
+
+ host_port = g_strdup_printf("localhost:%d", port);
+ fd = inet_listen(host_port, NULL, 0, SOCK_STREAM, 0, &err);
+ if (fd < 0) {
+ return false;
+ }
+
+ bs->listen_chan = io_channel_from_socket(fd);
+ g_io_add_watch(bs->listen_chan, G_IO_IN, tcp_adb_accept, bs);
+ return true;
+}
+
+
+/****************************************************************************
+ * Android Pipe adbd Interface
+ */
+
+static void* adb_pipe_init(void *hwpipe,void *opaque, const char *args)
+{
+ adb_pipe *apipe;
+ int i;
+
+ DPRINTF("%s: hwpipe=%p\n", __FUNCTION__, hwpipe);
+ apipe = g_malloc0(sizeof(adb_pipe));
+ apipe->hwpipe = hwpipe;
+ apipe->state = ADB_CONNECTION_STATE_UNCONNECTED;
+ apipe->out_next = NULL;
+ apipe->out_cnt = 0;
+ apipe->flags = 0;
+
+ for (i = 0; i < PIPE_QUEUE_LEN; i++) {
+ if (adb_state.adb_pipes[i] == NULL) {
+ adb_state.adb_pipes[i] = apipe;
+ break;
+ }
+ }
+
+ if (i == PIPE_QUEUE_LEN) {
+ DPRINTF("Could not handle more open adb pipes\n");
+ g_free(apipe);
+ return NULL;
+ }
+
+ return apipe;
+}
+
+static void adb_pipe_close(void *opaque )
+{
+ adb_pipe *apipe = opaque;
+ int i;
+
+ DPRINTF("%s: hwpipe=%p\n", __FUNCTION__, apipe->hwpipe);
+ if (adb_state.connected_pipe == apipe) {
+ tcp_adb_server_close(&adb_state);
+ adb_state.connected_pipe = NULL;
+ }
+ for (i = 0; i < PIPE_QUEUE_LEN; i++) {
+ if (adb_state.adb_pipes[i] == apipe) {
+ adb_state.adb_pipes[i] = NULL;
+ break;
+ }
+ }
+
+ g_free(apipe);
+}
+
+static int pipe_send_data(adb_pipe *apipe, char *data, unsigned len,
+ const AndroidPipeBuffer *buffers, int cnt)
+{
+ int avail = len;
+ int chunk;
+
+ while (cnt > 0 && avail > 0) {
+ if (avail < buffers[0].size) {
+ chunk = avail;
+ } else {
+ chunk = buffers[0].size;
+ }
+
+ memcpy(data, buffers[0].data, chunk);
+ data += chunk;
+ avail -= chunk;
+
+ buffers++;
+ cnt--;
+ }
+
+ return len - avail;
+}
+
+static bool match_request(const char *request, int len, const char *match)
+{
+ return len == strlen(match) && !strncmp(request, match, strlen(match));
+}
+
+static const char *handle_request(adb_pipe *apipe, const char *request, int len)
+{
+ adb_backend_state *bs = &adb_state;
+
+ if (match_request(request, len, _accept_req)) {
+ if (apipe->state != ADB_CONNECTION_STATE_UNCONNECTED) {
+ return _ko_resp;
+ }
+
+ apipe->state = ADB_CONNECTION_STATE_ACCEPT;
+
+ /* If we don't have any connected adb_pipe to the tcp backend yet,
+ * elect ourselves. If the tcp connection is ready as well, go ahead
+ * and thell adbd to carry on.
+ */
+ if (!bs->connected_pipe) {
+ bs->connected_pipe = apipe;
+ if (bs->chan) {
+ g_assert(!bs->connected_pipe->chan);
+ bs->connected_pipe->chan = bs->chan;
+ DPRINTF("Already have tcp connection, reply 'ok' to 'accept'\n");
+ return _ok_resp;
+ }
+ }
+
+ DPRINTF("no tcp connection, wait for it\n");
+ return NULL; /* wait until adb server connects */
+ } else if (match_request(request, len, _start_req)) {
+ if (apipe->state != ADB_CONNECTION_STATE_ACCEPT) {
+ DPRINTF("adbd requested 'start' when we are not ready, error\n");
+ android_pipe_close(apipe->hwpipe);
+ return NULL;
+ }
+
+ if (!bs->chan) {
+ DPRINTF("adbd requested 'start' but tcp connection not yet connected, error\n");
+ bs->connected_pipe->chan = NULL;
+ android_pipe_close(apipe->hwpipe);
+ return NULL;
+ }
+
+ apipe->state = ADB_CONNECTION_STATE_CONNECTED;
+ return NULL; /* start proxying data */
+ } else {
+ /* unrecognized command */
+ return _ko_resp;
+ }
+}
+
+static void adb_reply(adb_pipe *apipe, const char *reply)
+{
+ strcpy(apipe->out_buffer, reply);
+ apipe->out_cnt = strlen(reply);
+ apipe->out_next = &apipe->out_buffer[0];
+}
+
+static int adb_pipe_proxy_send(adb_pipe *apipe, const AndroidPipeBuffer *buffers,
+ int cnt)
+{
+ gsize total_copied = 0;
+ adb_backend_state *bs = &adb_state;
+
+ g_assert(apipe->chan);
+
+ DPRINTF("%s: %p/%d\n", __func__, buffers, cnt);
+ do {
+ GError *error = NULL;
+ gchar *bptr = (gchar *) buffers[0].data;
+ gsize bsize = buffers[0].size;
+ gsize copied = 0;
+ GIOStatus status = g_io_channel_write_chars(
+ apipe->chan, bptr, bsize, &copied, &error);
+
+ total_copied += copied;
+
+ /* If we have already copied some data lets signal that and
+ * let it come back to here.
+ */
+ if (total_copied > 0 &&
+ ((status == G_IO_STATUS_EOF || status == G_IO_STATUS_AGAIN))) {
+ bs->data_out = FALSE;
+ return total_copied;
+ }
+
+ /* Can't write and more data.... */
+ if (status == G_IO_STATUS_AGAIN) {
+ DPRINTF("%s: out of data, setting up watch\n", __func__);
+ g_io_add_watch(apipe->chan, G_IO_IN|G_IO_ERR|G_IO_HUP,
+ tcp_adb_server_data, bs);
+ bs->data_out = FALSE;
+ return PIPE_ERROR_AGAIN;
+ }
+
+ if (status == G_IO_STATUS_EOF) {
+ bs->data_out = FALSE;
+ tcp_adb_server_close(bs);
+ return 0;
+ }
+
+ if (status != G_IO_STATUS_NORMAL) {
+ DPRINTF("%s: went wrong (%d)\n", __func__, status);
+ bs->data_out = FALSE;
+ tcp_adb_server_close(bs);
+ return PIPE_ERROR_IO;
+ }
+
+ if (copied < bsize) {
+ g_assert(total_copied > 0);
+ bs->data_out = FALSE;
+ break;
+ }
+
+ buffers++;
+ } while (--cnt);
+
+ return total_copied;
+}
+
+static int adb_pipe_send(void *opaque, const AndroidPipeBuffer* buffers,
+ int cnt)
+{
+ adb_pipe *apipe = opaque;
+ int ret = 0;
+ char request[HANDSHAKE_MAXLEN + 1];
+ const char *reply;
+
+ if (apipe->state != ADB_CONNECTION_STATE_CONNECTED) {
+ if (apipe->out_next) {
+ /* Existing reply in progress, not ready */
+ DPRINTF("%s: unconnected pipe, reply in progress\n", __func__);
+ return PIPE_ERROR_AGAIN;
+ }
+ ret = pipe_send_data(apipe, request, sizeof(request), buffers, cnt);
+ reply = handle_request(apipe, request, ret);
+
+ if (reply) {
+ adb_reply(apipe, reply);
+ android_pipe_wake(adb_state.connected_pipe->hwpipe, PIPE_WAKE_READ);
+ }
+ } else {
+ ret = adb_pipe_proxy_send(apipe, buffers, cnt);
+ if (ret == 0) {
+ return PIPE_ERROR_AGAIN;
+ }
+ }
+
+ return ret;
+}
+
+static int pipe_recv_data(adb_pipe *apipe, const char *data, unsigned len,
+ AndroidPipeBuffer *buffers, int cnt)
+{
+ int remain = len;
+ int chunk;
+
+ while (remain > 0 && cnt > 0) {
+ if (remain > buffers[0].size) {
+ chunk = buffers[0].size;
+ } else {
+ chunk = remain;
+ }
+
+ memcpy(buffers[0].data, data, chunk);
+ data += chunk;
+ remain -= chunk;
+
+ buffers++;
+ cnt--;
+ }
+
+ return len - remain;
+}
+
+static int adb_pipe_proxy_recv(adb_pipe *apipe, AndroidPipeBuffer *buffers,
+ int cnt)
+{
+ gsize total_copied = 0;
+ adb_backend_state *bs = &adb_state;
+
+ g_assert(apipe->chan);
+ g_assert(bs->chan == apipe->chan);
+
+ DPRINTF("%s: hwpipe=%p (buffers %p/%d)\n", __func__, apipe->hwpipe, buffers, cnt);
+ do {
+ GError *error = NULL;
+ gchar *bptr = (gchar *) buffers[0].data;
+ gsize bsize = buffers[0].size;
+ gsize copied = 0;
+ GIOStatus status = g_io_channel_read_chars(
+ apipe->chan, bptr, bsize, &copied, &error);
+
+ DPRINTF("%s: read %zd bytes into %p[%zd] -> %d\n", __func__,
+ copied, bptr, bsize, status);
+
+ total_copied += copied;
+
+ /* If we have already copied some data lets signal that and
+ * let it come back to here */
+ if (total_copied > 0 &&
+ ((status == G_IO_STATUS_EOF || status == G_IO_STATUS_AGAIN))) {
+ bs->data_in = FALSE;
+ return total_copied;
+ }
+
+ /* no data to read.... */
+ if (status == G_IO_STATUS_AGAIN) {
+ DPRINTF("%s: out of data, setting up watch\n", __func__);
+ g_io_add_watch(apipe->chan, G_IO_IN|G_IO_ERR|G_IO_HUP,
+ tcp_adb_server_data, bs);
+ bs->data_in = FALSE;
+ return PIPE_ERROR_AGAIN;
+ }
+
+ if (status == G_IO_STATUS_EOF) {
+ bs->data_in = FALSE;
+ tcp_adb_server_close(bs);
+ return 0;
+ }
+
+ if (status != G_IO_STATUS_NORMAL) {
+ DPRINTF("%s: went wrong (%d)\n", __func__, status);
+ bs->data_in = FALSE;
+ tcp_adb_server_close(bs);
+ return PIPE_ERROR_IO;
+ }
+
+ if (copied < bsize) {
+ g_assert(total_copied > 0);
+ bs->data_in = FALSE;
+ break;
+ }
+ buffers++;
+ cnt--;
+ } while (cnt);
+
+ return total_copied;
+}
+
+static int adb_pipe_recv(void *opaque, AndroidPipeBuffer *buffers,
+ int cnt)
+{
+ adb_pipe *apipe = opaque;
+ adb_backend_state *bs = &adb_state;
+ int ret = 0;
+
+ if (apipe->state == ADB_CONNECTION_STATE_CONNECTED) {
+ if (bs->data_in) {
+ ret = adb_pipe_proxy_recv(apipe, buffers, cnt);
+ return ret;
+ } else {
+ return PIPE_ERROR_AGAIN;
+ }
+ }
+
+ assert(cnt > 0);
+
+ if (!apipe->out_next) {
+ return PIPE_ERROR_AGAIN;
+ }
+
+ assert((apipe->out_next - apipe->out_buffer)
+ + apipe->out_cnt < ADB_BUFFER_LEN);
+
+ ret = pipe_recv_data(apipe, apipe->out_next, apipe->out_cnt, buffers, cnt);
+ apipe->out_cnt -= ret;
+ if (ret == apipe->out_cnt) {
+ apipe->out_next = NULL;
+ } else {
+ apipe->out_next += ret;
+ }
+
+ return ret;
+}
+
+static unsigned adb_pipe_poll(void *opaque)
+{
+ adb_pipe *apipe = opaque;
+ adb_backend_state *bs = &adb_state;
+ unsigned flags = 0;
+
+ if (apipe->state != ADB_CONNECTION_STATE_CONNECTED) {
+ if (apipe->out_next) {
+ flags |= PIPE_POLL_IN;
+ } else {
+ flags |= PIPE_POLL_OUT;
+ }
+ } else {
+ if (bs->data_in) {
+ flags |= PIPE_POLL_IN;
+ }
+ /* We can always forward data to the socket as far as we know */
+ flags |= PIPE_POLL_OUT;
+ }
+
+ return flags;
+}
+
+static void adb_pipe_wake_on(void *opaque, int flags)
+{
+ adb_pipe *apipe = opaque;
+ DPRINTF("%s: setting flags 0x%x->0x%x\n", __func__, apipe->flags, flags);
+ apipe->flags |= flags;
+
+ if (flags & PIPE_WAKE_READ && adb_state.data_in) {
+ android_pipe_wake(apipe->hwpipe, PIPE_WAKE_READ);
+ }
+
+ if (flags & PIPE_WAKE_WRITE && adb_state.data_out) {
+ android_pipe_wake(apipe->hwpipe, PIPE_WAKE_WRITE);
+ }
+}
+
+static const AndroidPipeFuncs adb_pipe_funcs = {
+ adb_pipe_init,
+ adb_pipe_close,
+ adb_pipe_send,
+ adb_pipe_recv,
+ adb_pipe_poll,
+ adb_pipe_wake_on,
+};
+
+/* Initialize and try to listen on the specified port;
+ * return true on success or false if the port was in use.
+ * Note that there's no way to undo this, so the board must
+ * set up the console port first and the adb port second.
+ */
+bool adb_server_init(int port)
+{
+ if (!pipe_backend_initialized) {
+ adb_state.chan = NULL;
+ adb_state.listen_chan = NULL;
+ adb_state.data_in = FALSE;
+ adb_state.connected_pipe = NULL;
+
+ android_pipe_add_type("qemud:adb", NULL, &adb_pipe_funcs);
+ pipe_backend_initialized = true;
+ }
+
+ if (!adb_server_listen_incoming(port)) {
+ return false;
+ }
+ adb_server_notify(port);
+ return true;
+}
diff --git a/include/hw/misc/android_pipe.h b/include/hw/misc/android_pipe.h
index 3d1665c205..962f2e96bd 100644
--- a/include/hw/misc/android_pipe.h
+++ b/include/hw/misc/android_pipe.h
@@ -221,5 +221,8 @@ extern void android_zero_pipe_init(void);
extern void android_pingpong_init(void);
extern void android_throttle_init(void);
extern void android_adb_dbg_backend_init(void);
+extern void android_adb_backend_init(void);
+
+extern bool adb_server_init(int port);
#endif /* _HW_ANDROID_PIPE_H */