summaryrefslogtreecommitdiffstats
path: root/cpukit
diff options
context:
space:
mode:
authorSebastian Huber <sebastian.huber@embedded-brains.de>2012-03-05 15:06:02 +0100
committerSebastian Huber <sebastian.huber@embedded-brains.de>2012-03-13 12:24:18 +0100
commit01211720ec7e19f70e900c1e21b415e2a126436b (patch)
treea9f7eaee61a37c71bd829aacc124215710568e6d /cpukit
parentlibblock: Split file and simplify (diff)
downloadrtems-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.am1
-rw-r--r--cpukit/libblock/include/rtems/blkdev.h50
-rw-r--r--cpukit/libblock/src/blkdev-imfs.c380
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;
+}