/* * Copyright (c) 2014-2017, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ #include #include #include #include #include /* Storage for a fixed maximum number of IO entities, definable by platform */ static io_entity_t entity_pool[MAX_IO_HANDLES]; /* Simple way of tracking used storage - each entry is NULL or a pointer to an * entity */ static io_entity_t *entity_map[MAX_IO_HANDLES]; /* Track number of allocated entities */ static unsigned int entity_count; /* Array of fixed maximum of registered devices, definable by platform */ static const io_dev_info_t *devices[MAX_IO_DEVICES]; /* Number of currently registered devices */ static unsigned int dev_count; /* Extra validation functions only used when asserts are enabled */ #if ENABLE_ASSERTIONS /* Return a boolean value indicating whether a device connector is valid */ static int is_valid_dev_connector(const io_dev_connector_t *dev_con) { return (dev_con != NULL) && (dev_con->dev_open != NULL); } /* Return a boolean value indicating whether a device handle is valid */ static int is_valid_dev(const uintptr_t dev_handle) { const io_dev_info_t *dev = (io_dev_info_t *)dev_handle; return (dev != NULL) && (dev->funcs != NULL) && (dev->funcs->type != NULL) && (dev->funcs->type() < IO_TYPE_MAX); } /* Return a boolean value indicating whether an IO entity is valid */ static int is_valid_entity(const uintptr_t handle) { const io_entity_t *entity = (io_entity_t *)handle; return (entity != NULL) && (is_valid_dev((uintptr_t)entity->dev_handle)); } /* Return a boolean value indicating whether a seek mode is valid */ static int is_valid_seek_mode(io_seek_mode_t mode) { return ((mode != IO_SEEK_INVALID) && (mode < IO_SEEK_MAX)); } #endif /* ENABLE_ASSERTIONS */ /* End of extra validation functions only used when asserts are enabled */ /* Open a connection to a specific device */ static int dev_open(const io_dev_connector_t *dev_con, const uintptr_t dev_spec, io_dev_info_t **dev_info) { assert(dev_info != NULL); assert(is_valid_dev_connector(dev_con)); return dev_con->dev_open(dev_spec, dev_info); } /* Set a handle to track an entity */ static void set_handle(uintptr_t *handle, io_entity_t *entity) { assert(handle != NULL); *handle = (uintptr_t)entity; } /* Locate an entity in the pool, specified by address */ static int find_first_entity(const io_entity_t *entity, unsigned int *index_out) { int result = -ENOENT; for (unsigned int index = 0; index < MAX_IO_HANDLES; ++index) { if (entity_map[index] == entity) { result = 0; *index_out = index; break; } } return result; } /* Allocate an entity from the pool and return a pointer to it */ static int allocate_entity(io_entity_t **entity) { int result = -ENOMEM; assert(entity != NULL); if (entity_count < MAX_IO_HANDLES) { unsigned int index = 0; result = find_first_entity(NULL, &index); assert(result == 0); *entity = entity_map[index] = &entity_pool[index]; ++entity_count; } return result; } /* Release an entity back to the pool */ static int free_entity(const io_entity_t *entity) { int result; unsigned int index = 0; assert(entity != NULL); result = find_first_entity(entity, &index); if (result == 0) { entity_map[index] = NULL; --entity_count; } return result; } /* Exported API */ /* Register a device driver */ int io_register_device(const io_dev_info_t *dev_info) { int result = -ENOMEM; assert(dev_info != NULL); if (dev_count < MAX_IO_DEVICES) { devices[dev_count] = dev_info; dev_count++; result = 0; } return result; } /* Open a connection to an IO device */ int io_dev_open(const io_dev_connector_t *dev_con, const uintptr_t dev_spec, uintptr_t *handle) { assert(handle != NULL); return dev_open(dev_con, dev_spec, (io_dev_info_t **)handle); } /* Initialise an IO device explicitly - to permit lazy initialisation or * re-initialisation */ int io_dev_init(uintptr_t dev_handle, const uintptr_t init_params) { int result = 0; assert(dev_handle != (uintptr_t)NULL); assert(is_valid_dev(dev_handle)); io_dev_info_t *dev = (io_dev_info_t *)dev_handle; /* Absence of registered function implies NOP here */ if (dev->funcs->dev_init != NULL) { result = dev->funcs->dev_init(dev, init_params); } return result; } /* Close a connection to a device */ int io_dev_close(uintptr_t dev_handle) { int result = 0; assert(dev_handle != (uintptr_t)NULL); assert(is_valid_dev(dev_handle)); io_dev_info_t *dev = (io_dev_info_t *)dev_handle; /* Absence of registered function implies NOP here */ if (dev->funcs->dev_close != NULL) { result = dev->funcs->dev_close(dev); } return result; } /* Synchronous operations */ /* Open an IO entity */ int io_open(uintptr_t dev_handle, const uintptr_t spec, uintptr_t *handle) { int result; assert((spec != (uintptr_t)NULL) && (handle != NULL)); assert(is_valid_dev(dev_handle)); io_dev_info_t *dev = (io_dev_info_t *)dev_handle; io_entity_t *entity; result = allocate_entity(&entity); if (result == 0) { assert(dev->funcs->open != NULL); result = dev->funcs->open(dev, spec, entity); if (result == 0) { entity->dev_handle = dev; set_handle(handle, entity); } else free_entity(entity); } return result; } /* Seek to a specific position in an IO entity */ int io_seek(uintptr_t handle, io_seek_mode_t mode, signed long long offset) { int result = -ENODEV; assert(is_valid_entity(handle) && is_valid_seek_mode(mode)); io_entity_t *entity = (io_entity_t *)handle; io_dev_info_t *dev = entity->dev_handle; if (dev->funcs->seek != NULL) result = dev->funcs->seek(entity, mode, offset); return result; } /* Determine the length of an IO entity */ int io_size(uintptr_t handle, size_t *length) { int result = -ENODEV; assert(is_valid_entity(handle) && (length != NULL)); io_entity_t *entity = (io_entity_t *)handle; io_dev_info_t *dev = entity->dev_handle; if (dev->funcs->size != NULL) result = dev->funcs->size(entity, length); return result; } /* Read data from an IO entity */ int io_read(uintptr_t handle, uintptr_t buffer, size_t length, size_t *length_read) { int result = -ENODEV; assert(is_valid_entity(handle)); io_entity_t *entity = (io_entity_t *)handle; io_dev_info_t *dev = entity->dev_handle; if (dev->funcs->read != NULL) result = dev->funcs->read(entity, buffer, length, length_read); return result; } /* Write data to an IO entity */ int io_write(uintptr_t handle, const uintptr_t buffer, size_t length, size_t *length_written) { int result = -ENODEV; assert(is_valid_entity(handle)); io_entity_t *entity = (io_entity_t *)handle; io_dev_info_t *dev = entity->dev_handle; if (dev->funcs->write != NULL) { result = dev->funcs->write(entity, buffer, length, length_written); } return result; } /* Close an IO entity */ int io_close(uintptr_t handle) { int result = 0; assert(is_valid_entity(handle)); io_entity_t *entity = (io_entity_t *)handle; io_dev_info_t *dev = entity->dev_handle; /* Absence of registered function implies NOP here */ if (dev->funcs->close != NULL) result = dev->funcs->close(entity); /* Ignore improbable free_entity failure */ (void)free_entity(entity); return result; }