diff options
author | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2012-03-05 15:06:02 +0100 |
---|---|---|
committer | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2012-03-13 12:24:18 +0100 |
commit | 01211720ec7e19f70e900c1e21b415e2a126436b (patch) | |
tree | a9f7eaee61a37c71bd829aacc124215710568e6d /cpukit | |
parent | libblock: Split file and simplify (diff) | |
download | rtems-01211720ec7e19f70e900c1e21b415e2a126436b.tar.bz2 |
libblock: Add generic IMFS block device nodes
New functions
o rtems_blkdev_create(), and
o rtems_blkdev_create_partition().
New test libtests/block11.
Diffstat (limited to 'cpukit')
-rw-r--r-- | cpukit/libblock/Makefile.am | 1 | ||||
-rw-r--r-- | cpukit/libblock/include/rtems/blkdev.h | 50 | ||||
-rw-r--r-- | cpukit/libblock/src/blkdev-imfs.c | 380 |
3 files changed, 431 insertions, 0 deletions
diff --git a/cpukit/libblock/Makefile.am b/cpukit/libblock/Makefile.am index 0311319888..5cf143dae3 100644 --- a/cpukit/libblock/Makefile.am +++ b/cpukit/libblock/Makefile.am @@ -8,6 +8,7 @@ include $(top_srcdir)/automake/compile.am noinst_LIBRARIES = libblock.a libblock_a_SOURCES = src/bdbuf.c \ src/blkdev.c \ + src/blkdev-imfs.c \ src/blkdev-ioctl.c \ src/blkdev-ops.c \ src/diskdevs.c \ diff --git a/cpukit/libblock/include/rtems/blkdev.h b/cpukit/libblock/include/rtems/blkdev.h index 70259d6c43..c51238084b 100644 --- a/cpukit/libblock/include/rtems/blkdev.h +++ b/cpukit/libblock/include/rtems/blkdev.h @@ -307,6 +307,56 @@ rtems_blkdev_ioctl(rtems_disk_device *dd, uint32_t req, void *argp); */ extern const rtems_driver_address_table rtems_blkdev_generic_ops; +/** + * @brief Creates a block device. + * + * @param[in] device The path for the new block device. + * @param[in] block_size The block size. Must be positive. + * @param[in] block_count The block count. Must be positive. + * @param[in] handler The block device IO control handler. Must not be @c NULL. + * @param[in] driver_data The block device driver data. + * + * @retval RTEMS_SUCCESSFUL Successful operation. + * @retval RTEMS_INVALID_NUMBER Block size or block count is not positive. + * @retval RTEMS_NO_MEMORY Not enough memory. + * @retval RTEMS_UNSATISFIED Cannot create generic device node. + */ +rtems_status_code rtems_blkdev_create( + const char *device, + uint32_t block_size, + rtems_blkdev_bnum block_count, + rtems_block_device_ioctl handler, + void *driver_data +); + +/** + * @brief Creates a partition within a block device. + * + * A partition manages a subset of consecutive blocks contained in a block + * device. The blocks must be within the range of blocks managed by the + * associated block device. The media block size, block size, and IO control + * handler are inherited by the block device. + * + * @param[in] partition The path for the new partition device. + * @param[in] device The block device path. + * @param[in] block_begin The block begin of the partition. + * @param[in] block_count The block count of the partition. + * + * @retval RTEMS_SUCCESSFUL Successful operation. + * @retval RTEMS_INVALID_ID Block device node does not exist. + * @retval RTEMS_INVALID_NODE File system node is not a block device. + * @retval RTEMS_NOT_IMPLEMENTED Block device implementation is incomplete. + * @retval RTEMS_INVALID_NUMBER Block begin or block count is invalid. + * @retval RTEMS_NO_MEMORY Not enough memory. + * @retval RTEMS_UNSATISFIED Cannot create generic device node. + */ +rtems_status_code rtems_blkdev_create_partition( + const char *partition, + const char *device, + rtems_blkdev_bnum block_begin, + rtems_blkdev_bnum block_count +); + /** @} */ #ifdef __cplusplus diff --git a/cpukit/libblock/src/blkdev-imfs.c b/cpukit/libblock/src/blkdev-imfs.c new file mode 100644 index 0000000000..080ca2155e --- /dev/null +++ b/cpukit/libblock/src/blkdev-imfs.c @@ -0,0 +1,380 @@ +/* + * Copyright (c) 2012 embedded brains GmbH. All rights reserved. + * + * embedded brains GmbH + * Obere Lagerstr. 30 + * 82178 Puchheim + * Germany + * <rtems@embedded-brains.de> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + */ + +#if HAVE_CONFIG_H + #include "config.h" +#endif + +#include <sys/stat.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <string.h> + +#include <rtems/blkdev.h> +#include <rtems/bdbuf.h> +#include <rtems/imfs.h> + +typedef struct { + rtems_disk_device dd; + int fd; +} rtems_blkdev_imfs_context; + +static ssize_t rtems_blkdev_imfs_read( + rtems_libio_t *iop, + void *buffer, + size_t count +) +{ + int rv; + const rtems_blkdev_imfs_context *ctx = IMFS_generic_get_context_by_iop(iop); + const rtems_disk_device *dd = &ctx->dd; + ssize_t remaining = (ssize_t) count; + off_t offset = iop->offset; + ssize_t block_size = (ssize_t) rtems_disk_get_block_size(dd); + rtems_blkdev_bnum block = (rtems_blkdev_bnum) (offset / block_size); + ssize_t block_offset = (ssize_t) (offset % block_size); + char *dst = buffer; + + while (remaining > 0) { + rtems_bdbuf_buffer *bd; + rtems_status_code sc = rtems_bdbuf_read(dd, block, &bd); + + if (sc == RTEMS_SUCCESSFUL) { + ssize_t copy = block_size - block_offset; + + if (copy > remaining) { + copy = remaining; + } + + memcpy(dst, (char *) bd->buffer + block_offset, (size_t) copy); + + sc = rtems_bdbuf_release(bd); + if (sc == RTEMS_SUCCESSFUL) { + block_offset = 0; + remaining -= copy; + dst += copy; + ++block; + } else { + remaining = -1; + } + } else { + remaining = -1; + } + } + + if (remaining >= 0) { + rv = (ssize_t) count; + } else { + errno = EIO; + rv = -1; + } + + return rv; +} + +static ssize_t rtems_blkdev_imfs_write( + rtems_libio_t *iop, + const void *buffer, + size_t count +) +{ + int rv; + const rtems_blkdev_imfs_context *ctx = IMFS_generic_get_context_by_iop(iop); + const rtems_disk_device *dd = &ctx->dd; + ssize_t remaining = (ssize_t) count; + off_t offset = iop->offset; + ssize_t block_size = (ssize_t) rtems_disk_get_block_size(dd); + rtems_blkdev_bnum block = (rtems_blkdev_bnum) (offset / block_size); + ssize_t block_offset = (ssize_t) (offset % block_size); + const char *src = buffer; + + while (remaining > 0) { + rtems_status_code sc; + rtems_bdbuf_buffer *bd; + + if (block_offset == 0 && remaining >= block_size) { + sc = rtems_bdbuf_get(dd, block, &bd); + } else { + sc = rtems_bdbuf_read(dd, block, &bd); + } + + if (sc == RTEMS_SUCCESSFUL) { + ssize_t copy = block_size - block_offset; + + if (copy > remaining) { + copy = remaining; + } + + memcpy((char *) bd->buffer + block_offset, src, (size_t) copy); + + sc = rtems_bdbuf_release_modified(bd); + if (sc == RTEMS_SUCCESSFUL) { + block_offset = 0; + remaining -= copy; + src += copy; + ++block; + } else { + remaining = -1; + } + } else { + remaining = -1; + } + } + + if (remaining >= 0) { + rv = (ssize_t) count; + } else { + errno = EIO; + rv = -1; + } + + return rv; +} + +static int rtems_blkdev_imfs_ioctl( + rtems_libio_t *iop, + uint32_t request, + void *buffer +) +{ + int rv = 0; + + if (request != RTEMS_BLKIO_REQUEST) { + rtems_blkdev_imfs_context *ctx = IMFS_generic_get_context_by_iop(iop); + rtems_disk_device *dd = &ctx->dd; + + rv = (*dd->ioctl)(dd, request, buffer); + } else { + /* + * It is not allowed to directly access the driver circumventing the cache. + */ + errno = EINVAL; + rv = -1; + } + + return rv; +} + +static int rtems_blkdev_imfs_fstat( + const rtems_filesystem_location_info_t *loc, + struct stat *buf +) +{ + const rtems_blkdev_imfs_context *ctx = + IMFS_generic_get_context_by_location(loc); + const rtems_disk_device *dd = &ctx->dd; + + buf->st_rdev = rtems_disk_get_device_identifier(dd); + buf->st_blksize = rtems_disk_get_block_size(dd); + buf->st_blocks = rtems_disk_get_block_count(dd); + + return IMFS_stat(loc, buf); +} + +static int rtems_blkdev_imfs_fsync_or_fdatasync( + rtems_libio_t *iop +) +{ + int rv = 0; + const rtems_blkdev_imfs_context *ctx = IMFS_generic_get_context_by_iop(iop); + const rtems_disk_device *dd = &ctx->dd; + rtems_status_code sc = rtems_bdbuf_syncdev(dd); + + if (sc != RTEMS_SUCCESSFUL) { + errno = EIO; + rv = -1; + } + + return rv; +} + +static const rtems_filesystem_file_handlers_r rtems_blkdev_imfs_node = { + .open_h = rtems_filesystem_default_open, + .close_h = rtems_filesystem_default_close, + .read_h = rtems_blkdev_imfs_read, + .write_h = rtems_blkdev_imfs_write, + .ioctl_h = rtems_blkdev_imfs_ioctl, + .lseek_h = rtems_filesystem_default_lseek_success, + .fstat_h = rtems_blkdev_imfs_fstat, + .ftruncate_h = rtems_filesystem_default_ftruncate, + .fsync_h = rtems_blkdev_imfs_fsync_or_fdatasync, + .fdatasync_h = rtems_blkdev_imfs_fsync_or_fdatasync, + .fcntl_h = rtems_filesystem_default_fcntl +}; + +static IMFS_jnode_t *rtems_blkdev_imfs_initialize( + IMFS_jnode_t *node, + const IMFS_types_union *info +) +{ + rtems_blkdev_imfs_context *ctx; + rtems_disk_device *dd; + + node = IMFS_node_initialize_generic(node, info); + + ctx = IMFS_generic_get_context_by_node(node); + dd = &ctx->dd; + dd->dev = IMFS_generic_get_device_identifier_by_node(node); + + return node; +} + +static IMFS_jnode_t *rtems_blkdev_imfs_destroy(IMFS_jnode_t *node) +{ + rtems_blkdev_imfs_context *ctx = IMFS_generic_get_context_by_node(node); + rtems_disk_device *dd = &ctx->dd; + + rtems_bdbuf_syncdev(dd); + rtems_bdbuf_purge_dev(dd); + + if (ctx->fd >= 0) { + close(ctx->fd); + } else { + (*dd->ioctl)(dd, RTEMS_BLKIO_DELETED, NULL); + } + + free(ctx); + + return node; +} + +static const IMFS_node_control rtems_blkdev_imfs_control = { + .imfs_type = IMFS_GENERIC, + .handlers = &rtems_blkdev_imfs_node, + .node_initialize = rtems_blkdev_imfs_initialize, + .node_remove = IMFS_node_remove_default, + .node_destroy = rtems_blkdev_imfs_destroy +}; + +rtems_status_code rtems_blkdev_create( + const char *device, + uint32_t block_size, + rtems_blkdev_bnum block_count, + rtems_block_device_ioctl handler, + void *driver_data +) +{ + rtems_status_code sc = RTEMS_SUCCESSFUL; + + if (block_size > 0 && block_count > 0) { + rtems_blkdev_imfs_context *ctx = calloc(1, sizeof(*ctx)); + + if (ctx != NULL) { + rtems_disk_device *dd = &ctx->dd; + int rv; + + ctx->fd = -1; + + dd->phys_dev = dd; + dd->size = block_count; + dd->media_block_size = block_size; + dd->block_size = block_size; + dd->ioctl = handler; + dd->driver_data = driver_data; + + if ((*handler)(dd, RTEMS_BLKIO_CAPABILITIES, &dd->capabilities) != 0) { + dd->capabilities = 0; + } + + rv = IMFS_make_generic_node( + device, + S_IFBLK | S_IRWXU | S_IRWXG | S_IRWXO, + &rtems_blkdev_imfs_control, + ctx + ); + + if (rv != 0) { + free(ctx); + sc = RTEMS_UNSATISFIED; + } + } else { + sc = RTEMS_NO_MEMORY; + } + } else { + sc = RTEMS_INVALID_NUMBER; + } + + return sc; +} + +rtems_status_code rtems_blkdev_create_partition( + const char *partition, + const char *device, + rtems_blkdev_bnum block_begin, + rtems_blkdev_bnum block_count +) +{ + rtems_status_code sc = RTEMS_SUCCESSFUL; + int fd = open(device, O_RDWR); + + if (fd >= 0) { + int rv; + struct stat st; + + rv = fstat(fd, &st); + if (rv == 0 && S_ISBLK(st.st_mode)) { + rtems_disk_device *dd; + + rv = ioctl(fd, RTEMS_BLKIO_GETDISKDEV, &dd); + if (rv == 0) { + rtems_blkdev_bnum device_block_count = rtems_disk_get_block_count(dd); + + if ( + block_begin < device_block_count + && block_count > 0 + && block_count <= device_block_count - block_begin + ) { + rtems_blkdev_imfs_context *ctx = malloc(sizeof(*ctx)); + + if (ctx != NULL) { + memcpy(&ctx->dd, dd, sizeof(ctx->dd)); + + ctx->dd.start = block_begin; + ctx->dd.size = block_count; + ctx->fd = fd; + + rv = IMFS_make_generic_node( + partition, + S_IFBLK | S_IRWXU | S_IRWXG | S_IRWXO, + &rtems_blkdev_imfs_control, + ctx + ); + + if (rv != 0) { + free(ctx); + sc = RTEMS_UNSATISFIED; + } + } else { + sc = RTEMS_NO_MEMORY; + } + } else { + sc = RTEMS_INVALID_NUMBER; + } + } else { + sc = RTEMS_NOT_IMPLEMENTED; + } + } else { + sc = RTEMS_INVALID_NODE; + } + + if (sc != RTEMS_SUCCESSFUL) { + close(fd); + } + } else { + sc = RTEMS_INVALID_ID; + } + + return sc; +} |