blob: 204310a42a536f0f95d28cd39ce8e05e8c1120d5 [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>
34#include <stddef.h>
James Morrisseyf2f9bb52014-02-10 16:18:59 +000035
36
37#define MAX_DEVICES(plat_data) \
38 (sizeof((plat_data)->devices)/sizeof((plat_data)->devices[0]))
39
40
41/* Storage for a fixed maximum number of IO entities, definable by platform */
Dan Handleye2712bc2014-04-10 15:37:22 +010042static io_entity_t entity_pool[MAX_IO_HANDLES];
James Morrisseyf2f9bb52014-02-10 16:18:59 +000043
44/* Simple way of tracking used storage - each entry is NULL or a pointer to an
45 * entity */
Dan Handleye2712bc2014-04-10 15:37:22 +010046static io_entity_t *entity_map[MAX_IO_HANDLES];
James Morrisseyf2f9bb52014-02-10 16:18:59 +000047
48/* Track number of allocated entities */
49static unsigned int entity_count;
50
51
52/* Used to keep a reference to platform-specific data */
Dan Handleye2712bc2014-04-10 15:37:22 +010053static io_plat_data_t *platform_data;
James Morrisseyf2f9bb52014-02-10 16:18:59 +000054
55
56#if DEBUG /* Extra validation functions only used in debug builds */
57
58/* Return a boolean value indicating whether a device connector is valid */
Dan Handleye2712bc2014-04-10 15:37:22 +010059static int is_valid_dev_connector(const io_dev_connector_t *dev_con)
James Morrisseyf2f9bb52014-02-10 16:18:59 +000060{
61 int result = (dev_con != NULL) && (dev_con->dev_open != NULL);
62 return result;
63}
64
65
66/* Return a boolean value indicating whether a device handle is valid */
Dan Handleya4cb68e2014-04-23 13:47:06 +010067static int is_valid_dev(const uintptr_t dev_handle)
James Morrisseyf2f9bb52014-02-10 16:18:59 +000068{
Dan Handleya4cb68e2014-04-23 13:47:06 +010069 const io_dev_info_t *dev = (io_dev_info_t *)dev_handle;
James Morrisseyf2f9bb52014-02-10 16:18:59 +000070 int result = (dev != NULL) && (dev->funcs != NULL) &&
71 (dev->funcs->type != NULL) &&
72 (dev->funcs->type() < IO_TYPE_MAX);
73 return result;
74}
75
76
77/* Return a boolean value indicating whether an IO entity is valid */
Dan Handleya4cb68e2014-04-23 13:47:06 +010078static int is_valid_entity(const uintptr_t handle)
James Morrisseyf2f9bb52014-02-10 16:18:59 +000079{
Dan Handleya4cb68e2014-04-23 13:47:06 +010080 const io_entity_t *entity = (io_entity_t *)handle;
81 int result = (entity != NULL) &&
82 (is_valid_dev((uintptr_t)entity->dev_handle));
James Morrisseyf2f9bb52014-02-10 16:18:59 +000083 return result;
84}
85
86
87/* Return a boolean value indicating whether a seek mode is valid */
Dan Handleye2712bc2014-04-10 15:37:22 +010088static int is_valid_seek_mode(io_seek_mode_t mode)
James Morrisseyf2f9bb52014-02-10 16:18:59 +000089{
90 return ((mode != IO_SEEK_INVALID) && (mode < IO_SEEK_MAX));
91}
92
93#endif /* End of debug-only validation functions */
94
95
96/* Open a connection to a specific device */
Dan Handleya4cb68e2014-04-23 13:47:06 +010097static int dev_open(const io_dev_connector_t *dev_con, const uintptr_t dev_spec,
Dan Handleye2712bc2014-04-10 15:37:22 +010098 io_dev_info_t **dev_info)
James Morrisseyf2f9bb52014-02-10 16:18:59 +000099{
100 int result = IO_FAIL;
101 assert(dev_info != NULL);
102 assert(is_valid_dev_connector(dev_con));
103
104 result = dev_con->dev_open(dev_spec, dev_info);
105 return result;
106}
107
108
109/* Set a handle to track an entity */
Dan Handleya4cb68e2014-04-23 13:47:06 +0100110static void set_handle(uintptr_t *handle, io_entity_t *entity)
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000111{
112 assert(handle != NULL);
Dan Handleya4cb68e2014-04-23 13:47:06 +0100113 *handle = (uintptr_t)entity;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000114}
115
116
117/* Locate an entity in the pool, specified by address */
Dan Handleya4cb68e2014-04-23 13:47:06 +0100118static int find_first_entity(const io_entity_t *entity, unsigned int *index_out)
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000119{
120 int result = IO_FAIL;
121 for (int index = 0; index < MAX_IO_HANDLES; ++index) {
122 if (entity_map[index] == entity) {
123 result = IO_SUCCESS;
124 *index_out = index;
125 break;
126 }
127 }
128 return result;
129}
130
131
132/* Allocate an entity from the pool and return a pointer to it */
Dan Handleya4cb68e2014-04-23 13:47:06 +0100133static int allocate_entity(io_entity_t **entity)
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000134{
135 int result = IO_FAIL;
136 assert(entity != NULL);
137
138 if (entity_count < MAX_IO_HANDLES) {
139 unsigned int index = 0;
140 result = find_first_entity(NULL, &index);
141 assert(result == IO_SUCCESS);
142 *entity = entity_map[index] = &entity_pool[index];
143 ++entity_count;
144 } else
145 result = IO_RESOURCES_EXHAUSTED;
146
147 return result;
148}
149
150
151/* Release an entity back to the pool */
Dan Handleya4cb68e2014-04-23 13:47:06 +0100152static int free_entity(const io_entity_t *entity)
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000153{
154 int result = IO_FAIL;
155 unsigned int index = 0;
156 assert(entity != NULL);
157
158 result = find_first_entity(entity, &index);
159 if (result == IO_SUCCESS) {
160 entity_map[index] = NULL;
161 --entity_count;
162 }
163
164 return result;
165}
166
167
168/* Exported API */
169
170
171/* Initialise the IO layer */
Dan Handleya4cb68e2014-04-23 13:47:06 +0100172void io_init(io_plat_data_t *data)
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000173{
174 assert(data != NULL);
175 platform_data = data;
176}
177
178
179/* Register a device driver */
Dan Handleya4cb68e2014-04-23 13:47:06 +0100180int io_register_device(const io_dev_info_t *dev_info)
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000181{
182 int result = IO_FAIL;
183 assert(dev_info != NULL);
184 assert(platform_data != NULL);
185
186 unsigned int dev_count = platform_data->dev_count;
187
188 if (dev_count < MAX_DEVICES(platform_data)) {
189 platform_data->devices[dev_count] = dev_info;
190 platform_data->dev_count++;
191 result = IO_SUCCESS;
192 } else {
193 result = IO_RESOURCES_EXHAUSTED;
194 }
195
196 return result;
197}
198
199
200/* Open a connection to an IO device */
Dan Handleya4cb68e2014-04-23 13:47:06 +0100201int io_dev_open(const io_dev_connector_t *dev_con, const uintptr_t dev_spec,
202 uintptr_t *handle)
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000203{
204 int result = IO_FAIL;
205 assert(handle != NULL);
206
Dan Handleya4cb68e2014-04-23 13:47:06 +0100207 result = dev_open(dev_con, dev_spec, (io_dev_info_t **)handle);
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000208 return result;
209}
210
211
212/* Initialise an IO device explicitly - to permit lazy initialisation or
213 * re-initialisation */
Dan Handleya4cb68e2014-04-23 13:47:06 +0100214int io_dev_init(uintptr_t dev_handle, const uintptr_t init_params)
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000215{
216 int result = IO_FAIL;
Dan Handleya4cb68e2014-04-23 13:47:06 +0100217 assert(dev_handle != (uintptr_t)NULL);
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000218 assert(is_valid_dev(dev_handle));
219
Dan Handleya4cb68e2014-04-23 13:47:06 +0100220 io_dev_info_t *dev = (io_dev_info_t *)dev_handle;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000221
222 if (dev->funcs->dev_init != NULL) {
223 result = dev->funcs->dev_init(dev, init_params);
224 } else {
225 /* Absence of registered function implies NOP here */
226 result = IO_SUCCESS;
227 }
228 return result;
229}
230
231
232/* TODO: Consider whether an explicit "shutdown" API should be included */
233
234/* Close a connection to a device */
Dan Handleya4cb68e2014-04-23 13:47:06 +0100235int io_dev_close(uintptr_t dev_handle)
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000236{
237 int result = IO_FAIL;
Dan Handleya4cb68e2014-04-23 13:47:06 +0100238 assert(dev_handle != (uintptr_t)NULL);
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000239 assert(is_valid_dev(dev_handle));
240
Dan Handleya4cb68e2014-04-23 13:47:06 +0100241 io_dev_info_t *dev = (io_dev_info_t *)dev_handle;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000242
243 if (dev->funcs->dev_close != NULL) {
244 result = dev->funcs->dev_close(dev);
245 } else {
246 /* Absence of registered function implies NOP here */
247 result = IO_SUCCESS;
248 }
249
250 return result;
251}
252
253
254/* Synchronous operations */
255
256
257/* Open an IO entity */
Dan Handleya4cb68e2014-04-23 13:47:06 +0100258int io_open(uintptr_t dev_handle, const uintptr_t spec, uintptr_t *handle)
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000259{
260 int result = IO_FAIL;
Dan Handleya4cb68e2014-04-23 13:47:06 +0100261 assert((spec != (uintptr_t)NULL) && (handle != NULL));
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000262 assert(is_valid_dev(dev_handle));
263
Dan Handleya4cb68e2014-04-23 13:47:06 +0100264 io_dev_info_t *dev = (io_dev_info_t *)dev_handle;
Dan Handleye2712bc2014-04-10 15:37:22 +0100265 io_entity_t *entity;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000266
267 result = allocate_entity(&entity);
268
269 if (result == IO_SUCCESS) {
270 assert(dev->funcs->open != NULL);
271 result = dev->funcs->open(dev, spec, entity);
272
273 if (result == IO_SUCCESS) {
Dan Handleya4cb68e2014-04-23 13:47:06 +0100274 entity->dev_handle = dev;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000275 set_handle(handle, entity);
276 } else
277 free_entity(entity);
278 }
279 return result;
280}
281
282
283/* Seek to a specific position in an IO entity */
Dan Handleya4cb68e2014-04-23 13:47:06 +0100284int io_seek(uintptr_t handle, io_seek_mode_t mode, ssize_t offset)
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000285{
286 int result = IO_FAIL;
287 assert(is_valid_entity(handle) && is_valid_seek_mode(mode));
288
Dan Handleya4cb68e2014-04-23 13:47:06 +0100289 io_entity_t *entity = (io_entity_t *)handle;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000290
Dan Handleye2712bc2014-04-10 15:37:22 +0100291 io_dev_info_t *dev = entity->dev_handle;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000292
293 if (dev->funcs->seek != NULL)
294 result = dev->funcs->seek(entity, mode, offset);
295 else
296 result = IO_NOT_SUPPORTED;
297
298 return result;
299}
300
301
302/* Determine the length of an IO entity */
Dan Handleya4cb68e2014-04-23 13:47:06 +0100303int io_size(uintptr_t handle, size_t *length)
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000304{
305 int result = IO_FAIL;
306 assert(is_valid_entity(handle) && (length != NULL));
307
Dan Handleya4cb68e2014-04-23 13:47:06 +0100308 io_entity_t *entity = (io_entity_t *)handle;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000309
Dan Handleye2712bc2014-04-10 15:37:22 +0100310 io_dev_info_t *dev = entity->dev_handle;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000311
312 if (dev->funcs->size != NULL)
313 result = dev->funcs->size(entity, length);
314 else
315 result = IO_NOT_SUPPORTED;
316
317 return result;
318}
319
320
321/* Read data from an IO entity */
Dan Handleya4cb68e2014-04-23 13:47:06 +0100322int io_read(uintptr_t handle,
323 uintptr_t buffer,
324 size_t length,
325 size_t *length_read)
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000326{
327 int result = IO_FAIL;
Dan Handleya4cb68e2014-04-23 13:47:06 +0100328 assert(is_valid_entity(handle) && (buffer != (uintptr_t)NULL));
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000329
Dan Handleya4cb68e2014-04-23 13:47:06 +0100330 io_entity_t *entity = (io_entity_t *)handle;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000331
Dan Handleye2712bc2014-04-10 15:37:22 +0100332 io_dev_info_t *dev = entity->dev_handle;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000333
334 if (dev->funcs->read != NULL)
335 result = dev->funcs->read(entity, buffer, length, length_read);
336 else
337 result = IO_NOT_SUPPORTED;
338
339 return result;
340}
341
342
343/* Write data to an IO entity */
Dan Handleya4cb68e2014-04-23 13:47:06 +0100344int io_write(uintptr_t handle,
345 const uintptr_t buffer,
346 size_t length,
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000347 size_t *length_written)
348{
349 int result = IO_FAIL;
Dan Handleya4cb68e2014-04-23 13:47:06 +0100350 assert(is_valid_entity(handle) && (buffer != (uintptr_t)NULL));
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000351
Dan Handleya4cb68e2014-04-23 13:47:06 +0100352 io_entity_t *entity = (io_entity_t *)handle;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000353
Dan Handleye2712bc2014-04-10 15:37:22 +0100354 io_dev_info_t *dev = entity->dev_handle;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000355
356 if (dev->funcs->write != NULL) {
357 result = dev->funcs->write(entity, buffer, length,
358 length_written);
359 } else
360 result = IO_NOT_SUPPORTED;
361
362 return result;
363}
364
365
366/* Close an IO entity */
Dan Handleya4cb68e2014-04-23 13:47:06 +0100367int io_close(uintptr_t handle)
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000368{
369 int result = IO_FAIL;
370 assert(is_valid_entity(handle));
371
Dan Handleya4cb68e2014-04-23 13:47:06 +0100372 io_entity_t *entity = (io_entity_t *)handle;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000373
Dan Handleye2712bc2014-04-10 15:37:22 +0100374 io_dev_info_t *dev = entity->dev_handle;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000375
376 if (dev->funcs->close != NULL)
377 result = dev->funcs->close(entity);
378 else {
379 /* Absence of registered function implies NOP here */
380 result = IO_SUCCESS;
381 }
382 /* Ignore improbable free_entity failure */
383 (void)free_entity(entity);
384
385 return result;
386}