blob: a3a8186d0fede11f1a327004ca6e84de56c8355a [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{
99 int result = IO_FAIL;
100 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{
119 int result = IO_FAIL;
120 for (int index = 0; index < MAX_IO_HANDLES; ++index) {
121 if (entity_map[index] == entity) {
122 result = IO_SUCCESS;
123 *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{
134 int result = IO_FAIL;
135 assert(entity != NULL);
136
137 if (entity_count < MAX_IO_HANDLES) {
138 unsigned int index = 0;
139 result = find_first_entity(NULL, &index);
140 assert(result == IO_SUCCESS);
141 *entity = entity_map[index] = &entity_pool[index];
142 ++entity_count;
143 } else
144 result = IO_RESOURCES_EXHAUSTED;
145
146 return result;
147}
148
149
150/* Release an entity back to the pool */
Dan Handleya4cb68e2014-04-23 13:47:06 +0100151static int free_entity(const io_entity_t *entity)
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000152{
153 int result = IO_FAIL;
154 unsigned int index = 0;
155 assert(entity != NULL);
156
157 result = find_first_entity(entity, &index);
158 if (result == IO_SUCCESS) {
159 entity_map[index] = NULL;
160 --entity_count;
161 }
162
163 return result;
164}
165
166
167/* Exported API */
168
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000169/* Register a device driver */
Dan Handleya4cb68e2014-04-23 13:47:06 +0100170int io_register_device(const io_dev_info_t *dev_info)
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000171{
172 int result = IO_FAIL;
173 assert(dev_info != NULL);
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000174
Dan Handley3aa92162014-08-04 18:31:43 +0100175 if (dev_count < MAX_IO_DEVICES) {
176 devices[dev_count] = dev_info;
177 dev_count++;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000178 result = IO_SUCCESS;
179 } else {
180 result = IO_RESOURCES_EXHAUSTED;
181 }
182
183 return result;
184}
185
186
187/* Open a connection to an IO device */
Dan Handleya4cb68e2014-04-23 13:47:06 +0100188int io_dev_open(const io_dev_connector_t *dev_con, const uintptr_t dev_spec,
189 uintptr_t *handle)
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000190{
191 int result = IO_FAIL;
192 assert(handle != NULL);
193
Dan Handleya4cb68e2014-04-23 13:47:06 +0100194 result = dev_open(dev_con, dev_spec, (io_dev_info_t **)handle);
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000195 return result;
196}
197
198
199/* Initialise an IO device explicitly - to permit lazy initialisation or
200 * re-initialisation */
Dan Handleya4cb68e2014-04-23 13:47:06 +0100201int io_dev_init(uintptr_t dev_handle, const uintptr_t init_params)
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000202{
203 int result = IO_FAIL;
Dan Handleya4cb68e2014-04-23 13:47:06 +0100204 assert(dev_handle != (uintptr_t)NULL);
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000205 assert(is_valid_dev(dev_handle));
206
Dan Handleya4cb68e2014-04-23 13:47:06 +0100207 io_dev_info_t *dev = (io_dev_info_t *)dev_handle;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000208
209 if (dev->funcs->dev_init != NULL) {
210 result = dev->funcs->dev_init(dev, init_params);
211 } else {
212 /* Absence of registered function implies NOP here */
213 result = IO_SUCCESS;
214 }
215 return result;
216}
217
218
219/* TODO: Consider whether an explicit "shutdown" API should be included */
220
221/* Close a connection to a device */
Dan Handleya4cb68e2014-04-23 13:47:06 +0100222int io_dev_close(uintptr_t dev_handle)
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000223{
224 int result = IO_FAIL;
Dan Handleya4cb68e2014-04-23 13:47:06 +0100225 assert(dev_handle != (uintptr_t)NULL);
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000226 assert(is_valid_dev(dev_handle));
227
Dan Handleya4cb68e2014-04-23 13:47:06 +0100228 io_dev_info_t *dev = (io_dev_info_t *)dev_handle;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000229
230 if (dev->funcs->dev_close != NULL) {
231 result = dev->funcs->dev_close(dev);
232 } else {
233 /* Absence of registered function implies NOP here */
234 result = IO_SUCCESS;
235 }
236
237 return result;
238}
239
240
241/* Synchronous operations */
242
243
244/* Open an IO entity */
Dan Handleya4cb68e2014-04-23 13:47:06 +0100245int io_open(uintptr_t dev_handle, const uintptr_t spec, uintptr_t *handle)
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000246{
247 int result = IO_FAIL;
Dan Handleya4cb68e2014-04-23 13:47:06 +0100248 assert((spec != (uintptr_t)NULL) && (handle != NULL));
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000249 assert(is_valid_dev(dev_handle));
250
Dan Handleya4cb68e2014-04-23 13:47:06 +0100251 io_dev_info_t *dev = (io_dev_info_t *)dev_handle;
Dan Handleye2712bc2014-04-10 15:37:22 +0100252 io_entity_t *entity;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000253
254 result = allocate_entity(&entity);
255
256 if (result == IO_SUCCESS) {
257 assert(dev->funcs->open != NULL);
258 result = dev->funcs->open(dev, spec, entity);
259
260 if (result == IO_SUCCESS) {
Dan Handleya4cb68e2014-04-23 13:47:06 +0100261 entity->dev_handle = dev;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000262 set_handle(handle, entity);
263 } else
264 free_entity(entity);
265 }
266 return result;
267}
268
269
270/* Seek to a specific position in an IO entity */
Dan Handleya4cb68e2014-04-23 13:47:06 +0100271int io_seek(uintptr_t handle, io_seek_mode_t mode, ssize_t offset)
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000272{
273 int result = IO_FAIL;
274 assert(is_valid_entity(handle) && is_valid_seek_mode(mode));
275
Dan Handleya4cb68e2014-04-23 13:47:06 +0100276 io_entity_t *entity = (io_entity_t *)handle;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000277
Dan Handleye2712bc2014-04-10 15:37:22 +0100278 io_dev_info_t *dev = entity->dev_handle;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000279
280 if (dev->funcs->seek != NULL)
281 result = dev->funcs->seek(entity, mode, offset);
282 else
283 result = IO_NOT_SUPPORTED;
284
285 return result;
286}
287
288
289/* Determine the length of an IO entity */
Dan Handleya4cb68e2014-04-23 13:47:06 +0100290int io_size(uintptr_t handle, size_t *length)
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000291{
292 int result = IO_FAIL;
293 assert(is_valid_entity(handle) && (length != NULL));
294
Dan Handleya4cb68e2014-04-23 13:47:06 +0100295 io_entity_t *entity = (io_entity_t *)handle;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000296
Dan Handleye2712bc2014-04-10 15:37:22 +0100297 io_dev_info_t *dev = entity->dev_handle;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000298
299 if (dev->funcs->size != NULL)
300 result = dev->funcs->size(entity, length);
301 else
302 result = IO_NOT_SUPPORTED;
303
304 return result;
305}
306
307
308/* Read data from an IO entity */
Dan Handleya4cb68e2014-04-23 13:47:06 +0100309int io_read(uintptr_t handle,
310 uintptr_t buffer,
311 size_t length,
312 size_t *length_read)
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000313{
314 int result = IO_FAIL;
Dan Handleya4cb68e2014-04-23 13:47:06 +0100315 assert(is_valid_entity(handle) && (buffer != (uintptr_t)NULL));
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000316
Dan Handleya4cb68e2014-04-23 13:47:06 +0100317 io_entity_t *entity = (io_entity_t *)handle;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000318
Dan Handleye2712bc2014-04-10 15:37:22 +0100319 io_dev_info_t *dev = entity->dev_handle;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000320
321 if (dev->funcs->read != NULL)
322 result = dev->funcs->read(entity, buffer, length, length_read);
323 else
324 result = IO_NOT_SUPPORTED;
325
326 return result;
327}
328
329
330/* Write data to an IO entity */
Dan Handleya4cb68e2014-04-23 13:47:06 +0100331int io_write(uintptr_t handle,
332 const uintptr_t buffer,
333 size_t length,
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000334 size_t *length_written)
335{
336 int result = IO_FAIL;
Dan Handleya4cb68e2014-04-23 13:47:06 +0100337 assert(is_valid_entity(handle) && (buffer != (uintptr_t)NULL));
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000338
Dan Handleya4cb68e2014-04-23 13:47:06 +0100339 io_entity_t *entity = (io_entity_t *)handle;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000340
Dan Handleye2712bc2014-04-10 15:37:22 +0100341 io_dev_info_t *dev = entity->dev_handle;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000342
343 if (dev->funcs->write != NULL) {
344 result = dev->funcs->write(entity, buffer, length,
345 length_written);
346 } else
347 result = IO_NOT_SUPPORTED;
348
349 return result;
350}
351
352
353/* Close an IO entity */
Dan Handleya4cb68e2014-04-23 13:47:06 +0100354int io_close(uintptr_t handle)
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000355{
356 int result = IO_FAIL;
357 assert(is_valid_entity(handle));
358
Dan Handleya4cb68e2014-04-23 13:47:06 +0100359 io_entity_t *entity = (io_entity_t *)handle;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000360
Dan Handleye2712bc2014-04-10 15:37:22 +0100361 io_dev_info_t *dev = entity->dev_handle;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000362
363 if (dev->funcs->close != NULL)
364 result = dev->funcs->close(entity);
365 else {
366 /* Absence of registered function implies NOP here */
367 result = IO_SUCCESS;
368 }
369 /* Ignore improbable free_entity failure */
370 (void)free_entity(entity);
371
372 return result;
373}