diff options
Diffstat (limited to 'android/adb-qemud.c')
-rw-r--r-- | android/adb-qemud.c | 285 |
1 files changed, 285 insertions, 0 deletions
diff --git a/android/adb-qemud.c b/android/adb-qemud.c new file mode 100644 index 0000000..bc51807 --- /dev/null +++ b/android/adb-qemud.c @@ -0,0 +1,285 @@ +/* + * Copyright (C) 2011 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. + */ + +#include "qemu-common.h" +#include "android/globals.h" /* for android_hw */ +#include "android/hw-qemud.h" +#include "android/utils/misc.h" +#include "android/utils/system.h" +#include "android/utils/debug.h" +#include "android/adb-server.h" +#include "android/adb-qemud.h" + +#define E(...) derror(__VA_ARGS__) +#define W(...) dwarning(__VA_ARGS__) +#define D(...) VERBOSE_PRINT(adbclient,__VA_ARGS__) +#define D_ACTIVE VERBOSE_CHECK(adbclient) +#define QB(b, s) quote_bytes((const char*)b, (s < 32) ? s : 32) + +#define SERVICE_NAME "adb" + +/* Enumerates ADB client state values. */ +typedef enum AdbClientState { + /* Waiting on a connection from ADB host. */ + ADBC_STATE_WAIT_ON_HOST, + /* ADB host is connected. Waiting on the transport initialization completion + * in the guest. */ + ADBC_STATE_HOST_CONNECTED, + /* Connection between ADB host and ADB guest is fully established. */ + ADBC_STATE_CONNECTED, + /* ADB host has been disconnected. */ + ADBC_STATE_HOST_DISCONNECTED, + /* ADB guest has been disconnected. */ + ADBC_STATE_GUEST_DISCONNECTED, +} AdbClientState; + +/* ADB client descriptor. */ +typedef struct AdbClient AdbClient; +struct AdbClient { + /* Opaque pointer returned from adb_server_register_guest API. */ + void* opaque; + /* QEMUD client pipe for this client. */ + QemudClient* qemud_client; + /* Connection state. */ + AdbClientState state; +}; + +/******************************************************************************** + * ADB host communication. + *******************************************************************************/ + +/* A callback that is invoked when the host is connected. + * Param: + * opaque - AdbClient instance. + * connection - An opaque pointer that identifies connection with the ADB host. + */ +static void +_adb_on_host_connected(void* opaque, void* connection) +{ + AdbClient* const adb_client = (AdbClient*)opaque; + + if (adb_client->state == ADBC_STATE_WAIT_ON_HOST) { + D("ADB client %p(o=%p) is connected to the host %p", + adb_client, adb_client->opaque, connection); + + /* Bump the state up. */ + adb_client->state = ADBC_STATE_HOST_CONNECTED; + + /* Notify the ADB guest that host has been connected.This will unblock + * the guest from a 'read', then guest will register the transport, and + * will send 'setart' request, indicating that it is ready to receive + * data from the host. */ + qemud_client_send(adb_client->qemud_client, (const uint8_t*)"ok", 2); + } else { + D("Unexpected ADB host connection while state is %d", adb_client->state); + } +} + +/* A callback that is invoked when the host gets disconnected. + * Param: + * opaque - AdbClient instance. + * connection - An opaque pointer that identifies connection with the ADB host. + */ +static void +_adb_on_host_disconnect(void* opaque, void* connection) +{ + AdbClient* const adb_client = (AdbClient*)opaque; + + D("ADB client %p(o=%p) is disconnected from the host %p", + adb_client, adb_client->opaque, connection); + adb_client->state = ADBC_STATE_HOST_DISCONNECTED; +} + +/* A callback that is invoked when the host sends data. + * Param: + * opaque - AdbClient instance. + * connection - An opaque pointer that identifies connection with the ADB host. + * buff, size - Buffer containing the host data. + */ +static void +_adb_on_host_data(void* opaque, void* connection, const void* buff, int size) +{ + AdbClient* const adb_client = (AdbClient*)opaque; + D("ADB client %p(o=%p) received from the host %p %d bytes in %s", + adb_client, adb_client->opaque, connection, size, QB(buff, size)); + + if (adb_client->state == ADBC_STATE_CONNECTED) { + /* Dispatch data down to the guest. */ + qemud_client_send(adb_client->qemud_client, (const uint8_t*)buff, size); + } else { + D("Unexpected data from ADB host %p while client %p(o=%p) is in state %d", + connection, adb_client, adb_client->opaque, adb_client->state); + } +} + +/* ADB guest API required for adb_server_register_guest */ +static AdbGuestRoutines _adb_client_routines = { + /* A callback that is invoked when the host is connected. */ + _adb_on_host_connected, + /* A callback that is invoked when the host gets disconnected. */ + _adb_on_host_disconnect, + /* A callback that is invoked when the host sends data. */ + _adb_on_host_data, +}; + +/******************************************************************************** + * ADB guest communication. + *******************************************************************************/ + +/* Allocates AdbClient instance. */ +static AdbClient* +_adb_client_new(void) +{ + AdbClient* adb_client; + + ANEW0(adb_client); + + return adb_client; +} + +/* Frees AdbClient instance, allocated with _adb_client_new */ +static void +_adb_client_free(AdbClient* adb_client) +{ + if (adb_client != NULL) { + free(adb_client); + } +} + +/* A callback that is invoked when ADB guest sends data to the service. + * Param: + * opaque - AdbClient instance. + * msg, msglen - Message received from the ADB guest. + * client - adb QEMUD client. + */ +static void +_adb_client_recv(void* opaque, uint8_t* msg, int msglen, QemudClient* client) +{ + AdbClient* const adb_client = (AdbClient*)opaque; + + D("ADB client %p(o=%p) received from guest %d bytes in %s", + adb_client, adb_client->opaque, msglen, QB(msg, msglen)); + + /* Properly dispatch the message, depending on the client state. */ + switch (adb_client->state) { + case ADBC_STATE_CONNECTED: + /* Connection is fully established. Dispatch the message to the + * host. */ + adb_server_on_guest_message(adb_client->opaque, msg, msglen); + break; + + case ADBC_STATE_WAIT_ON_HOST: + /* At this state the only message that is allowed is 'accept' */ + if (msglen == 6 && !memcmp(msg, "accept", 6)) { + /* Register ADB guest connection with the ADB server. */ + adb_client->opaque = + adb_server_register_guest(adb_client, &_adb_client_routines); + if (adb_client->opaque == NULL) { + D("Unable to register ADB guest with the ADB server."); + /* KO the guest. */ + qemud_client_send(adb_client->qemud_client, + (const uint8_t*)"ko", 2); + } + } else { + D("Unexpected guest request while waiting on ADB host to connect."); + } + break; + + case ADBC_STATE_HOST_CONNECTED: + /* At this state the only message that is allowed is 'start' */ + if (msglen == 5 && !memcmp(msg, "start", 5)) { + adb_client->state = ADBC_STATE_CONNECTED; + adb_server_complete_connection(adb_client->opaque); + } else { + D("Unexpected request while waiting on connection to start."); + } + break; + + default: + D("Unexpected ADB guest request '%s' while client state is %d.", + QB(msg, msglen), adb_client->state); + break; + } +} + +/* A callback that is invoked when ADB guest disconnects from the service. */ +static void +_adb_client_close(void* opaque) +{ + AdbClient* const adb_client = (AdbClient*)opaque; + + D("ADB client %p(o=%p) is disconnected from the guest.", + adb_client, adb_client->opaque); + adb_client->state = ADBC_STATE_GUEST_DISCONNECTED; + if (adb_client->opaque != NULL) { + /* Close connection with the host. */ + adb_server_on_guest_closed(adb_client->opaque); + } + _adb_client_free(adb_client); +} + +/* A callback that is invoked when ADB daemon running inside the guest connects + * to the service. + * Client parameters are ignored here. Typically they contain the ADB port number + * which is always 5555 for the device / emulated system. + */ +static QemudClient* +_adb_service_connect(void* opaque, + QemudService* serv, + int channel, + const char* client_param) +{ + /* Create new QEMUD client for the connection with ADB daemon. */ + AdbClient* const adb_client = _adb_client_new(); + + D("Connecting ADB guest: '%s'", client_param ? client_param : "<null>"); + adb_client->qemud_client = + qemud_client_new(serv, channel, client_param, adb_client, + _adb_client_recv, _adb_client_close, NULL, NULL); + if (adb_client->qemud_client == NULL) { + D("Unable to create QEMUD client for ADB guest."); + _adb_client_free(adb_client); + return NULL; + } + + return adb_client->qemud_client; +} + +/******************************************************************************** + * ADB service API. + *******************************************************************************/ + +void +android_adb_service_init(void) +{ +static int _inited = 0; + + if (!adb_server_is_initialized()) { + return; + } + + if (!_inited) { + QemudService* serv = qemud_service_register(SERVICE_NAME, 0, NULL, + _adb_service_connect, + NULL, NULL); + if (serv == NULL) { + derror("%s: Could not register '%s' service", + __FUNCTION__, SERVICE_NAME); + return; + } + D("%s: Registered '%s' qemud service", __FUNCTION__, SERVICE_NAME); + } +} |