aboutsummaryrefslogtreecommitdiffstats
path: root/android/protocol/fb-updates-impl.c
diff options
context:
space:
mode:
authorVladimir Chtchetkine <vchtchetkine@google.com>2011-01-31 10:49:06 -0800
committerVladimir Chtchetkine <vchtchetkine@google.com>2011-01-31 10:49:06 -0800
commit94a2fba98924c6684650d66409934358cb0c9d09 (patch)
tree61438a0563eb274091cf81fe6136a35973495e8f /android/protocol/fb-updates-impl.c
parentf988ac730fecefbd7590d3a8068c9e80fc53e026 (diff)
downloadexternal_qemu-94a2fba98924c6684650d66409934358cb0c9d09.zip
external_qemu-94a2fba98924c6684650d66409934358cb0c9d09.tar.gz
external_qemu-94a2fba98924c6684650d66409934358cb0c9d09.tar.bz2
Refactor the framebuffer service
Change-Id: I8ac4580af65b8d58976c97b77b309dd202e75003
Diffstat (limited to 'android/protocol/fb-updates-impl.c')
-rw-r--r--android/protocol/fb-updates-impl.c264
1 files changed, 264 insertions, 0 deletions
diff --git a/android/protocol/fb-updates-impl.c b/android/protocol/fb-updates-impl.c
new file mode 100644
index 0000000..ebe7309
--- /dev/null
+++ b/android/protocol/fb-updates-impl.c
@@ -0,0 +1,264 @@
+/* Copyright (C) 2010 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.
+*/
+
+/*
+ * Contains UI-side framebuffer client that receives framebuffer updates
+ * from the core.
+ */
+
+#include "sysemu.h"
+#include "android/utils/system.h"
+#include "android/utils/debug.h"
+#include "android/utils/panic.h"
+#include "android/sync-utils.h"
+#include "android/protocol/fb-updates.h"
+#include "android/protocol/fb-updates-impl.h"
+
+/*Enumerates states for the client framebuffer update reader. */
+typedef enum ImplFBState {
+ /* The reader is waiting on update header. */
+ EXPECTS_HEADER,
+
+ /* The reader is waiting on pixels. */
+ EXPECTS_PIXELS,
+} ImplFBState;
+
+/* Descriptor for the UI-side implementation of the "framebufer" service.
+ */
+typedef struct ImplFramebuffer {
+ /* Framebuffer for this client. */
+ QFrameBuffer* fb;
+
+ /* Core connection instance for the framebuffer client. */
+ CoreConnection* core_connection;
+
+ /* Current update header. */
+ FBUpdateMessage update_header;
+
+ /* Reader's buffer. */
+ uint8_t* reader_buffer;
+
+ /* Offset in the reader's buffer where to read next chunk of data. */
+ size_t reader_offset;
+
+ /* Total number of bytes the reader expects to read. */
+ size_t reader_bytes;
+
+ /* Current state of the update reader. */
+ ImplFBState fb_state;
+
+ /* Socket descriptor for the framebuffer client. */
+ int sock;
+
+ /* Number of bits used to encode single pixel. */
+ int bits_per_pixel;
+} ImplFramebuffer;
+
+/* One and the only ImplFramebuffer instance. */
+static ImplFramebuffer _implFb;
+
+/*
+ * Updates a display rectangle.
+ * Param
+ * fb - Framebuffer where to update the rectangle.
+ * x, y, w, and h define rectangle to update.
+ * bits_per_pixel define number of bits used to encode a single pixel.
+ * pixels contains pixels for the rectangle. Buffer addressed by this parameter
+ * must be eventually freed with free()
+ */
+static void
+_update_rect(QFrameBuffer* fb, uint16_t x, uint16_t y, uint16_t w, uint16_t h,
+ uint8_t bits_per_pixel, uint8_t* pixels)
+{
+ if (fb != NULL) {
+ uint16_t n;
+ const uint8_t* src = pixels;
+ const uint16_t src_line_size = w * ((bits_per_pixel + 7) / 8);
+ uint8_t* dst = (uint8_t*)fb->pixels + y * fb->pitch + x *
+ fb->bytes_per_pixel;
+ for (n = 0; n < h; n++) {
+ memcpy(dst, src, src_line_size);
+ src += src_line_size;
+ dst += fb->pitch;
+ }
+ qframebuffer_update(fb, x, y, w, h);
+ }
+ free(pixels);
+}
+
+/*
+ * Asynchronous I/O callback launched when framebuffer notifications are ready
+ * to be read.
+ * Param:
+ * opaque - ImplFramebuffer instance.
+ */
+static void
+_implFb_read_cb(void* opaque)
+{
+ ImplFramebuffer* fb_client = opaque;
+ int ret;
+
+ // Read updates while they are immediately available.
+ for (;;) {
+ // Read next chunk of data.
+ ret = read(fb_client->sock, fb_client->reader_buffer + fb_client->reader_offset,
+ fb_client->reader_bytes - fb_client->reader_offset);
+ if (ret == 0) {
+ /* disconnection ! */
+ implFb_destroy();
+ return;
+ }
+ if (ret < 0) {
+ if (errno == EINTR) {
+ /* loop on EINTR */
+ continue;
+ } else if (errno == EWOULDBLOCK || errno == EAGAIN) {
+ // Chunk is not avalable at this point. Come back later.
+ return;
+ }
+ }
+
+ fb_client->reader_offset += ret;
+ if (fb_client->reader_offset != fb_client->reader_bytes) {
+ // There are still some data left in the pipe.
+ continue;
+ }
+
+ // All expected data has been read. Time to change the state.
+ if (fb_client->fb_state == EXPECTS_HEADER) {
+ // Update header has been read. Prepare for the pixels.
+ fb_client->fb_state = EXPECTS_PIXELS;
+ fb_client->reader_offset = 0;
+ fb_client->reader_bytes = fb_client->update_header.w *
+ fb_client->update_header.h *
+ (fb_client->bits_per_pixel / 8);
+ fb_client->reader_buffer = malloc(fb_client->reader_bytes);
+ if (fb_client->reader_buffer == NULL) {
+ APANIC("Unable to allocate memory for framebuffer update\n");
+ }
+ } else {
+ // Pixels have been read. Prepare for the header.
+ uint8_t* pixels = fb_client->reader_buffer;
+
+ fb_client->fb_state = EXPECTS_HEADER;
+ fb_client->reader_offset = 0;
+ fb_client->reader_bytes = sizeof(FBUpdateMessage);
+ fb_client->reader_buffer = (uint8_t*)&fb_client->update_header;
+
+ // Perform the update. Note that pixels buffer must be freed there.
+ _update_rect(fb_client->fb, fb_client->update_header.x,
+ fb_client->update_header.y, fb_client->update_header.w,
+ fb_client->update_header.h, fb_client->bits_per_pixel,
+ pixels);
+ }
+ }
+}
+
+int
+implFb_create(SockAddress* console_socket, const char* protocol, QFrameBuffer* fb)
+{
+ char* handshake = NULL;
+ char switch_cmd[256];
+
+ // Initialize descriptor.
+ _implFb.fb = fb;
+ _implFb.reader_buffer = (uint8_t*)&_implFb.update_header;
+ _implFb.reader_offset = 0;
+ _implFb.reader_bytes = sizeof(FBUpdateMessage);
+
+ // Connect to the framebuffer service.
+ snprintf(switch_cmd, sizeof(switch_cmd), "framebuffer %s", protocol);
+ _implFb.core_connection =
+ core_connection_create_and_switch(console_socket, switch_cmd, &handshake);
+ if (_implFb.core_connection == NULL) {
+ derror("Unable to connect to the framebuffer service: %s\n",
+ errno_str);
+ return -1;
+ }
+
+ // We expect core framebuffer to return us bits per pixel property in
+ // the handshake message.
+ _implFb.bits_per_pixel = 0;
+ if (handshake != NULL) {
+ char* bpp = strstr(handshake, "bitsperpixel=");
+ if (bpp != NULL) {
+ char* end;
+ bpp += strlen("bitsperpixel=");
+ end = strchr(bpp, ' ');
+ if (end == NULL) {
+ end = bpp + strlen(bpp);
+ }
+ _implFb.bits_per_pixel = strtol(bpp, &end, 0);
+ }
+ }
+ if (!_implFb.bits_per_pixel) {
+ derror("Unexpected core framebuffer reply: %s\n"
+ "Bits per pixel property is not there, or is invalid\n",
+ handshake);
+ implFb_destroy();
+ return -1;
+ }
+
+ _implFb.sock = core_connection_get_socket(_implFb.core_connection);
+
+ // At last setup read callback, and start receiving the updates.
+ if (qemu_set_fd_handler(_implFb.sock, _implFb_read_cb, NULL, &_implFb)) {
+ derror("Unable to set up framebuffer read callback.\n");
+ implFb_destroy();
+ return -1;
+ }
+ {
+ // Force the core to send us entire framebuffer now, when we're prepared
+ // to receive it.
+ FBRequestHeader hd;
+ SyncSocket* sk = syncsocket_init(_implFb.sock);
+
+ hd.request_type = AFB_REQUEST_REFRESH;
+ syncsocket_start_write(sk);
+ syncsocket_write(sk, &hd, sizeof(hd), 5000);
+ syncsocket_stop_write(sk);
+ syncsocket_free(sk);
+ }
+
+ fprintf(stdout, "framebuffer is now connected to the core at %s.",
+ sock_address_to_string(console_socket));
+ if (handshake != NULL) {
+ if (handshake[0] != '\0') {
+ fprintf(stdout, " Handshake: %s", handshake);
+ }
+ free(handshake);
+ }
+ fprintf(stdout, "\n");
+
+ return 0;
+}
+
+void
+implFb_destroy(void)
+{
+ if (_implFb.core_connection != NULL) {
+ // Disable the reader callback.
+ qemu_set_fd_handler(_implFb.sock, NULL, NULL, NULL);
+
+ // Close framebuffer connection.
+ core_connection_close(_implFb.core_connection);
+ core_connection_free(_implFb.core_connection);
+ _implFb.core_connection = NULL;
+ }
+
+ _implFb.fb = NULL;
+ if (_implFb.reader_buffer != NULL &&
+ _implFb.reader_buffer != (uint8_t*)&_implFb.update_header) {
+ free(_implFb.reader_buffer);
+ _implFb.reader_buffer = (uint8_t*)&_implFb.update_header;
+ }
+}