/* * Copyright (c) 2014, ARM Limited and Contributors. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * Neither the name of ARM nor the names of its contributors may be used * to endorse or promote products derived from this software without specific * prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include #include #include "io_storage.h" #include "io_driver.h" #define MAX_DEVICES(plat_data) \ (sizeof((plat_data)->devices)/sizeof((plat_data)->devices[0])) /* Storage for a fixed maximum number of IO entities, definable by platform */ static struct io_entity entity_pool[MAX_IO_HANDLES]; /* Simple way of tracking used storage - each entry is NULL or a pointer to an * entity */ static struct io_entity *entity_map[MAX_IO_HANDLES]; /* Track number of allocated entities */ static unsigned int entity_count; /* Used to keep a reference to platform-specific data */ static struct io_plat_data *platform_data; #if DEBUG /* Extra validation functions only used in debug builds */ /* Return a boolean value indicating whether a device connector is valid */ static int is_valid_dev_connector(const struct io_dev_connector *dev_con) { int result = (dev_con != NULL) && (dev_con->dev_open != NULL); return result; } /* Return a boolean value indicating whether a device handle is valid */ static int is_valid_dev(io_dev_handle handle) { const struct io_dev_info *dev = handle; int result = (dev != NULL) && (dev->funcs != NULL) && (dev->funcs->type != NULL) && (dev->funcs->type() < IO_TYPE_MAX); return result; } /* Return a boolean value indicating whether an IO entity is valid */ static int is_valid_entity(io_handle handle) { const struct io_entity *entity = handle; int result = (entity != NULL) && (is_valid_dev(entity->dev_handle)); return result; } /* Return a boolean value indicating whether a seek mode is valid */ static int is_valid_seek_mode(io_seek_mode mode) { return ((mode != IO_SEEK_INVALID) && (mode < IO_SEEK_MAX)); } #endif /* End of debug-only validation functions */ /* Open a connection to a specific device */ static int dev_open(const struct io_dev_connector *dev_con, void *dev_spec, struct io_dev_info **dev_info) { int result = IO_FAIL; assert(dev_info != NULL); assert(is_valid_dev_connector(dev_con)); result = dev_con->dev_open(dev_spec, dev_info); return result; } /* Set a handle to track an entity */ static void set_handle(io_handle *handle, struct io_entity *entity) { assert(handle != NULL); *handle = entity; } /* Locate an entity in the pool, specified by address */ static int find_first_entity(struct io_entity *entity, unsigned int *index_out) { int result = IO_FAIL; for (int index = 0; index < MAX_IO_HANDLES; ++index) { if (entity_map[index] == entity) { result = IO_SUCCESS; *index_out = index; break; } } return result; } /* Allocate an entity from the pool and return a pointer to it */ static int allocate_entity(struct io_entity **entity) { int result = IO_FAIL; assert(entity != NULL); if (entity_count < MAX_IO_HANDLES) { unsigned int index = 0; result = find_first_entity(NULL, &index); assert(result == IO_SUCCESS); *entity = entity_map[index] = &entity_pool[index]; ++entity_count; } else result = IO_RESOURCES_EXHAUSTED; return result; } /* Release an entity back to the pool */ static int free_entity(struct io_entity *entity) { int result = IO_FAIL; unsigned int index = 0; assert(entity != NULL); result = find_first_entity(entity, &index); if (result == IO_SUCCESS) { entity_map[index] = NULL; --entity_count; } return result; } /* Exported API */ /* Initialise the IO layer */ void io_init(struct io_plat_data *data) { assert(data != NULL); platform_data = data; } /* Register a device driver */ int io_register_device(struct io_dev_info *dev_info) { int result = IO_FAIL; assert(dev_info != NULL); assert(platform_data != NULL); unsigned int dev_count = platform_data->dev_count; if (dev_count < MAX_DEVICES(platform_data)) { platform_data->devices[dev_count] = dev_info; platform_data->dev_count++; result = IO_SUCCESS; } else { result = IO_RESOURCES_EXHAUSTED; } return result; } /* Open a connection to an IO device */ int io_dev_open(struct io_dev_connector *dev_con, void *dev_spec, io_dev_handle *handle) { int result = IO_FAIL; assert(handle != NULL); result = dev_open(dev_con, dev_spec, handle); return result; } /* Initialise an IO device explicitly - to permit lazy initialisation or * re-initialisation */ int io_dev_init(struct io_dev_info *dev_handle, const void *init_params) { int result = IO_FAIL; assert(dev_handle != NULL); assert(is_valid_dev(dev_handle)); struct io_dev_info *dev = dev_handle; if (dev->funcs->dev_init != NULL) { result = dev->funcs->dev_init(dev, init_params); } else { /* Absence of registered function implies NOP here */ result = IO_SUCCESS; } return result; } /* TODO: Consider whether an explicit "shutdown" API should be included */ /* Close a connection to a device */ int io_dev_close(io_dev_handle dev_handle) { int result = IO_FAIL; assert(dev_handle != NULL); assert(is_valid_dev(dev_handle)); struct io_dev_info *dev = dev_handle; if (dev->funcs->dev_close != NULL) { result = dev->funcs->dev_close(dev); } else { /* Absence of registered function implies NOP here */ result = IO_SUCCESS; } return result; } /* Synchronous operations */ /* Open an IO entity */ int io_open(io_dev_handle dev_handle, const void *spec, io_handle *handle) { int result = IO_FAIL; assert((spec != NULL) && (handle != NULL)); assert(is_valid_dev(dev_handle)); struct io_dev_info *dev = dev_handle; struct io_entity *entity; result = allocate_entity(&entity); if (result == IO_SUCCESS) { assert(dev->funcs->open != NULL); result = dev->funcs->open(dev, spec, entity); if (result == IO_SUCCESS) { entity->dev_handle = dev_handle; set_handle(handle, entity); } else free_entity(entity); } return result; } /* Seek to a specific position in an IO entity */ int io_seek(io_handle handle, io_seek_mode mode, ssize_t offset) { int result = IO_FAIL; assert(is_valid_entity(handle) && is_valid_seek_mode(mode)); struct io_entity *entity = handle; struct io_dev_info *dev = entity->dev_handle; if (dev->funcs->seek != NULL) result = dev->funcs->seek(entity, mode, offset); else result = IO_NOT_SUPPORTED; return result; } /* Determine the length of an IO entity */ int io_size(io_handle handle, size_t *length) { int result = IO_FAIL; assert(is_valid_entity(handle) && (length != NULL)); struct io_entity *entity = handle; struct io_dev_info *dev = entity->dev_handle; if (dev->funcs->size != NULL) result = dev->funcs->size(entity, length); else result = IO_NOT_SUPPORTED; return result; } /* Read data from an IO entity */ int io_read(io_handle handle, void *buffer, size_t length, size_t *length_read) { int result = IO_FAIL; assert(is_valid_entity(handle) && (buffer != NULL)); struct io_entity *entity = handle; struct io_dev_info *dev = entity->dev_handle; if (dev->funcs->read != NULL) result = dev->funcs->read(entity, buffer, length, length_read); else result = IO_NOT_SUPPORTED; return result; } /* Write data to an IO entity */ int io_write(io_handle handle, const void *buffer, size_t length, size_t *length_written) { int result = IO_FAIL; assert(is_valid_entity(handle) && (buffer != NULL)); struct io_entity *entity = handle; struct io_dev_info *dev = entity->dev_handle; if (dev->funcs->write != NULL) { result = dev->funcs->write(entity, buffer, length, length_written); } else result = IO_NOT_SUPPORTED; return result; } /* Close an IO entity */ int io_close(io_handle handle) { int result = IO_FAIL; assert(is_valid_entity(handle)); struct io_entity *entity = handle; struct io_dev_info *dev = entity->dev_handle; if (dev->funcs->close != NULL) result = dev->funcs->close(entity); else { /* Absence of registered function implies NOP here */ result = IO_SUCCESS; } /* Ignore improbable free_entity failure */ (void)free_entity(entity); return result; }