blob: 948f84813768d8c2204d4496cacd7adf9c1dbe4c [file] [log] [blame]
James Morrisseyf2f9bb52014-02-10 16:18:59 +00001/*
Antonio Nino Diaz3759e3f2017-03-22 15:48:51 +00002 * Copyright (c) 2014-2017, ARM Limited and Contributors. All rights reserved.
James Morrisseyf2f9bb52014-02-10 16:18:59 +00003 *
dp-armfa3cf0b2017-05-03 09:38:09 +01004 * SPDX-License-Identifier: BSD-3-Clause
James Morrisseyf2f9bb52014-02-10 16:18:59 +00005 */
6
James Morrisseyf2f9bb52014-02-10 16:18:59 +00007#include <assert.h>
Dan Handley714a0d22014-04-09 13:13:04 +01008#include <io_driver.h>
Dan Handley2bd4ef22014-04-09 13:14:54 +01009#include <io_storage.h>
Dan Handley3aa92162014-08-04 18:31:43 +010010#include <platform_def.h>
Dan Handley2bd4ef22014-04-09 13:14:54 +010011#include <stddef.h>
James Morrisseyf2f9bb52014-02-10 16:18:59 +000012
13
James Morrisseyf2f9bb52014-02-10 16:18:59 +000014/* Storage for a fixed maximum number of IO entities, definable by platform */
Dan Handleye2712bc2014-04-10 15:37:22 +010015static io_entity_t entity_pool[MAX_IO_HANDLES];
James Morrisseyf2f9bb52014-02-10 16:18:59 +000016
17/* Simple way of tracking used storage - each entry is NULL or a pointer to an
18 * entity */
Dan Handleye2712bc2014-04-10 15:37:22 +010019static io_entity_t *entity_map[MAX_IO_HANDLES];
James Morrisseyf2f9bb52014-02-10 16:18:59 +000020
21/* Track number of allocated entities */
22static unsigned int entity_count;
23
Dan Handley3aa92162014-08-04 18:31:43 +010024/* Array of fixed maximum of registered devices, definable by platform */
25static const io_dev_info_t *devices[MAX_IO_DEVICES];
James Morrisseyf2f9bb52014-02-10 16:18:59 +000026
Dan Handley3aa92162014-08-04 18:31:43 +010027/* Number of currently registered devices */
28static unsigned int dev_count;
James Morrisseyf2f9bb52014-02-10 16:18:59 +000029
Antonio Nino Diaz3759e3f2017-03-22 15:48:51 +000030/* Extra validation functions only used when asserts are enabled */
31#if ENABLE_ASSERTIONS
James Morrisseyf2f9bb52014-02-10 16:18:59 +000032
33/* Return a boolean value indicating whether a device connector is valid */
Dan Handleye2712bc2014-04-10 15:37:22 +010034static int is_valid_dev_connector(const io_dev_connector_t *dev_con)
James Morrisseyf2f9bb52014-02-10 16:18:59 +000035{
36 int result = (dev_con != NULL) && (dev_con->dev_open != NULL);
37 return result;
38}
39
40
41/* Return a boolean value indicating whether a device handle is valid */
Dan Handleya4cb68e2014-04-23 13:47:06 +010042static int is_valid_dev(const uintptr_t dev_handle)
James Morrisseyf2f9bb52014-02-10 16:18:59 +000043{
Dan Handleya4cb68e2014-04-23 13:47:06 +010044 const io_dev_info_t *dev = (io_dev_info_t *)dev_handle;
James Morrisseyf2f9bb52014-02-10 16:18:59 +000045 int result = (dev != NULL) && (dev->funcs != NULL) &&
46 (dev->funcs->type != NULL) &&
47 (dev->funcs->type() < IO_TYPE_MAX);
48 return result;
49}
50
51
52/* Return a boolean value indicating whether an IO entity is valid */
Dan Handleya4cb68e2014-04-23 13:47:06 +010053static int is_valid_entity(const uintptr_t handle)
James Morrisseyf2f9bb52014-02-10 16:18:59 +000054{
Dan Handleya4cb68e2014-04-23 13:47:06 +010055 const io_entity_t *entity = (io_entity_t *)handle;
56 int result = (entity != NULL) &&
57 (is_valid_dev((uintptr_t)entity->dev_handle));
James Morrisseyf2f9bb52014-02-10 16:18:59 +000058 return result;
59}
60
61
62/* Return a boolean value indicating whether a seek mode is valid */
Dan Handleye2712bc2014-04-10 15:37:22 +010063static int is_valid_seek_mode(io_seek_mode_t mode)
James Morrisseyf2f9bb52014-02-10 16:18:59 +000064{
65 return ((mode != IO_SEEK_INVALID) && (mode < IO_SEEK_MAX));
66}
67
Antonio Nino Diaz3759e3f2017-03-22 15:48:51 +000068#endif /* ENABLE_ASSERTIONS */
69/* End of extra validation functions only used when asserts are enabled */
James Morrisseyf2f9bb52014-02-10 16:18:59 +000070
71
72/* Open a connection to a specific device */
Dan Handleya4cb68e2014-04-23 13:47:06 +010073static int dev_open(const io_dev_connector_t *dev_con, const uintptr_t dev_spec,
Dan Handleye2712bc2014-04-10 15:37:22 +010074 io_dev_info_t **dev_info)
James Morrisseyf2f9bb52014-02-10 16:18:59 +000075{
Juan Castillo6e762062015-11-02 10:47:01 +000076 int result;
James Morrisseyf2f9bb52014-02-10 16:18:59 +000077 assert(dev_info != NULL);
78 assert(is_valid_dev_connector(dev_con));
79
80 result = dev_con->dev_open(dev_spec, dev_info);
81 return result;
82}
83
84
85/* Set a handle to track an entity */
Dan Handleya4cb68e2014-04-23 13:47:06 +010086static void set_handle(uintptr_t *handle, io_entity_t *entity)
James Morrisseyf2f9bb52014-02-10 16:18:59 +000087{
88 assert(handle != NULL);
Dan Handleya4cb68e2014-04-23 13:47:06 +010089 *handle = (uintptr_t)entity;
James Morrisseyf2f9bb52014-02-10 16:18:59 +000090}
91
92
93/* Locate an entity in the pool, specified by address */
Dan Handleya4cb68e2014-04-23 13:47:06 +010094static int find_first_entity(const io_entity_t *entity, unsigned int *index_out)
James Morrisseyf2f9bb52014-02-10 16:18:59 +000095{
Juan Castillo6e762062015-11-02 10:47:01 +000096 int result = -ENOENT;
Etienne Carriere48708f02017-06-07 16:43:19 +020097 for (unsigned int index = 0; index < MAX_IO_HANDLES; ++index) {
James Morrisseyf2f9bb52014-02-10 16:18:59 +000098 if (entity_map[index] == entity) {
Juan Castillo6e762062015-11-02 10:47:01 +000099 result = 0;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000100 *index_out = index;
101 break;
102 }
103 }
104 return result;
105}
106
107
108/* Allocate an entity from the pool and return a pointer to it */
Dan Handleya4cb68e2014-04-23 13:47:06 +0100109static int allocate_entity(io_entity_t **entity)
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000110{
Juan Castillo6e762062015-11-02 10:47:01 +0000111 int result = -ENOMEM;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000112 assert(entity != NULL);
113
114 if (entity_count < MAX_IO_HANDLES) {
115 unsigned int index = 0;
116 result = find_first_entity(NULL, &index);
Juan Castillo6e762062015-11-02 10:47:01 +0000117 assert(result == 0);
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000118 *entity = entity_map[index] = &entity_pool[index];
119 ++entity_count;
Juan Castillo6e762062015-11-02 10:47:01 +0000120 }
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000121
122 return result;
123}
124
125
126/* Release an entity back to the pool */
Dan Handleya4cb68e2014-04-23 13:47:06 +0100127static int free_entity(const io_entity_t *entity)
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000128{
Juan Castillo6e762062015-11-02 10:47:01 +0000129 int result;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000130 unsigned int index = 0;
131 assert(entity != NULL);
132
133 result = find_first_entity(entity, &index);
Juan Castillo6e762062015-11-02 10:47:01 +0000134 if (result == 0) {
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000135 entity_map[index] = NULL;
136 --entity_count;
137 }
138
139 return result;
140}
141
142
143/* Exported API */
144
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000145/* Register a device driver */
Dan Handleya4cb68e2014-04-23 13:47:06 +0100146int io_register_device(const io_dev_info_t *dev_info)
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000147{
Juan Castillo6e762062015-11-02 10:47:01 +0000148 int result = -ENOMEM;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000149 assert(dev_info != NULL);
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000150
Dan Handley3aa92162014-08-04 18:31:43 +0100151 if (dev_count < MAX_IO_DEVICES) {
152 devices[dev_count] = dev_info;
153 dev_count++;
Juan Castillo6e762062015-11-02 10:47:01 +0000154 result = 0;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000155 }
156
157 return result;
158}
159
160
161/* Open a connection to an IO device */
Dan Handleya4cb68e2014-04-23 13:47:06 +0100162int io_dev_open(const io_dev_connector_t *dev_con, const uintptr_t dev_spec,
163 uintptr_t *handle)
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000164{
Juan Castillo6e762062015-11-02 10:47:01 +0000165 int result;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000166 assert(handle != NULL);
167
Dan Handleya4cb68e2014-04-23 13:47:06 +0100168 result = dev_open(dev_con, dev_spec, (io_dev_info_t **)handle);
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000169 return result;
170}
171
172
173/* Initialise an IO device explicitly - to permit lazy initialisation or
174 * re-initialisation */
Dan Handleya4cb68e2014-04-23 13:47:06 +0100175int io_dev_init(uintptr_t dev_handle, const uintptr_t init_params)
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000176{
Juan Castillo6e762062015-11-02 10:47:01 +0000177 int result = 0;
Dan Handleya4cb68e2014-04-23 13:47:06 +0100178 assert(dev_handle != (uintptr_t)NULL);
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000179 assert(is_valid_dev(dev_handle));
180
Dan Handleya4cb68e2014-04-23 13:47:06 +0100181 io_dev_info_t *dev = (io_dev_info_t *)dev_handle;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000182
Juan Castillo6e762062015-11-02 10:47:01 +0000183 /* Absence of registered function implies NOP here */
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000184 if (dev->funcs->dev_init != NULL) {
185 result = dev->funcs->dev_init(dev, init_params);
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000186 }
Juan Castillo6e762062015-11-02 10:47:01 +0000187
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000188 return result;
189}
190
191
192/* TODO: Consider whether an explicit "shutdown" API should be included */
193
194/* Close a connection to a device */
Dan Handleya4cb68e2014-04-23 13:47:06 +0100195int io_dev_close(uintptr_t dev_handle)
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000196{
Juan Castillo6e762062015-11-02 10:47:01 +0000197 int result = 0;
Dan Handleya4cb68e2014-04-23 13:47:06 +0100198 assert(dev_handle != (uintptr_t)NULL);
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000199 assert(is_valid_dev(dev_handle));
200
Dan Handleya4cb68e2014-04-23 13:47:06 +0100201 io_dev_info_t *dev = (io_dev_info_t *)dev_handle;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000202
Juan Castillo6e762062015-11-02 10:47:01 +0000203 /* Absence of registered function implies NOP here */
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000204 if (dev->funcs->dev_close != NULL) {
205 result = dev->funcs->dev_close(dev);
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000206 }
207
208 return result;
209}
210
211
212/* Synchronous operations */
213
214
215/* Open an IO entity */
Dan Handleya4cb68e2014-04-23 13:47:06 +0100216int io_open(uintptr_t dev_handle, const uintptr_t spec, uintptr_t *handle)
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000217{
Juan Castillo6e762062015-11-02 10:47:01 +0000218 int result;
Dan Handleya4cb68e2014-04-23 13:47:06 +0100219 assert((spec != (uintptr_t)NULL) && (handle != NULL));
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000220 assert(is_valid_dev(dev_handle));
221
Dan Handleya4cb68e2014-04-23 13:47:06 +0100222 io_dev_info_t *dev = (io_dev_info_t *)dev_handle;
Dan Handleye2712bc2014-04-10 15:37:22 +0100223 io_entity_t *entity;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000224
225 result = allocate_entity(&entity);
226
Juan Castillo6e762062015-11-02 10:47:01 +0000227 if (result == 0) {
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000228 assert(dev->funcs->open != NULL);
229 result = dev->funcs->open(dev, spec, entity);
230
Juan Castillo6e762062015-11-02 10:47:01 +0000231 if (result == 0) {
Dan Handleya4cb68e2014-04-23 13:47:06 +0100232 entity->dev_handle = dev;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000233 set_handle(handle, entity);
234 } else
235 free_entity(entity);
236 }
237 return result;
238}
239
240
241/* Seek to a specific position in an IO entity */
Dan Handleya4cb68e2014-04-23 13:47:06 +0100242int io_seek(uintptr_t handle, io_seek_mode_t mode, ssize_t offset)
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000243{
Juan Castillo6e762062015-11-02 10:47:01 +0000244 int result = -ENODEV;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000245 assert(is_valid_entity(handle) && is_valid_seek_mode(mode));
246
Dan Handleya4cb68e2014-04-23 13:47:06 +0100247 io_entity_t *entity = (io_entity_t *)handle;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000248
Dan Handleye2712bc2014-04-10 15:37:22 +0100249 io_dev_info_t *dev = entity->dev_handle;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000250
251 if (dev->funcs->seek != NULL)
252 result = dev->funcs->seek(entity, mode, offset);
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000253
254 return result;
255}
256
257
258/* Determine the length of an IO entity */
Dan Handleya4cb68e2014-04-23 13:47:06 +0100259int io_size(uintptr_t handle, size_t *length)
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000260{
Juan Castillo6e762062015-11-02 10:47:01 +0000261 int result = -ENODEV;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000262 assert(is_valid_entity(handle) && (length != NULL));
263
Dan Handleya4cb68e2014-04-23 13:47:06 +0100264 io_entity_t *entity = (io_entity_t *)handle;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000265
Dan Handleye2712bc2014-04-10 15:37:22 +0100266 io_dev_info_t *dev = entity->dev_handle;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000267
268 if (dev->funcs->size != NULL)
269 result = dev->funcs->size(entity, length);
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000270
271 return result;
272}
273
274
275/* Read data from an IO entity */
Dan Handleya4cb68e2014-04-23 13:47:06 +0100276int io_read(uintptr_t handle,
277 uintptr_t buffer,
278 size_t length,
279 size_t *length_read)
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000280{
Juan Castillo6e762062015-11-02 10:47:01 +0000281 int result = -ENODEV;
Konstantin Porotchkinbfd04122018-05-07 12:52:48 +0300282 assert(is_valid_entity(handle));
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000283
Dan Handleya4cb68e2014-04-23 13:47:06 +0100284 io_entity_t *entity = (io_entity_t *)handle;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000285
Dan Handleye2712bc2014-04-10 15:37:22 +0100286 io_dev_info_t *dev = entity->dev_handle;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000287
288 if (dev->funcs->read != NULL)
289 result = dev->funcs->read(entity, buffer, length, length_read);
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000290
291 return result;
292}
293
294
295/* Write data to an IO entity */
Dan Handleya4cb68e2014-04-23 13:47:06 +0100296int io_write(uintptr_t handle,
297 const uintptr_t buffer,
298 size_t length,
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000299 size_t *length_written)
300{
Juan Castillo6e762062015-11-02 10:47:01 +0000301 int result = -ENODEV;
Konstantin Porotchkinbfd04122018-05-07 12:52:48 +0300302 assert(is_valid_entity(handle));
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000303
Dan Handleya4cb68e2014-04-23 13:47:06 +0100304 io_entity_t *entity = (io_entity_t *)handle;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000305
Dan Handleye2712bc2014-04-10 15:37:22 +0100306 io_dev_info_t *dev = entity->dev_handle;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000307
308 if (dev->funcs->write != NULL) {
309 result = dev->funcs->write(entity, buffer, length,
310 length_written);
Juan Castillo6e762062015-11-02 10:47:01 +0000311 }
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000312
313 return result;
314}
315
316
317/* Close an IO entity */
Dan Handleya4cb68e2014-04-23 13:47:06 +0100318int io_close(uintptr_t handle)
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000319{
Juan Castillo6e762062015-11-02 10:47:01 +0000320 int result = 0;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000321 assert(is_valid_entity(handle));
322
Dan Handleya4cb68e2014-04-23 13:47:06 +0100323 io_entity_t *entity = (io_entity_t *)handle;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000324
Dan Handleye2712bc2014-04-10 15:37:22 +0100325 io_dev_info_t *dev = entity->dev_handle;
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000326
Juan Castillo6e762062015-11-02 10:47:01 +0000327 /* Absence of registered function implies NOP here */
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000328 if (dev->funcs->close != NULL)
329 result = dev->funcs->close(entity);
Juan Castillo6e762062015-11-02 10:47:01 +0000330
James Morrisseyf2f9bb52014-02-10 16:18:59 +0000331 /* Ignore improbable free_entity failure */
332 (void)free_entity(entity);
333
334 return result;
335}