From b85d12a3074c13e37f59b47edb81e1d4ff34eeb0 Mon Sep 17 00:00:00 2001 From: Greg Hackmann Date: Fri, 8 Nov 2013 09:40:12 -0800 Subject: add libadf Helper library to enumerate ADF objects, wrap ADF ioctls, and read event data Change-Id: I7aa7f88935174e650a40b2f9db3212280121f760 Signed-off-by: Greg Hackmann --- adf/Android.mk | 18 ++ adf/libadf/Android.mk | 23 ++ adf/libadf/adf.c | 697 +++++++++++++++++++++++++++++++++++++++++++ adf/libadf/include/adf/adf.h | 234 +++++++++++++++ 4 files changed, 972 insertions(+) create mode 100644 adf/Android.mk create mode 100644 adf/libadf/Android.mk create mode 100644 adf/libadf/adf.c create mode 100644 adf/libadf/include/adf/adf.h diff --git a/adf/Android.mk b/adf/Android.mk new file mode 100644 index 0000000..64d486e --- /dev/null +++ b/adf/Android.mk @@ -0,0 +1,18 @@ +# +# Copyright (C) 2013 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. +# +LOCAL_PATH := $(my-dir) + +include $(call first-makefiles-under,$(LOCAL_PATH)) diff --git a/adf/libadf/Android.mk b/adf/libadf/Android.mk new file mode 100644 index 0000000..908aa6c --- /dev/null +++ b/adf/libadf/Android.mk @@ -0,0 +1,23 @@ +# Copyright (C) 2013 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. + +LOCAL_PATH:= $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_SRC_FILES := adf.c +LOCAL_MODULE := libadf +LOCAL_MODULE_TAGS := optional +LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include +LOCAL_C_INCLUDES += $(LOCAL_EXPORT_C_INCLUDE_DIRS) +include $(BUILD_STATIC_LIBRARY) diff --git a/adf/libadf/adf.c b/adf/libadf/adf.c new file mode 100644 index 0000000..dfcb0e4 --- /dev/null +++ b/adf/libadf/adf.c @@ -0,0 +1,697 @@ +/* + * Copyright (C) 2013 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 +#include +#include +#include +#include +#include + +#include + +#include + +#include + +#define ADF_BASE_PATH "/dev/" + +static ssize_t adf_find_nodes(const char *pattern, adf_id_t **ids) +{ + DIR *dir; + struct dirent *dirent; + size_t n = 0; + ssize_t ret; + adf_id_t *ids_ret = NULL; + + dir = opendir(ADF_BASE_PATH); + if (!dir) + return -errno; + + errno = 0; + while ((dirent = readdir(dir))) { + adf_id_t id; + int matched = sscanf(dirent->d_name, pattern, &id); + + if (matched < 0) { + ret = -errno; + goto done; + } else if (matched != 1) { + continue; + } + + adf_id_t *new_ids = realloc(ids_ret, (n + 1) * sizeof(ids_ret[0])); + if (!new_ids) { + ret = -ENOMEM; + goto done; + } + + ids_ret = new_ids; + ids_ret[n] = id; + n++; + } + if (errno) + ret = -errno; + else + ret = n; + +done: + closedir(dir); + if (ret < 0) + free(ids_ret); + else + *ids = ids_ret; + return ret; +} + +ssize_t adf_devices(adf_id_t **ids) +{ + return adf_find_nodes("adf%u", ids); +} + +int adf_device_open(adf_id_t id, int flags, struct adf_device *dev) +{ + char filename[64]; + int err; + + dev->id = id; + + snprintf(filename, sizeof(filename), ADF_BASE_PATH "adf%u", id); + dev->fd = open(filename, flags); + if (dev->fd < 0) + return -errno; + + return 0; +} + +void adf_device_close(struct adf_device *dev) +{ + if (dev->fd >= 0) + close(dev->fd); +} + +int adf_get_device_data(struct adf_device *dev, struct adf_device_data *data) +{ + int err; + int ret = 0; + + memset(data, 0, sizeof(*data)); + + err = ioctl(dev->fd, ADF_GET_DEVICE_DATA, data); + if (err < 0) + return -ENOMEM; + + if (data->n_attachments) { + data->attachments = malloc(sizeof(data->attachments[0]) * + data->n_attachments); + if (!data->attachments) + return -ENOMEM; + } + + if (data->n_allowed_attachments) { + data->allowed_attachments = + malloc(sizeof(data->allowed_attachments[0]) * + data->n_allowed_attachments); + if (!data->allowed_attachments) { + ret = -ENOMEM; + goto done; + } + } + + if (data->custom_data_size) { + data->custom_data = malloc(data->custom_data_size); + if (!data->custom_data) { + ret = -ENOMEM; + goto done; + } + } + + err = ioctl(dev->fd, ADF_GET_DEVICE_DATA, data); + if (err < 0) + ret = -errno; + +done: + if (ret < 0) + adf_free_device_data(data); + return ret; +} + +void adf_free_device_data(struct adf_device_data *data) +{ + free(data->attachments); + free(data->allowed_attachments); + free(data->custom_data); +} + +int adf_device_post(struct adf_device *dev, + adf_id_t *interfaces, size_t n_interfaces, + struct adf_buffer_config *bufs, size_t n_bufs, + void *custom_data, size_t custom_data_size) +{ + int err; + struct adf_post_config data; + + memset(&data, 0, sizeof(data)); + data.interfaces = interfaces; + data.n_interfaces = n_interfaces; + data.bufs = bufs; + data.n_bufs = n_bufs; + data.custom_data = custom_data; + data.custom_data_size = custom_data_size; + + err = ioctl(dev->fd, ADF_POST_CONFIG, &data); + if (err < 0) + return -errno; + + return (int)data.complete_fence; +} + +static int adf_device_attachment(struct adf_device *dev, + adf_id_t overlay_engine, adf_id_t interface, bool attach) +{ + int err; + struct adf_attachment_config data; + + memset(&data, 0, sizeof(data)); + data.overlay_engine = overlay_engine; + data.interface = interface; + + err = ioctl(dev->fd, attach ? ADF_ATTACH : ADF_DETACH, &data); + if (err < 0) + return -errno; + + return 0; +} + +int adf_device_attach(struct adf_device *dev, adf_id_t overlay_engine, + adf_id_t interface) +{ + return adf_device_attachment(dev, overlay_engine, interface, true); +} + +int adf_device_detach(struct adf_device *dev, adf_id_t overlay_engine, + adf_id_t interface) +{ + return adf_device_attachment(dev, overlay_engine, interface, false); +} + +ssize_t adf_interfaces(struct adf_device *dev, adf_id_t **interfaces) +{ + char pattern[64]; + + snprintf(pattern, sizeof(pattern), "adf-interface%u.%%u", dev->id); + return adf_find_nodes(pattern, interfaces); +} + +ssize_t adf_interfaces_for_overlay_engine(struct adf_device *dev, + adf_id_t overlay_engine, adf_id_t **interfaces) +{ + struct adf_device_data data; + ssize_t n = 0; + ssize_t ret; + adf_id_t *ids_ret = NULL; + + ret = adf_get_device_data(dev, &data); + if (ret < 0) + return ret; + + size_t i; + for (i = 0; i < data.n_allowed_attachments; i++) { + if (data.allowed_attachments[i].overlay_engine != overlay_engine) + continue; + + adf_id_t *new_ids = realloc(ids_ret, (n + 1) * sizeof(ids_ret[0])); + if (!new_ids) { + ret = -ENOMEM; + goto done; + } + + ids_ret = new_ids; + ids_ret[n] = data.allowed_attachments[i].interface; + n++; + } + + ret = n; + +done: + adf_free_device_data(&data); + if (ret < 0) + free(ids_ret); + else + *interfaces = ids_ret; + return ret; +} + +static ssize_t adf_interfaces_filter(struct adf_device *dev, + adf_id_t *in, size_t n_in, adf_id_t **out, + bool (*filter)(struct adf_interface_data *data, __u32 match), + __u32 match) +{ + size_t n = 0; + ssize_t ret; + adf_id_t *ids_ret = NULL; + + size_t i; + for (i = 0; i < n_in; i++) { + int fd = adf_interface_open(dev, in[i], O_RDONLY); + if (fd < 0) { + ret = fd; + goto done; + } + + struct adf_interface_data data; + ret = adf_get_interface_data(fd, &data); + close(fd); + if (ret < 0) + goto done; + + if (!filter(&data, match)) + continue; + + adf_id_t *new_ids = realloc(ids_ret, (n + 1) * sizeof(ids_ret[0])); + if (!new_ids) { + ret = -ENOMEM; + goto done; + } + + ids_ret = new_ids; + ids_ret[n] = in[i]; + n++; + } + + ret = n; + +done: + if (ret < 0) + free(ids_ret); + else + *out = ids_ret; + return ret; +} + +static bool adf_interface_type_filter(struct adf_interface_data *data, + __u32 type) +{ + return data->type == (enum adf_interface_type)type; +} + +ssize_t adf_interfaces_filter_by_type(struct adf_device *dev, + enum adf_interface_type type, + adf_id_t *in, size_t n_in, adf_id_t **out) +{ + return adf_interfaces_filter(dev, in, n_in, out, adf_interface_type_filter, + type); +} + +static bool adf_interface_flags_filter(struct adf_interface_data *data, + __u32 flag) +{ + return !!(data->flags & flag); +} + +ssize_t adf_interfaces_filter_by_flag(struct adf_device *dev, __u32 flag, + adf_id_t *in, size_t n_in, adf_id_t **out) +{ + return adf_interfaces_filter(dev, in, n_in, out, adf_interface_flags_filter, + flag); +} + +int adf_interface_open(struct adf_device *dev, adf_id_t id, int flags) +{ + char filename[64]; + + snprintf(filename, sizeof(filename), ADF_BASE_PATH "adf-interface%u.%u", + dev->id, id); + + int fd = open(filename, flags); + if (fd < 0) + return -errno; + return fd; +} + +int adf_get_interface_data(int fd, struct adf_interface_data *data) +{ + int err; + int ret = 0; + + memset(data, 0, sizeof(*data)); + + err = ioctl(fd, ADF_GET_INTERFACE_DATA, data); + if (err < 0) + return -errno; + + if (data->n_available_modes) { + data->available_modes = malloc(sizeof(data->available_modes[0]) * + data->n_available_modes); + if (!data->available_modes) + return -ENOMEM; + } + + if (data->custom_data_size) { + data->custom_data = malloc(data->custom_data_size); + if (!data->custom_data) { + ret = -ENOMEM; + goto done; + } + } + + err = ioctl(fd, ADF_GET_INTERFACE_DATA, data); + if (err < 0) + ret = -errno; + +done: + if (ret < 0) + adf_free_interface_data(data); + return ret; +} + +void adf_free_interface_data(struct adf_interface_data *data) +{ + free(data->available_modes); + free(data->custom_data); +} + +int adf_interface_blank(int fd, __u8 mode) +{ + int err = ioctl(fd, ADF_BLANK, mode); + if (err < 0) + return -errno; + return 0; +} + +int adf_interface_set_mode(int fd, struct drm_mode_modeinfo *mode) +{ + int err = ioctl(fd, ADF_SET_MODE, mode); + if (err < 0) + return -errno; + return 0; +} + +int adf_interface_simple_buffer_alloc(int fd, __u32 w, __u32 h, + __u32 format, __u32 *offset, __u32 *pitch) +{ + int err; + struct adf_simple_buffer_alloc data; + + memset(&data, 0, sizeof(data)); + data.w = w; + data.h = h; + data.format = format; + + err = ioctl(fd, ADF_SIMPLE_BUFFER_ALLOC, &data); + if (err < 0) + return -errno; + + *offset = data.offset; + *pitch = data.pitch; + return (int)data.fd; +} + +int adf_interface_simple_post(int fd, __u32 overlay_engine, + __u32 w, __u32 h, __u32 format, int buf_fd, __u32 offset, + __u32 pitch, int acquire_fence) +{ + int ret; + struct adf_simple_post_config data; + + memset(&data, 0, sizeof(data)); + data.buf.overlay_engine = overlay_engine; + data.buf.w = w; + data.buf.h = h; + data.buf.format = format; + data.buf.fd[0] = buf_fd; + data.buf.offset[0] = offset; + data.buf.pitch[0] = pitch; + data.buf.n_planes = 1; + data.buf.acquire_fence = acquire_fence; + + ret = ioctl(fd, ADF_SIMPLE_POST_CONFIG, &data); + if (ret < 0) + return -errno; + + return (int)data.complete_fence; +} + +ssize_t adf_overlay_engines(struct adf_device *dev, adf_id_t **overlay_engines) +{ + char pattern[64]; + + snprintf(pattern, sizeof(pattern), "adf-overlay-engine%u.%%u", dev->id); + return adf_find_nodes(pattern, overlay_engines); +} + +ssize_t adf_overlay_engines_for_interface(struct adf_device *dev, + adf_id_t interface, adf_id_t **overlay_engines) +{ + struct adf_device_data data; + ssize_t n = 0; + ssize_t ret; + adf_id_t *ids_ret = NULL; + + ret = adf_get_device_data(dev, &data); + if (ret < 0) + return ret; + + size_t i; + for (i = 0; i < data.n_allowed_attachments; i++) { + if (data.allowed_attachments[i].interface != interface) + continue; + + adf_id_t *new_ids = realloc(ids_ret, (n + 1) * sizeof(ids_ret[0])); + if (!new_ids) { + ret = -ENOMEM; + goto done; + } + + ids_ret = new_ids; + ids_ret[n] = data.allowed_attachments[i].overlay_engine; + n++; + } + + ret = n; + +done: + adf_free_device_data(&data); + if (ret < 0) + free(ids_ret); + else + *overlay_engines = ids_ret; + return ret; +} + +static ssize_t adf_overlay_engines_filter(struct adf_device *dev, + adf_id_t *in, size_t n_in, adf_id_t **out, + bool (*filter)(struct adf_overlay_engine_data *data, __u32 match), + __u32 match) +{ + size_t n = 0; + ssize_t ret; + adf_id_t *ids_ret = NULL; + + size_t i; + for (i = 0; i < n_in; i++) { + int fd = adf_overlay_engine_open(dev, in[i], O_RDONLY); + if (fd < 0) { + ret = fd; + goto done; + } + + struct adf_overlay_engine_data data; + ret = adf_get_overlay_engine_data(fd, &data); + close(fd); + if (ret < 0) + goto done; + + if (!filter(&data, match)) + continue; + + adf_id_t *new_ids = realloc(ids_ret, (n + 1) * sizeof(ids_ret[0])); + if (!new_ids) { + ret = -ENOMEM; + goto done; + } + + ids_ret = new_ids; + ids_ret[n] = in[i]; + n++; + } + + ret = n; + +done: + if (ret < 0) + free(ids_ret); + else + *out = ids_ret; + return ret; +} + +static bool adf_overlay_engine_format_filter(struct adf_overlay_engine_data *data, + __u32 format) +{ + size_t i; + for (i = 0; i < data->n_supported_formats; i++) + if (data->supported_formats[i] == format) + return true; + return false; +} + +ssize_t adf_overlay_engines_filter_by_format(struct adf_device *dev, + __u32 format, adf_id_t *in, size_t n_in, adf_id_t **out) +{ + return adf_overlay_engines_filter(dev, in, n_in, out, + adf_overlay_engine_format_filter, format); +} + +int adf_overlay_engine_open(struct adf_device *dev, adf_id_t id, int flags) +{ + char filename[64]; + + snprintf(filename, sizeof(filename), + ADF_BASE_PATH "adf-overlay-engine%u.%u", dev->id, id); + + int fd = open(filename, flags); + if (fd < 0) + return -errno; + return fd; +} + +int adf_get_overlay_engine_data(int fd, struct adf_overlay_engine_data *data) +{ + int err; + int ret = 0; + + memset(data, 0, sizeof(*data)); + + err = ioctl(fd, ADF_GET_OVERLAY_ENGINE_DATA, data); + if (err < 0) + return -errno; + + if (data->n_supported_formats) { + data->supported_formats = malloc(sizeof(data->supported_formats[0]) * + data->n_supported_formats); + if (!data->supported_formats) + return -ENOMEM; + } + + if (data->custom_data_size) { + data->custom_data = malloc(data->custom_data_size); + if (!data->custom_data) { + ret = -ENOMEM; + goto done; + } + } + + err = ioctl(fd, ADF_GET_OVERLAY_ENGINE_DATA, data); + if (err < 0) + ret = -errno; + +done: + if (ret < 0) + adf_free_overlay_engine_data(data); + return ret; +} + +void adf_free_overlay_engine_data(struct adf_overlay_engine_data *data) +{ + free(data->supported_formats); + free(data->custom_data); +} + +bool adf_overlay_engine_supports_format(int fd, __u32 format) +{ + struct adf_overlay_engine_data data; + bool ret = false; + size_t i; + + int err = adf_get_overlay_engine_data(fd, &data); + if (err < 0) + return false; + + for (i = 0; i < data.n_supported_formats; i++) { + if (data.supported_formats[i] == format) { + ret = true; + break; + } + } + + adf_free_overlay_engine_data(&data); + return ret; +} + +int adf_set_event(int fd, enum adf_event_type type, bool enabled) +{ + struct adf_set_event data; + + data.type = type; + data.enabled = enabled; + + int err = ioctl(fd, ADF_SET_EVENT, &data); + if (err < 0) + return -errno; + return 0; +} + +int adf_read_event(int fd, struct adf_event **event) +{ + struct adf_event header; + struct { + struct adf_event base; + uint8_t data[0]; + } *event_ret; + size_t data_size; + int ret = 0; + + int err = read(fd, &header, sizeof(header)); + if (err < 0) + return -errno; + if ((size_t)err < sizeof(header)) + return -EIO; + if (header.length < sizeof(header)) + return -EIO; + + event_ret = malloc(header.length); + if (!event_ret) + return -ENOMEM; + data_size = header.length - sizeof(header); + + memcpy(event_ret, &header, sizeof(header)); + ssize_t read_size = read(fd, &event_ret->data, data_size); + if (read_size < 0) { + ret = -errno; + goto done; + } + if ((size_t)read_size < data_size) { + ret = -EIO; + goto done; + } + + *event = &event_ret->base; + +done: + if (ret < 0) + free(event_ret); + return ret; +} + +void adf_format_str(__u32 format, char buf[ADF_FORMAT_STR_SIZE]) +{ + buf[0] = format & 0xFF; + buf[1] = (format >> 8) & 0xFF; + buf[2] = (format >> 16) & 0xFF; + buf[3] = (format >> 24) & 0xFF; + buf[4] = '\0'; +} diff --git a/adf/libadf/include/adf/adf.h b/adf/libadf/include/adf/adf.h new file mode 100644 index 0000000..5d301f3 --- /dev/null +++ b/adf/libadf/include/adf/adf.h @@ -0,0 +1,234 @@ +/* + * Copyright (C) 2013 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. + */ + +#ifndef _LIBADF_ADF_H_ +#define _LIBADF_ADF_H_ + +#include +#include +#include +#include +#include