Home | History | Annotate | Download | only in io
      1 /*
      2  * Copyright (c) 2014-2015, Linaro Ltd and Contributors. All rights reserved.
      3  * Copyright (c) 2014-2015, Hisilicon Ltd and Contributors. All rights reserved.
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions are met:
      7  *
      8  * Redistributions of source code must retain the above copyright notice, this
      9  * list of conditions and the following disclaimer.
     10  *
     11  * Redistributions in binary form must reproduce the above copyright notice,
     12  * this list of conditions and the following disclaimer in the documentation
     13  * and/or other materials provided with the distribution.
     14  *
     15  * Neither the name of ARM nor the names of its contributors may be used
     16  * to endorse or promote products derived from this software without specific
     17  * prior written permission.
     18  *
     19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
     20  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     22  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
     23  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     29  * POSSIBILITY OF SUCH DAMAGE.
     30  */
     31 
     32 #include <assert.h>
     33 #include <bl_common.h>
     34 #include <debug.h>
     35 #include <errno.h>
     36 #include <io_block.h>
     37 #include <io_driver.h>
     38 #include <io_storage.h>
     39 #include <mmio.h>
     40 #include <stdint.h>
     41 #include <string.h>
     42 
     43 /* As we need to be able to keep state for seek, only one file can be open
     44  * at a time. Make this a structure and point to the entity->info. When we
     45  * can malloc memory we can change this to support more open files.
     46  */
     47 typedef struct {
     48 	/* Use the 'in_use' flag as any value for base and file_pos could be
     49 	 * valid.
     50 	 */
     51 	int		in_use;
     52 	uintptr_t	base;
     53 	size_t		file_pos;
     54 	uint32_t	flags;
     55 } file_state_t;
     56 
     57 struct block_info {
     58 	struct block_ops	ops;
     59 	int			init;
     60 	uint32_t		flags;
     61 };
     62 
     63 static file_state_t current_file = {0};
     64 
     65 static struct block_info block_info;
     66 
     67 static int block_open(io_dev_info_t *dev_info, const uintptr_t spec,
     68 		      io_entity_t *entity);
     69 static int block_seek(io_entity_t *entity, int mode, ssize_t offset);
     70 static int block_read(io_entity_t *entity, uintptr_t buffer,
     71 		      size_t length, size_t *length_read);
     72 static int block_write(io_entity_t *entity, uintptr_t buffer,
     73 		       size_t length, size_t *length_written);
     74 static int block_close(io_entity_t *entity);
     75 
     76 static int blk_dev_init(io_dev_info_t *dev_info,
     77 			const uintptr_t init_params);
     78 static int blk_dev_open(const uintptr_t dev_spec, io_dev_info_t **dev_info);
     79 static int blk_dev_close(io_dev_info_t *dev_info);
     80 
     81 /* Identify the device type as block */
     82 io_type_t device_type_block(void)
     83 {
     84 	return IO_TYPE_BLOCK;
     85 }
     86 
     87 static const io_dev_connector_t blk_dev_connector = {
     88 	.dev_open = blk_dev_open
     89 };
     90 
     91 static const io_dev_funcs_t blk_dev_funcs = {
     92 	.type = device_type_block,
     93 	.open = block_open,
     94 	.seek = block_seek,
     95 	.size = NULL,
     96 	.read = block_read,
     97 	.write = block_write,
     98 	.close = block_close,
     99 	.dev_init = blk_dev_init,
    100 	.dev_close = blk_dev_close,
    101 };
    102 
    103 
    104 /* No state associated with this device so structure can be const */
    105 static const io_dev_info_t blk_dev_info = {
    106 	.funcs = &blk_dev_funcs,
    107 	.info = (uintptr_t)&block_info,
    108 };
    109 
    110 /* Open a connection to the block device */
    111 static int blk_dev_open(const uintptr_t dev_spec __attribute__((unused)),
    112 			   io_dev_info_t **dev_info)
    113 {
    114 	struct block_ops	*funcs, *block_spec;
    115 
    116 	assert(dev_info != NULL);
    117 	*dev_info = (io_dev_info_t *)&blk_dev_info; /* cast away const */
    118 
    119 	if (dev_spec) {
    120 		funcs = &block_info.ops;
    121 		block_spec = (struct block_ops *)dev_spec;
    122 		funcs->init = block_spec->init;
    123 		funcs->read = block_spec->read;
    124 		funcs->write = block_spec->write;
    125 	}
    126 
    127 	return IO_SUCCESS;
    128 }
    129 
    130 /* Close a connection to the block device */
    131 static int blk_dev_close(io_dev_info_t *dev_info)
    132 {
    133 	/* NOP */
    134 	/* TODO: Consider tracking open files and cleaning them up here */
    135 	return IO_SUCCESS;
    136 }
    137 
    138 
    139 /* Open a file on the block device */
    140 /* TODO: Can we do any sensible limit checks on requested memory */
    141 static int block_open(io_dev_info_t *dev_info, const uintptr_t spec,
    142 			     io_entity_t *entity)
    143 {
    144 	int result = IO_FAIL;
    145 	const io_block_spec_t *block_spec = (io_block_spec_t *)spec;
    146 	struct block_info *info = (struct block_info *)(dev_info->info);
    147 
    148 	/* Since we need to track open state for seek() we only allow one open
    149 	 * spec at a time. When we have dynamic memory we can malloc and set
    150 	 * entity->info.
    151 	 */
    152 	if (current_file.in_use == 0) {
    153 		assert(block_spec != NULL);
    154 		assert(entity != NULL);
    155 
    156 		current_file.in_use = 1;
    157 		current_file.base = block_spec->offset;
    158 		/* File cursor offset for seek and incremental reads etc. */
    159 		current_file.file_pos = 0;
    160 		current_file.flags = info->flags;
    161 		entity->info = (uintptr_t)&current_file;
    162 		result = IO_SUCCESS;
    163 	} else {
    164 		WARN("A block device is already active. Close first.\n");
    165 		result = IO_RESOURCES_EXHAUSTED;
    166 	}
    167 
    168 	return result;
    169 }
    170 
    171 /* Seek to a particular file offset on the block device */
    172 static int block_seek(io_entity_t *entity, int mode, ssize_t offset)
    173 {
    174 	int result = IO_FAIL;
    175 
    176 	/* We only support IO_SEEK_SET for the moment. */
    177 	if (mode == IO_SEEK_SET) {
    178 		assert(entity != NULL);
    179 
    180 		/* TODO: can we do some basic limit checks on seek? */
    181 		((file_state_t *)entity->info)->file_pos = offset;
    182 		result = IO_SUCCESS;
    183 	} else {
    184 		result = IO_FAIL;
    185 	}
    186 
    187 	return result;
    188 }
    189 
    190 
    191 /* Read data from a file on the block device */
    192 static int block_read(io_entity_t *entity, uintptr_t buffer,
    193 		      size_t length, size_t *length_read)
    194 {
    195 	file_state_t *fp;
    196 	int result;
    197 
    198 	assert(entity != NULL);
    199 	assert(buffer != (uintptr_t)NULL);
    200 	assert(length_read != NULL);
    201 
    202 	fp = (file_state_t *)entity->info;
    203 
    204 	if (!block_info.ops.read) {
    205 		ERROR("There's no read function on the block device.\n");
    206 		return IO_NOT_SUPPORTED;
    207 	}
    208 	result = block_info.ops.read(fp->base + fp->file_pos, length,
    209 				     buffer, fp->flags);
    210 	if (result) {
    211 		WARN("Failed to read block offset 0x%x\n",
    212 		     fp->base + fp->file_pos);
    213 		return result;
    214 	}
    215 
    216 	*length_read = length;
    217 	/* advance the file 'cursor' for incremental reads */
    218 	fp->file_pos += length;
    219 
    220 	return IO_SUCCESS;
    221 }
    222 
    223 static int block_write(io_entity_t *entity, uintptr_t buffer,
    224 		       size_t length, size_t *length_written)
    225 {
    226 	file_state_t *fp;
    227 	int result;
    228 
    229 	assert(entity != NULL);
    230 	assert(buffer != (uintptr_t)NULL);
    231 	assert(length_written != NULL);
    232 
    233 	fp = (file_state_t *)entity->info;
    234 
    235 	if (!block_info.ops.write) {
    236 		ERROR("There's no write function on the block device.\n");
    237 		return IO_NOT_SUPPORTED;
    238 	}
    239 	result = block_info.ops.write(fp->base + fp->file_pos, length,
    240 				      buffer, fp->flags);
    241 	if (result) {
    242 		WARN("Failed to write block offset 0x%x\n",
    243 		     fp->base + fp->file_pos);
    244 		return result;
    245 	}
    246 
    247 	*length_written = length;
    248 	/* advance the file 'cursor' for incremental reads */
    249 	fp->file_pos += length;
    250 
    251 	return IO_SUCCESS;
    252 }
    253 
    254 /* Close a file on the BLOCK device */
    255 static int block_close(io_entity_t *entity)
    256 {
    257 	assert(entity != NULL);
    258 
    259 	entity->info = 0;
    260 
    261 	/* This would be a mem free() if we had malloc.*/
    262 	memset((void *)&current_file, 0, sizeof(current_file));
    263 
    264 	return IO_SUCCESS;
    265 }
    266 
    267 static int blk_dev_init(io_dev_info_t *dev_info, const uintptr_t init_params)
    268 {
    269 	struct block_info *info = (struct block_info *)(dev_info->info);
    270 
    271 	if (!info->init) {
    272 		if (block_info.ops.init)
    273 			block_info.ops.init();
    274 		info->init = 1;
    275 	}
    276 	info->flags = init_params;
    277 	return IO_SUCCESS;
    278 }
    279 
    280 /* Exported functions */
    281 
    282 /* Register the block driver with the IO abstraction */
    283 int register_io_dev_block(const io_dev_connector_t **dev_con)
    284 {
    285 	int result = IO_FAIL;
    286 	assert(dev_con != NULL);
    287 
    288 	result = io_register_device(&blk_dev_info);
    289 	if (result == IO_SUCCESS)
    290 		*dev_con = &blk_dev_connector;
    291 
    292 	return result;
    293 }
    294