summaryrefslogtreecommitdiff
path: root/widget_provider/src/fb_wayland.c
diff options
context:
space:
mode:
Diffstat (limited to 'widget_provider/src/fb_wayland.c')
-rw-r--r--widget_provider/src/fb_wayland.c677
1 files changed, 677 insertions, 0 deletions
diff --git a/widget_provider/src/fb_wayland.c b/widget_provider/src/fb_wayland.c
new file mode 100644
index 0000000..1f9316b
--- /dev/null
+++ b/widget_provider/src/fb_wayland.c
@@ -0,0 +1,677 @@
+/*
+ * Copyright 2013 Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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 <stdio.h>
+#include <errno.h>
+#include <sys/mman.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <stddef.h>
+#include <sys/shm.h>
+#include <sys/ipc.h>
+
+#include <dlog.h>
+#include <widget_errno.h>
+#include <widget_service.h>
+#include <widget_buffer.h>
+#include <widget_util.h>
+
+#include <wayland-client.h>
+#include <tbm_bufmgr.h>
+
+#include "debug.h"
+#include "util.h"
+#include "widget_provider.h"
+#include "widget_provider_buffer.h"
+#include "provider_buffer_internal.h"
+#include "fb.h"
+#include "dlist.h"
+
+int errno;
+
+struct gem_data {
+ tbm_bo pixmap_bo;
+ int count;
+ int buf_count;
+ int w;
+ int h;
+ int depth;
+ void *data; /* Gem layer */
+ int refcnt;
+ int pixmap; /* FD in case of wayland */
+};
+
+static struct info {
+ tbm_bufmgr bufmgr;
+ int fd;
+ struct wl_display *disp;
+ int disp_is_opened;
+ int master_disconnected;
+ struct dlist *gem_list;
+} s_info = {
+ .bufmgr = NULL,
+ .fd = -1,
+ .disp = NULL,
+ .disp_is_opened = 0,
+ .master_disconnected = 0,
+ .gem_list = NULL,
+};
+
+int fb_init(void *disp)
+{
+ int ret;
+
+ s_info.disp = disp;
+ if (!s_info.disp) {
+ s_info.disp = wl_display_connect(NULL);
+ if (!s_info.disp) {
+ ErrPrint("Failed to open a display\n");
+ return WIDGET_ERROR_FAULT;
+ }
+
+ s_info.disp_is_opened = 1;
+ }
+
+ ret = widget_util_get_drm_fd(s_info.disp, &s_info.fd);
+ if (ret != WIDGET_ERROR_NONE || s_info.fd < 0) {
+ ErrPrint("Failed to open a drm device: (%d)\n", errno);
+ return WIDGET_ERROR_NONE;
+ }
+
+ s_info.bufmgr = tbm_bufmgr_init(s_info.fd);
+ if (!s_info.bufmgr) {
+ ErrPrint("Failed to init bufmgr\n");
+ widget_util_release_drm_fd(s_info.fd);
+ s_info.fd = -1;
+ return WIDGET_ERROR_NONE;
+ }
+
+ s_info.master_disconnected = 0;
+ return WIDGET_ERROR_NONE;
+}
+
+int fb_fini(void)
+{
+ if (s_info.bufmgr) {
+ tbm_bufmgr_deinit(s_info.bufmgr);
+ s_info.bufmgr = NULL;
+ }
+
+ if (s_info.fd >= 0) {
+ widget_util_release_drm_fd(s_info.fd);
+ s_info.fd = -1;
+ }
+
+ if (s_info.disp_is_opened && s_info.disp) {
+ wl_display_disconnect(s_info.disp);
+ s_info.disp = NULL;
+ }
+
+ return WIDGET_ERROR_NONE;
+}
+
+static inline int sync_for_file(struct fb_info *info)
+{
+ int fd;
+ widget_fb_t buffer;
+ const char *path;
+
+ buffer = info->buffer;
+ if (!buffer) {
+ DbgPrint("Buffer is NIL, skip sync\n");
+ return WIDGET_ERROR_NONE;
+ }
+
+ if (buffer->state != WIDGET_FB_STATE_CREATED) {
+ ErrPrint("Invalid state of a FB\n");
+ return WIDGET_ERROR_INVALID_PARAMETER;
+ }
+
+ if (buffer->type != WIDGET_FB_TYPE_FILE) {
+ DbgPrint("Ingore sync\n");
+ return WIDGET_ERROR_NONE;
+ }
+
+ path = widget_util_uri_to_path(info->id);
+ if (!path) {
+ ErrPrint("Invalid id: %s\n", info->id);
+ return WIDGET_ERROR_INVALID_PARAMETER;
+ }
+
+ fd = open(path, O_WRONLY | O_CREAT, 0644);
+ if (fd < 0) {
+ ErrPrint("open %s: %d\n", path, errno);
+ return WIDGET_ERROR_IO_ERROR;
+ }
+
+ if (write(fd, buffer->data, info->bufsz) != info->bufsz) {
+ ErrPrint("write: %d\n", errno);
+ if (close(fd) < 0) {
+ ErrPrint("close: %d\n", errno);
+ }
+ return WIDGET_ERROR_IO_ERROR;
+ }
+
+ if (close(fd) < 0) {
+ ErrPrint("close: %d\n", errno);
+ }
+ return WIDGET_ERROR_NONE;
+}
+
+static inline int sync_for_pixmap(struct fb_info *info)
+{
+ widget_fb_t buffer;
+
+ buffer = info->buffer;
+ if (!buffer) {
+ DbgPrint("Buffer is NIL, skip sync\n");
+ return WIDGET_ERROR_NONE;
+ }
+
+ if (buffer->state != WIDGET_FB_STATE_CREATED) {
+ ErrPrint("Invalid state of a FB\n");
+ return WIDGET_ERROR_INVALID_PARAMETER;
+ }
+
+ if (buffer->type != WIDGET_FB_TYPE_PIXMAP) {
+ DbgPrint("Invalid buffer\n");
+ return WIDGET_ERROR_NONE;
+ }
+
+ if (info->handle == 0) {
+ ErrPrint("Pixmap ID is not valid\n");
+ return WIDGET_ERROR_INVALID_PARAMETER;
+ }
+
+ if (info->bufsz == 0) {
+ DbgPrint("Nothing can be sync\n");
+ return WIDGET_ERROR_NONE;
+ }
+
+ /**
+ * @note
+ * Do nothing for Wayland
+ */
+
+ return WIDGET_ERROR_NONE;
+}
+
+int fb_sync(struct fb_info *info)
+{
+ if (!info) {
+ ErrPrint("FB Handle is not valid\n");
+ return WIDGET_ERROR_INVALID_PARAMETER;
+ }
+
+ if (!info->id || info->id[0] == '\0') {
+ DbgPrint("Ingore sync\n");
+ return WIDGET_ERROR_NONE;
+ }
+
+ if (!strncasecmp(info->id, SCHEMA_FILE, strlen(SCHEMA_FILE))) {
+ return sync_for_file(info);
+ } else if (!strncasecmp(info->id, SCHEMA_PIXMAP, strlen(SCHEMA_PIXMAP))) {
+ return sync_for_pixmap(info);
+ } else if (!strncasecmp(info->id, SCHEMA_SHM, strlen(SCHEMA_SHM))) {
+ return WIDGET_ERROR_NONE;
+ }
+
+ ErrPrint("Invalid URI: [%s]\n", info->id);
+ return WIDGET_ERROR_INVALID_PARAMETER;
+}
+
+struct fb_info *fb_create(const char *id, int w, int h)
+{
+ struct fb_info *info;
+
+ if (!id || id[0] == '\0') {
+ ErrPrint("Invalid id\n");
+ return NULL;
+ }
+
+ info = calloc(1, sizeof(*info));
+ if (!info) {
+ ErrPrint("calloc: %d\n", errno);
+ return NULL;
+ }
+
+ info->id = strdup(id);
+ if (!info->id) {
+ ErrPrint("strdup: %d\n", errno);
+ free(info);
+ return NULL;
+ }
+
+ info->pixels = sizeof(int);
+
+ if (sscanf(info->id, SCHEMA_SHM "%d", &info->handle) == 1) {
+ DbgPrint("SHMID: %d\n", info->handle);
+ } else if (sscanf(info->id, SCHEMA_PIXMAP "%d:%d", &info->handle, &info->pixels) == 2) {
+ DbgPrint("PIXMAP: %d\n", info->handle);
+ } else if (!strncasecmp(info->id, SCHEMA_FILE, strlen(SCHEMA_FILE))) {
+ info->handle = -1;
+ } else {
+ ErrPrint("Unsupported schema: %s\n", info->id);
+ free(info->id);
+ free(info);
+ return NULL;
+ }
+
+ info->w = w;
+ info->h = h;
+
+ return info;
+}
+
+int fb_has_gem(struct fb_info *info)
+{
+ return !strncasecmp(info->id, SCHEMA_PIXMAP, strlen(SCHEMA_PIXMAP)) && (s_info.fd >= 0 || info->gem);
+}
+
+static inline struct gem_data *create_gem(unsigned int pixmap, int w, int h, int depth, int auto_align)
+{
+ struct gem_data *gem;
+
+ if (!s_info.bufmgr) {
+ /*
+ * @note
+ * In rare case,
+ * if the mastser is disconnected, service provider should not try to create the gem buffer
+ * To avoid this, returns NULL from here.
+ */
+ ErrPrint("Buffer manager is not initialized\n");
+ return NULL;
+ }
+
+ gem = calloc(1, sizeof(*gem));
+ if (!gem) {
+ ErrPrint("calloc: %d\n", errno);
+ return NULL;
+ }
+
+ gem->count = 1;
+ gem->w = w;
+ gem->h = h;
+ gem->depth = depth;
+ gem->pixmap = pixmap;
+
+ gem->pixmap_bo = tbm_bo_import(s_info.bufmgr, gem->pixmap);
+ if (!gem->pixmap_bo) {
+ ErrPrint("Failed to import BO\n");
+ free(gem);
+ return NULL;
+ }
+
+ s_info.gem_list = dlist_append(s_info.gem_list, gem);
+ return gem;
+}
+
+static inline int destroy_gem(struct gem_data *gem)
+{
+ dlist_remove_data(s_info.gem_list, gem);
+
+ if (!s_info.master_disconnected && gem->pixmap_bo) {
+ DbgPrint("unref pixmap bo\n");
+ tbm_bo_unref(gem->pixmap_bo);
+ gem->pixmap_bo = NULL;
+ } else {
+ ErrPrint("Resource is not trusted anymore(%u)\n", gem->pixmap);
+ }
+
+ free(gem);
+ return WIDGET_ERROR_NONE;
+}
+
+int fb_create_gem(struct fb_info *info, int auto_align)
+{
+ if (info->gem) {
+ DbgPrint("Already created\n");
+ return WIDGET_ERROR_NONE;
+ }
+
+ info->gem = create_gem(info->handle, info->w, info->h, info->pixels, auto_align);
+ return info->gem ? WIDGET_ERROR_NONE : WIDGET_ERROR_FAULT;
+}
+
+int fb_destroy_gem(struct fb_info *info)
+{
+ if (!info || !info->gem) {
+ ErrPrint("GEM is not created\n");
+ return WIDGET_ERROR_INVALID_PARAMETER;
+ }
+
+ destroy_gem(info->gem);
+ info->gem = NULL;
+ return WIDGET_ERROR_NONE;
+}
+
+void *fb_acquire_gem(struct fb_info *info)
+{
+ struct gem_data *gem;
+
+ if (!info->gem) {
+ ErrPrint("Invalid FB info\n");
+ return NULL;
+ }
+
+ gem = info->gem;
+
+ if (!gem->data) {
+ tbm_bo_handle handle;
+ handle = tbm_bo_map(gem->pixmap_bo, TBM_DEVICE_CPU, TBM_OPTION_READ | TBM_OPTION_WRITE);
+ gem->data = handle.ptr;
+ if (!gem->data) {
+ ErrPrint("Failed to get BO\n");
+ }
+ }
+
+ gem->refcnt++;
+ return gem->data;
+}
+
+int fb_release_gem(struct fb_info *info)
+{
+ struct gem_data *gem;
+
+ gem = info->gem;
+
+ gem->refcnt--;
+ if (gem->refcnt == 0) {
+ tbm_bo_unmap(gem->pixmap_bo);
+ gem->data = NULL;
+ } else if (gem->refcnt < 0) {
+ ErrPrint("Invalid refcnt: %d (reset)\n", gem->refcnt);
+ gem->refcnt = 0;
+ }
+
+ return WIDGET_ERROR_NONE;
+}
+
+int fb_destroy(struct fb_info *info)
+{
+ if (!info) {
+ ErrPrint("Handle is not valid\n");
+ return WIDGET_ERROR_INVALID_PARAMETER;
+ }
+
+ if (info->buffer) {
+ widget_fb_t buffer;
+ buffer = info->buffer;
+ buffer->info = NULL;
+ }
+
+ free(info->id);
+ free(info);
+ return WIDGET_ERROR_NONE;
+}
+
+int fb_is_created(struct fb_info *info)
+{
+ if (!info) {
+ ErrPrint("Handle is not valid\n");
+ return 0;
+ }
+
+ if (!strncasecmp(info->id, SCHEMA_PIXMAP, strlen(SCHEMA_PIXMAP)) && info->handle != 0) {
+ return 1;
+ } else if (!strncasecmp(info->id, SCHEMA_SHM, strlen(SCHEMA_SHM)) && info->handle > 0) {
+ return 1;
+ } else {
+ const char *path;
+ path = widget_util_uri_to_path(info->id);
+ if (path && access(path, F_OK | R_OK) == 0) {
+ return 1;
+ } else {
+ ErrPrint("access: %d (%s)\n", errno, path);
+ }
+ }
+
+ return 0;
+}
+
+void *fb_acquire_buffer(struct fb_info *info)
+{
+ widget_fb_t buffer;
+ void *addr;
+
+ if (!info) {
+ return NULL;
+ }
+
+ if (!info->buffer) {
+ if (!strncasecmp(info->id, SCHEMA_PIXMAP, strlen(SCHEMA_PIXMAP))) {
+ ErrPrint("S/W Backed is not supported\n");
+ return NULL;
+ } else if (!strncasecmp(info->id, SCHEMA_FILE, strlen(SCHEMA_FILE))) {
+ info->bufsz = info->w * info->h * info->pixels;
+ buffer = calloc(1, sizeof(*buffer) + info->bufsz);
+ if (!buffer) {
+ ErrPrint("calloc: %d\n", errno);
+ info->bufsz = 0;
+ return NULL;
+ }
+
+ buffer->type = WIDGET_FB_TYPE_FILE;
+ buffer->refcnt = 0;
+ buffer->state = WIDGET_FB_STATE_CREATED;
+ buffer->info = info;
+ info->buffer = buffer;
+ } else if (!strncasecmp(info->id, SCHEMA_SHM, strlen(SCHEMA_SHM))) {
+ buffer = shmat(info->handle, NULL, 0);
+ if (buffer == (void *)-1) {
+ ErrPrint("shmat: %d\n", errno);
+ return NULL;
+ }
+
+ info->buffer = buffer;
+ } else {
+ DbgPrint("Buffer is NIL\n");
+ return NULL;
+ }
+ }
+
+ buffer = info->buffer;
+ switch (buffer->type) {
+ case WIDGET_FB_TYPE_FILE:
+ buffer->refcnt++;
+ /* Fall through */
+ case WIDGET_FB_TYPE_SHM:
+ addr = buffer->data;
+ break;
+ case WIDGET_FB_TYPE_PIXMAP:
+ ErrPrint("S/W Backend is not supported\n");
+ default:
+ addr = NULL;
+ break;
+ }
+
+ return addr;
+}
+
+int fb_release_buffer(void *data)
+{
+ widget_fb_t buffer;
+
+ if (!data) {
+ return WIDGET_ERROR_NONE;
+ }
+
+ buffer = container_of(data, struct widget_fb, data);
+
+ if (buffer->state != WIDGET_FB_STATE_CREATED) {
+ ErrPrint("Invalid handle\n");
+ return WIDGET_ERROR_INVALID_PARAMETER;
+ }
+
+ switch (buffer->type) {
+ case WIDGET_FB_TYPE_SHM:
+ /*!
+ * \note
+ * SHM can not use the "info"
+ * it is NULL
+ */
+ if (shmdt(buffer) < 0) {
+ ErrPrint("shmdt: %d\n", errno);
+ }
+ break;
+ case WIDGET_FB_TYPE_PIXMAP:
+ ErrPrint("S/W Backend is not supported\n");
+ return WIDGET_ERROR_NOT_SUPPORTED;
+ case WIDGET_FB_TYPE_FILE:
+ buffer->refcnt--;
+ if (buffer->refcnt == 0) {
+ struct fb_info *info;
+ info = buffer->info;
+ if (info && info->buffer == buffer) {
+ info->buffer = NULL;
+ }
+ buffer->state = WIDGET_FB_STATE_DESTROYED;
+ free(buffer);
+ }
+ break;
+ default:
+ ErrPrint("Unknown type\n");
+ return WIDGET_ERROR_INVALID_PARAMETER;
+ }
+
+ return WIDGET_ERROR_NONE;
+}
+
+int fb_type(struct fb_info *info)
+{
+ widget_fb_t buffer;
+
+ if (!info) {
+ return WIDGET_FB_TYPE_ERROR;
+ }
+
+ buffer = info->buffer;
+ if (!buffer) {
+ int type = WIDGET_FB_TYPE_ERROR;
+ /*!
+ * \note
+ * Try to get this from SCHEMA
+ */
+ if (info->id) {
+ if (!strncasecmp(info->id, SCHEMA_FILE, strlen(SCHEMA_FILE))) {
+ type = WIDGET_FB_TYPE_FILE;
+ } else if (!strncasecmp(info->id, SCHEMA_PIXMAP, strlen(SCHEMA_PIXMAP))) {
+ type = WIDGET_FB_TYPE_PIXMAP;
+ } else if (!strncasecmp(info->id, SCHEMA_SHM, strlen(SCHEMA_SHM))) {
+ type = WIDGET_FB_TYPE_SHM;
+ }
+ }
+
+ return type;
+ }
+
+ return buffer->type;
+}
+
+int fb_refcnt(void *data)
+{
+ widget_fb_t buffer;
+ struct shmid_ds buf;
+ int ret;
+
+ if (!data) {
+ return WIDGET_ERROR_INVALID_PARAMETER;
+ }
+
+ buffer = container_of(data, struct widget_fb, data);
+
+ if (buffer->state != WIDGET_FB_STATE_CREATED) {
+ ErrPrint("Invalid handle\n");
+ return WIDGET_ERROR_INVALID_PARAMETER;
+ }
+
+ switch (buffer->type) {
+ case WIDGET_FB_TYPE_SHM:
+ if (shmctl(buffer->refcnt, IPC_STAT, &buf) < 0) {
+ ErrPrint("shmctl: %d\n", errno);
+ ret = WIDGET_ERROR_FAULT;
+ break;
+ }
+
+ ret = buf.shm_nattch; /*!< This means attached count of process */
+ break;
+ case WIDGET_FB_TYPE_PIXMAP:
+ ret = buffer->refcnt;
+ break;
+ case WIDGET_FB_TYPE_FILE:
+ ret = buffer->refcnt;
+ break;
+ default:
+ ret = WIDGET_ERROR_INVALID_PARAMETER;
+ break;
+ }
+
+ return ret;
+}
+
+const char *fb_id(struct fb_info *info)
+{
+ return info ? info->id : NULL;
+}
+
+int fb_get_size(struct fb_info *info, int *w, int *h)
+{
+ if (!info) {
+ ErrPrint("Handle is not valid\n");
+ return WIDGET_ERROR_INVALID_PARAMETER;
+ }
+
+ *w = info->w;
+ *h = info->h;
+ return WIDGET_ERROR_NONE;
+}
+
+int fb_size(struct fb_info *info)
+{
+ if (!info) {
+ ErrPrint("Handle is not valid\n");
+ return 0;
+ }
+
+ info->bufsz = info->w * info->h * info->pixels;
+ return info->bufsz;
+}
+
+int fb_sync_xdamage(struct fb_info *info, widget_damage_region_s *region)
+{
+ return WIDGET_ERROR_NOT_SUPPORTED;
+}
+
+int fb_stride(struct fb_info *info)
+{
+ int stride;
+
+ stride = info->w * info->pixels;
+ DbgPrint("Stride: %d\n", stride);
+
+ return stride;
+}
+
+void fb_master_disconnected(void)
+{
+ s_info.master_disconnected = 1;
+}
+
+/* End of a file */