19da7a653SHaojian Zhuang /* 29da7a653SHaojian Zhuang * Copyright (c) 2016, ARM Limited and Contributors. All rights reserved. 39da7a653SHaojian Zhuang * 49da7a653SHaojian Zhuang * Redistribution and use in source and binary forms, with or without 59da7a653SHaojian Zhuang * modification, are permitted provided that the following conditions are met: 69da7a653SHaojian Zhuang * 79da7a653SHaojian Zhuang * Redistributions of source code must retain the above copyright notice, this 89da7a653SHaojian Zhuang * list of conditions and the following disclaimer. 99da7a653SHaojian Zhuang * 109da7a653SHaojian Zhuang * Redistributions in binary form must reproduce the above copyright notice, 119da7a653SHaojian Zhuang * this list of conditions and the following disclaimer in the documentation 129da7a653SHaojian Zhuang * and/or other materials provided with the distribution. 139da7a653SHaojian Zhuang * 149da7a653SHaojian Zhuang * Neither the name of ARM nor the names of its contributors may be used 159da7a653SHaojian Zhuang * to endorse or promote products derived from this software without specific 169da7a653SHaojian Zhuang * prior written permission. 179da7a653SHaojian Zhuang * 189da7a653SHaojian Zhuang * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 199da7a653SHaojian Zhuang * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 209da7a653SHaojian Zhuang * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 219da7a653SHaojian Zhuang * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 229da7a653SHaojian Zhuang * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 239da7a653SHaojian Zhuang * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 249da7a653SHaojian Zhuang * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 259da7a653SHaojian Zhuang * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 269da7a653SHaojian Zhuang * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 279da7a653SHaojian Zhuang * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 289da7a653SHaojian Zhuang * POSSIBILITY OF SUCH DAMAGE. 299da7a653SHaojian Zhuang */ 309da7a653SHaojian Zhuang 319da7a653SHaojian Zhuang #include <assert.h> 329da7a653SHaojian Zhuang #include <debug.h> 339da7a653SHaojian Zhuang #include <errno.h> 349da7a653SHaojian Zhuang #include <io_block.h> 359da7a653SHaojian Zhuang #include <io_driver.h> 369da7a653SHaojian Zhuang #include <io_storage.h> 379da7a653SHaojian Zhuang #include <platform_def.h> 389da7a653SHaojian Zhuang #include <string.h> 399da7a653SHaojian Zhuang 409da7a653SHaojian Zhuang typedef struct { 419da7a653SHaojian Zhuang io_block_dev_spec_t *dev_spec; 429da7a653SHaojian Zhuang uintptr_t base; 439da7a653SHaojian Zhuang size_t file_pos; 449da7a653SHaojian Zhuang size_t size; 459da7a653SHaojian Zhuang } block_dev_state_t; 469da7a653SHaojian Zhuang 479da7a653SHaojian Zhuang #define is_power_of_2(x) ((x != 0) && ((x & (x - 1)) == 0)) 489da7a653SHaojian Zhuang 499da7a653SHaojian Zhuang io_type_t device_type_block(void); 509da7a653SHaojian Zhuang 519da7a653SHaojian Zhuang static int block_open(io_dev_info_t *dev_info, const uintptr_t spec, 529da7a653SHaojian Zhuang io_entity_t *entity); 539da7a653SHaojian Zhuang static int block_seek(io_entity_t *entity, int mode, ssize_t offset); 549da7a653SHaojian Zhuang static int block_read(io_entity_t *entity, uintptr_t buffer, size_t length, 559da7a653SHaojian Zhuang size_t *length_read); 569da7a653SHaojian Zhuang static int block_write(io_entity_t *entity, const uintptr_t buffer, 579da7a653SHaojian Zhuang size_t length, size_t *length_written); 589da7a653SHaojian Zhuang static int block_close(io_entity_t *entity); 599da7a653SHaojian Zhuang static int block_dev_open(const uintptr_t dev_spec, io_dev_info_t **dev_info); 609da7a653SHaojian Zhuang static int block_dev_close(io_dev_info_t *dev_info); 619da7a653SHaojian Zhuang 629da7a653SHaojian Zhuang static const io_dev_connector_t block_dev_connector = { 639da7a653SHaojian Zhuang .dev_open = block_dev_open 649da7a653SHaojian Zhuang }; 659da7a653SHaojian Zhuang 669da7a653SHaojian Zhuang static const io_dev_funcs_t block_dev_funcs = { 679da7a653SHaojian Zhuang .type = device_type_block, 689da7a653SHaojian Zhuang .open = block_open, 699da7a653SHaojian Zhuang .seek = block_seek, 709da7a653SHaojian Zhuang .size = NULL, 719da7a653SHaojian Zhuang .read = block_read, 729da7a653SHaojian Zhuang .write = block_write, 739da7a653SHaojian Zhuang .close = block_close, 749da7a653SHaojian Zhuang .dev_init = NULL, 759da7a653SHaojian Zhuang .dev_close = block_dev_close, 769da7a653SHaojian Zhuang }; 779da7a653SHaojian Zhuang 789da7a653SHaojian Zhuang static block_dev_state_t state_pool[MAX_IO_BLOCK_DEVICES]; 799da7a653SHaojian Zhuang static io_dev_info_t dev_info_pool[MAX_IO_BLOCK_DEVICES]; 809da7a653SHaojian Zhuang 819da7a653SHaojian Zhuang /* Track number of allocated block state */ 829da7a653SHaojian Zhuang static unsigned int block_dev_count; 839da7a653SHaojian Zhuang 849da7a653SHaojian Zhuang io_type_t device_type_block(void) 859da7a653SHaojian Zhuang { 869da7a653SHaojian Zhuang return IO_TYPE_BLOCK; 879da7a653SHaojian Zhuang } 889da7a653SHaojian Zhuang 899da7a653SHaojian Zhuang /* Locate a block state in the pool, specified by address */ 909da7a653SHaojian Zhuang static int find_first_block_state(const io_block_dev_spec_t *dev_spec, 919da7a653SHaojian Zhuang unsigned int *index_out) 929da7a653SHaojian Zhuang { 939da7a653SHaojian Zhuang int result = -ENOENT; 949da7a653SHaojian Zhuang for (int index = 0; index < MAX_IO_BLOCK_DEVICES; ++index) { 959da7a653SHaojian Zhuang /* dev_spec is used as identifier since it's unique */ 969da7a653SHaojian Zhuang if (state_pool[index].dev_spec == dev_spec) { 979da7a653SHaojian Zhuang result = 0; 989da7a653SHaojian Zhuang *index_out = index; 999da7a653SHaojian Zhuang break; 1009da7a653SHaojian Zhuang } 1019da7a653SHaojian Zhuang } 1029da7a653SHaojian Zhuang return result; 1039da7a653SHaojian Zhuang } 1049da7a653SHaojian Zhuang 1059da7a653SHaojian Zhuang /* Allocate a device info from the pool and return a pointer to it */ 1069da7a653SHaojian Zhuang static int allocate_dev_info(io_dev_info_t **dev_info) 1079da7a653SHaojian Zhuang { 1089da7a653SHaojian Zhuang int result = -ENOMEM; 1099da7a653SHaojian Zhuang assert(dev_info != NULL); 1109da7a653SHaojian Zhuang 1119da7a653SHaojian Zhuang if (block_dev_count < MAX_IO_BLOCK_DEVICES) { 1129da7a653SHaojian Zhuang unsigned int index = 0; 1139da7a653SHaojian Zhuang result = find_first_block_state(NULL, &index); 1149da7a653SHaojian Zhuang assert(result == 0); 1159da7a653SHaojian Zhuang /* initialize dev_info */ 1169da7a653SHaojian Zhuang dev_info_pool[index].funcs = &block_dev_funcs; 1179da7a653SHaojian Zhuang dev_info_pool[index].info = (uintptr_t)&state_pool[index]; 1189da7a653SHaojian Zhuang *dev_info = &dev_info_pool[index]; 1199da7a653SHaojian Zhuang ++block_dev_count; 1209da7a653SHaojian Zhuang } 1219da7a653SHaojian Zhuang 1229da7a653SHaojian Zhuang return result; 1239da7a653SHaojian Zhuang } 1249da7a653SHaojian Zhuang 1259da7a653SHaojian Zhuang 1269da7a653SHaojian Zhuang /* Release a device info to the pool */ 1279da7a653SHaojian Zhuang static int free_dev_info(io_dev_info_t *dev_info) 1289da7a653SHaojian Zhuang { 1299da7a653SHaojian Zhuang int result; 1309da7a653SHaojian Zhuang unsigned int index = 0; 1319da7a653SHaojian Zhuang block_dev_state_t *state; 1329da7a653SHaojian Zhuang assert(dev_info != NULL); 1339da7a653SHaojian Zhuang 1349da7a653SHaojian Zhuang state = (block_dev_state_t *)dev_info->info; 1359da7a653SHaojian Zhuang result = find_first_block_state(state->dev_spec, &index); 1369da7a653SHaojian Zhuang if (result == 0) { 1379da7a653SHaojian Zhuang /* free if device info is valid */ 1389da7a653SHaojian Zhuang memset(state, 0, sizeof(block_dev_state_t)); 1399da7a653SHaojian Zhuang memset(dev_info, 0, sizeof(io_dev_info_t)); 1409da7a653SHaojian Zhuang --block_dev_count; 1419da7a653SHaojian Zhuang } 1429da7a653SHaojian Zhuang 1439da7a653SHaojian Zhuang return result; 1449da7a653SHaojian Zhuang } 1459da7a653SHaojian Zhuang 1469da7a653SHaojian Zhuang static int block_open(io_dev_info_t *dev_info, const uintptr_t spec, 1479da7a653SHaojian Zhuang io_entity_t *entity) 1489da7a653SHaojian Zhuang { 1499da7a653SHaojian Zhuang block_dev_state_t *cur; 1509da7a653SHaojian Zhuang io_block_spec_t *region; 1519da7a653SHaojian Zhuang 1529da7a653SHaojian Zhuang assert((dev_info->info != (uintptr_t)NULL) && 1539da7a653SHaojian Zhuang (spec != (uintptr_t)NULL) && 1549da7a653SHaojian Zhuang (entity->info == (uintptr_t)NULL)); 1559da7a653SHaojian Zhuang 1569da7a653SHaojian Zhuang region = (io_block_spec_t *)spec; 1579da7a653SHaojian Zhuang cur = (block_dev_state_t *)dev_info->info; 1589da7a653SHaojian Zhuang assert(((region->offset % cur->dev_spec->block_size) == 0) && 1599da7a653SHaojian Zhuang ((region->length % cur->dev_spec->block_size) == 0)); 1609da7a653SHaojian Zhuang 1619da7a653SHaojian Zhuang cur->base = region->offset; 1629da7a653SHaojian Zhuang cur->size = region->length; 1639da7a653SHaojian Zhuang cur->file_pos = 0; 1649da7a653SHaojian Zhuang 1659da7a653SHaojian Zhuang entity->info = (uintptr_t)cur; 1669da7a653SHaojian Zhuang return 0; 1679da7a653SHaojian Zhuang } 1689da7a653SHaojian Zhuang 1699da7a653SHaojian Zhuang /* parameter offset is relative address at here */ 1709da7a653SHaojian Zhuang static int block_seek(io_entity_t *entity, int mode, ssize_t offset) 1719da7a653SHaojian Zhuang { 1729da7a653SHaojian Zhuang block_dev_state_t *cur; 1739da7a653SHaojian Zhuang 1749da7a653SHaojian Zhuang assert(entity->info != (uintptr_t)NULL); 1759da7a653SHaojian Zhuang 1769da7a653SHaojian Zhuang cur = (block_dev_state_t *)entity->info; 1779da7a653SHaojian Zhuang assert((offset >= 0) && (offset < cur->size)); 1789da7a653SHaojian Zhuang 1799da7a653SHaojian Zhuang switch (mode) { 1809da7a653SHaojian Zhuang case IO_SEEK_SET: 1819da7a653SHaojian Zhuang cur->file_pos = offset; 1829da7a653SHaojian Zhuang break; 1839da7a653SHaojian Zhuang case IO_SEEK_CUR: 1849da7a653SHaojian Zhuang cur->file_pos += offset; 1859da7a653SHaojian Zhuang break; 1869da7a653SHaojian Zhuang default: 1879da7a653SHaojian Zhuang return -EINVAL; 1889da7a653SHaojian Zhuang } 1899da7a653SHaojian Zhuang assert(cur->file_pos < cur->size); 1909da7a653SHaojian Zhuang return 0; 1919da7a653SHaojian Zhuang } 1929da7a653SHaojian Zhuang 1939da7a653SHaojian Zhuang static int block_read(io_entity_t *entity, uintptr_t buffer, size_t length, 1949da7a653SHaojian Zhuang size_t *length_read) 1959da7a653SHaojian Zhuang { 1969da7a653SHaojian Zhuang block_dev_state_t *cur; 1979da7a653SHaojian Zhuang io_block_spec_t *buf; 1989da7a653SHaojian Zhuang io_block_ops_t *ops; 1999da7a653SHaojian Zhuang size_t aligned_length, skip, count, left, padding, block_size; 2009da7a653SHaojian Zhuang int lba; 201*9d063aa2SHaojian Zhuang int buffer_not_aligned; 2029da7a653SHaojian Zhuang 2039da7a653SHaojian Zhuang assert(entity->info != (uintptr_t)NULL); 2049da7a653SHaojian Zhuang cur = (block_dev_state_t *)entity->info; 2059da7a653SHaojian Zhuang ops = &(cur->dev_spec->ops); 2069da7a653SHaojian Zhuang buf = &(cur->dev_spec->buffer); 2079da7a653SHaojian Zhuang block_size = cur->dev_spec->block_size; 2089da7a653SHaojian Zhuang assert((length <= cur->size) && 2099da7a653SHaojian Zhuang (length > 0) && 2109da7a653SHaojian Zhuang (ops->read != 0)); 2119da7a653SHaojian Zhuang 212*9d063aa2SHaojian Zhuang if ((buffer & (block_size - 1)) != 0) { 213*9d063aa2SHaojian Zhuang /* 214*9d063aa2SHaojian Zhuang * buffer isn't aligned with block size. 215*9d063aa2SHaojian Zhuang * Block device always relies on DMA operation. 216*9d063aa2SHaojian Zhuang * It's better to make the buffer as block size aligned. 217*9d063aa2SHaojian Zhuang */ 218*9d063aa2SHaojian Zhuang buffer_not_aligned = 1; 219*9d063aa2SHaojian Zhuang } else { 220*9d063aa2SHaojian Zhuang buffer_not_aligned = 0; 221*9d063aa2SHaojian Zhuang } 222*9d063aa2SHaojian Zhuang 2239da7a653SHaojian Zhuang skip = cur->file_pos % block_size; 2249da7a653SHaojian Zhuang aligned_length = ((skip + length) + (block_size - 1)) & 2259da7a653SHaojian Zhuang ~(block_size - 1); 2269da7a653SHaojian Zhuang padding = aligned_length - (skip + length); 2279da7a653SHaojian Zhuang left = aligned_length; 2289da7a653SHaojian Zhuang do { 2299da7a653SHaojian Zhuang lba = (cur->file_pos + cur->base) / block_size; 2309da7a653SHaojian Zhuang if (left >= buf->length) { 231*9d063aa2SHaojian Zhuang /* 232*9d063aa2SHaojian Zhuang * Since left is larger, it's impossible to padding. 233*9d063aa2SHaojian Zhuang * 234*9d063aa2SHaojian Zhuang * If buffer isn't aligned, we need to use aligned 235*9d063aa2SHaojian Zhuang * buffer instead. 236*9d063aa2SHaojian Zhuang */ 237*9d063aa2SHaojian Zhuang if (skip || buffer_not_aligned) { 2389da7a653SHaojian Zhuang /* 2399da7a653SHaojian Zhuang * The beginning address (file_pos) isn't 2409da7a653SHaojian Zhuang * aligned with block size, we need to use 2419da7a653SHaojian Zhuang * block buffer to read block. Since block 2429da7a653SHaojian Zhuang * device is always relied on DMA operation. 2439da7a653SHaojian Zhuang */ 2449da7a653SHaojian Zhuang count = ops->read(lba, buf->offset, 2459da7a653SHaojian Zhuang buf->length); 2469da7a653SHaojian Zhuang } else { 2479da7a653SHaojian Zhuang count = ops->read(lba, buffer, buf->length); 2489da7a653SHaojian Zhuang } 2499da7a653SHaojian Zhuang assert(count == buf->length); 2509da7a653SHaojian Zhuang cur->file_pos += count - skip; 251*9d063aa2SHaojian Zhuang if (skip || buffer_not_aligned) { 2529da7a653SHaojian Zhuang /* 253*9d063aa2SHaojian Zhuang * Since there's not aligned block size caused 254*9d063aa2SHaojian Zhuang * by skip or not aligned buffer, block buffer 255*9d063aa2SHaojian Zhuang * is used to store data. 2569da7a653SHaojian Zhuang */ 2579da7a653SHaojian Zhuang memcpy((void *)buffer, 2589da7a653SHaojian Zhuang (void *)(buf->offset + skip), 2599da7a653SHaojian Zhuang count - skip); 2609da7a653SHaojian Zhuang } 2619da7a653SHaojian Zhuang left = left - (count - skip); 2629da7a653SHaojian Zhuang } else { 263*9d063aa2SHaojian Zhuang if (skip || padding || buffer_not_aligned) { 2649da7a653SHaojian Zhuang /* 2659da7a653SHaojian Zhuang * The beginning address (file_pos) isn't 2669da7a653SHaojian Zhuang * aligned with block size, we have to read 2679da7a653SHaojian Zhuang * full block by block buffer instead. 2689da7a653SHaojian Zhuang * The size isn't aligned with block size. 2699da7a653SHaojian Zhuang * Use block buffer to avoid overflow. 270*9d063aa2SHaojian Zhuang * 271*9d063aa2SHaojian Zhuang * If buffer isn't aligned, use block buffer 272*9d063aa2SHaojian Zhuang * to avoid DMA error. 2739da7a653SHaojian Zhuang */ 2749da7a653SHaojian Zhuang count = ops->read(lba, buf->offset, left); 2759da7a653SHaojian Zhuang } else 2769da7a653SHaojian Zhuang count = ops->read(lba, buffer, left); 2779da7a653SHaojian Zhuang assert(count == left); 2789da7a653SHaojian Zhuang left = left - (skip + padding); 2799da7a653SHaojian Zhuang cur->file_pos += left; 280*9d063aa2SHaojian Zhuang if (skip || padding || buffer_not_aligned) { 2819da7a653SHaojian Zhuang /* 282*9d063aa2SHaojian Zhuang * Since there's not aligned block size or 283*9d063aa2SHaojian Zhuang * buffer, block buffer is used to store data. 2849da7a653SHaojian Zhuang */ 2859da7a653SHaojian Zhuang memcpy((void *)buffer, 2869da7a653SHaojian Zhuang (void *)(buf->offset + skip), 2879da7a653SHaojian Zhuang left); 2889da7a653SHaojian Zhuang } 2899da7a653SHaojian Zhuang /* It's already the last block operation */ 2909da7a653SHaojian Zhuang left = 0; 2919da7a653SHaojian Zhuang } 2929da7a653SHaojian Zhuang skip = cur->file_pos % block_size; 2939da7a653SHaojian Zhuang } while (left > 0); 2949da7a653SHaojian Zhuang *length_read = length; 2959da7a653SHaojian Zhuang 2969da7a653SHaojian Zhuang return 0; 2979da7a653SHaojian Zhuang } 2989da7a653SHaojian Zhuang 2999da7a653SHaojian Zhuang static int block_write(io_entity_t *entity, const uintptr_t buffer, 3009da7a653SHaojian Zhuang size_t length, size_t *length_written) 3019da7a653SHaojian Zhuang { 3029da7a653SHaojian Zhuang block_dev_state_t *cur; 3039da7a653SHaojian Zhuang io_block_spec_t *buf; 3049da7a653SHaojian Zhuang io_block_ops_t *ops; 3059da7a653SHaojian Zhuang size_t aligned_length, skip, count, left, padding, block_size; 3069da7a653SHaojian Zhuang int lba; 307*9d063aa2SHaojian Zhuang int buffer_not_aligned; 3089da7a653SHaojian Zhuang 3099da7a653SHaojian Zhuang assert(entity->info != (uintptr_t)NULL); 3109da7a653SHaojian Zhuang cur = (block_dev_state_t *)entity->info; 3119da7a653SHaojian Zhuang ops = &(cur->dev_spec->ops); 3129da7a653SHaojian Zhuang buf = &(cur->dev_spec->buffer); 3139da7a653SHaojian Zhuang block_size = cur->dev_spec->block_size; 3149da7a653SHaojian Zhuang assert((length <= cur->size) && 3159da7a653SHaojian Zhuang (length > 0) && 3169da7a653SHaojian Zhuang (ops->read != 0) && 3179da7a653SHaojian Zhuang (ops->write != 0)); 3189da7a653SHaojian Zhuang 319*9d063aa2SHaojian Zhuang if ((buffer & (block_size - 1)) != 0) { 320*9d063aa2SHaojian Zhuang /* 321*9d063aa2SHaojian Zhuang * buffer isn't aligned with block size. 322*9d063aa2SHaojian Zhuang * Block device always relies on DMA operation. 323*9d063aa2SHaojian Zhuang * It's better to make the buffer as block size aligned. 324*9d063aa2SHaojian Zhuang */ 325*9d063aa2SHaojian Zhuang buffer_not_aligned = 1; 326*9d063aa2SHaojian Zhuang } else { 327*9d063aa2SHaojian Zhuang buffer_not_aligned = 0; 328*9d063aa2SHaojian Zhuang } 329*9d063aa2SHaojian Zhuang 3309da7a653SHaojian Zhuang skip = cur->file_pos % block_size; 3319da7a653SHaojian Zhuang aligned_length = ((skip + length) + (block_size - 1)) & 3329da7a653SHaojian Zhuang ~(block_size - 1); 3339da7a653SHaojian Zhuang padding = aligned_length - (skip + length); 3349da7a653SHaojian Zhuang left = aligned_length; 3359da7a653SHaojian Zhuang do { 3369da7a653SHaojian Zhuang lba = (cur->file_pos + cur->base) / block_size; 3379da7a653SHaojian Zhuang if (left >= buf->length) { 3389da7a653SHaojian Zhuang /* Since left is larger, it's impossible to padding. */ 339*9d063aa2SHaojian Zhuang if (skip || buffer_not_aligned) { 3409da7a653SHaojian Zhuang /* 3419da7a653SHaojian Zhuang * The beginning address (file_pos) isn't 342*9d063aa2SHaojian Zhuang * aligned with block size or buffer isn't 343*9d063aa2SHaojian Zhuang * aligned, we need to use block buffer to 344*9d063aa2SHaojian Zhuang * write block. 3459da7a653SHaojian Zhuang */ 3469da7a653SHaojian Zhuang count = ops->read(lba, buf->offset, 3479da7a653SHaojian Zhuang buf->length); 3489da7a653SHaojian Zhuang assert(count == buf->length); 3499da7a653SHaojian Zhuang memcpy((void *)(buf->offset + skip), 3509da7a653SHaojian Zhuang (void *)buffer, 3519da7a653SHaojian Zhuang count - skip); 3529da7a653SHaojian Zhuang count = ops->write(lba, buf->offset, 3539da7a653SHaojian Zhuang buf->length); 3549da7a653SHaojian Zhuang } else 3559da7a653SHaojian Zhuang count = ops->write(lba, buffer, buf->length); 3569da7a653SHaojian Zhuang assert(count == buf->length); 3579da7a653SHaojian Zhuang cur->file_pos += count - skip; 3589da7a653SHaojian Zhuang left = left - (count - skip); 3599da7a653SHaojian Zhuang } else { 360*9d063aa2SHaojian Zhuang if (skip || padding || buffer_not_aligned) { 3619da7a653SHaojian Zhuang /* 3629da7a653SHaojian Zhuang * The beginning address (file_pos) isn't 3639da7a653SHaojian Zhuang * aligned with block size, we need to avoid 3649da7a653SHaojian Zhuang * poluate data in the beginning. Reading and 3659da7a653SHaojian Zhuang * skipping the beginning is the only way. 3669da7a653SHaojian Zhuang * The size isn't aligned with block size. 3679da7a653SHaojian Zhuang * Use block buffer to avoid overflow. 368*9d063aa2SHaojian Zhuang * 369*9d063aa2SHaojian Zhuang * If buffer isn't aligned, use block buffer 370*9d063aa2SHaojian Zhuang * to avoid DMA error. 3719da7a653SHaojian Zhuang */ 3729da7a653SHaojian Zhuang count = ops->read(lba, buf->offset, left); 3739da7a653SHaojian Zhuang assert(count == left); 3749da7a653SHaojian Zhuang memcpy((void *)(buf->offset + skip), 3759da7a653SHaojian Zhuang (void *)buffer, 3769da7a653SHaojian Zhuang left - skip - padding); 3779da7a653SHaojian Zhuang count = ops->write(lba, buf->offset, left); 3789da7a653SHaojian Zhuang } else 3799da7a653SHaojian Zhuang count = ops->write(lba, buffer, left); 3809da7a653SHaojian Zhuang assert(count == left); 3819da7a653SHaojian Zhuang cur->file_pos += left - (skip + padding); 3829da7a653SHaojian Zhuang /* It's already the last block operation */ 3839da7a653SHaojian Zhuang left = 0; 3849da7a653SHaojian Zhuang } 3859da7a653SHaojian Zhuang skip = cur->file_pos % block_size; 3869da7a653SHaojian Zhuang } while (left > 0); 3879da7a653SHaojian Zhuang *length_written = length; 3889da7a653SHaojian Zhuang return 0; 3899da7a653SHaojian Zhuang } 3909da7a653SHaojian Zhuang 3919da7a653SHaojian Zhuang static int block_close(io_entity_t *entity) 3929da7a653SHaojian Zhuang { 3939da7a653SHaojian Zhuang entity->info = (uintptr_t)NULL; 3949da7a653SHaojian Zhuang return 0; 3959da7a653SHaojian Zhuang } 3969da7a653SHaojian Zhuang 3979da7a653SHaojian Zhuang static int block_dev_open(const uintptr_t dev_spec, io_dev_info_t **dev_info) 3989da7a653SHaojian Zhuang { 3999da7a653SHaojian Zhuang block_dev_state_t *cur; 4009da7a653SHaojian Zhuang io_block_spec_t *buffer; 4019da7a653SHaojian Zhuang io_dev_info_t *info; 4029da7a653SHaojian Zhuang size_t block_size; 4039da7a653SHaojian Zhuang int result; 4049da7a653SHaojian Zhuang 4059da7a653SHaojian Zhuang assert(dev_info != NULL); 4069da7a653SHaojian Zhuang result = allocate_dev_info(&info); 4079da7a653SHaojian Zhuang if (result) 4089da7a653SHaojian Zhuang return -ENOENT; 4099da7a653SHaojian Zhuang 4109da7a653SHaojian Zhuang cur = (block_dev_state_t *)info->info; 4119da7a653SHaojian Zhuang /* dev_spec is type of io_block_dev_spec_t. */ 4129da7a653SHaojian Zhuang cur->dev_spec = (io_block_dev_spec_t *)dev_spec; 4139da7a653SHaojian Zhuang buffer = &(cur->dev_spec->buffer); 4149da7a653SHaojian Zhuang block_size = cur->dev_spec->block_size; 4159da7a653SHaojian Zhuang assert((block_size > 0) && 4169da7a653SHaojian Zhuang (is_power_of_2(block_size) != 0) && 4179da7a653SHaojian Zhuang ((buffer->offset % block_size) == 0) && 4189da7a653SHaojian Zhuang ((buffer->length % block_size) == 0)); 4199da7a653SHaojian Zhuang 4209da7a653SHaojian Zhuang *dev_info = info; /* cast away const */ 4219da7a653SHaojian Zhuang (void)block_size; 4229da7a653SHaojian Zhuang (void)buffer; 4239da7a653SHaojian Zhuang return 0; 4249da7a653SHaojian Zhuang } 4259da7a653SHaojian Zhuang 4269da7a653SHaojian Zhuang static int block_dev_close(io_dev_info_t *dev_info) 4279da7a653SHaojian Zhuang { 4289da7a653SHaojian Zhuang return free_dev_info(dev_info); 4299da7a653SHaojian Zhuang } 4309da7a653SHaojian Zhuang 4319da7a653SHaojian Zhuang /* Exported functions */ 4329da7a653SHaojian Zhuang 4339da7a653SHaojian Zhuang /* Register the Block driver with the IO abstraction */ 4349da7a653SHaojian Zhuang int register_io_dev_block(const io_dev_connector_t **dev_con) 4359da7a653SHaojian Zhuang { 4369da7a653SHaojian Zhuang int result; 4379da7a653SHaojian Zhuang 4389da7a653SHaojian Zhuang assert(dev_con != NULL); 4399da7a653SHaojian Zhuang 4409da7a653SHaojian Zhuang /* 4419da7a653SHaojian Zhuang * Since dev_info isn't really used in io_register_device, always 4429da7a653SHaojian Zhuang * use the same device info at here instead. 4439da7a653SHaojian Zhuang */ 4449da7a653SHaojian Zhuang result = io_register_device(&dev_info_pool[0]); 4459da7a653SHaojian Zhuang if (result == 0) 4469da7a653SHaojian Zhuang *dev_con = &block_dev_connector; 4479da7a653SHaojian Zhuang return result; 4489da7a653SHaojian Zhuang } 449