From e95660aadc669784406d5f5a867988b8ecc2ed0d Mon Sep 17 00:00:00 2001 From: Vladimir Chtchetkine Date: Mon, 20 Dec 2010 08:28:03 -0800 Subject: Resubmit framebuffer service implementation Change-Id: I184e27a1e8d88835bc9f0502eccfa3f64a7aaf9e --- Makefile.android | 2 + android/async-utils.h | 6 +- android/console.c | 77 ++++++++++++- android/core-connection.c | 6 + android/core-connection.h | 8 ++ android/display-core.c | 48 ++++++-- android/display-core.h | 22 +++- android/framebuffer-common.h | 37 +++++++ android/framebuffer-core.c | 258 +++++++++++++++++++++++++++++++++++++++++++ android/framebuffer-core.h | 55 +++++++++ android/framebuffer-ui.c | 223 +++++++++++++++++++++++++++++++++++++ android/framebuffer-ui.h | 49 ++++++++ android/looper-qemu.c | 6 +- android/main-ui.c | 14 ++- android/sync-utils.c | 6 + android/sync-utils.h | 8 ++ framebuffer.c | 35 +++++- framebuffer.h | 2 + vl-android-ui.c | 8 ++ vl-android.c | 2 +- 20 files changed, 850 insertions(+), 22 deletions(-) create mode 100644 android/framebuffer-common.h create mode 100644 android/framebuffer-core.c create mode 100644 android/framebuffer-core.h create mode 100644 android/framebuffer-ui.c create mode 100644 android/framebuffer-ui.h diff --git a/Makefile.android b/Makefile.android index 565aa6a..7849775 100644 --- a/Makefile.android +++ b/Makefile.android @@ -1045,6 +1045,7 @@ VL_SOURCES := framebuffer.c \ android/looper-qemu.c \ android/looper-generic.c \ android/display-core.c \ + android/framebuffer-core.c \ # Add common system libraries # @@ -1201,6 +1202,7 @@ VL_SOURCES := framebuffer.c \ vl-android-ui.c \ console-ui.c \ iolooper-select.c \ + android/framebuffer-ui.c \ # Add common system libraries # diff --git a/android/async-utils.h b/android/async-utils.h index e34e1bb..0b52f37 100644 --- a/android/async-utils.h +++ b/android/async-utils.h @@ -80,8 +80,8 @@ typedef struct { size_t pos; } AsyncWriter; -/* Setup an ASyncReader, by giving the address of the read buffer, - * and the number of bytes we want to read. +/* Setup an ASyncWriter, by giving the address of the write buffer, + * and the number of bytes we want to write. * * This also calls loopIo_wantWrite(io) for you. */ @@ -100,7 +100,7 @@ void asyncWriter_init(AsyncWriter* aw, * ECONNRESET in case of disconnection. * * ASYNC_NEED_MORE: If not all bytes could be sent yet (or if 'events' - * doesn't contain LOOP_IO_READ). + * doesn't contain LOOP_IO_WRITE). */ AsyncStatus asyncWriter_write(AsyncWriter* aw, LoopIo* io); diff --git a/android/console.c b/android/console.c index 68b9480..398218a 100644 --- a/android/console.c +++ b/android/console.c @@ -25,7 +25,6 @@ #include "qemu-char.h" #include "sysemu.h" #include "android/android.h" -#include "sockets.h" #include "cpu.h" #include "hw/goldfish_device.h" #include "hw/power_supply.h" @@ -52,6 +51,8 @@ #include "android/keycode-array.h" #include "android/charmap.h" #include "android/core-ui-protocol.h" +#include "android/display-core.h" +#include "android/framebuffer-core.h" #if defined(CONFIG_SLIRP) #include "libslirp.h" @@ -112,9 +113,14 @@ typedef struct ControlGlobalRec_ } ControlGlobalRec; +#ifdef CONFIG_STANDALONE_CORE /* UI client currently attached to the core. */ ControlClient attached_ui_client = NULL; +/* Core framebuffer service client. */ +ControlClient framebuffer_client = NULL; +#endif // CONFIG_STANDALONE_CORE + static int control_global_add_redir( ControlGlobal global, int host_port, @@ -214,10 +220,21 @@ control_client_destroy( ControlClient client ) D(( "destroying control client %p\n", client )); +#ifdef CONFIG_STANDALONE_CORE if (client == attached_ui_client) { attached_ui_client = NULL; } + if (client == framebuffer_client) { + CoreFramebuffer* core_fb = coredisplay_detach_fb_service(); + if (core_fb != NULL) { + corefb_destroy(core_fb); + AFREE(core_fb); + } + framebuffer_client = NULL; + } +#endif // CONFIG_STANDALONE_CORE + sock = control_client_detach( client ); if (sock >= 0) socket_close(sock); @@ -2436,6 +2453,7 @@ do_qemu_monitor( ControlClient client, char* args ) return 0; } +#ifdef CONFIG_STANDALONE_CORE /* UI settings, passed to the core via -ui-settings command line parameter. */ extern char* android_op_ui_settings; @@ -2463,16 +2481,73 @@ do_attach_ui( ControlClient client, char* args ) return 0; } +/* Core display instance. */ +extern CoreDisplay core_display; + +static int +do_create_framebuffer_service( ControlClient client, char* args ) +{ + CoreFramebuffer* core_fb; + const char* protocol = "-raw"; // Default framebuffer exchange protocol. + + // Protocol type is defined by the arguments passed with the stream switch + // command. + if (args != NULL && *args != '\0') { + size_t token_len; + const char* param_end = strchr(args, ' '); + if (param_end == NULL) { + param_end = args + strlen(args); + } + token_len = param_end - args; + protocol = args; + + // Make sure that this is one of the supported protocols. + if (strncmp(protocol, "-raw", token_len) && + strncmp(protocol, "-shared", token_len)) { + derror("Invalid framebuffer parameter %s\n", protocol); + control_write( client, "KO: Invalid parameter\r\n" ); + control_client_destroy(client); + return -1; + } + } + + // Make sure that there are no framebuffer client already existing. + if (framebuffer_client != NULL) { + control_write( client, "KO: Another framebuffer service is already existing!\r\n" ); + control_client_destroy(client); + return -1; + } + + core_fb = corefb_create(client->sock, protocol); + if (!coredisplay_attach_fb_service(core_fb)) { + framebuffer_client = client; + control_write( client, "OK\r\n"); + } else { + control_write( client, "KO\r\n" ); + control_client_destroy(client); + return -1; + } + + return 0; +} +#endif // CONFIG_STANDALONE_CORE + static const CommandDefRec qemu_commands[] = { { "monitor", "enter QEMU monitor", "Enter the QEMU virtual machine monitor\r\n", NULL, do_qemu_monitor, NULL }, +#ifdef CONFIG_STANDALONE_CORE { "attach UI", "attach UI to the core", "Attach UI to the core\r\n", NULL, do_attach_ui, NULL }, + { "framebuffer", "create framebuffer service", + "Create framebuffer service\r\n", + NULL, do_create_framebuffer_service, NULL }, +#endif // CONFIG_STANDALONE_CORE + { NULL, NULL, NULL, NULL, NULL, NULL } }; diff --git a/android/core-connection.c b/android/core-connection.c index 6e05be0..f3950a7 100644 --- a/android/core-connection.c +++ b/android/core-connection.c @@ -321,3 +321,9 @@ core_connection_detach(CoreConnection* desc) { core_connection_write(desc, "\n", 1, NULL); } + +int +core_connection_get_socket(CoreConnection* desc) +{ + return (desc != NULL) ? syncsocket_get_socket(desc->ssocket) : -1; +} diff --git a/android/core-connection.h b/android/core-connection.h index fc1be43..19e91a1 100644 --- a/android/core-connection.h +++ b/android/core-connection.h @@ -130,4 +130,12 @@ int core_connection_switch_stream(CoreConnection* desc, */ void core_connection_detach(CoreConnection* desc); +/* Gets socket descriptor associated with the core connection. + * Param: + * desc Console client descriptor opened with core_connection_open. + * Return + * Socket descriptor associated with the core connection. + */ +int core_connection_get_socket(CoreConnection* desc); + #endif // QEMU_ANDROID_CORE_CONNECTION_H diff --git a/android/display-core.c b/android/display-core.c index e0574a3..70e7b14 100644 --- a/android/display-core.c +++ b/android/display-core.c @@ -21,14 +21,17 @@ /* Core display descriptor. */ struct CoreDisplay { /* Display state for this core display. */ - DisplayState* ds; + DisplayState* ds; /* Framebuffer for this core display. */ - QFrameBuffer* fb; + QFrameBuffer* fb; + + /* Framebuffer service associated with this core display. */ + CoreFramebuffer* core_fb; }; /* One and only one core display instance. */ -static CoreDisplay core_display; +CoreDisplay core_display; /* * Framebuffer calls this routine when it detects changes. This routine will @@ -36,8 +39,12 @@ static CoreDisplay core_display; * See QFrameBufferUpdateFunc in framebuffer.h for more info on this callback. */ static void -core_display_fb_update(void* opaque, int x, int y, int w, int h) +coredisplay_fb_update(void* opaque, int x, int y, int w, int h) { + CoreDisplay* cd = (CoreDisplay*)opaque; + if (cd->core_fb) { + corefb_update(cd->core_fb, cd->ds, cd->fb, x, y, w, h); + } } /* @@ -45,7 +52,7 @@ core_display_fb_update(void* opaque, int x, int y, int w, int h) * info on this callback. */ static void -core_display_fb_rotate(void* opaque, int rotation) +coredisplay_fb_rotate(void* opaque, int rotation) { } @@ -54,7 +61,7 @@ core_display_fb_rotate(void* opaque, int rotation) * info on this callback. */ static void -core_display_fb_poll(void* opaque) +coredisplay_fb_poll(void* opaque) { // This will eventually call core_display_fb_update. qframebuffer_check_updates(); @@ -65,18 +72,19 @@ core_display_fb_poll(void* opaque) * info on this callback. */ static void -core_display_fb_done(void* opaque) +coredisplay_fb_done(void* opaque) { } void -core_display_init(DisplayState* ds) +coredisplay_init(DisplayState* ds) { core_display.ds = ds; /* Create and initialize framebuffer instance that will be used for core * display. */ ANEW0(core_display.fb); + core_display.core_fb = NULL; qframebuffer_init(core_display.fb, ds->surface->width, ds->surface->height, 0, QFRAME_BUFFER_RGB565 ); qframebuffer_fifo_add(core_display.fb); @@ -85,7 +93,27 @@ core_display_init(DisplayState* ds) * core all framebuffer callbacks are essentially no-ops. */ qframebuffer_add_client(core_display.fb, &core_display, - core_display_fb_update, core_display_fb_rotate, - core_display_fb_poll, core_display_fb_done); + coredisplay_fb_update, coredisplay_fb_rotate, + coredisplay_fb_poll, coredisplay_fb_done); android_display_init(ds, core_display.fb); } + +int +coredisplay_attach_fb_service(CoreFramebuffer* core_fb) +{ + if (core_display.core_fb == NULL) { + core_display.core_fb = core_fb; + return 0; + } else { + return -1; + } +} + +CoreFramebuffer* +coredisplay_detach_fb_service(void) +{ + CoreFramebuffer* ret = core_display.core_fb; + core_display.core_fb = NULL; + return ret; +} + diff --git a/android/display-core.h b/android/display-core.h index 9a44f9a..02301c5 100644 --- a/android/display-core.h +++ b/android/display-core.h @@ -18,8 +18,9 @@ #ifndef _ANDROID_DISPLAY_CORE_H #define _ANDROID_DISPLAY_CORE_H -#include "android/display.h" #include "framebuffer.h" +#include "android/display.h" +#include "android/framebuffer-core.h" /* Descriptor for a core display instance */ typedef struct CoreDisplay CoreDisplay; @@ -29,6 +30,23 @@ typedef struct CoreDisplay CoreDisplay; * Param: * ds - Display state to use for the core display. */ -extern void core_display_init(DisplayState* ds); +extern void coredisplay_init(DisplayState* ds); + +/* + * Attaches framebuffer service to the core display. + * Param: + * core_fb - Framebuffer service descriptor to attach. + * Return: + * 0 on success, or -1 on failure. + */ +extern int coredisplay_attach_fb_service(CoreFramebuffer* core_fb); + +/* + * Detaches framebuffer service previously attached to the core display. + * Return: + * Framebuffer service descriptor attached to the core display, or NULL if + * the core display didn't have framebuffer service attached to it. + */ +extern CoreFramebuffer* coredisplay_detach_fb_service(void); #endif /* _ANDROID_DISPLAY_CORE_H */ diff --git a/android/framebuffer-common.h b/android/framebuffer-common.h new file mode 100644 index 0000000..7ca654c --- /dev/null +++ b/android/framebuffer-common.h @@ -0,0 +1,37 @@ +/* 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 framebuffer declarations that are shared by the core and the UI. + */ + +#ifndef _ANDROID_FRAMEBUFFER_COMMON_H +#define _ANDROID_FRAMEBUFFER_COMMON_H + +#include "sysemu.h" + +/* Header of framebuffer update message sent from the core to the UI. */ +typedef struct FBUpdateMessage { + /* x, y, w, and h identify the rectangle that is being updated. */ + uint16_t x; + uint16_t y; + uint16_t w; + uint16_t h; + + /* Number of bits used to encode a single pixel. */ + uint8_t bits_per_pixel; + + /* Contains updating rectangle copied over from the framebuffer's pixels. */ + uint8_t rect[0]; +} FBUpdateMessage; + +#endif /* _ANDROID_FRAMEBUFFER_UI_H */ diff --git a/android/framebuffer-core.c b/android/framebuffer-core.c new file mode 100644 index 0000000..a473415 --- /dev/null +++ b/android/framebuffer-core.c @@ -0,0 +1,258 @@ +/* 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 core-side framebuffer service that sends framebuffer updates + * to the UI connected to the core. + */ + +#include "console.h" +#include "framebuffer.h" +#include "android/looper.h" +#include "android/display-core.h" +#include "android/async-utils.h" +#include "android/framebuffer-common.h" +#include "android/framebuffer-core.h" +#include "android/utils/system.h" +#include "android/utils/debug.h" + +/* Core framebuffer descriptor. */ +struct CoreFramebuffer { + /* Writer used to send FB update notification messages. */ + AsyncWriter fb_update_writer; + + /* I/O associated with this descriptor. */ + LoopIo io; + + /* Looper used to communicate framebuffer updates. */ + Looper* looper; + + /* Head of the list of pending FB update notifications. */ + struct FBUpdateNotify* fb_update_head; + + /* Tail of the list of pending FB update notifications. */ + struct FBUpdateNotify* fb_update_tail; + + /* Socket used to communicate framebuffer updates. */ + int sock; +}; + +/* Framebuffer update notification descriptor to the core. */ +typedef struct FBUpdateNotify { + /* Links all pending FB update notifications. */ + struct FBUpdateNotify* next_fb_update; + + /* Core framebuffer instance that owns the message. */ + CoreFramebuffer* core_fb; + + /* Size of the message to transfer. */ + size_t message_size; + + /* Update message. */ + FBUpdateMessage message; +} FBUpdateNotify; + +/* + * Gets pointer in framebuffer's pixels for the given pixel. + * Param: + * fb Framebuffer containing pixels. + * x, and y identify the pixel to get pointer for. + * Return: + * Pointer in framebuffer's pixels for the given pixel. + */ +static const uint8_t* +_pixel_offset(const QFrameBuffer* fb, int x, int y) +{ + return (const uint8_t*)fb->pixels + y * fb->pitch + x * fb->bytes_per_pixel; +} + +/* + * Copies pixels from a framebuffer rectangle. + * Param: + * rect - Buffer where to copy pixel. + * fb - Framebuffer containing the rectangle to copy. + * x, y, w, and h - dimensions of the rectangle to copy. + */ +static void +_copy_fb_rect(uint8_t* rect, const QFrameBuffer* fb, int x, int y, int w, int h) +{ + const uint8_t* start = _pixel_offset(fb, x, y); + for (; h > 0; h--) { + memcpy(rect, start, w * fb->bytes_per_pixel); + start += fb->pitch; + rect += w * fb->bytes_per_pixel; + } +} + +/* + * Allocates and initializes framebuffer update notification descriptor. + * Param: + * ds - Display state for the framebuffer. + * fb Framebuffer containing pixels. + * x, y, w, and h identify the rectangle that is being updated. + * Return: + * Initialized framebuffer update notification descriptor. + */ +static FBUpdateNotify* +fbupdatenotify_create(CoreFramebuffer* core_fb, const QFrameBuffer* fb, + int x, int y, int w, int h) +{ + const size_t rect_size = w * h * fb->bytes_per_pixel; + FBUpdateNotify* ret = malloc(sizeof(FBUpdateNotify) + rect_size); + + ret->next_fb_update = NULL; + ret->core_fb = core_fb; + ret->message_size = sizeof(FBUpdateMessage) + rect_size; + ret->message.x = x; + ret->message.y = y; + ret->message.w = w; + ret->message.h = h; + ret->message.bits_per_pixel = fb->bits_per_pixel; + _copy_fb_rect(ret->message.rect, fb, x, y, w, h); + return ret; +} + +/* + * Deletes FBUpdateNotify descriptor, created with fbupdatenotify_create. + * Param: + * desc - Descreptor to delete. + */ +static void +fbupdatenotify_delete(FBUpdateNotify* desc) +{ + if (desc != NULL) { + free(desc); + } +} + +/* + * Asynchronous I/O callback launched when writing framebuffer notifications + * to the socket. + * Param: + * opaque - CoreFramebuffer instance. + */ +static void +corefb_io_func(void* opaque, int fd, unsigned events) +{ + CoreFramebuffer* core_fb = opaque; + + while (core_fb->fb_update_head != NULL) { + FBUpdateNotify* current_update = core_fb->fb_update_head; + // Lets continue writing of the current notification. + const AsyncStatus status = + asyncWriter_write(&core_fb->fb_update_writer, &core_fb->io); + switch (status) { + case ASYNC_COMPLETE: + // Done with the current update. Move on to the next one. + break; + case ASYNC_ERROR: + // Done with the current update. Move on to the next one. + loopIo_dontWantWrite(&core_fb->io); + break; + + case ASYNC_NEED_MORE: + // Transfer will eventually come back into this routine. + return; + } + + // Advance the list of updates + core_fb->fb_update_head = current_update->next_fb_update; + if (core_fb->fb_update_head == NULL) { + core_fb->fb_update_tail = NULL; + } + fbupdatenotify_delete(current_update); + + if (core_fb->fb_update_head != NULL) { + // Schedule the next one. + asyncWriter_init(&core_fb->fb_update_writer, + &core_fb->fb_update_head->message, + core_fb->fb_update_head->message_size, + &core_fb->io); + } + } +} + +CoreFramebuffer* +corefb_create(int sock, const char* protocol) +{ + // At this point we're implementing the -raw protocol only. + CoreFramebuffer* ret; + ANEW0(ret); + ret->sock = sock; + ret->looper = looper_newCore(); + loopIo_init(&ret->io, ret->looper, sock, corefb_io_func, ret); + ret->fb_update_head = NULL; + ret->fb_update_tail = NULL; + return ret; +} + +void +corefb_destroy(CoreFramebuffer* core_fb) +{ + if (core_fb != NULL) { + if (core_fb->looper != NULL) { + // Stop all I/O that may still be going on. + loopIo_done(&core_fb->io); + // Delete all pending frame updates. + while (core_fb->fb_update_head != NULL) { + FBUpdateNotify* pending_update = core_fb->fb_update_head; + core_fb->fb_update_head = pending_update->next_fb_update; + fbupdatenotify_delete(pending_update); + } + core_fb->fb_update_tail = NULL; + looper_free(core_fb->looper); + core_fb->looper = NULL; + } + } +} + +void +corefb_update(CoreFramebuffer* core_fb, struct DisplayState* ds, + struct QFrameBuffer* fb, int x, int y, int w, int h) +{ + AsyncStatus status; + FBUpdateNotify* descr = fbupdatenotify_create(core_fb, fb, x, y, w, h); + + printf("Update framebuffer (%u bytes): %d:%d - %d:%d ", + descr->message_size, x, y, w, h); + + // Lets see if we should list it behind other pending updates. + if (core_fb->fb_update_tail != NULL) { + core_fb->fb_update_tail->next_fb_update = descr; + core_fb->fb_update_tail = descr; + printf("PENDED\n"); + return; + } + + // We're first in the list. Just send it now. + core_fb->fb_update_head = core_fb->fb_update_tail = descr; + asyncWriter_init(&core_fb->fb_update_writer, + &core_fb->fb_update_head->message, + core_fb->fb_update_head->message_size, &core_fb->io); + status = asyncWriter_write(&core_fb->fb_update_writer, &core_fb->io); + switch (status) { + case ASYNC_COMPLETE: + fbupdatenotify_delete(descr); + core_fb->fb_update_head = core_fb->fb_update_tail = NULL; + printf("COMPLETED\n"); + return; + case ASYNC_ERROR: + printf("FAILED: %s\n", errno_str); + fbupdatenotify_delete(descr); + core_fb->fb_update_head = core_fb->fb_update_tail = NULL; + return; + case ASYNC_NEED_MORE: + // Update transfer will eventually complete in corefb_io_func + printf("PARTIAL\n"); + return; + } +} diff --git a/android/framebuffer-core.h b/android/framebuffer-core.h new file mode 100644 index 0000000..6738d0e --- /dev/null +++ b/android/framebuffer-core.h @@ -0,0 +1,55 @@ +/* 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 core-side framebuffer service that sends framebuffer updates + * to the UI connected to the core. + */ + +#ifndef _ANDROID_FRAMEBUFFER_CORE_H +#define _ANDROID_FRAMEBUFFER_CORE_H + +/* Descriptor for a framebuffer core service instance */ +typedef struct CoreFramebuffer CoreFramebuffer; + +/* + * Creates framebuffer service. + * Param: + * sock - Socket descriptor for the service + * protocol - Defines protocol to use when sending FB updates to the UI. The + * supported values ar: + * -raw Transfers the updating rectangle buffer over the socket. + * -shared Used a shared memory to transfer the updating rectangle buffer. + * Return: + * Framebuffer service descriptor. + */ +CoreFramebuffer* corefb_create(int sock, const char* protocol); + +/* + * Destroys framebuffer service created with corefb_create. + * Param: + * core_fb - Framebuffer service descriptor created with corefb_create + */ +void corefb_destroy(CoreFramebuffer* core_fb); + +/* + * Notifies framebuffer client about changes in framebuffer. + * Param: + * core_fb - Framebuffer service descriptor created with corefb_create + * ds - Display state for the framebuffer. + * fb Framebuffer containing pixels. + * x, y, w, and h identify the rectangle that has benn changed. + */ +void corefb_update(CoreFramebuffer* core_fb, struct DisplayState* ds, + struct QFrameBuffer* fb, int x, int y, int w, int h); + +#endif /* _ANDROID_FRAMEBUFFER_CORE_H */ diff --git a/android/framebuffer-ui.c b/android/framebuffer-ui.c new file mode 100644 index 0000000..0402df0 --- /dev/null +++ b/android/framebuffer-ui.c @@ -0,0 +1,223 @@ +/* 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 "android/framebuffer-common.h" +#include "android/framebuffer-ui.h" +#include "android/utils/system.h" +#include "android/utils/debug.h" + +#define PANIC(...) do { fprintf(stderr, __VA_ARGS__); \ + exit(1); \ + } while (0) + +/* + * Enumerates states for the client framebuffer update reader. + */ +typedef enum ClientFBState { + /* The reader is waiting on update header. */ + WAIT_HEADER, + + /* The reader is waiting on pixels. */ + WAIT_PIXELS, +} ClientFBState; + +/* + * Descriptor for the framebuffer client. + */ +struct ClientFramebuffer { + /* 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. */ + ClientFBState fb_state; + + /* Socket descriptor for the framebuffer client. */ + int sock; +}; + +/* The only instance of framebuffer client. */ +static ClientFramebuffer _client_fb; + +/* + * Updates a desplay rectangle. + * Param + * 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() + */ +void +update_rect(uint16_t x, uint16_t y, uint16_t w, uint16_t h, + uint8_t bits_per_pixel, uint8_t* pixels) +{ + // TODO: Do the actual update! + printf("Update rectangle (%d bytes): %d:%d %d:%d\n", + w * h * (bits_per_pixel / 8), x, y, w, h); + free(pixels); +} + +/* + * Asynchronous I/O callback launched when framebuffer notifications are ready + * to be read. + * Param: + * opaque - ClientFramebuffer instance. + */ +static void +_clientfb_read_cb(void* opaque) +{ + ClientFramebuffer* 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 ! */ + clientfb_destroy(fb_client); + 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 == WAIT_HEADER) { + // Update header has been read. Prepare for the pixels. + fb_client->fb_state = WAIT_PIXELS; + fb_client->reader_offset = 0; + fb_client->reader_bytes = fb_client->update_header.w * + fb_client->update_header.h * + (fb_client->update_header.bits_per_pixel / 8); + fb_client->reader_buffer = malloc(fb_client->reader_bytes); + if (fb_client->reader_buffer == NULL) { + PANIC("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 = WAIT_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->update_header.x, fb_client->update_header.y, + fb_client->update_header.w, fb_client->update_header.h, + fb_client->update_header.bits_per_pixel, pixels); + } + } +} + +ClientFramebuffer* +clientfb_create(SockAddress* console_socket, const char* protocol) +{ + char* connect_message = NULL; + char switch_cmd[256]; + + // Connect to the framebuffer service. + _client_fb.core_connection = core_connection_create(console_socket); + if (_client_fb.core_connection == NULL) { + derror("Framebuffer client is unable to connect to the console: %s\n", + errno_str); + return NULL; + } + if (core_connection_open(_client_fb.core_connection)) { + core_connection_free(_client_fb.core_connection); + _client_fb.core_connection = NULL; + derror("Framebuffer client is unable to ioen the console: %s\n", + errno_str); + return NULL; + } + snprintf(switch_cmd, sizeof(switch_cmd), "framebuffer %s", protocol); + if (core_connection_switch_stream(_client_fb.core_connection, switch_cmd, + &connect_message)) { + derror("Unable to attach to the framebuffer %s: %s\n", + switch_cmd, connect_message ? connect_message : ""); + if (connect_message != NULL) { + free(connect_message); + } + core_connection_close(_client_fb.core_connection); + core_connection_free(_client_fb.core_connection); + _client_fb.core_connection = NULL; + return NULL; + } + if (connect_message != NULL) { + free(connect_message); + } + + // Now that we're connected lets initialize the descriptor. + _client_fb.sock = core_connection_get_socket(_client_fb.core_connection); + _client_fb.fb_state = WAIT_HEADER; + _client_fb.reader_buffer = (uint8_t*)&_client_fb.update_header; + _client_fb.reader_offset = 0; + _client_fb.reader_bytes = sizeof(FBUpdateMessage); + + // At last setup read callback, and start receiving the updates. + if (qemu_set_fd_handler(_client_fb.sock, _clientfb_read_cb, NULL, &_client_fb)) { + derror("Unable to set up framebuffer read callback\n"); + core_connection_close(_client_fb.core_connection); + core_connection_free(_client_fb.core_connection); + _client_fb.core_connection = NULL; + return NULL; + } + + fprintf(stdout, "Framebuffer %s is now attached to the core %s\n", + protocol, sock_address_to_string(console_socket)); + + return &_client_fb; +} + +void +clientfb_destroy(ClientFramebuffer* client_fb) +{ + if (client_fb != NULL && client_fb->core_connection != NULL) { + // Disable the reader callback. + qemu_set_fd_handler(client_fb->sock, NULL, NULL, NULL); + + // Close framebuffer connection. + core_connection_close(client_fb->core_connection); + core_connection_free(client_fb->core_connection); + client_fb->core_connection = NULL; + } +} diff --git a/android/framebuffer-ui.h b/android/framebuffer-ui.h new file mode 100644 index 0000000..188a3c1 --- /dev/null +++ b/android/framebuffer-ui.h @@ -0,0 +1,49 @@ +/* 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. + */ + +#ifndef _ANDROID_FRAMEBUFFER_UI_H +#define _ANDROID_FRAMEBUFFER_UI_H + +#include "console.h" +#include "android/looper.h" +#include "android/async-utils.h" +#include "android/core-connection.h" + +/* Descriptor for the framebuffer client. */ +typedef struct ClientFramebuffer ClientFramebuffer; + +/* + * Creates framebuffer client, and connects it with the core. + * Param: + * console_socket Address of the core's console socket. + * protocol Protocol to use for the updates: + * -raw Stream pixels over socket + * -shared Use shared memory for pixels. + * Return: + * Descriptor for the framebuffer client on success, or NULL on failure. + */ +ClientFramebuffer* clientfb_create(SockAddress* console_socket, + const char* protocol); + +/* + * Disconnects and destroys framebuffer client. + * Param: + * client_fb Framebuffer client descriptor created with clientfb_create. + */ +void clientfb_destroy(ClientFramebuffer* client_fb); + +#endif /* _ANDROID_FRAMEBUFFER_UI_H */ diff --git a/android/looper-qemu.c b/android/looper-qemu.c index 400f7c1..cce0bfa 100644 --- a/android/looper-qemu.c +++ b/android/looper-qemu.c @@ -199,13 +199,15 @@ qloopio_modify(QLoopIo* io, unsigned wanted) /* if we're pending, but the new mask doesn't care about * out state, remove from pending list */ - if (io->ready && (io->ready & wanted) == 0) + if (io->ready && (io->ready & wanted) == 0) { qloopio_removePending(io); + } /* recompute read/write handlers for QEMU */ IOHandler* fd_read = (wanted & LOOP_IO_READ) ? qloopio_handleRead : NULL; IOHandler* fd_write = (wanted & LOOP_IO_WRITE) ? qloopio_handleWrite : NULL; qemu_set_fd_handler(io->fd, fd_read, fd_write, io); + io->wanted = wanted; } static void @@ -312,7 +314,7 @@ qlooper_addPendingIo(QLooper* looper, QLoopIo* io) qemu_bh_schedule(looper->io_bh); } io->pendingNext = looper->io_pending; - looper->io_pending = io->pendingNext; + looper->io_pending = io; } static void diff --git a/android/main-ui.c b/android/main-ui.c index 5f3b8e9..043a9a8 100644 --- a/android/main-ui.c +++ b/android/main-ui.c @@ -61,6 +61,7 @@ #include "android/snapshot.h" #include "android/core-connection.h" +#include "android/framebuffer-ui.h" #include "framebuffer.h" #include "iolooper.h" @@ -103,6 +104,9 @@ unsigned long android_verbose; /* Instance of the "attach UI" Emulator's core console client. */ CoreConnection* attach_client = NULL; +/* Instance of the "framebuffer" console client. */ +ClientFramebuffer* fb_client = NULL; + /* -ui-settings parameters received from the core on UI attachment. */ char* core_ui_settings = ""; @@ -208,7 +212,7 @@ sdl_set_window_icon( void ) SDL_FreeSurface(icon); free( icon_pixels ); } -#endif /* !_WIN32 */ +#endif /* !_WIN32 */ } } @@ -921,7 +925,6 @@ attach_to_core(AndroidOptions* opts) { fprintf(stdout, "UI setting for the core%s:\n", core_ui_settings); } - return 0; } else { derror("Unable to attach to the core %s: %s\n", sock_address_to_string(&console_socket), @@ -939,6 +942,13 @@ attach_to_core(AndroidOptions* opts) { } else { return -1; } + + fb_client = clientfb_create(&console_socket, "-raw"); + if (fb_client == NULL) { + return -1; + } + + return 0; } int main(int argc, char **argv) diff --git a/android/sync-utils.c b/android/sync-utils.c index 8d55823..c5674b2 100644 --- a/android/sync-utils.c +++ b/android/sync-utils.c @@ -271,3 +271,9 @@ syncsocket_read_line(SyncSocket* ssocket, char* buffer, size_t size, int timeout return syncsocket_read_line_absolute(ssocket, buffer, size, iolooper_now() + timeout); } + +int +syncsocket_get_socket(SyncSocket* ssocket) +{ + return (ssocket != NULL) ? ssocket->fd : -1; +} diff --git a/android/sync-utils.h b/android/sync-utils.h index f522e27..726b310 100644 --- a/android/sync-utils.h +++ b/android/sync-utils.h @@ -205,4 +205,12 @@ ssize_t syncsocket_read_line(SyncSocket* ssocket, size_t size, int timeout); +/* Gets socket descriptor associated with the sync socket. + * Param: + * ssocket - SyncSocket descriptor obtained from syncsocket_connect routine. + * Return + * Socket descriptor associated with the sync socket. + */ +int syncsocket_get_socket(SyncSocket* ssocket); + #endif // ANDROID_SYNC_UTILS_H diff --git a/framebuffer.c b/framebuffer.c index 966ced5..225d60d 100644 --- a/framebuffer.c +++ b/framebuffer.c @@ -42,6 +42,29 @@ _get_pitch( int width, QFrameBufferFormat format ) } } +static int +_get_bits_per_pixel(QFrameBufferFormat format) +{ + + switch (format) { + case QFRAME_BUFFER_RGB565: + return 16; + default: + return -1; + } +} + +static int +_get_bytes_per_pixel(QFrameBufferFormat format) +{ + + switch (format) { + case QFRAME_BUFFER_RGB565: + return 2; + default: + return -1; + } +} int qframebuffer_init( QFrameBuffer* qfbuff, @@ -50,7 +73,7 @@ qframebuffer_init( QFrameBuffer* qfbuff, int rotation, QFrameBufferFormat format ) { - int pitch; + int pitch, bytes_per_pixel, bits_per_pixel; rotation &= 3; @@ -61,6 +84,14 @@ qframebuffer_init( QFrameBuffer* qfbuff, if (pitch < 0) return -1; + bits_per_pixel = _get_bits_per_pixel(format); + if (bits_per_pixel < 0) + return -1; + + bytes_per_pixel = _get_bytes_per_pixel(format); + if (bytes_per_pixel < 0) + return -1; + memset( qfbuff, 0, sizeof(*qfbuff) ); qfbuff->extra = calloc( 1, sizeof(QFrameBufferExtra) ); @@ -77,6 +108,8 @@ qframebuffer_init( QFrameBuffer* qfbuff, qfbuff->height = height; qfbuff->pitch = pitch; qfbuff->format = format; + qfbuff->bits_per_pixel = bits_per_pixel; + qfbuff->bytes_per_pixel = bytes_per_pixel; qframebuffer_set_dpi( qfbuff, DEFAULT_FRAMEBUFFER_DPI, DEFAULT_FRAMEBUFFER_DPI ); return 0; diff --git a/framebuffer.h b/framebuffer.h index b8c4580..0e502d1 100644 --- a/framebuffer.h +++ b/framebuffer.h @@ -47,6 +47,8 @@ struct QFrameBuffer { int width; /* width in pixels */ int height; /* height in pixels */ int pitch; /* bytes per line */ + int bits_per_pixel; /* bits per pixel */ + int bytes_per_pixel; /* bytes per pixel */ int rotation; /* rotation to be applied when displaying */ QFrameBufferFormat format; void* pixels; /* pixel buffer */ diff --git a/vl-android-ui.c b/vl-android-ui.c index 316902c..828590d 100644 --- a/vl-android-ui.c +++ b/vl-android-ui.c @@ -42,6 +42,7 @@ #include "android/utils/bufprint.h" #include "android/utils/system.h" #include "android/core-connection.h" +#include "android/framebuffer-ui.h" #ifdef CONFIG_MEMCHECK #include "memcheck/memcheck.h" @@ -205,6 +206,8 @@ extern void dprint( const char* format, ... ); /* Instance of the "attach UI" Emulator's core console client. */ extern CoreConnection* attach_client; +/* Instance of the "framebuffer" console client. */ +extern ClientFramebuffer* fb_client; #define TFR(expr) do { if ((expr) != -1) break; } while (errno == EINTR) @@ -600,6 +603,11 @@ int main(int argc, char **argv, char **envp) core_connection_free(attach_client); } + if (fb_client != NULL) { + clientfb_destroy(fb_client); + fb_client = NULL; + } + quit_timers(); return 0; } diff --git a/vl-android.c b/vl-android.c index 3f658d1..1b79404 100644 --- a/vl-android.c +++ b/vl-android.c @@ -5347,7 +5347,7 @@ int main(int argc, char **argv, char **envp) break; #elif defined(CONFIG_STANDALONE_CORE) case DT_SDL: - core_display_init(ds); + coredisplay_init(ds); break; #endif case DT_VNC: -- cgit v1.1