forked from Imagelibrary/rtems
libblock: Add generic IMFS block device nodes
New functions o rtems_blkdev_create(), and o rtems_blkdev_create_partition(). New test libtests/block11.
This commit is contained in:
@@ -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 \
|
||||
|
||||
@@ -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
|
||||
|
||||
380
cpukit/libblock/src/blkdev-imfs.c
Normal file
380
cpukit/libblock/src/blkdev-imfs.c
Normal file
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user