blob: 7cb1a6aa2e5988ed9755bb20e38de297f0843c96 [file] [log] [blame]
James Morrisseyf2f9bb52014-02-10 16:18:59 +00001/*
2 * Copyright (c) 2014, ARM Limited and Contributors. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are met:
6 *
7 * Redistributions of source code must retain the above copyright notice, this
8 * list of conditions and the following disclaimer.
9 *
10 * Redistributions in binary form must reproduce the above copyright notice,
11 * this list of conditions and the following disclaimer in the documentation
12 * and/or other materials provided with the distribution.
13 *
14 * Neither the name of ARM nor the names of its contributors may be used
15 * to endorse or promote products derived from this software without specific
16 * prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
22 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28 * POSSIBILITY OF SUCH DAMAGE.
29 */
30
James Morrisseyf2f9bb52014-02-10 16:18:59 +000031#include <assert.h>
Dan Handley714a0d22014-04-09 13:13:04 +010032#include <io_driver.h>
Dan Handley2bd4ef22014-04-09 13:14:54 +010033#include <io_storage.h>
Dan Handley3aa92162014-08-04 18:31:43 +010034#include <platform_def.h>
Dan Handley2bd4ef22014-04-09 13:14:54 +010035#include <stddef.h>
James Morrisseyf2f9bb52014-02-10 16:18:59 +000036
37
James Morrisseyf2f9bb52014-02-10 16:18:59 +000038/* Storage for a fixed maximum number of IO entities, definable by platform */
Dan Handleye2712bc2014-04-10 15:37:22 +010039static io_entity_t entity_pool[MAX_IO_HANDLES];
James Morrisseyf2f9bb52014-02-10 16:18:59 +000040
41/* Simple way of tracking used storage - each entry is NULL or a pointer to an
42 * entity */
Dan Handleye2712bc2014-04-10 15:37:22 +010043static io_entity_t *entity_map[MAX_IO_HANDLES];
James Morrisseyf2f9bb52014-02-10 16:18:59 +000044
45/* Track number of allocated entities */
46static unsigned int entity_count;
47
Dan Handley3aa92162014-08-04 18:31:43 +010048/* Array of fixed maximum of registered devices, definable by platform */
49static const io_dev_info_t *devices[MAX_IO_DEVICES];
James Morrisseyf2f9bb52014-02-10 16:18:59 +000050
Dan Handley3aa92162014-08-04 18:31:43 +010051/* Number of currently registered devices */
52static unsigned int dev_count;
James Morrisseyf2f9bb52014-02-10 16:18:59 +000053
54
55#if DEBUG /* Extra validation functions only used in debug builds */
56
57/* Return a boolean value indicating whether a device connector is valid */
Dan Handleye2712bc2014-04-10 15:37:22 +010058static int is_valid_dev_connector(const io_dev_connector_t *dev_con)
James Morrisseyf2f9bb52014-02-10 16:18:59 +000059{
60 int result = (dev_con != NULL) && (dev_con->dev_open != NULL);
61 return result;
62}
63
64
65/* Return a boolean value indicating whether a device handle is valid */
Dan Handleya4cb68e2014-04-23 13:47:06 +010066static int is_valid_dev(const uintptr_t dev_handle)
James Morrisseyf2f9bb52014-02-10 16:18:59 +000067{
Dan Handleya4cb68e2014-04-23 13:47:06 +010068 const io_dev_info_t *dev = (io_dev_info_t *)dev_handle;
James Morrisseyf2f9bb52014-02-10 16:18:59 +000069 int result = (dev != NULL) && (dev->funcs != NULL) &&
70 (dev->funcs->type != NULL) &&
71 (dev->funcs->type() < IO_TYPE_MAX);
72 return result;
73}
74
75
76/* Return a boolean value indicating whether an IO entity is valid */
Dan Handleya4cb68e2014-04-23 13:47:06 +010077static int is_valid_entity(const uintptr_t handle)
James Morrisseyf2f9bb52014-02-10 16:18:59 +000078{
Dan Handleya4cb68e2014-04-23 13:47:06 +010079 const io_entity_t *entity = (io_entity_t *)handle;
80 int result = (entity != NULL) &&
81 (is_valid_dev((uintptr_t)entity->dev_handle));
James Morrisseyf2f9bb52014-02-10 16:18:59 +000082 return result;
83}
84
85
86/* Return a boolean value indicating whether a seek mode is valid */
Dan Handleye2712bc2014-04-10 15:37:22 +010087static int is_valid_seek_mode(io_seek_mode_t mode)
James Morrisseyf2f9bb52014-02-10 16:18:59 +000088{
89 return ((mode != IO_SEEK_INVALID) && (mode < IO_SEEK_MAX));
90}
91
92#endif /* End of debug-only validation functions */
93
94
95/* Open a connection to a specific device */
Dan Handleya4cb68e2014-04-23 13:47:06 +010096static int dev_open(const io_dev_connector_t *dev_con, const uintptr_t dev_spec,
Dan Handleye2712bc2014-04-10 15:37:22 +010097 io_dev_info_t **dev_info)
James Morrisseyf2f9bb52014-02-10 16:18:59 +000098{
Juan Castillo6e762062015-11-02 10:47:01 +000099 int result;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000100 assert(dev_info != NULL);
101 assert(is_valid_dev_connector(dev_con));
102
103 result = dev_con->dev_open(dev_spec, dev_info);
104 return result;
105}
106
107
108/* Set a handle to track an entity */
Dan Handleya4cb68e2014-04-23 13:47:06 +0100109static void set_handle(uintptr_t *handle, io_entity_t *entity)
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000110{
111 assert(handle != NULL);
Dan Handleya4cb68e2014-04-23 13:47:06 +0100112 *handle = (uintptr_t)entity;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000113}
114
115
116/* Locate an entity in the pool, specified by address */
Dan Handleya4cb68e2014-04-23 13:47:06 +0100117static int find_first_entity(const io_entity_t *entity, unsigned int *index_out)
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000118{
Juan Castillo6e762062015-11-02 10:47:01 +0000119 int result = -ENOENT;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000120 for (int index = 0; index < MAX_IO_HANDLES; ++index) {
121 if (entity_map[index] == entity) {
Juan Castillo6e762062015-11-02 10:47:01 +0000122 result = 0;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000123 *index_out = index;
124 break;
125 }
126 }
127 return result;
128}
129
130
131/* Allocate an entity from the pool and return a pointer to it */
Dan Handleya4cb68e2014-04-23 13:47:06 +0100132static int allocate_entity(io_entity_t **entity)
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000133{
Juan Castillo6e762062015-11-02 10:47:01 +0000134 int result = -ENOMEM;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000135 assert(entity != NULL);
136
137 if (entity_count < MAX_IO_HANDLES) {
138 unsigned int index = 0;
139 result = find_first_entity(NULL, &index);
Juan Castillo6e762062015-11-02 10:47:01 +0000140 assert(result == 0);
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000141 *entity = entity_map[index] = &entity_pool[index];
142 ++entity_count;
Juan Castillo6e762062015-11-02 10:47:01 +0000143 }
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000144
145 return result;
146}
147
148
149/* Release an entity back to the pool */
Dan Handleya4cb68e2014-04-23 13:47:06 +0100150static int free_entity(const io_entity_t *entity)
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000151{
Juan Castillo6e762062015-11-02 10:47:01 +0000152 int result;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000153 unsigned int index = 0;
154 assert(entity != NULL);
155
156 result = find_first_entity(entity, &index);
Juan Castillo6e762062015-11-02 10:47:01 +0000157 if (result == 0) {
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000158 entity_map[index] = NULL;
159 --entity_count;
160 }
161
162 return result;
163}
164
165
166/* Exported API */
167
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000168/* Register a device driver */
Dan Handleya4cb68e2014-04-23 13:47:06 +0100169int io_register_device(const io_dev_info_t *dev_info)
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000170{
Juan Castillo6e762062015-11-02 10:47:01 +0000171 int result = -ENOMEM;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000172 assert(dev_info != NULL);
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000173
Dan Handley3aa92162014-08-04 18:31:43 +0100174 if (dev_count < MAX_IO_DEVICES) {
175 devices[dev_count] = dev_info;
176 dev_count++;
Juan Castillo6e762062015-11-02 10:47:01 +0000177 result = 0;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000178 }
179
180 return result;
181}
182
183
184/* Open a connection to an IO device */
Dan Handleya4cb68e2014-04-23 13:47:06 +0100185int io_dev_open(const io_dev_connector_t *dev_con, const uintptr_t dev_spec,
186 uintptr_t *handle)
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000187{
Juan Castillo6e762062015-11-02 10:47:01 +0000188 int result;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000189 assert(handle != NULL);
190
Dan Handleya4cb68e2014-04-23 13:47:06 +0100191 result = dev_open(dev_con, dev_spec, (io_dev_info_t **)handle);
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000192 return result;
193}
194
195
196/* Initialise an IO device explicitly - to permit lazy initialisation or
197 * re-initialisation */
Dan Handleya4cb68e2014-04-23 13:47:06 +0100198int io_dev_init(uintptr_t dev_handle, const uintptr_t init_params)
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000199{
Juan Castillo6e762062015-11-02 10:47:01 +0000200 int result = 0;
Dan Handleya4cb68e2014-04-23 13:47:06 +0100201 assert(dev_handle != (uintptr_t)NULL);
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000202 assert(is_valid_dev(dev_handle));
203
Dan Handleya4cb68e2014-04-23 13:47:06 +0100204 io_dev_info_t *dev = (io_dev_info_t *)dev_handle;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000205
Juan Castillo6e762062015-11-02 10:47:01 +0000206 /* Absence of registered function implies NOP here */
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000207 if (dev->funcs->dev_init != NULL) {
208 result = dev->funcs->dev_init(dev, init_params);
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000209 }
Juan Castillo6e762062015-11-02 10:47:01 +0000210
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000211 return result;
212}
213
214
215/* TODO: Consider whether an explicit "shutdown" API should be included */
216
217/* Close a connection to a device */
Dan Handleya4cb68e2014-04-23 13:47:06 +0100218int io_dev_close(uintptr_t dev_handle)
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000219{
Juan Castillo6e762062015-11-02 10:47:01 +0000220 int result = 0;
Dan Handleya4cb68e2014-04-23 13:47:06 +0100221 assert(dev_handle != (uintptr_t)NULL);
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000222 assert(is_valid_dev(dev_handle));
223
Dan Handleya4cb68e2014-04-23 13:47:06 +0100224 io_dev_info_t *dev = (io_dev_info_t *)dev_handle;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000225
Juan Castillo6e762062015-11-02 10:47:01 +0000226 /* Absence of registered function implies NOP here */
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000227 if (dev->funcs->dev_close != NULL) {
228 result = dev->funcs->dev_close(dev);
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000229 }
230
231 return result;
232}
233
234
235/* Synchronous operations */
236
237
238/* Open an IO entity */
Dan Handleya4cb68e2014-04-23 13:47:06 +0100239int io_open(uintptr_t dev_handle, const uintptr_t spec, uintptr_t *handle)
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000240{
Juan Castillo6e762062015-11-02 10:47:01 +0000241 int result;
Dan Handleya4cb68e2014-04-23 13:47:06 +0100242 assert((spec != (uintptr_t)NULL) && (handle != NULL));
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000243 assert(is_valid_dev(dev_handle));
244
Dan Handleya4cb68e2014-04-23 13:47:06 +0100245 io_dev_info_t *dev = (io_dev_info_t *)dev_handle;
Dan Handleye2712bc2014-04-10 15:37:22 +0100246 io_entity_t *entity;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000247
248 result = allocate_entity(&entity);
249
Juan Castillo6e762062015-11-02 10:47:01 +0000250 if (result == 0) {
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000251 assert(dev->funcs->open != NULL);
252 result = dev->funcs->open(dev, spec, entity);
253
Juan Castillo6e762062015-11-02 10:47:01 +0000254 if (result == 0) {
Dan Handleya4cb68e2014-04-23 13:47:06 +0100255 entity->dev_handle = dev;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000256 set_handle(handle, entity);
257 } else
258 free_entity(entity);
259 }
260 return result;
261}
262
263
264/* Seek to a specific position in an IO entity */
Dan Handleya4cb68e2014-04-23 13:47:06 +0100265int io_seek(uintptr_t handle, io_seek_mode_t mode, ssize_t offset)
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000266{
Juan Castillo6e762062015-11-02 10:47:01 +0000267 int result = -ENODEV;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000268 assert(is_valid_entity(handle) && is_valid_seek_mode(mode));
269
Dan Handleya4cb68e2014-04-23 13:47:06 +0100270 io_entity_t *entity = (io_entity_t *)handle;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000271
Dan Handleye2712bc2014-04-10 15:37:22 +0100272 io_dev_info_t *dev = entity->dev_handle;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000273
274 if (dev->funcs->seek != NULL)
275 result = dev->funcs->seek(entity, mode, offset);
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000276
277 return result;
278}
279
280
281/* Determine the length of an IO entity */
Dan Handleya4cb68e2014-04-23 13:47:06 +0100282int io_size(uintptr_t handle, size_t *length)
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000283{
Juan Castillo6e762062015-11-02 10:47:01 +0000284 int result = -ENODEV;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000285 assert(is_valid_entity(handle) && (length != NULL));
286
Dan Handleya4cb68e2014-04-23 13:47:06 +0100287 io_entity_t *entity = (io_entity_t *)handle;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000288
Dan Handleye2712bc2014-04-10 15:37:22 +0100289 io_dev_info_t *dev = entity->dev_handle;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000290
291 if (dev->funcs->size != NULL)
292 result = dev->funcs->size(entity, length);
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000293
294 return result;
295}
296
297
298/* Read data from an IO entity */
Dan Handleya4cb68e2014-04-23 13:47:06 +0100299int io_read(uintptr_t handle,
300 uintptr_t buffer,
301 size_t length,
302 size_t *length_read)
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000303{
Juan Castillo6e762062015-11-02 10:47:01 +0000304 int result = -ENODEV;
Dan Handleya4cb68e2014-04-23 13:47:06 +0100305 assert(is_valid_entity(handle) && (buffer != (uintptr_t)NULL));
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000306
Dan Handleya4cb68e2014-04-23 13:47:06 +0100307 io_entity_t *entity = (io_entity_t *)handle;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000308
Dan Handleye2712bc2014-04-10 15:37:22 +0100309 io_dev_info_t *dev = entity->dev_handle;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000310
311 if (dev->funcs->read != NULL)
312 result = dev->funcs->read(entity, buffer, length, length_read);
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000313
314 return result;
315}
316
317
318/* Write data to an IO entity */
Dan Handleya4cb68e2014-04-23 13:47:06 +0100319int io_write(uintptr_t handle,
320 const uintptr_t buffer,
321 size_t length,
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000322 size_t *length_written)
323{
Juan Castillo6e762062015-11-02 10:47:01 +0000324 int result = -ENODEV;
Dan Handleya4cb68e2014-04-23 13:47:06 +0100325 assert(is_valid_entity(handle) && (buffer != (uintptr_t)NULL));
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000326
Dan Handleya4cb68e2014-04-23 13:47:06 +0100327 io_entity_t *entity = (io_entity_t *)handle;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000328
Dan Handleye2712bc2014-04-10 15:37:22 +0100329 io_dev_info_t *dev = entity->dev_handle;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000330
331 if (dev->funcs->write != NULL) {
332 result = dev->funcs->write(entity, buffer, length,
333 length_written);
Juan Castillo6e762062015-11-02 10:47:01 +0000334 }
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000335
336 return result;
337}
338
339
340/* Close an IO entity */
Dan Handleya4cb68e2014-04-23 13:47:06 +0100341int io_close(uintptr_t handle)
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000342{
Juan Castillo6e762062015-11-02 10:47:01 +0000343 int result = 0;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000344 assert(is_valid_entity(handle));
345
Dan Handleya4cb68e2014-04-23 13:47:06 +0100346 io_entity_t *entity = (io_entity_t *)handle;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000347
Dan Handleye2712bc2014-04-10 15:37:22 +0100348 io_dev_info_t *dev = entity->dev_handle;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000349
Juan Castillo6e762062015-11-02 10:47:01 +0000350 /* Absence of registered function implies NOP here */
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000351 if (dev->funcs->close != NULL)
352 result = dev->funcs->close(entity);
Juan Castillo6e762062015-11-02 10:47:01 +0000353
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000354 /* Ignore improbable free_entity failure */
355 (void)free_entity(entity);
356
357 return result;
358}