blob: 746aec33fe052af37cdb1adc4726efdfc38d3f64 [file] [log] [blame]
/* Copyright (C) 2007-2008 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/hw-qemud.h"
#include "android/emulation/android_qemud.h"
#include "android/utils/debug.h"
#include <stddef.h>
#include <stdlib.h>
#define D(...) VERBOSE_PRINT(qemud,__VA_ARGS__)
/*
* This implements support for the 'qemud' multiplexing communication
* channel between clients running in the emulated system and 'services'
* provided by the emulator.
*
* For additional details, please read docs/ANDROID-QEMUD.TXT
*
*/
/*
* IMPLEMENTATION DETAILS:
*
* We use one charpipe to connect the emulated serial port to the 'QemudSerial'
* object. This object is used to receive data from the serial port, and
* unframe messages (i.e. extract payload length + channel id from header,
* then the payload itself), before sending them to a generic receiver.
*
* The QemudSerial object can also be used to send messages to the daemon
* through the serial port (see qemud_serial_send())
*
* The multiplexer is connected to one or more 'service' objects.
* are themselves connected through a charpipe to an emulated device or
* control sub-module in the emulator.
*
* tty <==charpipe==> QemudSerial ---> QemudMultiplexer ----> QemudClient
* ^ |
* | |
* +--------------------------------------+
*
*/
/* this is the end of the serial charpipe that must be passed
* to the emulated tty implementation. The other end of the
* charpipe must be passed to qemud_multiplexer_init().
*/
static CSerialLine* android_qemud_serial_line = NULL;
CSerialLine* android_qemud_get_serial_line(void) {
if (!android_qemud_serial_line) {
CSerialLine* slIn;
CSerialLine* slOut;
if (android_serialline_pipe_open(&slIn, &slOut) < 0) {
derror( "%s: can't create charpipe to serial port",
__FUNCTION__ );
exit(1);
}
android_qemud_init(slOut);
android_qemud_serial_line = slIn;
}
return android_qemud_serial_line;
}
/*
* The following code is used for backwards compatibility reasons.
* It allows you to implement a given qemud-based service through
* a charpipe.
*
* In other words, this implements a QemudService and corresponding
* QemudClient that connects a qemud client running in the emulated
* system, to a CharDriverState object implemented through a charpipe.
*
* QemudCharClient <===charpipe====> (char driver user)
*
* For example, this is used to implement the "gsm" service when the
* modem emulation is provided through an external serial device.
*
* A QemudCharService can have only one client by definition.
* There is no QemudCharClient object because we can store a single
* CharDriverState handle in the 'opaque' field for simplicity.
*/
// A simple struct to pass some data to qemud client callbacks
struct qemud_char_client {
CSerialLine* sl;
QemudClient* client;
};
/* called whenever a new message arrives from a qemud client.
* this simply sends the message through the charpipe to the user.
*/
static void _qemud_char_client_recv(void* opaque, uint8_t* msg, int msglen,
QemudClient* client) {
qemud_char_client* const data = static_cast<qemud_char_client*>(opaque);
CSerialLine* sl = data->sl;
android_serialline_write(sl, msg, msglen);
}
/* we don't expect clients of char. services to exit. Just
* print an error to signal an unexpected situation. We should
* be able to recover from these though, so don't panic.
*/
static void _qemud_char_client_close(void* opaque) {
qemud_char_client* const data = static_cast<qemud_char_client*>(opaque);
QemudClient* client = data->client;
/* At this point modem driver still uses char pipe to communicate with
* hw-qemud, while communication with the guest is done over qemu pipe.
* So, when guest disconnects from the qemu pipe, and emulator-side client
* goes through the disconnection process, this routine is called, since it
* has been set to called during service registration. Unless modem driver
* is changed to drop char pipe communication, this routine will be called
* due to guest disconnection. As long as the client was a qemu pipe - based
* client, it's fine, since we don't really need to do anything in this case.
*/
if (!qemud_is_pipe_client(client)) {
derror("unexpected qemud char. channel close");
}
}
/* called by the charpipe to know how much data can be read from
* the user. Since we send everything directly to the serial port
* we can return an arbitrary number.
*/
static int _qemud_char_service_can_read(void* opaque) {
return 8192; /* whatever */
}
/* called to read data from the charpipe and send it to the client.
* used qemud_service_broadcast() even if there is a single client
* because we don't need a QemudCharClient object this way.
*/
static void _qemud_char_service_read(void* opaque, const uint8_t* from, int len) {
QemudService* sv = static_cast<QemudService*>(opaque);
qemud_service_broadcast(sv, from, len);
}
/* called when a qemud client tries to connect to a char. service.
* we simply create a new client and open the charpipe to receive
* data from it.
*/
static QemudClient* _qemud_char_service_connect(void* opaque,
QemudService* sv,
int channel,
const char* client_param) {
CSerialLine* sl = static_cast<CSerialLine*>(opaque);
qemud_char_client* const data = new qemud_char_client();
data->sl = sl;
data->client = qemud_client_new(sv, channel, client_param,
data,
_qemud_char_client_recv,
_qemud_char_client_close,
NULL, NULL);
/* now we can open the gates :-) */
android_serialline_addhandlers(sl, sv,
_qemud_char_service_can_read,
_qemud_char_service_read);
return data->client;
}
static int android_qemud_channel_connect(const char* name, CSerialLine* sl) {
qemud_service_register(name, 1, sl, _qemud_char_service_connect, NULL, NULL);
return 0;
}
/* returns a charpipe endpoint that can be used by an emulated
* device or external serial port to implement a char. service
*/
int android_qemud_get_channel(const char* name, CSerialLine** psl) {
CSerialLine* sl;
if (android_serialline_pipe_open(&sl, psl) < 0) {
derror("can't open charpipe for '%s' qemud service", name);
return -1;
}
return android_qemud_channel_connect(name, sl);
}
/* set the character driver state for a given qemud communication channel. this
* is used to attach the channel to an external char driver device directly.
* returns 0 on success, -1 on error
*/
int android_qemud_set_channel(const char* name, CSerialLine* peer_sl) {
CSerialLine* buffer = android_serialline_buffer_open(peer_sl);
if (!buffer) {
return -1;
}
return android_qemud_channel_connect(name, buffer);
}