summaryrefslogtreecommitdiffstats
path: root/cpukit/libblock
diff options
context:
space:
mode:
Diffstat (limited to 'cpukit/libblock')
-rw-r--r--cpukit/libblock/.cvsignore2
-rw-r--r--cpukit/libblock/Makefile.am37
-rw-r--r--cpukit/libblock/README14
-rw-r--r--cpukit/libblock/include/rtems/bdbuf.h594
-rw-r--r--cpukit/libblock/include/rtems/bdpart.h409
-rw-r--r--cpukit/libblock/include/rtems/blkdev.h276
-rw-r--r--cpukit/libblock/include/rtems/diskdevs.h341
-rw-r--r--cpukit/libblock/include/rtems/flashdisk.h366
-rw-r--r--cpukit/libblock/include/rtems/ide_part_table.h217
-rw-r--r--cpukit/libblock/include/rtems/media.h521
-rw-r--r--cpukit/libblock/include/rtems/nvdisk-sram.h25
-rw-r--r--cpukit/libblock/include/rtems/nvdisk.h209
-rw-r--r--cpukit/libblock/include/rtems/ramdisk.h243
-rw-r--r--cpukit/libblock/src/bdbuf.c2989
-rw-r--r--cpukit/libblock/src/bdpart-create.c160
-rw-r--r--cpukit/libblock/src/bdpart-dump.c97
-rw-r--r--cpukit/libblock/src/bdpart-mount.c184
-rw-r--r--cpukit/libblock/src/bdpart-read.c348
-rw-r--r--cpukit/libblock/src/bdpart-register.c163
-rw-r--r--cpukit/libblock/src/bdpart-sort.c48
-rw-r--r--cpukit/libblock/src/bdpart-write.c302
-rw-r--r--cpukit/libblock/src/blkdev-ops.c32
-rw-r--r--cpukit/libblock/src/blkdev.c263
-rw-r--r--cpukit/libblock/src/diskdevs.c589
-rw-r--r--cpukit/libblock/src/flashdisk.c2603
-rw-r--r--cpukit/libblock/src/ide_part_table.c594
-rw-r--r--cpukit/libblock/src/media-desc.c72
-rw-r--r--cpukit/libblock/src/media-dev-ident.c47
-rw-r--r--cpukit/libblock/src/media-path.c86
-rw-r--r--cpukit/libblock/src/media-server.c152
-rw-r--r--cpukit/libblock/src/media.c1012
-rw-r--r--cpukit/libblock/src/nvdisk-sram.c68
-rw-r--r--cpukit/libblock/src/nvdisk.c845
-rw-r--r--cpukit/libblock/src/ramdisk-config.c90
-rw-r--r--cpukit/libblock/src/ramdisk-driver.c134
-rw-r--r--cpukit/libblock/src/ramdisk-init.c126
-rw-r--r--cpukit/libblock/src/show_bdbuf.c909
37 files changed, 15167 insertions, 0 deletions
diff --git a/cpukit/libblock/.cvsignore b/cpukit/libblock/.cvsignore
new file mode 100644
index 0000000000..282522db03
--- /dev/null
+++ b/cpukit/libblock/.cvsignore
@@ -0,0 +1,2 @@
+Makefile
+Makefile.in
diff --git a/cpukit/libblock/Makefile.am b/cpukit/libblock/Makefile.am
new file mode 100644
index 0000000000..15c3852da4
--- /dev/null
+++ b/cpukit/libblock/Makefile.am
@@ -0,0 +1,37 @@
+##
+## $Id$
+##
+
+include $(top_srcdir)/automake/multilib.am
+include $(top_srcdir)/automake/compile.am
+
+noinst_LIBRARIES = libblock.a
+libblock_a_SOURCES = src/bdbuf.c \
+ src/blkdev.c \
+ src/blkdev-ops.c \
+ src/diskdevs.c \
+ src/flashdisk.c \
+ src/ramdisk-driver.c \
+ src/ramdisk-init.c \
+ src/ramdisk-config.c \
+ src/ide_part_table.c \
+ src/nvdisk.c \
+ src/nvdisk-sram.c \
+ src/bdpart-create.c \
+ src/bdpart-dump.c \
+ src/bdpart-mount.c \
+ src/bdpart-read.c \
+ src/bdpart-register.c \
+ src/bdpart-sort.c \
+ src/bdpart-write.c \
+ src/media-path.c \
+ src/media.c \
+ src/media-server.c \
+ src/media-desc.c \
+ src/media-dev-ident.c \
+ include/rtems/bdbuf.h include/rtems/blkdev.h \
+ include/rtems/diskdevs.h include/rtems/flashdisk.h \
+ include/rtems/ramdisk.h include/rtems/nvdisk.h include/rtems/nvdisk-sram.h \
+ include/rtems/ide_part_table.h
+
+include $(top_srcdir)/automake/local.am
diff --git a/cpukit/libblock/README b/cpukit/libblock/README
new file mode 100644
index 0000000000..671cc7f645
--- /dev/null
+++ b/cpukit/libblock/README
@@ -0,0 +1,14 @@
+#
+# $Id$
+#
+
+This directory contains the block device (HDD, CDROMs, etc) support code.
+It includes:
+ - block device driver interface
+ - generic open/close/read/write/ioctl primitives for block device drivers
+ - disk I/O buffering
+ - logical disk support
+ - RAM disk block device driver
+
+Victor V. Vengerov, <vvv@oktet.ru>
+November, 7 2001
diff --git a/cpukit/libblock/include/rtems/bdbuf.h b/cpukit/libblock/include/rtems/bdbuf.h
new file mode 100644
index 0000000000..66a9bbf59b
--- /dev/null
+++ b/cpukit/libblock/include/rtems/bdbuf.h
@@ -0,0 +1,594 @@
+/**
+ * @file
+ *
+ * @ingroup rtems_bdbuf
+ *
+ * Block device buffer management.
+ */
+
+/*
+ * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia
+ * Author: Victor V. Vengerov <vvv@oktet.ru>
+ *
+ * Copyright (C) 2008,2009 Chris Johns <chrisj@rtems.org>
+ * Rewritten to remove score mutex access. Fixes many performance
+ * issues.
+ * Change to support demand driven variable buffer sizes.
+ *
+ * Copyright (c) 2009 embedded brains GmbH.
+ *
+ * @(#) bdbuf.h,v 1.9 2005/02/02 00:06:18 joel Exp
+ */
+
+#ifndef _RTEMS_BDBUF_H
+#define _RTEMS_BDBUF_H
+
+#include <rtems.h>
+#include <rtems/libio.h>
+#include <rtems/chain.h>
+
+#include <rtems/blkdev.h>
+#include <rtems/diskdevs.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @defgroup rtems_libblock Block Device Library
+ *
+ * Block device modules.
+ */
+
+/**
+ * @defgroup rtems_bdbuf Block Device Buffer Management
+ *
+ * @ingroup rtems_libblock
+ *
+ * The Block Device Buffer Management implements a cache between the disk
+ * devices and file systems. The code provides read ahead and write queuing to
+ * the drivers and fast cache look-up using an AVL tree.
+ *
+ * The block size used by a file system can be set at runtime and must be a
+ * multiple of the disk device block size. The disk device's physical block
+ * size is called the media block size. The file system can set the block size
+ * it uses to a larger multiple of the media block size. The driver must be
+ * able to handle buffers sizes larger than one media block.
+ *
+ * The user configures the amount of memory to be used as buffers in the cache,
+ * and the minimum and maximum buffer size. The cache will allocate additional
+ * memory for the buffer descriptors and groups. There are enough buffer
+ * descriptors allocated so all the buffer memory can be used as minimum sized
+ * buffers.
+ *
+ * The cache is a single pool of buffers. The buffer memory is divided into
+ * groups where the size of buffer memory allocated to a group is the maximum
+ * buffer size. A group's memory can be divided down into small buffer sizes
+ * that are a multiple of 2 of the minimum buffer size. A group is the minimum
+ * allocation unit for buffers of a specific size. If a buffer of maximum size
+ * is request the group will have a single buffer. If a buffer of minimum size
+ * is requested the group is divided into minimum sized buffers and the
+ * remaining buffers are held ready for use. A group keeps track of which
+ * buffers are with a file system or driver and groups who have buffer in use
+ * cannot be realloced. Groups with no buffers in use can be taken and
+ * realloced to a new size. This is how buffers of different sizes move around
+ * the cache.
+
+ * The buffers are held in various lists in the cache. All buffers follow this
+ * state machine:
+ *
+ * @dot
+ * digraph state {
+ * size="16,8";
+ * f [label="FREE",style="filled",fillcolor="aquamarine"];
+ * e [label="EMPTY",style="filled",fillcolor="seagreen"];
+ * c [label="CACHED",style="filled",fillcolor="chartreuse"];
+ * ac [label="ACCESS CACHED",style="filled",fillcolor="royalblue"];
+ * am [label="ACCESS MODIFIED",style="filled",fillcolor="royalblue"];
+ * ae [label="ACCESS EMPTY",style="filled",fillcolor="royalblue"];
+ * ap [label="ACCESS PURGED",style="filled",fillcolor="royalblue"];
+ * t [label="TRANSFER",style="filled",fillcolor="red"];
+ * tp [label="TRANSFER PURGED",style="filled",fillcolor="red"];
+ * s [label="SYNC",style="filled",fillcolor="red"];
+ * m [label="MODIFIED",style="filled",fillcolor="gold"];
+ * i [label="INITIAL"];
+ *
+ * legend_transfer [label="Transfer Wake-Up",fontcolor="red",shape="none"];
+ * legend_access [label="Access Wake-Up",fontcolor="royalblue",shape="none"];
+ *
+ * i -> f [label="Init"];
+ * f -> e [label="Buffer Recycle"];
+ * e -> ae [label="Get"];
+ * e -> t [label="Read"];
+ * e -> f [label="Nobody Waits"];
+ * c -> ac [label="Get\nRead"];
+ * c -> e [label="Buffer Recycle\nPurge"];
+ * c -> f [label="Reallocate\nBlock Size Changed"];
+ * t -> c [label="Transfer Done",color="red",fontcolor="red"];
+ * t -> e [label="Transfer Error",color="red",fontcolor="red"];
+ * t -> tp [label="Purge"];
+ * tp -> e [label="Transfer Done\nTransfer Error",color="red",fontcolor="red"];
+ * m -> t [label="Swapout"];
+ * m -> s [label="Block Size Changed"];
+ * m -> am [label="Get\nRead"];
+ * m -> e [label="Purge"];
+ * ac -> m [label="Release Modified",color="royalblue",fontcolor="royalblue"];
+ * ac -> s [label="Sync",color="royalblue",fontcolor="royalblue"];
+ * ac -> c [label="Release",color="royalblue",fontcolor="royalblue"];
+ * ac -> ap [label="Purge"];
+ * am -> m [label="Release\nRelease Modified",color="royalblue",fontcolor="royalblue"];
+ * am -> s [label="Sync",color="royalblue",fontcolor="royalblue"];
+ * am -> ap [label="Purge"];
+ * ae -> m [label="Release Modified",color="royalblue",fontcolor="royalblue"];
+ * ae -> s [label="Sync",color="royalblue",fontcolor="royalblue"];
+ * ae -> e [label="Release",color="royalblue",fontcolor="royalblue"];
+ * ae -> ap [label="Purge"];
+ * ap -> e [label="Release\nRelease Modified\nSync",color="royalblue",fontcolor="royalblue"];
+ * s -> t [label="Swapout"];
+ * s -> e [label="Purge",color="red",fontcolor="red"];
+ * }
+ * @enddot
+ *
+ * Empty or cached buffers are added to the LRU list and removed from this
+ * queue when a caller requests a buffer. This is referred to as getting a
+ * buffer in the code and the event get in the state diagram. The buffer is
+ * assigned to a block and inserted to the AVL based on the block/device key.
+ * If the block is to be read by the user and not in the cache it is transfered
+ * from the disk into memory. If no buffers are on the LRU list the modified
+ * list is checked. If buffers are on the modified the swap out task will be
+ * woken. The request blocks until a buffer is available for recycle.
+ *
+ * A block being accessed is given to the file system layer and not accessible
+ * to another requester until released back to the cache. The same goes to a
+ * buffer in the transfer state. The transfer state means being read or
+ * written. If the file system has modifed the block and releases it as
+ * modified it placed on the cache's modified list and a hold timer
+ * initialised. The buffer is held for the hold time before being written to
+ * disk. Buffers are held for a configurable period of time on the modified
+ * list as a write sets the state to transfer and this locks the buffer out
+ * from the file system until the write completes. Buffers are often accessed
+ * and modified in a series of small updates so if sent to the disk when
+ * released as modified the user would have to block waiting until it had been
+ * written. This would be a performance problem.
+ *
+ * The code performs multiple block reads and writes. Multiple block reads or
+ * read ahead increases performance with hardware that supports it. It also
+ * helps with a large cache as the disk head movement is reduced. It however
+ * is a speculative operation so excessive use can remove valuable and needed
+ * blocks from the cache.
+ *
+ * The cache has the following lists of buffers:
+ * - LRU: Accessed or transfered buffers released in least recently used
+ * order. Empty buffers will be placed to the front.
+ * - Modified: Buffers waiting to be written to disk.
+ * - Sync: Buffers to be synchronized with the disk.
+ *
+ * A cache look-up will be performed to find a suitable buffer. A suitable
+ * buffer is one that matches the same allocation size as the device the buffer
+ * is for. The a buffer's group has no buffers in use with the file system or
+ * driver the group is reallocated. This means the buffers in the group are
+ * invalidated, resized and placed on the LRU queue. There is a performance
+ * issue with this design. The reallocation of a group may forced recently
+ * accessed buffers out of the cache when they should not. The design should be
+ * change to have groups on a LRU list if they have no buffers in use.
+ *
+ * @{
+ */
+
+/**
+ * @brief State of a buffer of the cache.
+ *
+ * The state has several implications. Depending on the state a buffer can be
+ * in the AVL tree, in a list, in use by an entity and a group user or not.
+ *
+ * <table>
+ * <tr>
+ * <th>State</th><th>Valid Data</th><th>AVL Tree</th>
+ * <th>LRU List</th><th>Modified List</th><th>Synchronization List</th>
+ * <th>Group User</th><th>External User</th>
+ * </tr>
+ * <tr>
+ * <td>FREE</td><td></td><td></td>
+ * <td>X</td><td></td><td></td><td></td><td></td>
+ * </tr>
+ * <tr>
+ * <td>EMPTY</td><td></td><td>X</td>
+ * <td></td><td></td><td></td><td></td><td></td>
+ * </tr>
+ * <tr>
+ * <td>CACHED</td><td>X</td><td>X</td>
+ * <td>X</td><td></td><td></td><td></td><td></td>
+ * </tr>
+ * <tr>
+ * <td>ACCESS CACHED</td><td>X</td><td>X</td>
+ * <td></td><td></td><td></td><td>X</td><td>X</td>
+ * </tr>
+ * <tr>
+ * <td>ACCESS MODIFIED</td><td>X</td><td>X</td>
+ * <td></td><td></td><td></td><td>X</td><td>X</td>
+ * </tr>
+ * <tr>
+ * <td>ACCESS EMPTY</td><td></td><td>X</td>
+ * <td></td><td></td><td></td><td>X</td><td>X</td>
+ * </tr>
+ * <tr>
+ * <td>ACCESS PURGED</td><td></td><td>X</td>
+ * <td></td><td></td><td></td><td>X</td><td>X</td>
+ * </tr>
+ * <tr>
+ * <td>MODIFIED</td><td>X</td><td>X</td>
+ * <td></td><td>X</td><td></td><td>X</td><td></td>
+ * </tr>
+ * <tr>
+ * <td>SYNC</td><td>X</td><td>X</td>
+ * <td></td><td></td><td>X</td><td>X</td><td></td>
+ * </tr>
+ * <tr>
+ * <td>TRANSFER</td><td>X</td><td>X</td>
+ * <td></td><td></td><td></td><td>X</td><td>X</td>
+ * </tr>
+ * <tr>
+ * <td>TRANSFER PURGED</td><td></td><td>X</td>
+ * <td></td><td></td><td></td><td>X</td><td>X</td>
+ * </tr>
+ * </table>
+ */
+typedef enum
+{
+ /**
+ * @brief Free.
+ */
+ RTEMS_BDBUF_STATE_FREE = 0,
+
+ /**
+ * @brief Empty.
+ */
+ RTEMS_BDBUF_STATE_EMPTY,
+
+ /**
+ * @brief Cached.
+ */
+ RTEMS_BDBUF_STATE_CACHED,
+
+ /**
+ * @brief Accessed by upper layer with cached data.
+ */
+ RTEMS_BDBUF_STATE_ACCESS_CACHED,
+
+ /**
+ * @brief Accessed by upper layer with modified data.
+ */
+ RTEMS_BDBUF_STATE_ACCESS_MODIFIED,
+
+ /**
+ * @brief Accessed by upper layer with invalid data.
+ */
+ RTEMS_BDBUF_STATE_ACCESS_EMPTY,
+
+ /**
+ * @brief Accessed by upper layer with purged data.
+ */
+ RTEMS_BDBUF_STATE_ACCESS_PURGED,
+
+ /**
+ * @brief Modified by upper layer.
+ */
+ RTEMS_BDBUF_STATE_MODIFIED,
+
+ /**
+ * @brief Scheduled for synchronization.
+ */
+ RTEMS_BDBUF_STATE_SYNC,
+
+ /**
+ * @brief In transfer by block device driver.
+ */
+ RTEMS_BDBUF_STATE_TRANSFER,
+
+ /**
+ * @brief In transfer by block device driver and purged.
+ */
+ RTEMS_BDBUF_STATE_TRANSFER_PURGED
+} rtems_bdbuf_buf_state;
+
+/**
+ * Forward reference to the block.
+ */
+struct rtems_bdbuf_group;
+typedef struct rtems_bdbuf_group rtems_bdbuf_group;
+
+/**
+ * To manage buffers we using buffer descriptors (BD). A BD holds a buffer plus
+ * a range of other information related to managing the buffer in the cache. To
+ * speed-up buffer lookup descriptors are organized in AVL-Tree. The fields
+ * 'dev' and 'block' are search keys.
+ */
+typedef struct rtems_bdbuf_buffer
+{
+ rtems_chain_node link; /**< Link the BD onto a number of lists. */
+
+ struct rtems_bdbuf_avl_node
+ {
+ struct rtems_bdbuf_buffer* left; /**< Left Child */
+ struct rtems_bdbuf_buffer* right; /**< Right Child */
+ signed char cache; /**< Cache */
+ signed char bal; /**< The balance of the sub-tree */
+ } avl;
+
+ dev_t dev; /**< device number */
+
+ rtems_blkdev_bnum block; /**< block number on the device */
+
+ unsigned char* buffer; /**< Pointer to the buffer memory area */
+
+ volatile rtems_bdbuf_buf_state state; /**< State of the buffer. */
+
+ volatile uint32_t waiters; /**< The number of threads waiting on this
+ * buffer. */
+ rtems_bdbuf_group* group; /**< Pointer to the group of BDs this BD is
+ * part of. */
+ volatile uint32_t hold_timer; /**< Timer to indicate how long a buffer
+ * has been held in the cache modified. */
+
+ int references; /**< Allow reference counting by owner. */
+ void* user; /**< User data. */
+} rtems_bdbuf_buffer;
+
+/**
+ * A group is a continuous block of buffer descriptors. A group covers the
+ * maximum configured buffer size and is the allocation size for the buffers to
+ * a specific buffer size. If you allocate a buffer to be a specific size, all
+ * buffers in the group, if there are more than 1 will also be that size. The
+ * number of buffers in a group is a multiple of 2, ie 1, 2, 4, 8, etc.
+ */
+struct rtems_bdbuf_group
+{
+ rtems_chain_node link; /**< Link the groups on a LRU list if they
+ * have no buffers in use. */
+ size_t bds_per_group; /**< The number of BD allocated to this
+ * group. This value must be a multiple of
+ * 2. */
+ uint32_t users; /**< How many users the block has. */
+ rtems_bdbuf_buffer* bdbuf; /**< First BD this block covers. */
+};
+
+/**
+ * Buffering configuration definition. See confdefs.h for support on using this
+ * structure.
+ */
+typedef struct rtems_bdbuf_config {
+ uint32_t max_read_ahead_blocks; /**< Number of blocks to read
+ * ahead. */
+ uint32_t max_write_blocks; /**< Number of blocks to write
+ * at once. */
+ rtems_task_priority swapout_priority; /**< Priority of the swap out
+ * task. */
+ uint32_t swapout_period; /**< Period swapout checks buf
+ * timers. */
+ uint32_t swap_block_hold; /**< Period a buffer is held. */
+ size_t swapout_workers; /**< The number of worker
+ * threads for the swapout
+ * task. */
+ rtems_task_priority swapout_worker_priority; /**< Priority of the swap out
+ * task. */
+ size_t size; /**< Size of memory in the
+ * cache */
+ uint32_t buffer_min; /**< Minimum buffer size. */
+ uint32_t buffer_max; /**< Maximum buffer size
+ * supported. It is also the
+ * allocation size. */
+} rtems_bdbuf_config;
+
+/**
+ * External reference to the configuration.
+ *
+ * The configuration is provided by the application.
+ */
+extern const rtems_bdbuf_config rtems_bdbuf_configuration;
+
+/**
+ * The max_read_ahead_blocks value is altered if there are fewer buffers
+ * than this defined max. This stops thrashing in the cache.
+ */
+#define RTEMS_BDBUF_MAX_READ_AHEAD_BLOCKS_DEFAULT 0
+
+/**
+ * Default maximum number of blocks to write at once.
+ */
+#define RTEMS_BDBUF_MAX_WRITE_BLOCKS_DEFAULT 16
+
+/**
+ * Default swap-out task priority.
+ */
+#define RTEMS_BDBUF_SWAPOUT_TASK_PRIORITY_DEFAULT 15
+
+/**
+ * Default swap-out task swap period in milli seconds.
+ */
+#define RTEMS_BDBUF_SWAPOUT_TASK_SWAP_PERIOD_DEFAULT 250
+
+/**
+ * Default swap-out task block hold time in milli seconds.
+ */
+#define RTEMS_BDBUF_SWAPOUT_TASK_BLOCK_HOLD_DEFAULT 1000
+
+/**
+ * Default swap-out worker tasks. Currently disabled.
+ */
+#define RTEMS_BDBUF_SWAPOUT_WORKER_TASKS_DEFAULT 0
+
+/**
+ * Default swap-out worker task priority. The same as the swapout task.
+ */
+#define RTEMS_BDBUF_SWAPOUT_WORKER_TASK_PRIORITY_DEFAULT \
+ RTEMS_BDBUF_SWAPOUT_TASK_PRIORITY_DEFAULT
+
+/**
+ * Default size of memory allocated to the cache.
+ */
+#define RTEMS_BDBUF_CACHE_MEMORY_SIZE_DEFAULT (64 * 512)
+
+/**
+ * Default minimum size of buffers.
+ */
+#define RTEMS_BDBUF_BUFFER_MIN_SIZE_DEFAULT (512)
+
+/**
+ * Default maximum size of buffers.
+ */
+#define RTEMS_BDBUF_BUFFER_MAX_SIZE_DEFAULT (4096)
+
+/**
+ * Prepare buffering layer to work - initialize buffer descritors and (if it is
+ * neccessary) buffers. After initialization all blocks is placed into the
+ * ready state.
+ *
+ * @return RTEMS status code (RTEMS_SUCCESSFUL if operation completed
+ * successfully or error code if error is occured)
+ */
+rtems_status_code
+rtems_bdbuf_init (void);
+
+/**
+ * Get block buffer for data to be written into. The buffers is set to the
+ * access or modifed access state. If the buffer is in the cache and modified
+ * the state is access modified else the state is access. This buffer contents
+ * are not initialised if the buffer is not already in the cache. If the block
+ * is already resident in memory it is returned how-ever if not in memory the
+ * buffer is not read from disk. This call is used when writing the whole block
+ * on a disk rather than just changing a part of it. If there is no buffers
+ * available this call will block. A buffer obtained with this call will not be
+ * involved in a transfer request and will not be returned to another user
+ * until released. If the buffer is already with a user when this call is made
+ * the call is blocked until the buffer is returned. The highest priority
+ * waiter will obtain the buffer first.
+ *
+ * The block number is the linear block number. This is relative to the start
+ * of the partition on the media.
+ *
+ * @param device Device number (constructed of major and minor device number)
+ * @param block Linear media block number
+ * @param bd Reference to the buffer descriptor pointer.
+ *
+ * @return RTEMS status code (RTEMS_SUCCESSFUL if operation completed
+ * successfully or error code if error is occured)
+ */
+rtems_status_code
+rtems_bdbuf_get (dev_t device, rtems_blkdev_bnum block, rtems_bdbuf_buffer** bd);
+
+/**
+ * Get the block buffer and if not already in the cache read from the disk. If
+ * specified block already cached return. The buffer is set to the access or
+ * modifed access state. If the buffer is in the cache and modified the state
+ * is access modified else the state is access. If block is already being read
+ * from disk for being written to disk this call blocks. If the buffer is
+ * waiting to be written it is removed from modified queue and returned to the
+ * user. If the buffer is not in the cache a new buffer is obtained and the
+ * data read from disk. The call may block until these operations complete. A
+ * buffer obtained with this call will not be involved in a transfer request
+ * and will not be returned to another user until released. If the buffer is
+ * already with a user when this call is made the call is blocked until the
+ * buffer is returned. The highest priority waiter will obtain the buffer
+ * first.
+ *
+ * @param device Device number (constructed of major and minor device number)
+ * @param block Linear media block number
+ * @param bd Reference to the buffer descriptor pointer.
+ *
+ * @return RTEMS status code (RTEMS_SUCCESSFUL if operation completed
+ * successfully or error code if error is occured)
+ */
+rtems_status_code
+rtems_bdbuf_read (dev_t device, rtems_blkdev_bnum block, rtems_bdbuf_buffer** bd);
+
+/**
+ * Release the buffer obtained by a read call back to the cache. If the buffer
+ * was obtained by a get call and was not already in the cache the release
+ * modified call should be used. A buffer released with this call obtained by a
+ * get call may not be in sync with the contents on disk. If the buffer was in
+ * the cache and modified before this call it will be returned to the modified
+ * queue. The buffers is returned to the end of the LRU list.
+ *
+ * @param bd Reference to the buffer descriptor.
+ *
+ * @return RTEMS status code (RTEMS_SUCCESSFUL if operation completed
+ * successfully or error code if error is occured)
+ */
+rtems_status_code
+rtems_bdbuf_release (rtems_bdbuf_buffer* bd);
+
+/**
+ * Release the buffer allocated with a get or read call placing it on the
+ * modidied list. If the buffer was not released modified before the hold
+ * timer is set to the configuration value. If the buffer had been released
+ * modified before but not written to disk the hold timer is not updated. The
+ * buffer will be written to disk when the hold timer has expired, there are
+ * not more buffers available in the cache and a get or read buffer needs one
+ * or a sync call has been made. If the buffer is obtained with a get or read
+ * before the hold timer has expired the buffer will be returned to the user.
+ *
+ * @param bd Reference to the buffer descriptor.
+ *
+ * @return RTEMS status code (RTEMS_SUCCESSFUL if operation completed
+ * successfully or error code if error is occured)
+ */
+rtems_status_code
+rtems_bdbuf_release_modified (rtems_bdbuf_buffer* bd);
+
+/**
+ * Release the buffer as modified and wait until it has been synchronized with
+ * the disk by writing it. This buffer will be the first to be transfer to disk
+ * and other buffers may also be written if the maximum number of blocks in a
+ * requests allows it.
+ *
+ * @note This code does not lock the sync mutex and stop additions to the
+ * modified queue.
+
+ * @param bd Reference to the buffer descriptor.
+ *
+ * @return RTEMS status code (RTEMS_SUCCESSFUL if operation completed
+ * successfully or error code if error is occured)
+ */
+rtems_status_code
+rtems_bdbuf_sync (rtems_bdbuf_buffer* bd);
+
+/**
+ * Synchronize all modified buffers for this device with the disk and wait
+ * until the transfers have completed. The sync mutex for the cache is locked
+ * stopping the addition of any further modifed buffers. It is only the
+ * currently modified buffers that are written.
+ *
+ * @note Nesting calls to sync multiple devices will be handled sequentially. A
+ * nested call will be blocked until the first sync request has complete.
+ *
+ * @param dev Block device number
+ *
+ * @return RTEMS status code (RTEMS_SUCCESSFUL if operation completed
+ * successfully or error code if error is occured)
+ */
+rtems_status_code
+rtems_bdbuf_syncdev (dev_t dev);
+
+/**
+ * @brief Purges all buffers that matches the device identifier @a dev.
+ *
+ * This may result in loss of data.
+ */
+void
+rtems_bdbuf_purge_dev (dev_t dev);
+
+/**
+ * @brief Purges all buffers that matches the device major number @a major.
+ *
+ * This may result in loss of data.
+ */
+void
+rtems_bdbuf_purge_major (rtems_device_major_number major);
+
+/** @} */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/cpukit/libblock/include/rtems/bdpart.h b/cpukit/libblock/include/rtems/bdpart.h
new file mode 100644
index 0000000000..907866eb43
--- /dev/null
+++ b/cpukit/libblock/include/rtems/bdpart.h
@@ -0,0 +1,409 @@
+/**
+ * @file
+ *
+ * @ingroup rtems_bdpart
+ *
+ * Block device partition management.
+ */
+
+/*
+ * Copyright (c) 2009, 2010
+ * embedded brains GmbH
+ * Obere Lagerstr. 30
+ * D-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.
+ */
+
+#ifndef RTEMS_BDPART_H
+#define RTEMS_BDPART_H
+
+#include <uuid/uuid.h>
+
+#include <rtems.h>
+#include <rtems/blkdev.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/**
+ * @defgroup rtems_bdpart Block Device Partition Management
+ *
+ * @ingroup rtems_libblock
+ *
+ * This module provides functions to manage partitions of a disk device.
+ *
+ * A @ref rtems_disk "disk" is a set of blocks which are identified by a
+ * consecutive set of non-negative integers starting at zero. There are also
+ * logical disks which contain a subset of consecutive disk blocks. The
+ * logical disks are used to represent the partitions of a disk. The disk
+ * devices are accessed via the @ref rtems_disk "block device buffer module".
+ *
+ * The partition format on the physical disk will be converted to an internal
+ * representation. It is possible to convert the internal representation into
+ * a specific output format and write it to the physical disk. One of the
+ * constrains for the internal representation was to support the GPT format
+ * easily.
+ *
+ * Currently two physical partition formats are supported. These are the MBR
+ * and the GPT format. Please note that the GPT support is not implemented.
+ * With MBR format we mean the partition format of the wide spread IBM
+ * PC-compatible systems. The GPT format is defined in the Extensible Firmware
+ * Interface (EFI).
+ *
+ * The most common task will be to read the partition information of a disk and
+ * register logical disks for each partition. This can be done with the
+ * rtems_bdpart_register_from_disk() function. Afterwards you can
+ * @ref rtems_fsmount "mount" the file systems within the partitions.
+ *
+ * You can read the partition information from a disk with rtems_bdpart_read()
+ * and write it to the disk with rtems_bdpart_write().
+ *
+ * To create a partition table from scratch for a disk use
+ * rtems_bdpart_create().
+ *
+ * You can access some disk functions with the shell command @c fdisk.
+ *
+ * References used to create this module:
+ * - <a href="http://en.wikipedia.org/wiki/UUID">Universally Unique Identifier</a>
+ * - <a href="http://en.wikipedia.org/wiki/Globally_Unique_Identifier">Globally Unique Identifier</a>
+ * - <a href="http://en.wikipedia.org/wiki/Disk_partitioning">Disk Paritioning</a>
+ * - <a href="http://en.wikipedia.org/wiki/GUID_Partition_Table">GUID Partition Table</a>
+ * - <a href="http://en.wikipedia.org/wiki/Master_boot_record">Master Boot Record</a>
+ * - <a href="http://en.wikipedia.org/wiki/Extended_boot_record">Extended Boot Record</a>
+ * - <a href="http://en.wikipedia.org/wiki/Cylinder-head-sector">Cylinder Head Sector</a>
+ * - <a href="http://www.win.tue.nl/~aeb/partitions/partition_types-1.html">Partition Types</a>
+ *
+ * @{
+ */
+
+/**
+ * @name MBR Partition Types and Flags
+ *
+ * @{
+ */
+
+#define RTEMS_BDPART_MBR_EMPTY 0x0U
+
+#define RTEMS_BDPART_MBR_FAT_12 0x1U
+
+#define RTEMS_BDPART_MBR_FAT_16 0x4U
+
+#define RTEMS_BDPART_MBR_FAT_16_LBA 0xeU
+
+#define RTEMS_BDPART_MBR_FAT_32 0xbU
+
+#define RTEMS_BDPART_MBR_FAT_32_LBA 0xcU
+
+#define RTEMS_BDPART_MBR_EXTENDED 0x5U
+
+#define RTEMS_BDPART_MBR_DATA 0xdaU
+
+#define RTEMS_BDPART_MBR_GPT 0xeeU
+
+#define RTEMS_BDPART_MBR_FLAG_ACTIVE 0x80U
+
+/** @} */
+
+/**
+ * Recommended maximum partition table size.
+ */
+#define RTEMS_BDPART_PARTITION_NUMBER_HINT 16
+
+/**
+ * Partition description.
+ */
+typedef struct rtems_bdpart_partition {
+ /**
+ * Block index for partition begin.
+ */
+ rtems_blkdev_bnum begin;
+
+ /**
+ * Block index for partition end (this block is not a part of the partition).
+ */
+ rtems_blkdev_bnum end;
+
+ /**
+ * Partition type.
+ */
+ uuid_t type;
+
+ /**
+ * Partition ID.
+ */
+ uuid_t id;
+
+ /**
+ * Partition flags.
+ */
+ uint64_t flags;
+} rtems_bdpart_partition;
+
+/**
+ * Disk format for the partition tables.
+ */
+typedef enum {
+ /**
+ * Type value for MBR format.
+ */
+ RTEMS_BDPART_FORMAT_MBR,
+
+ /**
+ * Type value for GPT format.
+ */
+ RTEMS_BDPART_FORMAT_GPT
+} rtems_bdpart_format_type;
+
+/**
+ * Disk format description.
+ */
+typedef union {
+ /**
+ * Format type.
+ */
+ rtems_bdpart_format_type type;
+
+ /**
+ * MBR format fields.
+ */
+ struct {
+ rtems_bdpart_format_type type;
+
+ /**
+ * Disk ID in MBR at offset 440.
+ */
+ uint32_t disk_id;
+
+ /**
+ * This option is used for partition table creation and validation checks
+ * before a write to the disk. It ensures that the first primary
+ * partition and the logical partitions start at head one and sector one
+ * under the virtual one head and 63 sectors geometry. Each begin and
+ * end of a partition will be aligned to the virtual cylinder boundary.
+ */
+ bool dos_compatibility;
+ } mbr;
+
+ /**
+ * GPT format fields.
+ */
+ struct {
+ rtems_bdpart_format_type type;
+
+ /**
+ * Disk ID in GPT header.
+ */
+ uuid_t disk_id;
+ } gpt;
+} rtems_bdpart_format;
+
+/**
+ * Reads the partition information from the physical disk device with name
+ * @a disk_name.
+ *
+ * The partition information will be stored in the partition table
+ * @a partitions with a maximum of @a count partitions. The number of actual
+ * partitions will be stored in @a count. If there are more partitions than
+ * space for storage an error status will be returned. The partition table
+ * format recognized on the disk will be stored in @a format.
+ */
+rtems_status_code rtems_bdpart_read(
+ const char *disk_name,
+ rtems_bdpart_format *format,
+ rtems_bdpart_partition *partitions,
+ size_t *count
+);
+
+/**
+ * Sorts the partition table @a partitions with @a count partitions to have
+ * ascending begin blocks
+ */
+void rtems_bdpart_sort( rtems_bdpart_partition *partitions, size_t count);
+
+/**
+ * Writes the partition table to the physical disk device with name
+ * @a disk_name.
+ *
+ * The partition table @a partitions with @a count partitions will be written
+ * to the disk. The output format for the partition table on the disk is
+ * specified by @a format. There are some consistency checks applied to the
+ * partition table. The partition table must be sorted such that the begin
+ * blocks are in ascending order. This can be done with the
+ * rtems_bdpart_sort() function. The partitions must not overlap. The
+ * partitions must have a positive size. The partitions must be within the
+ * disk. Depending on the output format there are additional constrains.
+ */
+rtems_status_code rtems_bdpart_write(
+ const char *disk_name,
+ const rtems_bdpart_format *format,
+ const rtems_bdpart_partition *partitions,
+ size_t count
+);
+
+/**
+ * Creates a partition table in @a partitions with @a count partitions for the
+ * physical disk device with name @a disk_name.
+ *
+ * The array of positive integer weights in @a distribution must have exactly
+ * @a count elements. The weights in the distribution array are summed up.
+ * Each weight is then divided by the sum to obtain the disk fraction which
+ * forms the corresponding partition. The partition boundaries are generated
+ * with respect to the output format in @a format.
+ */
+rtems_status_code rtems_bdpart_create(
+ const char *disk_name,
+ const rtems_bdpart_format *format,
+ rtems_bdpart_partition *partitions,
+ const unsigned *distribution,
+ size_t count
+);
+
+/**
+ * Registers the partitions as logical disks for the physical disk device with
+ * name @a disk_name.
+ *
+ * For each partition of the partition table @a partitions with @a count
+ * partitions a logical disk is registered. The partition number equals the
+ * partition table index plus one. The name of the logical disk device is the
+ * concatenation of the physical disk device name and the partition number.
+ */
+rtems_status_code rtems_bdpart_register(
+ const char *disk_name,
+ const rtems_bdpart_partition *partitions,
+ size_t count
+);
+
+/**
+ * Reads the partition table from the disk device with name @a disk_name and
+ * registers the partitions as logical disks.
+ *
+ * @see rtems_bdpart_register() and rtems_fsmount().
+ */
+rtems_status_code rtems_bdpart_register_from_disk( const char *disk_name);
+
+/**
+ * Deletes the logical disks associated with the partitions of the disk device
+ * with name @a disk_name.
+ *
+ * The partition table @a partitions with @a count partitions will be used to
+ * determine which disks need to be deleted. It may be obtained from
+ * rtems_bdpart_read().
+ */
+rtems_status_code rtems_bdpart_unregister(
+ const char *disk_name,
+ const rtems_bdpart_partition *partitions,
+ size_t count
+);
+
+/**
+ * Mounts all supported file systems inside the logical disks derived from the
+ * partitions of the physical disk device with name @a disk_name.
+ *
+ * For each partition in the partition table @a partitions with @a count
+ * partitions it will be checked if it contains a supported file system. In
+ * this case a mount point derived from the disk name will be created in the
+ * mount base path @a mount_base. The file system will be mounted there. The
+ * partition number equals the partition table index plus one. The mount point
+ * name for each partition will be the concatenation of the mount base path,
+ * the disk device file name and the parition number.
+ *
+ * @see rtems_bdpart_read().
+ */
+rtems_status_code rtems_bdpart_mount(
+ const char *disk_name,
+ const rtems_bdpart_partition *partitions,
+ size_t count,
+ const char *mount_base
+);
+
+/**
+ * Unmounts all file systems mounted with rtems_bdpart_mount().
+ */
+rtems_status_code rtems_bdpart_unmount(
+ const char *disk_name,
+ const rtems_bdpart_partition *partitions,
+ size_t count,
+ const char *mount_base
+);
+
+/**
+ * Prints the partition table @a partitions with @a count partitions to
+ * standard output.
+ */
+void rtems_bdpart_dump( const rtems_bdpart_partition *partitions, size_t count);
+
+/**
+ * Returns the partition type for the MBR partition type value @a mbr_type in
+ * @a type.
+ */
+void rtems_bdpart_to_partition_type( uint8_t mbr_type, uuid_t type);
+
+/**
+ * Converts the partition type in @a type to the MBR partition type.
+ *
+ * The result will be stored in @a mbr_type. Returns @c true in case of a
+ * successful convertion and otherwise @c false. Both arguments must not be
+ * @c NULL.
+ */
+bool rtems_bdpart_to_mbr_partition_type(
+ const uuid_t type,
+ uint8_t *mbr_type
+);
+
+/** @} */
+
+#define RTEMS_BDPART_MBR_CYLINDER_SIZE 63
+
+#define RTEMS_BDPART_NUMBER_SIZE 4
+
+#define RTEMS_BDPART_BLOCK_SIZE 512
+
+#define RTEMS_BDPART_MBR_TABLE_ENTRY_SIZE 16
+
+#define RTEMS_BDPART_MBR_OFFSET_TABLE_0 446
+
+#define RTEMS_BDPART_MBR_OFFSET_TABLE_1 \
+ (RTEMS_BDPART_MBR_OFFSET_TABLE_0 + RTEMS_BDPART_MBR_TABLE_ENTRY_SIZE)
+
+#define RTEMS_BDPART_MBR_OFFSET_DISK_ID 440
+
+#define RTEMS_BDPART_MBR_OFFSET_SIGNATURE_0 510
+
+#define RTEMS_BDPART_MBR_OFFSET_SIGNATURE_1 511
+
+#define RTEMS_BDPART_MBR_SIGNATURE_0 0x55U
+
+#define RTEMS_BDPART_MBR_SIGNATURE_1 0xaaU
+
+#define RTEMS_BDPART_MBR_OFFSET_BEGIN 8
+
+#define RTEMS_BDPART_MBR_OFFSET_SIZE 12
+
+#define RTEMS_BDPART_MBR_OFFSET_TYPE 4
+
+#define RTEMS_BDPART_MBR_OFFSET_FLAGS 0
+
+static inline uint8_t rtems_bdpart_mbr_partition_type(
+ const uuid_t type
+)
+{
+ return type [0];
+}
+
+rtems_status_code rtems_bdpart_get_disk_data(
+ const char *disk_name,
+ dev_t *disk,
+ rtems_blkdev_bnum *disk_end
+);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* RTEMS_BDPART_H */
diff --git a/cpukit/libblock/include/rtems/blkdev.h b/cpukit/libblock/include/rtems/blkdev.h
new file mode 100644
index 0000000000..e9fa86b248
--- /dev/null
+++ b/cpukit/libblock/include/rtems/blkdev.h
@@ -0,0 +1,276 @@
+/**
+ * @file
+ *
+ * @ingroup rtems_blkdev
+ *
+ * Block device management.
+ */
+
+/*
+ * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia
+ * Author: Victor V. Vengerov <vvv@oktet.ru>
+ *
+ * @(#) $Id$
+ */
+
+#ifndef _RTEMS_BLKDEV_H
+#define _RTEMS_BLKDEV_H
+
+#include <rtems.h>
+#include <rtems/diskdevs.h>
+#include <sys/ioctl.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @defgroup rtems_blkdev Block Device Management
+ *
+ * @ingroup rtems_libblock
+ *
+ * Interface between device drivers and the
+ * @ref rtems_bdbuf "block device buffer module".
+ *
+ * The heart of the block device driver is the @ref RTEMS_BLKIO_REQUEST IO
+ * control. This call puts IO @ref rtems_blkdev_request "requests" to the block
+ * device for asynchronous processing. When a driver executes a request, it
+ * invokes the request done callback function to finish the request.
+ *
+ * @{
+ */
+
+/**
+ * Block device request type.
+ *
+ * @warning The sync request is an IO one and only used from the cache. Use the
+ * Block IO when operating at the device level. We need a sync request
+ * to avoid requests looping for ever.
+ */
+typedef enum rtems_blkdev_request_op {
+ RTEMS_BLKDEV_REQ_READ, /**< Read the requested blocks of data. */
+ RTEMS_BLKDEV_REQ_WRITE, /**< Write the requested blocks of data. */
+ RTEMS_BLKDEV_REQ_SYNC /**< Sync any data with the media. */
+} rtems_blkdev_request_op;
+
+/**
+ * @brief Block device request done callback function type.
+ *
+ * The first parameter @a arg must be the argument provided by the block device
+ * request structure @ref rtems_blkdev_request.
+ *
+ * The second parameter @a status should contain the status of the operation:
+ * - @c RTEMS_SUCCESSFUL Operation was successful.
+ * - @c RTEMS_IO_ERROR Some sort of input or output error.
+ * - @c RTEMS_UNSATISFIED Media no more present.
+ */
+typedef void (*rtems_blkdev_request_cb)(void *arg, rtems_status_code status);
+
+/**
+ * Block device scatter or gather buffer structure.
+ */
+typedef struct rtems_blkdev_sg_buffer {
+ /**
+ * Block index.
+ */
+ rtems_blkdev_bnum block;
+
+ /**
+ * Buffer length.
+ */
+ uint32_t length;
+
+ /**
+ * Buffer pointer.
+ */
+ void *buffer;
+
+ /**
+ * User pointer.
+ */
+ void *user;
+} rtems_blkdev_sg_buffer;
+
+/**
+ * The block device request structure is used to read or write a number of
+ * blocks from or to the device.
+ *
+ * TODO: The use of these req blocks is not a great design. The req is a
+ * struct with a single 'bufs' declared in the req struct and the
+ * others are added in the outer level struct. This relies on the
+ * structs joining as a single array and that assumes the compiler
+ * packs the structs. Why not just place on a list ? The BD has a
+ * node that can be used.
+ */
+typedef struct rtems_blkdev_request {
+ /**
+ * Block device operation (read or write).
+ */
+ rtems_blkdev_request_op req;
+
+ /**
+ * Request done callback function.
+ */
+ rtems_blkdev_request_cb req_done;
+
+ /**
+ * Argument to be passed to callback function.
+ */
+ void *done_arg;
+
+ /**
+ * Last IO operation completion status.
+ */
+ rtems_status_code status;
+
+ /**
+ * Number of blocks for this request.
+ */
+ uint32_t bufnum;
+
+ /**
+ * The task requesting the IO operation.
+ */
+ rtems_id io_task;
+
+ /**
+ * List of scatter or gather buffers.
+ */
+ rtems_blkdev_sg_buffer bufs[0];
+} rtems_blkdev_request;
+
+/**
+ * The start block in a request.
+ *
+ * Only valid if the driver has returned the @ref RTEMS_BLKIO_CAPABILITIES of
+ * @ref RTEMS_BLKDEV_CAP_MULTISECTOR_CONT.
+ */
+#define RTEMS_BLKDEV_START_BLOCK(req) (req->bufs[0].block)
+
+/**
+ * @name IO Control Request Codes
+ *
+ * @{
+ */
+
+#define RTEMS_BLKIO_REQUEST _IOWR('B', 1, rtems_blkdev_request)
+#define RTEMS_BLKIO_GETMEDIABLKSIZE _IOR('B', 2, uint32_t)
+#define RTEMS_BLKIO_GETBLKSIZE _IOR('B', 3, uint32_t)
+#define RTEMS_BLKIO_SETBLKSIZE _IOW('B', 4, uint32_t)
+#define RTEMS_BLKIO_GETSIZE _IOR('B', 5, rtems_blkdev_bnum)
+#define RTEMS_BLKIO_SYNCDEV _IO('B', 6)
+#define RTEMS_BLKIO_DELETED _IO('B', 7)
+#define RTEMS_BLKIO_CAPABILITIES _IO('B', 8)
+
+/** @} */
+
+/**
+ * Only consecutive multi-sector buffer requests are supported.
+ *
+ * This option means the cache will only supply multiple buffers that are
+ * inorder so the ATA multi-sector command for example can be used. This is a
+ * hack to work around the current ATA driver.
+ */
+#define RTEMS_BLKDEV_CAP_MULTISECTOR_CONT (1 << 0)
+
+/**
+ * The driver will accept a sync call. A sync call is made to a driver
+ * after a bdbuf cache sync has finished.
+ */
+#define RTEMS_BLKDEV_CAP_SYNC (1 << 1)
+
+/**
+ * The device driver interface conventions suppose that a driver may contain an
+ * initialize, open, close, read, write and IO control entry points. These
+ * primitives (except initialize) can be implemented in a generic fashion based
+ * upon the supplied block device driver IO control handler. Every block device
+ * driver should provide an initialize entry point, which registers the
+ * appropriate IO control handler.
+ */
+#define RTEMS_GENERIC_BLOCK_DEVICE_DRIVER_ENTRIES \
+ rtems_blkdev_generic_open, \
+ rtems_blkdev_generic_close, \
+ rtems_blkdev_generic_read, \
+ rtems_blkdev_generic_write, \
+ rtems_blkdev_generic_ioctl
+
+/**
+ * Generic block device read primitive.
+ *
+ * Implemented using block device buffer management primitives.
+ */
+rtems_device_driver
+rtems_blkdev_generic_read(
+ rtems_device_major_number major,
+ rtems_device_minor_number minor,
+ void * arg
+);
+
+/**
+ * Generic block device write primitive.
+ *
+ * Implemented using block device buffer management primitives.
+ */
+rtems_device_driver
+rtems_blkdev_generic_write(
+ rtems_device_major_number major,
+ rtems_device_minor_number minor,
+ void * arg
+);
+
+/**
+ * Generic block device open primitive.
+ *
+ * Implemented using block device buffer management primitives.
+ */
+rtems_device_driver
+rtems_blkdev_generic_open(
+ rtems_device_major_number major,
+ rtems_device_minor_number minor,
+ void * arg
+);
+
+/**
+ * Generic block device close primitive.
+ *
+ * Implemented using block device buffer management primitives.
+ */
+rtems_device_driver
+rtems_blkdev_generic_close(
+ rtems_device_major_number major,
+ rtems_device_minor_number minor,
+ void * arg
+);
+
+/**
+ * Generic block device IO control primitive.
+ *
+ * Implemented using block device buffer management primitives.
+ */
+rtems_device_driver
+rtems_blkdev_generic_ioctl(
+ rtems_device_major_number major,
+ rtems_device_minor_number minor,
+ void * arg
+);
+
+/**
+ * Common IO control primitive.
+ *
+ * Use this in all block devices to handle the common set of ioctl requests.
+ */
+int
+rtems_blkdev_ioctl(rtems_disk_device *dd, uint32_t req, void *argp);
+
+/**
+ * @brief Generic block operations driver address table.
+ */
+extern const rtems_driver_address_table rtems_blkdev_generic_ops;
+
+/** @} */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/cpukit/libblock/include/rtems/diskdevs.h b/cpukit/libblock/include/rtems/diskdevs.h
new file mode 100644
index 0000000000..8884999d95
--- /dev/null
+++ b/cpukit/libblock/include/rtems/diskdevs.h
@@ -0,0 +1,341 @@
+/**
+ * @file
+ *
+ * @ingroup rtems_disk
+ *
+ * @brief Block device disk management API.
+ */
+
+/*
+ * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia
+ * Author: Victor V. Vengerov <vvv@oktet.ru>
+ *
+ * @(#) $Id$
+ */
+
+#ifndef _RTEMS_DISKDEVS_H
+#define _RTEMS_DISKDEVS_H
+
+#include <rtems.h>
+#include <rtems/libio.h>
+#include <stdlib.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct rtems_disk_device rtems_disk_device;
+
+/**
+ * @defgroup rtems_disk Block Device Disk Management
+ *
+ * @ingroup rtems_libblock
+ *
+ * @brief This module provides functions to manage disk devices.
+ *
+ * A disk is a set of blocks which are identified by a consecutive set of
+ * non-negative integers starting at zero. There are also logical disks which
+ * contain a subset of consecutive disk blocks. The logical disks are used to
+ * represent the partitions of a disk. The disk devices are accessed via the
+ * @ref rtems_bdbuf "block device buffer module".
+ *
+ * @{
+ */
+
+/**
+ * @brief Block device block index type.
+ */
+typedef uint32_t rtems_blkdev_bnum;
+
+/**
+ * @brief Block device IO control handler type.
+ */
+typedef int (*rtems_block_device_ioctl)(
+ rtems_disk_device *dd,
+ uint32_t req,
+ void *argp
+);
+
+/**
+ * @brief Description of a disk device (logical and physical disks).
+ *
+ * An array of pointer tables to rtems_disk_device structures is maintained.
+ * The first table will be indexed by the major number and the second table
+ * will be indexed by the minor number. This allows quick lookup using a data
+ * structure of moderated size.
+ */
+struct rtems_disk_device {
+ /**
+ * @brief Device identifier (concatenation of major and minor number).
+ */
+ dev_t dev;
+
+ /**
+ * @brief Physical device identifier (equals the @c dev entry if it specifies a
+ * physical device).
+ */
+ rtems_disk_device *phys_dev;
+
+ /**
+ * @brief Driver capabilities.
+ */
+ uint32_t capabilities;
+
+ /**
+ * @brief Disk device name.
+ */
+ char *name;
+
+ /**
+ * @brief Usage counter.
+ *
+ * Devices cannot be deleted if they are in use.
+ */
+ unsigned uses;
+
+ /**
+ * @brief Start block number.
+ *
+ * Equals zero for physical devices. It is a block offset to the related
+ * physical device for logical device.
+ */
+ rtems_blkdev_bnum start;
+
+ /**
+ * @brief Size of the physical or logical disk in blocks.
+ */
+ rtems_blkdev_bnum size;
+
+ /**
+ * @brief Device block size in bytes.
+ *
+ * This is the minimum transfer unit. It can be any size.
+ */
+ uint32_t block_size;
+
+ /**
+ * @brief Device media block size in bytes.
+ *
+ * This is the media transfer unit the hardware defaults to.
+ */
+ uint32_t media_block_size;
+
+ /**
+ * @brief IO control handler for this disk.
+ */
+ rtems_block_device_ioctl ioctl;
+
+ /**
+ * @brief Private data for the disk driver.
+ */
+ void *driver_data;
+
+ /**
+ * @brief Indicates that this disk should be deleted as soon as the last user
+ * releases this disk.
+ */
+ bool deleted;
+};
+
+/**
+ * @name Disk Device Data
+ *
+ * @{
+ */
+
+static inline dev_t rtems_disk_get_device_identifier(
+ const rtems_disk_device *dd
+)
+{
+ return dd->dev;
+}
+
+static inline rtems_device_major_number rtems_disk_get_major_number(
+ const rtems_disk_device *dd
+)
+{
+ return rtems_filesystem_dev_major_t(dd->dev);
+}
+
+static inline rtems_device_minor_number rtems_disk_get_minor_number(
+ const rtems_disk_device *dd
+)
+{
+ return rtems_filesystem_dev_minor_t(dd->dev);
+}
+
+static inline void *rtems_disk_get_driver_data(
+ const rtems_disk_device *dd
+)
+{
+ return dd->driver_data;
+}
+
+static inline uint32_t rtems_disk_get_media_block_size(
+ const rtems_disk_device *dd
+)
+{
+ return dd->media_block_size;
+}
+
+/** @} */
+
+/**
+ * @name Disk Device Maintainance
+ *
+ * @{
+ */
+
+/**
+ * @brief Creates a physical disk with device identifier @a dev.
+ *
+ * The block size @a block_size must be positive. The disk will have
+ * @a block_count blocks. The block index starts with zero. The associated disk
+ * device driver will be invoked via the IO control handler @a handler. A
+ * device node will be registered in the file system with absolute path @a
+ * name, if @a name is not @c NULL. This function is usually invoked from a
+ * block device driver during initialization when a physical device is detected
+ * in the system. The device driver provides an IO control handler to allow
+ * block device operations.
+ *
+ * @retval RTEMS_SUCCESSFUL Successful operation.
+ * @retval RTEMS_NOT_CONFIGURED Cannot lock disk device operation mutex.
+ * @retval RTEMS_INVALID_ADDRESS IO control handler is @c NULL.
+ * @retval RTEMS_INVALID_NUMBER Block size is zero.
+ * @retval RTEMS_NO_MEMORY Not enough memory.
+ * @retval RTEMS_RESOURCE_IN_USE Disk device descriptor is already in use.
+ * @retval RTEMS_UNSATISFIED Cannot create device node.
+ */
+rtems_status_code rtems_disk_create_phys(
+ dev_t dev,
+ uint32_t block_size,
+ rtems_blkdev_bnum block_count,
+ rtems_block_device_ioctl handler,
+ void *driver_data,
+ const char *name
+);
+
+/**
+ * @brief Creates a logical disk with device identifier @a dev.
+ *
+ * A logical disk manages a subset of consecutive blocks contained in the
+ * physical disk with identifier @a phys. The start block index of the logical
+ * disk device is @a begin_block. The block count of the logcal disk will be
+ * @a block_count. The blocks must be within the range of blocks managed by
+ * the associated physical disk device. A device node will be registered in
+ * the file system with absolute path @a name, if @a name is not @c NULL. The
+ * block size and IO control handler are inherited by the physical disk.
+ *
+ * @retval RTEMS_SUCCESSFUL Successful operation.
+ * @retval RTEMS_NOT_CONFIGURED Cannot lock disk device operation mutex.
+ * @retval RTEMS_INVALID_ID Specified physical disk identifier does not
+ * correspond to a physical disk.
+ * @retval RTEMS_INVALID_NUMBER Begin block or block count are out of range.
+ * @retval RTEMS_NO_MEMORY Not enough memory.
+ * @retval RTEMS_RESOURCE_IN_USE Disk device descriptor for logical disk
+ * identifier is already in use.
+ * @retval RTEMS_UNSATISFIED Cannot create device node.
+ */
+rtems_status_code rtems_disk_create_log(
+ dev_t dev,
+ dev_t phys,
+ rtems_blkdev_bnum begin_block,
+ rtems_blkdev_bnum block_count,
+ const char *name
+);
+
+/**
+ * @brief Deletes a physical or logical disk device with identifier @a dev.
+ *
+ * Marks the disk device as deleted. When a physical disk device is deleted,
+ * all corresponding logical disk devices will marked as deleted too. Disks
+ * that are marked as deleted and have a usage counter of zero will be deleted.
+ * The corresponding device nodes will be removed from the file system. In
+ * case of a physical disk deletion the IO control handler will be invoked with
+ * a RTEMS_BLKIO_DELETED request. Disks that are still in use will be deleted
+ * upon release.
+ *
+ * @retval RTEMS_SUCCESSFUL Successful operation.
+ * @retval RTEMS_NOT_CONFIGURED Cannot lock disk device operation mutex.
+ * @retval RTEMS_INVALID_ID No disk for specified device identifier.
+ */
+rtems_status_code rtems_disk_delete(dev_t dev);
+
+/**
+ * @brief Returns the disk device descriptor for the device identifier @a dev.
+ *
+ * Increments usage counter by one. You should release the disk device
+ * descriptor with rtems_disk_release().
+ *
+ * @return Pointer to the disk device descriptor or @c NULL if no corresponding
+ * disk exists.
+ */
+rtems_disk_device *rtems_disk_obtain(dev_t dev);
+
+/**
+ * @brief Releases the disk device descriptor @a dd.
+ *
+ * Decrements usage counter by one.
+ *
+ * @retval RTEMS_SUCCESSFUL Successful operation.
+ */
+rtems_status_code rtems_disk_release(rtems_disk_device *dd);
+
+/** @} */
+
+/**
+ * @name Disk Management
+ *
+ * @{
+ */
+
+/**
+ * @brief Initializes the disk device management.
+ *
+ * This functions returns successful if the disk device management is already
+ * initialized. There is no protection against concurrent access.
+ *
+ * @retval RTEMS_SUCCESSFUL Successful initialization.
+ * @retval RTEMS_NO_MEMORY Not enough memory or no semaphore available.
+ * @retval RTEMS_UNSATISFIED Block device buffer initialization failed.
+ */
+rtems_status_code rtems_disk_io_initialize(void);
+
+/**
+ * @brief Releases all resources allocated for disk device management.
+ *
+ * There is no protection against concurrent access. If parts of the system
+ * are still in use the behaviour is undefined.
+ *
+ * @retval RTEMS_SUCCESSFUL Successful operation.
+ */
+rtems_status_code rtems_disk_io_done(void);
+
+/** @} */
+
+/** @} */
+
+/**
+ * @brief Disk device iterator.
+ *
+ * Returns the next disk device descriptor with a device identifier larger than
+ * @a dev. If there is no such device, @c NULL will be returned. Use minus
+ * one to start the search.
+ *
+ * @code
+ * rtems_status_code sc = RTEMS_SUCCESSFUL;
+ * rtems_disk_device *dd = (dev_t) -1;
+ *
+ * while (sc == RTEMS_SUCCESSFUL && (dd = rtems_disk_next(dev)) != NULL) {
+ * dev = rtems_disk_get_device_identifier(dd);
+ * sc = rtems_disk_release(dd);
+ * }
+ * @endcode
+ */
+rtems_disk_device *rtems_disk_next(dev_t dev);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/cpukit/libblock/include/rtems/flashdisk.h b/cpukit/libblock/include/rtems/flashdisk.h
new file mode 100644
index 0000000000..c24b9b08be
--- /dev/null
+++ b/cpukit/libblock/include/rtems/flashdisk.h
@@ -0,0 +1,366 @@
+/*
+ * flashdisk.h -- Flash disk block device implementation
+ *
+ * Copyright (C) 2007 Chris Johns
+ *
+ * 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.
+ *
+ * $Id$
+ */
+
+#if !defined (_RTEMS_FLASHDISK_H_)
+#define _RTEMS_FLASHDISK_H_
+
+#include <stdint.h>
+#include <sys/ioctl.h>
+
+#include <rtems.h>
+
+/**
+ * The base name of the flash disks.
+ */
+#define RTEMS_FLASHDISK_DEVICE_BASE_NAME "/dev/fdd"
+
+/**
+ * Flash disk specific ioctl request types. To use open the
+ * device and issue the ioctl call.
+ *
+ * @code
+ * int fd = open ("/dev/flashdisk0", O_WRONLY, 0);
+ * if (fd < 0)
+ * {
+ * printf ("driver open failed: %s\n", strerror (errno));
+ * exit (1);
+ * }
+ * if (ioctl (fd, RTEMS_FDISK_IOCTL_ERASE_DISK) < 0)
+ * {
+ * printf ("driver erase failed: %s\n", strerror (errno));
+ * exit (1);
+ * }
+ * close (fd);
+ * @endcode
+ */
+#define RTEMS_FDISK_IOCTL_ERASE_DISK _IO('B', 128)
+#define RTEMS_FDISK_IOCTL_COMPACT _IO('B', 129)
+#define RTEMS_FDISK_IOCTL_ERASE_USED _IO('B', 130)
+#define RTEMS_FDISK_IOCTL_MONITORING _IO('B', 131)
+#define RTEMS_FDISK_IOCTL_INFO_LEVEL _IO('B', 132)
+#define RTEMS_FDISK_IOCTL_PRINT_STATUS _IO('B', 133)
+
+/**
+ * Flash Disk Monitoring Data allows a user to obtain
+ * the current status of the disk.
+ */
+typedef struct rtems_fdisk_monitor_data
+{
+ uint32_t block_size;
+ uint32_t block_count;
+ uint32_t unavail_blocks;
+ uint32_t device_count;
+ uint32_t segment_count;
+ uint32_t page_count;
+ uint32_t blocks_used;
+ uint32_t segs_available;
+ uint32_t segs_used;
+ uint32_t segs_failed;
+ uint32_t seg_erases;
+ uint32_t pages_desc;
+ uint32_t pages_active;
+ uint32_t pages_used;
+ uint32_t pages_bad;
+ uint32_t info_level;
+} rtems_fdisk_monitor_data;
+
+/**
+ * Flash Segment Descriptor holds, number of continuous segments in the
+ * device of this type, the base segment number in the device, the
+ * address offset of the base segment in the device, and the size of
+ * segment.
+ *
+ * Typically this structure is part of a table of segments in the
+ * device which is referenced in the flash disk configuration table.
+ * The reference is kept in the driver and used all the time to
+ * manage the flash device, therefore it must always exist.
+ */
+typedef struct rtems_fdisk_segment_desc
+{
+ uint16_t count; /**< Number of segments of this type in a row. */
+ uint16_t segment; /**< The base segment number. */
+ uint32_t offset; /**< Address offset of base segment in device. */
+ uint32_t size; /**< Size of the segment in bytes. */
+} rtems_fdisk_segment_desc;
+
+/**
+ * Return the number of kilo-bytes.
+ */
+#define RTEMS_FDISK_KBYTES(_k) (UINT32_C(1024) * (_k))
+
+/**
+ * Forward declaration of the device descriptor.
+ */
+struct rtems_fdisk_device_desc;
+
+/**
+ * Flash Low Level driver handlers.
+
+ * Typically this structure is part of a table of handlers in the
+ * device which is referenced in the flash disk configuration table.
+ * The reference is kept in the driver and used all the time to
+ * manage the flash device, therefore it must always exist.
+ */
+typedef struct rtems_fdisk_driver_handlers
+{
+ /**
+ * Read data from the device into the buffer. Return an errno
+ * error number if the device cannot be read. A segment descriptor
+ * can describe more than one segment in a device if the device has
+ * repeating segments. The segment number is the device segment to
+ * access and the segment descriptor must reference the segment
+ * being requested. For example the segment number must resided in
+ * the range [base, base + count).
+ *
+ * @param sd The segment descriptor.
+ * @param device The device to read data from.
+ * @param segment The segment within the device to read.
+ * @param offset The offset in the segment to read.
+ * @param buffer The buffer to read the data into.
+ * @param size The amount of data to read.
+ * @retval 0 No error.
+ * @retval EIO The read did not complete.
+ */
+ int (*read) (const rtems_fdisk_segment_desc* sd,
+ uint32_t device,
+ uint32_t segment,
+ uint32_t offset,
+ void* buffer,
+ uint32_t size);
+
+ /**
+ * Write data from the buffer to the device. Return an errno
+ * error number if the device cannot be written to. A segment
+ * descriptor can describe more than segment in a device if the
+ * device has repeating segments. The segment number is the device
+ * segment to access and the segment descriptor must reference
+ * the segment being requested. For example the segment number must
+ * resided in the range [base, base + count).
+ *
+ * @param sd The segment descriptor.
+ * @param device The device to write data from.
+ * @param segment The segment within the device to write to.
+ * @param offset The offset in the segment to write.
+ * @param buffer The buffer to write the data from.
+ * @param size The amount of data to write.
+ * @retval 0 No error.
+ * @retval EIO The write did not complete or verify.
+ */
+ int (*write) (const rtems_fdisk_segment_desc* sd,
+ uint32_t device,
+ uint32_t segment,
+ uint32_t offset,
+ const void* buffer,
+ uint32_t size);
+
+ /**
+ * Blank a segment in the device. Return an errno error number
+ * if the device cannot be read or is not blank. A segment descriptor
+ * can describe more than segment in a device if the device has
+ * repeating segments. The segment number is the device segment to
+ * access and the segment descriptor must reference the segment
+ * being requested. For example the segment number must resided in
+ * the range [base, base + count).
+ *
+ * @param sd The segment descriptor.
+ * @param device The device to read data from.
+ * @param segment The segment within the device to read.
+ * @param offset The offset in the segment to checl.
+ * @param size The amount of data to check.
+ * @retval 0 No error.
+ * @retval EIO The segment is not blank.
+ */
+ int (*blank) (const rtems_fdisk_segment_desc* sd,
+ uint32_t device,
+ uint32_t segment,
+ uint32_t offset,
+ uint32_t size);
+
+ /**
+ * Verify data in the buffer to the data in the device. Return an
+ * errno error number if the device cannot be read. A segment
+ * descriptor can describe more than segment in a device if the
+ * device has repeating segments. The segment number is the
+ * segment to access and the segment descriptor must reference
+ * the device segment being requested. For example the segment number
+ * must resided in the range [base, base + count).
+ *
+ * @param sd The segment descriptor.
+ * @param device The device to verify data in.
+ * @param segment The segment within the device to verify.
+ * @param offset The offset in the segment to verify.
+ * @param buffer The buffer to verify the data in the device with.
+ * @param size The amount of data to verify.
+ * @retval 0 No error.
+ * @retval EIO The data did not verify.
+ */
+ int (*verify) (const rtems_fdisk_segment_desc* sd,
+ uint32_t device,
+ uint32_t segment,
+ uint32_t offset,
+ const void* buffer,
+ uint32_t size);
+
+ /**
+ * Erase the segment. Return an errno error number if the
+ * segment cannot be erased. A segment descriptor can describe
+ * more than segment in a device if the device has repeating
+ * segments. The segment number is the device segment to access and
+ * the segment descriptor must reference the segment being requested.
+ *
+ * @param sd The segment descriptor.
+ * @param device The device to erase the segment of.
+ * @param segment The segment within the device to erase.
+ * @retval 0 No error.
+ * @retval EIO The segment was not erased.
+ */
+ int (*erase) (const rtems_fdisk_segment_desc* sd,
+ uint32_t device,
+ uint32_t segment);
+
+ /**
+ * Erase the device. Return an errno error number if the
+ * segment cannot be erased. A segment descriptor can describe
+ * more than segment in a device if the device has repeating
+ * segments. The segment number is the segment to access and
+ * the segment descriptor must reference the segment being requested.
+ *
+ * @param sd The segment descriptor.
+ * @param device The device to erase.
+ * @retval 0 No error.
+ * @retval EIO The device was not erased.
+ */
+ int (*erase_device) (const struct rtems_fdisk_device_desc* dd,
+ uint32_t device);
+
+} rtems_fdisk_driver_handlers;
+
+/**
+ * Flash Device Descriptor holds the segments in a device. The
+ * placing of the segments in a device decriptor allows the
+ * low level driver to share the segment descriptors for a
+ * number of devices.
+ *
+ * Typically this structure is part of a table of segments in the
+ * device which is referenced in the flash disk configuration table.
+ * The reference is kept in the driver and used all the time to
+ * manage the flash device, therefore it must always exist.
+ */
+typedef struct rtems_fdisk_device_desc
+{
+ uint32_t segment_count; /**< Number of segments. */
+ const rtems_fdisk_segment_desc* segments; /**< Array of segments. */
+ const rtems_fdisk_driver_handlers* flash_ops; /**< Device handlers. */
+} rtems_fdisk_device_desc;
+
+/**
+ * RTEMS Flash Disk configuration table used to initialise the
+ * driver.
+ *
+ * The unavailable blocks count is the number of blocks less than the
+ * available number of blocks the file system is given. This means there
+ * will always be that number of blocks available when the file system
+ * thinks the disk is full. The compaction code needs blocks to compact
+ * with so you will never be able to have all the blocks allocated to the
+ * file system and be able to full the disk.
+ *
+ * The compacting segment count is the number of segments that are
+ * moved into a new segment. A high number will mean more segments with
+ * low active page counts and high used page counts will be moved into
+ * avaliable pages how-ever this extends the compaction time due to
+ * time it takes the erase the pages. There is no pont making this number
+ * greater than the maximum number of pages in a segment.
+ *
+ * The available compacting segment count is the level when compaction occurs
+ * when writing. If you set this to 0 then compaction will fail because
+ * there will be no segments to compact into.
+ *
+ * The info level can be 0 for off with error, and abort messages allowed.
+ * Level 1 is warning messages, level 1 is informational messages, and level 3
+ * is debugging type prints. The info level can be turned off with a compile
+ * time directive on the command line to the compiler of:
+ *
+ * -DRTEMS_FDISK_TRACE=0
+ */
+typedef struct rtems_flashdisk_config
+{
+ uint32_t block_size; /**< The block size. */
+ uint32_t device_count; /**< The number of devices. */
+ const rtems_fdisk_device_desc* devices; /**< The device descriptions. */
+ uint32_t flags; /**< Set of flags to control
+ driver. */
+ uint32_t unavail_blocks; /**< Number of blocks not
+ available to the file sys. */
+ uint32_t compact_segs; /**< Max number of segs to
+ compact in one pass. */
+ uint32_t avail_compact_segs; /**< The number of segments
+ when compaction occurs
+ when writing. */
+ uint32_t info_level; /**< Default info level. */
+} rtems_flashdisk_config;
+
+/*
+ * Driver flags.
+ */
+
+/**
+ * Leave the erasing of used segment to the background handler.
+ */
+#define RTEMS_FDISK_BACKGROUND_ERASE (1 << 0)
+
+/**
+ * Leave the compacting of of used segment to the background handler.
+ */
+#define RTEMS_FDISK_BACKGROUND_COMPACT (1 << 1)
+
+/**
+ * Check the pages during initialisation to see which pages are
+ * valid and which are not. This could slow down initialising the
+ * disk driver.
+ */
+#define RTEMS_FDISK_CHECK_PAGES (1 << 2)
+
+/**
+ * Blank check the flash device before writing to them. This is needed if
+ * you think you have a driver or device problem.
+ */
+#define RTEMS_FDISK_BLANK_CHECK_BEFORE_WRITE (1 << 3)
+
+/**
+ * Flash disk device driver initialization. Place in a table as the
+ * initialisation entry and remainder of the entries are the
+ * RTEMS block device generic handlers.
+ *
+ * @param major Flash disk major device number.
+ * @param minor Minor device number, not applicable.
+ * @param arg Initialization argument, not applicable.
+ * @return The rtems_device_driver is actually just
+ * rtems_status_code.
+ */
+rtems_device_driver
+rtems_fdisk_initialize (rtems_device_major_number major,
+ rtems_device_minor_number minor,
+ void* arg);
+
+/**
+ * External reference to the configuration. Please supply.
+ * Support is present in confdefs.h for providing this variable.
+ */
+extern const rtems_flashdisk_config rtems_flashdisk_configuration[];
+
+/**
+ * External reference to the number of configurations. Please supply.
+ * Support is present in confdefs.h for providing this variable.
+ */
+extern uint32_t rtems_flashdisk_configuration_size;
+
+#endif
diff --git a/cpukit/libblock/include/rtems/ide_part_table.h b/cpukit/libblock/include/rtems/ide_part_table.h
new file mode 100644
index 0000000000..c6fe587e02
--- /dev/null
+++ b/cpukit/libblock/include/rtems/ide_part_table.h
@@ -0,0 +1,217 @@
+/**
+ * @file rtems/ide_part_table.h
+ *
+ * Support for "MS-DOS-style" partition tables
+ */
+
+/*
+ * Copyright (C) 2002 OKTET Ltd., St.-Petersburg, Russia
+ *
+ * Author: Konstantin Abramenko <Konstantin.Abramenko@oktet.ru>
+ * Alexander Kukuta <Alexander.Kukuta@oktet.ru>
+ *
+ * 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.
+ *
+ * $Id$
+ *
+ *****************************************************************************/
+
+#ifndef _RTEMS_IDE_PART_TABLE_H
+#define _RTEMS_IDE_PART_TABLE_H
+
+#include <rtems/chain.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <rtems.h>
+#include <rtems/blkdev.h>
+#include <rtems/libio.h>
+#include <rtems/libio_.h>
+#include <rtems/bdbuf.h>
+#include <rtems/seterr.h>
+
+/* Minor base number for all logical devices */
+#define RTEMS_IDE_SECTOR_BITS 9
+#define RTEMS_IDE_SECTOR_SIZE 512
+#define RTEMS_IDE_PARTITION_DESCRIPTOR_SIZE 16
+#define RTEMS_IDE_PARTITION_MAX_PARTITION_NUMBER 63
+#define RTEMS_IDE_PARTITION_MAX_SUB_PARTITION_NUMBER 4
+#define RTEMS_IDE_PARTITION_DEV_NAME_LENGTH_MAX 16
+
+#define RTEMS_IDE_PARTITION_MSDOS_SIGNATURE_DATA1 0x55
+#define RTEMS_IDE_PARTITION_MSDOS_SIGNATURE_DATA2 0xaa
+#define RTEMS_IDE_PARTITION_MSDOS_SIGNATURE_OFFSET 0x1fe
+#define RTEMS_IDE_PARTITION_TABLE_OFFSET 0x1be
+#define RTEMS_IDE_PARTITION_TABLE_SIZE (4 * 16)
+#define RTEMS_IDE_PARTITION_BOOTABLE_OFFSET 0
+#define RTEMS_IDE_PARTITION_SYS_TYPE_OFFSET 4
+#define RTEMS_IDE_PARTITION_START_OFFSET 8
+#define RTEMS_IDE_PARTITION_SIZE_OFFSET 12
+
+/*
+ * Conversion from and to little-endian byte order. (no-op on i386/i486)
+ */
+
+#if (CPU_BIG_ENDIAN == TRUE)
+# define LE_TO_CPU_U16(v) CPU_swap_u16(v)
+# define LE_TO_CPU_U32(v) CPU_swap_u32(v)
+# define CPU_TO_LE_U16(v) CPU_swap_u16(v)
+# define CPU_TO_LE_U32(v) CPU_swap_u32(v)
+#else
+# define LE_TO_CPU_U16(v) (v)
+# define LE_TO_CPU_U32(v) (v)
+# define CPU_TO_LE_U16(v) (v)
+# define CPU_TO_LE_U32(v) (v)
+#endif
+
+
+/*
+ * sector_data_t --
+ * corresponds to the sector on the device
+ */
+typedef struct rtems_sector_data_s
+{
+ uint32_t sector_num; /* sector number on the device */
+ uint8_t data[0]; /* raw sector data */
+} rtems_sector_data_t;
+
+
+/*
+ * Enum partition types
+ * see list at http://ata-atapi.com/hiwtab.htm
+ *
+ * @todo Should these have RTEMS before them.
+ */
+enum {
+ EMPTY_PARTITION = 0x00,
+ DOS_FAT12_PARTITION = 0x01,
+ DOS_FAT16_PARTITION = 0x04,
+ EXTENDED_PARTITION = 0x05,
+ DOS_P32MB_PARTITION = 0x06,
+ FAT32_PARTITION = 0x0B,
+ FAT32_LBA_PARTITION = 0x0C,
+ FAT16_LBA_PARTITION = 0x0E,
+ DM6_PARTITION = 0x54,
+ EZD_PARTITION = 0x55,
+ DM6_AUX1PARTITION = 0x51,
+ DM6_AUX3PARTITION = 0x53,
+ LINUX_SWAP = 0x82,
+ LINUX_NATIVE = 0x83,
+ LINUX_EXTENDED = 0x85
+};
+
+
+/* Forward declaration */
+struct rtems_disk_desc_s;
+
+/*
+ * part_desc_t --
+ * contains all neccessary information about partition
+ */
+typedef struct rtems_part_desc_s {
+ uint8_t bootable; /* is the partition active */
+ uint8_t sys_type; /* type of partition */
+ uint8_t log_id; /* logical number of partition */
+ uint32_t start; /* first partition sector, in absolute
+ * numeration */
+ uint32_t size; /* size in sectors */
+ uint32_t end; /* last partition sector, end = start + size - 1 */
+ struct rtems_disk_desc_s *disk_desc; /* descriptor of disk, partition
+ * contains in */
+ struct rtems_part_desc_s *ext_part; /* extended partition containing this
+ * one */
+
+ /* partitions, containing in this one */
+ struct rtems_part_desc_s *sub_part[RTEMS_IDE_PARTITION_MAX_SUB_PARTITION_NUMBER];
+} rtems_part_desc_t;
+
+
+
+typedef struct rtems_disk_desc_s {
+ dev_t dev; /* device number */
+
+ /* device name in /dev filesystem */
+ char dev_name[RTEMS_IDE_PARTITION_DEV_NAME_LENGTH_MAX];
+
+ uint32_t sector_size; /* size of sector */
+ uint32_t sector_bits; /* the base-2 logarithm of sector_size */
+ uint32_t lba_size; /* total amount of sectors in lba address mode */
+ int last_log_id; /* used for logical disks enumerating */
+
+ /* primary partition descriptors */
+ rtems_part_desc_t *partitions[RTEMS_IDE_PARTITION_MAX_PARTITION_NUMBER];
+} rtems_disk_desc_t;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * rtems_ide_part_table_free --
+ * frees disk descriptor structure
+ *
+ * PARAMETERS:
+ * disk_desc - disc descriptor structure to free
+ *
+ * RETURNS:
+ * N/A
+ */
+/**
+ * @deprecated Use the @ref rtems_bdpart "block device partition module" instead.
+ */
+void rtems_ide_part_table_free(
+ rtems_disk_desc_t *disk_desc
+) RTEMS_COMPILER_DEPRECATED_ATTRIBUTE;
+
+
+/*
+ * rtems_ide_part_table_get --
+ * reads partition table structure from the device
+ * and creates disk description structure
+ *
+ * PARAMETERS:
+ * dev_name - path to physical device in /dev filesystem
+ * disk_desc - returned disc description structure
+ *
+ * RETURNS:
+ * RTEMS_SUCCESSFUL if success, or -1 and corresponding errno else
+ */
+/**
+ * @deprecated Use the @ref rtems_bdpart "block device partition module" instead.
+ */
+rtems_status_code rtems_ide_part_table_get(
+ const char *dev_name,
+ rtems_disk_desc_t *disk_desc
+) RTEMS_COMPILER_DEPRECATED_ATTRIBUTE;
+
+
+/*
+ * rtems_ide_part_table_initialize --
+ * initializes logical devices on the physical IDE drive
+ *
+ * PARAMETERS:
+ * dev_name - path to physical device in /dev filesystem
+ *
+ * RETURNS:
+ * RTEMS_SUCCESSFUL if success, or -1 and corresponding errno else
+ */
+/**
+ * @deprecated Use the @ref rtems_bdpart "block device partition module" instead.
+ */
+rtems_status_code rtems_ide_part_table_initialize(
+ char *dev_name
+) RTEMS_COMPILER_DEPRECATED_ATTRIBUTE;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _RTEMS_IDE_PART_TABLE_H */
diff --git a/cpukit/libblock/include/rtems/media.h b/cpukit/libblock/include/rtems/media.h
new file mode 100644
index 0000000000..038357c16d
--- /dev/null
+++ b/cpukit/libblock/include/rtems/media.h
@@ -0,0 +1,521 @@
+/**
+ * @file
+ *
+ * @ingroup RTEMSMedia
+ *
+ * @brief Media API.
+ */
+
+/*
+ * Copyright (c) 2009, 2010 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.
+ */
+
+#ifndef RTEMS_MEDIA_H
+#define RTEMS_MEDIA_H
+
+#include <rtems.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/**
+ * @defgroup RTEMSMedia Media Manager
+ *
+ * @brief Removable media support.
+ *
+ * The media manager may be used to maintain the life cycle of a removable
+ * media. Currently only disk devices are supported. The initiator posts an
+ * event to the media manager and it will respond with appropriate default
+ * actions. For example a disk attach will lead to inspection of the partition
+ * table and mounted file systems. Clients can register listeners to react to
+ * events.
+ * @{
+ */
+
+#define RTEMS_MEDIA_MOUNT_BASE "/media"
+
+#define RTEMS_MEDIA_DELIMITER '-'
+
+/**
+ * Disk life cycle events:
+ * @dot
+ * digraph disk_events {
+ * "DISK ATTACH" -> "PARTITION INQUIRY";
+ * "DISK ATTACH" -> "MOUNT";
+ * "PARTITION INQUIRY" -> "PARTITION ATTACH";
+ * "PARTITION INQUIRY" -> "DISK DETACH";
+ * "PARTITION ATTACH" -> "MOUNT";
+ * "MOUNT" -> "UNMOUNT";
+ * "UNMOUNT" -> "PARTITION DETACH";
+ * "UNMOUNT" -> "DISK DETACH";
+ * "PARTITION DETACH" -> "DISK DETACH";
+ * }
+ * @enddot
+ */
+typedef enum {
+ RTEMS_MEDIA_EVENT_DISK_ATTACH,
+ RTEMS_MEDIA_EVENT_DISK_DETACH,
+ RTEMS_MEDIA_EVENT_MOUNT,
+ RTEMS_MEDIA_EVENT_UNMOUNT,
+ RTEMS_MEDIA_EVENT_PARTITION_INQUIRY,
+ RTEMS_MEDIA_EVENT_PARTITION_ATTACH,
+ RTEMS_MEDIA_EVENT_PARTITION_DETACH,
+ RTEMS_MEDIA_EVENT_ERROR
+} rtems_media_event;
+
+/**
+ * Normal state transition:
+ * @dot
+ * digraph state {
+ * INQUIRY -> READY [label="all listeners\nreturned successful"];
+ * INQUIRY -> ABORTED [label="otherwise"];
+ * READY -> SUCCESS [label="the worker\nreturned successful"];
+ * READY -> FAILED [label="otherwise"];
+ * }
+ * @enddot
+ */
+typedef enum {
+ RTEMS_MEDIA_STATE_INQUIRY,
+ RTEMS_MEDIA_STATE_READY,
+ RTEMS_MEDIA_STATE_ABORTED,
+ RTEMS_MEDIA_STATE_SUCCESS,
+ RTEMS_MEDIA_STATE_FAILED,
+ RTEMS_MEDIA_ERROR_DISK_UNKNOWN,
+ RTEMS_MEDIA_ERROR_DISK_EXISTS,
+ RTEMS_MEDIA_ERROR_DISK_OR_PARTITION_UNKNOWN,
+ RTEMS_MEDIA_ERROR_DISK_OR_PARTITION_EXISTS,
+ RTEMS_MEDIA_ERROR_PARTITION_UNKNOWN,
+ RTEMS_MEDIA_ERROR_PARTITION_ORPHAN,
+ RTEMS_MEDIA_ERROR_PARTITION_DETACH_WITH_MOUNT,
+ RTEMS_MEDIA_ERROR_PARTITION_WITH_UNKNOWN_DISK,
+ RTEMS_MEDIA_ERROR_MOUNT_POINT_UNKNOWN,
+ RTEMS_MEDIA_ERROR_MOUNT_POINT_EXISTS,
+ RTEMS_MEDIA_ERROR_MOUNT_POINT_ORPHAN
+} rtems_media_state;
+
+/**
+ * @brief Event listener.
+ *
+ * The listener will be called with the @a listener_arg passed to
+ * rtems_media_listener_add().
+ *
+ * Source and destination values for each event and state:
+ * <table>
+ * <tr><th>Event</th><th>State</th><th>Source</th><th>Destination</th></tr>
+ * <tr>
+ * <td rowspan="5">RTEMS_MEDIA_EVENT_DISK_ATTACH</td>
+ * <td>RTEMS_MEDIA_STATE_INQUIRY</td><td>driver name</td><td>NULL</td>
+ * </tr>
+ * <tr>
+ * <td>RTEMS_MEDIA_STATE_READY</td><td>driver name</td><td>NULL</td>
+ * </tr>
+ * <tr>
+ * <td>RTEMS_MEDIA_STATE_ABORTED</td><td>driver name</td><td>NULL</td>
+ * </tr>
+ * <tr>
+ * <td>RTEMS_MEDIA_STATE_SUCCESS</td><td>driver name</td><td>disk path</td>
+ * </tr>
+ * <tr>
+ * <td>RTEMS_MEDIA_STATE_FAILED</td><td>driver name</td><td>NULL</td>
+ * </tr>
+ * <tr>
+ * <td rowspan="5">RTEMS_MEDIA_EVENT_DISK_DETACH</td>
+ * <td>RTEMS_MEDIA_STATE_INQUIRY</td><td>disk path</td><td>NULL</td>
+ * </tr>
+ * <tr>
+ * <td>RTEMS_MEDIA_STATE_READY</td><td>disk path</td><td>NULL</td>
+ * </tr>
+ * <tr>
+ * <td>RTEMS_MEDIA_STATE_ABORTED</td><td>disk path</td><td>NULL</td>
+ * </tr>
+ * <tr>
+ * <td>RTEMS_MEDIA_STATE_SUCCESS</td><td>disk path</td><td>NULL</td>
+ * </tr>
+ * <tr>
+ * <td>RTEMS_MEDIA_STATE_FAILED</td><td>disk path</td><td>NULL</td>
+ * </tr>
+ * <tr>
+ * <td rowspan="5">RTEMS_MEDIA_EVENT_PARTITION_INQUIRY</td>
+ * <td>RTEMS_MEDIA_STATE_INQUIRY</td><td>disk path</td><td>NULL</td>
+ * </tr>
+ * <tr>
+ * <td>RTEMS_MEDIA_STATE_READY</td><td>disk path</td><td>NULL</td>
+ * </tr>
+ * <tr>
+ * <td>RTEMS_MEDIA_STATE_ABORTED</td><td>disk path</td><td>NULL</td>
+ * </tr>
+ * <tr>
+ * <td>RTEMS_MEDIA_STATE_SUCCESS</td><td>disk path</td><td>NULL</td>
+ * </tr>
+ * <tr>
+ * <td>RTEMS_MEDIA_STATE_FAILED</td><td>disk path</td><td>NULL</td>
+ * </tr>
+ * <tr>
+ * <td rowspan="5">RTEMS_MEDIA_EVENT_PARTITION_ATTACH</td>
+ * <td>RTEMS_MEDIA_STATE_INQUIRY</td><td>disk path</td><td>NULL</td>
+ * </tr>
+ * <tr>
+ * <td>RTEMS_MEDIA_STATE_READY</td><td>disk path</td><td>NULL</td>
+ * </tr>
+ * <tr>
+ * <td>RTEMS_MEDIA_STATE_ABORTED</td><td>disk path</td><td>NULL</td>
+ * </tr>
+ * <tr>
+ * <td>RTEMS_MEDIA_STATE_SUCCESS</td>
+ * <td>disk path</td><td>partition path</td>
+ * </tr>
+ * <tr>
+ * <td>RTEMS_MEDIA_STATE_FAILED</td><td>disk path</td><td>NULL</td>
+ * </tr>
+ * <tr>
+ * <td rowspan="5">RTEMS_MEDIA_EVENT_PARTITION_DETACH</td>
+ * <td>RTEMS_MEDIA_STATE_INQUIRY</td><td>partition path</td><td>NULL</td>
+ * </tr>
+ * <tr>
+ * <td>RTEMS_MEDIA_STATE_READY</td><td>partition path</td><td>NULL</td>
+ * </tr>
+ * <tr>
+ * <td>RTEMS_MEDIA_STATE_ABORTED</td><td>partition path</td><td>NULL</td>
+ * </tr>
+ * <tr>
+ * <td>RTEMS_MEDIA_STATE_SUCCESS</td><td>partition path</td><td>NULL</td>
+ * </tr>
+ * <tr>
+ * <td>RTEMS_MEDIA_STATE_FAILED</td><td>partition path</td><td>NULL</td>
+ * </tr>
+ * <tr>
+ * <td rowspan="5">RTEMS_MEDIA_EVENT_MOUNT</td>
+ * <td>RTEMS_MEDIA_STATE_INQUIRY</td>
+ * <td>disk or partition path</td><td>NULL</td>
+ * </tr>
+ * <tr>
+ * <td>RTEMS_MEDIA_STATE_READY</td>
+ * <td>disk or partition path</td><td>NULL</td>
+ * </tr>
+ * <tr>
+ * <td>RTEMS_MEDIA_STATE_ABORTED</td>
+ * <td>disk or partition path</td><td>NULL</td>
+ * </tr>
+ * <tr>
+ * <td>RTEMS_MEDIA_STATE_SUCCESS</td>
+ * <td>disk or partition path</td><td>mount path</td>
+ * </tr>
+ * <tr>
+ * <td>RTEMS_MEDIA_STATE_FAILED</td>
+ * <td>disk or partition path</td><td>NULL</td>
+ * </tr>
+ * <tr>
+ * <td rowspan="5">RTEMS_MEDIA_EVENT_UNMOUNT</td>
+ * <td>RTEMS_MEDIA_STATE_INQUIRY</td><td>mount path</td><td>NULL</td>
+ * </tr>
+ * <tr>
+ * <td>RTEMS_MEDIA_STATE_READY</td><td>mount path</td><td>NULL</td>
+ * </tr>
+ * <tr>
+ * <td>RTEMS_MEDIA_STATE_ABORTED</td><td>mount path</td><td>NULL</td>
+ * </tr>
+ * <tr>
+ * <td>RTEMS_MEDIA_STATE_SUCCESS</td><td>mount path</td><td>NULL</td>
+ * </tr>
+ * <tr>
+ * <td>RTEMS_MEDIA_STATE_FAILED</td><td>mount path</td><td>NULL</td>
+ * </tr>
+ * <tr>
+ * <td rowspan="11">RTEMS_MEDIA_EVENT_ERROR</td>
+ * <td>RTEMS_MEDIA_ERROR_DISK_UNKNOWN</td><td>disk path</td><td>NULL</td>
+ * </tr>
+ * <tr>
+ * <td>RTEMS_MEDIA_ERROR_DISK_EXISTS</td><td>disk path</td><td>NULL</td>
+ * </tr>
+ * <tr>
+ * <td>RTEMS_MEDIA_ERROR_DISK_OR_PARTITION_UNKNOWN</td>
+ * <td>disk or partition path</td><td>NULL</td>
+ * </tr>
+ * <tr>
+ * <td>RTEMS_MEDIA_ERROR_DISK_OR_PARTITION_EXISTS</td>
+ * <td>disk or partition path</td><td>NULL</td>
+ * </tr>
+ * <tr>
+ * <td>RTEMS_MEDIA_ERROR_PARTITION_UNKNOWN</td>
+ * <td>partition path</td><td>NULL</td>
+ * </tr>
+ * <tr>
+ * <td>RTEMS_MEDIA_ERROR_PARTITION_ORPHAN</td>
+ * <td>partition path</td><td>disk path</td>
+ * </tr>
+ * <tr>
+ * <td>RTEMS_MEDIA_ERROR_PARTITION_DETACH_WITH_MOUNT</td>
+ * <td>partition path</td><td>mount path</td>
+ * </tr>
+ * <tr>
+ * <td>RTEMS_MEDIA_ERROR_PARTITION_WITH_UNKNOWN_DISK</td>
+ * <td>partition path</td><td>disk path</td>
+ * </tr>
+ * <tr>
+ * <td>RTEMS_MEDIA_ERROR_MOUNT_POINT_UNKNOWN</td>
+ * <td>mount path</td><td>NULL</td>
+ * </tr>
+ * <tr>
+ * <td>RTEMS_MEDIA_ERROR_MOUNT_POINT_EXISTS</td>
+ * <td>mount path</td><td>NULL</td>
+ * </tr>
+ * <tr>
+ * <td>RTEMS_MEDIA_ERROR_MOUNT_POINT_ORPHAN</td>
+ * <td>mount path</td><td>disk path</td>
+ * </tr>
+ * </table>
+ *
+ * @retval RTEMS_SUCCESSFUL Successful operation.
+ * @retval RTEMS_IO_ERROR In the inquiry state this will abort the action.
+ */
+typedef rtems_status_code (*rtems_media_listener)(
+ rtems_media_event event,
+ rtems_media_state state,
+ const char *src,
+ const char *dest,
+ void *listener_arg
+);
+
+/**
+ * @brief Do the work corresponding to an event.
+ *
+ * The @a state will be
+ * - RTEMS_MEDIA_STATE_READY, or
+ * - RTEMS_MEDIA_STATE_ABORTED.
+ *
+ * It will be called with the @a src and @a worker_arg arguments passed to
+ * rtems_media_post_event().
+ *
+ * The destination shall be returned in @a dest in case of success. It shall
+ * be allocated with malloc().
+ *
+ * @retval RTEMS_SUCCESSFUL Successful operation.
+ * @retval RTEMS_IO_ERROR Failure.
+ */
+typedef rtems_status_code (*rtems_media_worker)(
+ rtems_media_state state,
+ const char *src,
+ char **dest,
+ void *worker_arg
+);
+
+/**
+ * @name Base
+ *
+ * @{
+ */
+
+/**
+ * @brief Initializes the media manager.
+ *
+ * Calling this function more than once will have no effects. There is no
+ * protection against concurrent access.
+ *
+ * @retval RTEMS_SUCCESSFUL Successful operation.
+ * @retval RTEMS_NO_MEMORY Not enough resources.
+ */
+rtems_status_code rtems_media_initialize(void);
+
+/**
+ * @brief Adds the @a listener with argument @a listener_arg.
+ *
+ * @retval RTEMS_SUCCESSFUL Successful operation.
+ * @retval RTEMS_NO_MEMORY Not enough memory.
+ * @retval RTEMS_TOO_MANY Such a listener is already present.
+ */
+rtems_status_code rtems_media_listener_add(
+ rtems_media_listener listener,
+ void *listener_arg
+);
+
+/**
+ * @brief Removes the @a listener with argument @a listener_arg.
+ *
+ * @retval RTEMS_SUCCESSFUL Successful operation.
+ * @retval RTEMS_INVALID_ID No such listener is present.
+ */
+rtems_status_code rtems_media_listener_remove(
+ rtems_media_listener listener,
+ void *listener_arg
+);
+
+/**
+ * @brief Posts the @a event with source @a src.
+ *
+ * The @a worker will be called with the @a worker_arg argument.
+ *
+ * The destination will be returned in @a dest in case of success. It will be
+ * allocated with malloc() and should be freed if not needed anymore.
+ *
+ * The work will be done by the calling thread. You can avoid this if you use
+ * the media server via rtems_media_server_post_event().
+ *
+ * @retval RTEMS_SUCCESSFUL Successful operation.
+ * @retval RTEMS_UNSATISFIED One or more listeners aborted the action.
+ * @retval RTEMS_IO_ERROR The worker returned with an error status.
+ */
+rtems_status_code rtems_media_post_event(
+ rtems_media_event event,
+ const char *src,
+ char **dest,
+ rtems_media_worker worker,
+ void *worker_arg
+);
+
+/** @} */
+
+/**
+ * @name Server
+ *
+ * @{
+ */
+
+/**
+ * @brief Initializes the media manager and media server.
+ *
+ * It creates a server task with the @a priority, @a stack_size, @a modes, and
+ * @a attributes parameters.
+ *
+ * Calling this function more than once will have no effects. There is no
+ * protection against concurrent access.
+ *
+ * @retval RTEMS_SUCCESSFUL Successful operation.
+ * @retval RTEMS_NO_MEMORY Not enough resources.
+ */
+rtems_status_code rtems_media_server_initialize(
+ rtems_task_priority priority,
+ size_t stack_size,
+ rtems_mode modes,
+ rtems_attribute attributes
+);
+
+/**
+ * @brief Sends an event message to the media server.
+ *
+ * @see See rtems_media_post_event().
+ *
+ * @retval RTEMS_SUCCESSFUL Successful operation.
+ * @retval RTEMS_NO_MEMORY Not enough resources to notify the media server.
+ * @retval RTEMS_NOT_CONFIGURED Media server is not initialized.
+ */
+rtems_status_code rtems_media_server_post_event(
+ rtems_media_event event,
+ const char *src,
+ rtems_media_worker worker,
+ void *worker_arg
+);
+
+/**
+ * @brief See rtems_media_server_post_event().
+ */
+static inline rtems_status_code rtems_media_server_disk_attach(
+ const char *driver_name,
+ rtems_media_worker worker,
+ void *worker_arg
+)
+{
+ return rtems_media_server_post_event(
+ RTEMS_MEDIA_EVENT_DISK_ATTACH,
+ driver_name,
+ worker,
+ worker_arg
+ );
+}
+
+/**
+ * @brief See rtems_media_server_post_event().
+ */
+static inline rtems_status_code rtems_media_server_disk_detach(
+ const char *disk_path
+)
+{
+ return rtems_media_server_post_event(
+ RTEMS_MEDIA_EVENT_DISK_DETACH,
+ disk_path,
+ NULL,
+ NULL
+ );
+}
+
+/** @} */
+
+/**
+ * @name Path Construction
+ *
+ * @{
+ */
+
+/**
+ * @brief Creates a new path as "prefix/name-major".
+ *
+ * @return New string, or @c NULL if no memory is available.
+ */
+char *rtems_media_create_path(
+ const char *prefix,
+ const char *name,
+ rtems_device_major_number major
+);
+
+/**
+ * @brief Replaces the prefix of the @a path with @a new_prefix.
+ *
+ * The prefix is everything up to the last '/'.
+ *
+ * @return New string, or @c NULL if no memory is available.
+ */
+char *rtems_media_replace_prefix(const char *new_prefix, const char *path);
+
+/**
+ * @brief Appends the @a minor number to the @a path resulting in "path-minor".
+ *
+ * @return New string, or @c NULL if no memory is available.
+ */
+char *rtems_media_append_minor(
+ const char *path,
+ rtems_device_minor_number minor
+);
+
+/** @} */
+
+/**
+ * @name Support
+ *
+ * @{
+ */
+
+/**
+ * @brief Returns the device identifier for the device located at
+ * @a device_path in @a device_identifier.
+ *
+ * @retval RTEMS_SUCCESSFUL Successful operation.
+ * @retval RTEMS_INVALID_ID No device at this path.
+ */
+rtems_status_code rtems_media_get_device_identifier(
+ const char *device_path,
+ dev_t *device_identifier
+);
+
+const char *rtems_media_event_description(rtems_media_event event);
+
+const char *rtems_media_state_description(rtems_media_state state);
+
+/** @} */
+
+/** @} */
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* RTEMS_MEDIA_H */
diff --git a/cpukit/libblock/include/rtems/nvdisk-sram.h b/cpukit/libblock/include/rtems/nvdisk-sram.h
new file mode 100644
index 0000000000..450cfd8602
--- /dev/null
+++ b/cpukit/libblock/include/rtems/nvdisk-sram.h
@@ -0,0 +1,25 @@
+/*
+ * $Id$
+ *
+ * RTEMS Project (http://www.rtems.org/)
+ *
+ * Copyright 2007 Chris Johns (chrisj@rtems.org)
+ */
+
+/**
+ * NV Disk Static RAM Device Driver.
+ *
+ * This driver maps an NV disk to static RAM. You can use this
+ */
+
+#if !defined (_RTEMS_NVDISK_SRAM_H_)
+#define _RTEMS_NVDISK_SRAM_H_
+
+#include <rtems/nvdisk.h>
+
+/**
+ * The handlers for the NV Disk SRAM driver.
+ */
+extern const rtems_nvdisk_driver_handlers rtems_nvdisk_sram_handlers;
+
+#endif
diff --git a/cpukit/libblock/include/rtems/nvdisk.h b/cpukit/libblock/include/rtems/nvdisk.h
new file mode 100644
index 0000000000..48d8ba0b16
--- /dev/null
+++ b/cpukit/libblock/include/rtems/nvdisk.h
@@ -0,0 +1,209 @@
+/*
+ * nvdisk.h -- Non-volatile disk block device implementation
+ *
+ * Copyright (C) 2007 Chris Johns
+ *
+ * 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.
+ *
+ * $Id$
+ */
+
+/**
+ * The Non-volatile disk provides a simple directly mapped disk
+ * driver with checksums for each. It is designed to provied a
+ * disk that can survive a restart. Examples are EEPROM devices
+ * which have byte writeable locations, or a battery backed up
+ * RAM disk.
+ *
+ * The low level driver provides the physical access to the
+ * hardware.
+ */
+#if !defined (_RTEMS_NVDISK_H_)
+#define _RTEMS_NVDISK_H_
+
+#include <stdint.h>
+#include <sys/ioctl.h>
+
+#include <rtems.h>
+
+/**
+ * The base name of the nv disks.
+ */
+#define RTEMS_NVDISK_DEVICE_BASE_NAME "/dev/nvd"
+
+/**
+ * NV disk specific ioctl request types. To use open the
+ * device and issue the ioctl call.
+ *
+ * @code
+ * int fd = open ("/dev/nvdisk0", O_WRONLY, 0);
+ * if (fd < 0)
+ * {
+ * printf ("driver open failed: %s\n", strerror (errno));
+ * exit (1);
+ * }
+ * if (ioctl (fd, RTEMS_NVDISK_IOCTL_ERASE_DISK) < 0)
+ * {
+ * printf ("driver erase failed: %s\n", strerror (errno));
+ * exit (1);
+ * }
+ * close (fd);
+ * @endcode
+ */
+#define RTEMS_NVDISK_IOCTL_ERASE_DISK _IO('B', 128)
+#define RTEMS_NVDISK_IOCTL_MONITORING _IO('B', 129)
+#define RTEMS_NVDISK_IOCTL_INFO_LEVEL _IO('B', 130)
+#define RTEMS_NVDISK_IOCTL_PRINT_STATUS _IO('B', 131)
+
+/**
+ * NV Disk Monitoring Data allows a user to obtain
+ * the current status of the disk.
+ */
+typedef struct rtems_nvdisk_monitor_data
+{
+ uint32_t block_size;
+ uint32_t block_count;
+ uint32_t page_count;
+ uint32_t pages_available;
+ uint32_t pages_used;
+ uint32_t info_level;
+} rtems_nvdisk_monitor_data;
+
+/**
+ * Return the number of kilo-bytes.
+ */
+#define RTEMS_NVDISK_KBYTES(_k) ((_k) * 1024)
+
+/**
+ * NV Low Level driver handlers.
+
+ * Typically this structure is part of a table of handlers in the
+ * device which is referenced in the nvdisk configuration table.
+ * The reference is kept in the driver and used all the time to
+ * manage the nv device, therefore it must always exist.
+ */
+typedef struct rtems_nvdisk_driver_handlers
+{
+ /**
+ * Read data from the device into the buffer. Return an errno
+ * error number if the data cannot be read.
+ *
+ * @param device The device to read data from.
+ * @param flags Device specific flags for the driver.
+ * @param base The base address of the device.
+ * @param offset The offset in the segment to read.
+ * @param buffer The buffer to read the data into.
+ * @param size The amount of data to read.
+ * @retval 0 No error.
+ * @retval EIO The read did not complete.
+ */
+ int (*read) (uint32_t device, uint32_t flags, void* base,
+ uint32_t offset, void* buffer, size_t size);
+
+ /**
+ * Write data from the buffer to the device. Return an errno
+ * error number if the device cannot be written to.
+ *
+ * @param device The device to write data to.
+ * @param flags Device specific flags for the driver.
+ * @param base The base address of the device.
+ * @param offset The offset in the device to write to.
+ * @param buffer The buffer to write the data from.
+ * @param size The amount of data to write.
+ * @retval 0 No error.
+ * @retval EIO The write did not complete or verify.
+ */
+ int (*write) (uint32_t device, uint32_t flags, void* base,
+ uint32_t offset, const void* buffer, size_t size);
+
+ /**
+ * Verify data in the buffer to the data in the device. Return an
+ * errno error number if the device cannot be read or the data verified.
+ *
+ * @param device The device to verify the data with.
+ * @param flags Device specific flags for the driver.
+ * @param base The base address of the device.
+ * @param offset The offset in the device to verify.
+ * @param buffer The buffer to verify the data in the device with.
+ * @param size The amount of data to verify.
+ * @retval 0 No error.
+ * @retval EIO The data did not verify.
+ */
+ int (*verify) (uint32_t device, uint32_t flags, void* base,
+ uint32_t offset, const void* buffer, size_t size);
+
+} rtems_nvdisk_driver_handlers;
+
+/**
+ * NV Device Descriptor holds the description of a device that is
+ * part of the NV disk.
+ *
+ * Typically this structure is part of a table of the device which
+ * is referenced in the nvdisk configuration table.
+ * The reference is kept in the driver and used all the time to
+ * manage the nv device, therefore it must always exist.
+ */
+typedef struct rtems_nvdisk_device_desc
+{
+ uint32_t flags; /**< Private user flags. */
+ void* base; /**< Base address of the device. */
+ uint32_t size; /**< Size of the device. */
+ const rtems_nvdisk_driver_handlers* nv_ops; /**< Device handlers. */
+} rtems_nvdisk_device_desc;
+
+/**
+ * RTEMS Non-Volatile Disk configuration table used to initialise the
+ * driver.
+ */
+typedef struct rtems_nvdisk_config
+{
+ uint32_t block_size; /**< The block size. */
+ uint32_t device_count; /**< The number of devices. */
+ const rtems_nvdisk_device_desc* devices; /**< The device descriptions. */
+ uint32_t flags; /**< Set of flags to control
+ driver. */
+ uint32_t info_level; /**< Default info level. */
+} rtems_nvdisk_config;
+
+/*
+ * Driver flags.
+ */
+
+/**
+ * Check the pages during initialisation to see which pages are
+ * valid and which are not. This could slow down initialising the
+ * disk driver.
+ */
+#define RTEMS_NVDISK_CHECK_PAGES (1 << 0)
+
+/**
+ * Non-volatile disk device driver initialization. Place in a table as the
+ * initialisation entry and remainder of the entries are the RTEMS block
+ * device generic handlers.
+ *
+ * @param major NV disk major device number.
+ * @param minor Minor device number, not applicable.
+ * @param arg Initialization argument, not applicable.
+ * @return The rtems_device_driver is actually just
+ * rtems_status_code.
+ */
+rtems_device_driver
+rtems_nvdisk_initialize (rtems_device_major_number major,
+ rtems_device_minor_number minor,
+ void* arg);
+
+/**
+ * External reference to the configuration. Please supply.
+ * Support is present in confdefs.h for providing this variable.
+ */
+extern const rtems_nvdisk_config rtems_nvdisk_configuration[];
+
+/**
+ * External reference to the number of configurations. Please supply.
+ * Support is present in confdefs.h for providing this variable.
+ */
+extern uint32_t rtems_nvdisk_configuration_size;
+
+#endif
diff --git a/cpukit/libblock/include/rtems/ramdisk.h b/cpukit/libblock/include/rtems/ramdisk.h
new file mode 100644
index 0000000000..71efc9fda0
--- /dev/null
+++ b/cpukit/libblock/include/rtems/ramdisk.h
@@ -0,0 +1,243 @@
+/**
+ * @file
+ *
+ * @ingroup rtems_ramdisk
+ *
+ * @brief RAM disk block device API.
+ */
+
+/*
+ * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia
+ * Author: Victor V. Vengerov <vvv@oktet.ru>
+ *
+ * @(#) $Id$
+ */
+
+#ifndef _RTEMS_RAMDISK_H
+#define _RTEMS_RAMDISK_H
+
+
+#include <rtems.h>
+#include <rtems/blkdev.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @defgroup rtems_ramdisk RAM Disk Device
+ *
+ * @ingroup rtems_blkdev
+ *
+ * @{
+ */
+
+/**
+ * @name Static Configuration
+ *
+ * @{
+ */
+
+/**
+ * @brief RAM disk configuration table entry.
+ */
+typedef struct rtems_ramdisk_config {
+ /**
+ * @brief RAM disk block size.
+ */
+ uint32_t block_size;
+
+ /**
+ * @brief Number of blocks on this RAM disk.
+ */
+ rtems_blkdev_bnum block_num;
+
+ /**
+ * @brief RAM disk location or @c NULL if RAM disk memory should be allocated
+ * dynamically.
+ */
+ void *location;
+} rtems_ramdisk_config;
+
+/**
+ * @brief External reference to the RAM disk configuration table describing
+ * each RAM disk in the system.
+ *
+ * The configuration table is provided by the application.
+ */
+extern rtems_ramdisk_config rtems_ramdisk_configuration [];
+
+/**
+ * @brief External reference the size of the RAM disk configuration table @ref
+ * rtems_ramdisk_configuration.
+ *
+ * The configuration table size is provided by the application.
+ */
+extern size_t rtems_ramdisk_configuration_size;
+
+/**
+ * @brief RAM disk driver initialization entry point.
+ */
+rtems_device_driver ramdisk_initialize(
+ rtems_device_major_number major,
+ rtems_device_minor_number minor,
+ void *arg
+);
+
+/**
+ * RAM disk driver table entry.
+ */
+#define RAMDISK_DRIVER_TABLE_ENTRY \
+ { \
+ .initialization_entry = ramdisk_initialize, \
+ RTEMS_GENERIC_BLOCK_DEVICE_DRIVER_ENTRIES \
+ }
+
+#define RAMDISK_DEVICE_BASE_NAME "/dev/rd"
+
+/** @} */
+
+/**
+ * @name Runtime Configuration
+ *
+ * @{
+ */
+
+/**
+ * @brief RAM disk descriptor.
+ */
+typedef struct ramdisk {
+ /**
+ * @brief RAM disk block size, the media size.
+ */
+ uint32_t block_size;
+
+ /**
+ * @brief Number of blocks on this RAM disk.
+ */
+ rtems_blkdev_bnum block_num;
+
+ /**
+ * @brief RAM disk memory area.
+ */
+ void *area;
+
+ /**
+ * @brief RAM disk is initialized.
+ */
+ bool initialized;
+
+ /**
+ * @brief Indicates if memory is allocated by malloc() for this RAM disk.
+ */
+ bool malloced;
+
+ /**
+ * @brief Trace enable.
+ */
+ bool trace;
+} ramdisk;
+
+extern const rtems_driver_address_table ramdisk_ops;
+
+int ramdisk_ioctl(rtems_disk_device *dd, uint32_t req, void *argp);
+
+/**
+ * @brief Allocates and initializes a RAM disk descriptor.
+ *
+ * The block size will be @a block_size. The block count will be @a
+ * block_count. The disk storage area begins at @a area_begin. If @a
+ * area_begin is @c NULL, the memory will be allocated and zeroed. Sets the
+ * trace enable to @a trace.
+ *
+ * @return Pointer to allocated and initialized ramdisk structure, or @c NULL
+ * if no memory is available.
+ *
+ * @note
+ * Runtime configuration example:
+ * @code
+ * #include <rtems.h>
+ * #include <rtems/libio.h>
+ * #include <rtems/ramdisk.h>
+ *
+ * rtems_status_code create_ramdisk(
+ * const char *disk_name_path,
+ * uint32_t block_size,
+ * rtems_blkdev_bnum block_count
+ * )
+ * {
+ * rtems_status_code sc = RTEMS_SUCCESSFUL;
+ * rtems_device_major_number major = 0;
+ * ramdisk *rd = NULL;
+ * dev_t dev = 0;
+ *
+ * sc = rtems_io_register_driver(0, &ramdisk_ops, &major);
+ * if (sc != RTEMS_SUCCESSFUL) {
+ * return RTEMS_UNSATISFIED;
+ * }
+ *
+ * rd = ramdisk_allocate(NULL, block_size, block_count, false);
+ * if (rd == NULL) {
+ * rtems_io_unregister_driver(major);
+ *
+ * return RTEMS_UNSATISFIED;
+ * }
+ *
+ * dev = rtems_filesystem_make_dev_t(major, 0);
+ *
+ * sc = rtems_disk_create_phys(
+ * dev,
+ * block_size,
+ * block_count,
+ * ramdisk_ioctl,
+ * rd,
+ * disk_name_path
+ * );
+ * if (sc != RTEMS_SUCCESSFUL) {
+ * ramdisk_free(rd);
+ * rtems_io_unregister_driver(major);
+ *
+ * return RTEMS_UNSATISFIED;
+ * }
+ *
+ * return RTEMS_SUCCESSFUL;
+ * }
+ * @endcode
+ */
+ramdisk *ramdisk_allocate(
+ void *area_begin,
+ uint32_t block_size,
+ rtems_blkdev_bnum block_count,
+ bool trace
+);
+
+void ramdisk_free(ramdisk *rd);
+
+/**
+ * @brief Allocates, initializes and registers a RAM disk.
+ *
+ * The block size will be @a block_size. The block count will be @a
+ * block_count. The disk storage will be allocated. Sets the trace enable to
+ * @a trace. Registers a device node with disk name path @a disk. The
+ * registered device number will be returned in @a dev.
+ *
+ * @retval RTEMS_SUCCESSFUL Successful operation.
+ * @retval RTEMS_UNSATISFIED Something is wrong.
+ */
+rtems_status_code ramdisk_register(
+ uint32_t block_size,
+ rtems_blkdev_bnum block_count,
+ bool trace,
+ const char *disk,
+ dev_t *dev
+);
+
+/** @} */
+
+/** @} */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/cpukit/libblock/src/bdbuf.c b/cpukit/libblock/src/bdbuf.c
new file mode 100644
index 0000000000..841c03bdbe
--- /dev/null
+++ b/cpukit/libblock/src/bdbuf.c
@@ -0,0 +1,2989 @@
+/**
+ * @file
+ *
+ * @ingroup rtems_bdbuf
+ *
+ * Block device buffer management.
+ */
+
+/*
+ * Disk I/O buffering
+ * Buffer managment
+ *
+ * Copyright (C) 2001 OKTET Ltd., St.-Peterburg, Russia
+ * Author: Andrey G. Ivanov <Andrey.Ivanov@oktet.ru>
+ * Victor V. Vengerov <vvv@oktet.ru>
+ * Alexander Kukuta <kam@oktet.ru>
+ *
+ * Copyright (C) 2008,2009 Chris Johns <chrisj@rtems.org>
+ * Rewritten to remove score mutex access. Fixes many performance
+ * issues.
+ *
+ * Copyright (c) 2009 embedded brains GmbH.
+ *
+ * @(#) bdbuf.c,v 1.14 2004/04/17 08:15:17 ralf Exp
+ */
+
+/**
+ * Set to 1 to enable debug tracing.
+ */
+#define RTEMS_BDBUF_TRACE 0
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include <limits.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+
+#include <rtems.h>
+#include <rtems/error.h>
+#include <rtems/malloc.h>
+
+#include "rtems/bdbuf.h"
+
+#define BDBUF_INVALID_DEV ((dev_t) -1)
+
+/*
+ * Simpler label for this file.
+ */
+#define bdbuf_config rtems_bdbuf_configuration
+
+/**
+ * A swapout transfer transaction data. This data is passed to a worked thread
+ * to handle the write phase of the transfer.
+ */
+typedef struct rtems_bdbuf_swapout_transfer
+{
+ rtems_chain_control bds; /**< The transfer list of BDs. */
+ dev_t dev; /**< The device the transfer is for. */
+ bool syncing; /**< The data is a sync'ing. */
+ rtems_blkdev_request* write_req; /**< The write request array. */
+ uint32_t bufs_per_bd; /**< Number of buffers per bd. */
+} rtems_bdbuf_swapout_transfer;
+
+/**
+ * Swapout worker thread. These are available to take processing from the
+ * main swapout thread and handle the I/O operation.
+ */
+typedef struct rtems_bdbuf_swapout_worker
+{
+ rtems_chain_node link; /**< The threads sit on a chain when
+ * idle. */
+ rtems_id id; /**< The id of the task so we can wake
+ * it. */
+ volatile bool enabled; /**< The worker is enabled. */
+ rtems_bdbuf_swapout_transfer transfer; /**< The transfer data for this
+ * thread. */
+} rtems_bdbuf_swapout_worker;
+
+/**
+ * Buffer waiters synchronization.
+ */
+typedef struct rtems_bdbuf_waiters {
+ volatile unsigned count;
+ rtems_id sema;
+} rtems_bdbuf_waiters;
+
+/**
+ * The BD buffer cache.
+ */
+typedef struct rtems_bdbuf_cache
+{
+ rtems_id swapout; /**< Swapout task ID */
+ volatile bool swapout_enabled; /**< Swapout is only running if
+ * enabled. Set to false to kill the
+ * swap out task. It deletes itself. */
+ rtems_chain_control swapout_workers; /**< The work threads for the swapout
+ * task. */
+
+ rtems_bdbuf_buffer* bds; /**< Pointer to table of buffer
+ * descriptors. */
+ void* buffers; /**< The buffer's memory. */
+ size_t buffer_min_count; /**< Number of minimum size buffers
+ * that fit the buffer memory. */
+ size_t max_bds_per_group; /**< The number of BDs of minimum
+ * buffer size that fit in a group. */
+ uint32_t flags; /**< Configuration flags. */
+
+ rtems_id lock; /**< The cache lock. It locks all
+ * cache data, BD and lists. */
+ rtems_id sync_lock; /**< Sync calls block writes. */
+ volatile bool sync_active; /**< True if a sync is active. */
+ volatile rtems_id sync_requester; /**< The sync requester. */
+ volatile dev_t sync_device; /**< The device to sync and
+ * BDBUF_INVALID_DEV not a device
+ * sync. */
+
+ rtems_bdbuf_buffer* tree; /**< Buffer descriptor lookup AVL tree
+ * root. There is only one. */
+ rtems_chain_control lru; /**< Least recently used list */
+ rtems_chain_control modified; /**< Modified buffers list */
+ rtems_chain_control sync; /**< Buffers to sync list */
+
+ rtems_bdbuf_waiters access_waiters; /**< Wait for a buffer in
+ * ACCESS_CACHED, ACCESS_MODIFIED or
+ * ACCESS_EMPTY
+ * state. */
+ rtems_bdbuf_waiters transfer_waiters; /**< Wait for a buffer in TRANSFER
+ * state. */
+ rtems_bdbuf_waiters buffer_waiters; /**< Wait for a buffer and no one is
+ * available. */
+
+ size_t group_count; /**< The number of groups. */
+ rtems_bdbuf_group* groups; /**< The groups. */
+
+ bool initialised; /**< Initialised state. */
+} rtems_bdbuf_cache;
+
+/**
+ * Fatal errors
+ */
+#define RTEMS_BLKDEV_FATAL_ERROR(n) \
+ (((uint32_t)'B' << 24) | ((uint32_t)(n) & (uint32_t)0x00FFFFFF))
+
+#define RTEMS_BLKDEV_FATAL_BDBUF_STATE_11 RTEMS_BLKDEV_FATAL_ERROR(1)
+#define RTEMS_BLKDEV_FATAL_BDBUF_STATE_4 RTEMS_BLKDEV_FATAL_ERROR(2)
+#define RTEMS_BLKDEV_FATAL_BDBUF_STATE_5 RTEMS_BLKDEV_FATAL_ERROR(3)
+#define RTEMS_BLKDEV_FATAL_BDBUF_STATE_6 RTEMS_BLKDEV_FATAL_ERROR(4)
+#define RTEMS_BLKDEV_FATAL_BDBUF_STATE_7 RTEMS_BLKDEV_FATAL_ERROR(5)
+#define RTEMS_BLKDEV_FATAL_BDBUF_STATE_8 RTEMS_BLKDEV_FATAL_ERROR(6)
+#define RTEMS_BLKDEV_FATAL_BDBUF_STATE_9 RTEMS_BLKDEV_FATAL_ERROR(7)
+#define RTEMS_BLKDEV_FATAL_BDBUF_STATE_10 RTEMS_BLKDEV_FATAL_ERROR(8)
+#define RTEMS_BLKDEV_FATAL_BDBUF_TREE_RM RTEMS_BLKDEV_FATAL_ERROR(9)
+#define RTEMS_BLKDEV_FATAL_BDBUF_SWAPOUT RTEMS_BLKDEV_FATAL_ERROR(10)
+#define RTEMS_BLKDEV_FATAL_BDBUF_SYNC_LOCK RTEMS_BLKDEV_FATAL_ERROR(11)
+#define RTEMS_BLKDEV_FATAL_BDBUF_SYNC_UNLOCK RTEMS_BLKDEV_FATAL_ERROR(12)
+#define RTEMS_BLKDEV_FATAL_BDBUF_CACHE_LOCK RTEMS_BLKDEV_FATAL_ERROR(13)
+#define RTEMS_BLKDEV_FATAL_BDBUF_CACHE_UNLOCK RTEMS_BLKDEV_FATAL_ERROR(14)
+#define RTEMS_BLKDEV_FATAL_BDBUF_PREEMPT_DIS RTEMS_BLKDEV_FATAL_ERROR(15)
+#define RTEMS_BLKDEV_FATAL_BDBUF_CACHE_WAIT_2 RTEMS_BLKDEV_FATAL_ERROR(16)
+#define RTEMS_BLKDEV_FATAL_BDBUF_PREEMPT_RST RTEMS_BLKDEV_FATAL_ERROR(17)
+#define RTEMS_BLKDEV_FATAL_BDBUF_CACHE_WAIT_TO RTEMS_BLKDEV_FATAL_ERROR(18)
+#define RTEMS_BLKDEV_FATAL_BDBUF_CACHE_WAKE RTEMS_BLKDEV_FATAL_ERROR(19)
+#define RTEMS_BLKDEV_FATAL_BDBUF_SO_WAKE RTEMS_BLKDEV_FATAL_ERROR(20)
+#define RTEMS_BLKDEV_FATAL_BDBUF_SO_NOMEM RTEMS_BLKDEV_FATAL_ERROR(21)
+#define RTEMS_BLKDEV_FATAL_BDBUF_SO_WK_CREATE RTEMS_BLKDEV_FATAL_ERROR(22)
+#define RTEMS_BLKDEV_FATAL_BDBUF_SO_WK_START RTEMS_BLKDEV_FATAL_ERROR(23)
+#define BLKDEV_FATAL_BDBUF_SWAPOUT_RE RTEMS_BLKDEV_FATAL_ERROR(24)
+#define BLKDEV_FATAL_BDBUF_SWAPOUT_TS RTEMS_BLKDEV_FATAL_ERROR(25)
+#define RTEMS_BLKDEV_FATAL_BDBUF_WAIT_EVNT RTEMS_BLKDEV_FATAL_ERROR(26)
+#define RTEMS_BLKDEV_FATAL_BDBUF_RECYCLE RTEMS_BLKDEV_FATAL_ERROR(27)
+#define RTEMS_BLKDEV_FATAL_BDBUF_STATE_0 RTEMS_BLKDEV_FATAL_ERROR(28)
+#define RTEMS_BLKDEV_FATAL_BDBUF_STATE_1 RTEMS_BLKDEV_FATAL_ERROR(29)
+#define RTEMS_BLKDEV_FATAL_BDBUF_STATE_2 RTEMS_BLKDEV_FATAL_ERROR(30)
+#define RTEMS_BLKDEV_FATAL_BDBUF_DISK_REL RTEMS_BLKDEV_FATAL_ERROR(31)
+
+/**
+ * The events used in this code. These should be system events rather than
+ * application events.
+ */
+#define RTEMS_BDBUF_TRANSFER_SYNC RTEMS_EVENT_1
+#define RTEMS_BDBUF_SWAPOUT_SYNC RTEMS_EVENT_2
+
+/**
+ * The swap out task size. Should be more than enough for most drivers with
+ * tracing turned on.
+ */
+#define SWAPOUT_TASK_STACK_SIZE (8 * 1024)
+
+/**
+ * Lock semaphore attributes. This is used for locking type mutexes.
+ *
+ * @warning Priority inheritance is on.
+ */
+#define RTEMS_BDBUF_CACHE_LOCK_ATTRIBS \
+ (RTEMS_PRIORITY | RTEMS_BINARY_SEMAPHORE | \
+ RTEMS_INHERIT_PRIORITY | RTEMS_NO_PRIORITY_CEILING | RTEMS_LOCAL)
+
+/**
+ * Waiter semaphore attributes.
+ *
+ * @warning Do not configure as inherit priority. If a driver is in the driver
+ * initialisation table this locked semaphore will have the IDLE task
+ * as the holder and a blocking task will raise the priority of the
+ * IDLE task which can cause unsual side effects.
+ */
+#define RTEMS_BDBUF_CACHE_WAITER_ATTRIBS \
+ (RTEMS_PRIORITY | RTEMS_SIMPLE_BINARY_SEMAPHORE | \
+ RTEMS_NO_INHERIT_PRIORITY | RTEMS_NO_PRIORITY_CEILING | RTEMS_LOCAL)
+
+/**
+ * Waiter timeout. Set to non-zero to find some info on a waiter that is
+ * waiting too long.
+ */
+#define RTEMS_BDBUF_WAIT_TIMEOUT RTEMS_NO_TIMEOUT
+#if !defined (RTEMS_BDBUF_WAIT_TIMEOUT)
+#define RTEMS_BDBUF_WAIT_TIMEOUT \
+ (TOD_MICROSECONDS_TO_TICKS (20000000))
+#endif
+
+/*
+ * The swap out task.
+ */
+static rtems_task rtems_bdbuf_swapout_task(rtems_task_argument arg);
+
+/**
+ * The Buffer Descriptor cache.
+ */
+static rtems_bdbuf_cache bdbuf_cache;
+
+#if RTEMS_BDBUF_TRACE
+/**
+ * If true output the trace message.
+ */
+bool rtems_bdbuf_tracer;
+
+/**
+ * Return the number of items on the list.
+ *
+ * @param list The chain control.
+ * @return uint32_t The number of items on the list.
+ */
+uint32_t
+rtems_bdbuf_list_count (rtems_chain_control* list)
+{
+ rtems_chain_node* node = rtems_chain_first (list);
+ uint32_t count = 0;
+ while (!rtems_chain_is_tail (list, node))
+ {
+ count++;
+ node = rtems_chain_next (node);
+ }
+ return count;
+}
+
+/**
+ * Show the usage for the bdbuf cache.
+ */
+void
+rtems_bdbuf_show_usage (void)
+{
+ uint32_t group;
+ uint32_t total = 0;
+ uint32_t val;
+
+ for (group = 0; group < bdbuf_cache.group_count; group++)
+ total += bdbuf_cache.groups[group].users;
+ printf ("bdbuf:group users=%lu", total);
+ val = rtems_bdbuf_list_count (&bdbuf_cache.lru);
+ printf (", lru=%lu", val);
+ total = val;
+ val = rtems_bdbuf_list_count (&bdbuf_cache.modified);
+ printf (", mod=%lu", val);
+ total += val;
+ val = rtems_bdbuf_list_count (&bdbuf_cache.sync);
+ printf (", sync=%lu", val);
+ total += val;
+ printf (", total=%lu\n", total);
+}
+
+/**
+ * Show the users for a group of a bd.
+ *
+ * @param where A label to show the context of output.
+ * @param bd The bd to show the users of.
+ */
+void
+rtems_bdbuf_show_users (const char* where, rtems_bdbuf_buffer* bd)
+{
+ const char* states[] =
+ { "EM", "FR", "CH", "AC", "AM", "MD", "SY", "TR" };
+
+ printf ("bdbuf:users: %15s: [%" PRIu32 " (%s)] %td:%td = %" PRIu32 " %s\n",
+ where,
+ bd->block, states[bd->state],
+ bd->group - bdbuf_cache.groups,
+ bd - bdbuf_cache.bds,
+ bd->group->users,
+ bd->group->users > 8 ? "<<<<<<<" : "");
+}
+#else
+#define rtems_bdbuf_tracer (0)
+#define rtems_bdbuf_show_usage() ((void) 0)
+#define rtems_bdbuf_show_users(_w, _b) ((void) 0)
+#endif
+
+/**
+ * The default maximum height of 32 allows for AVL trees having between
+ * 5,704,880 and 4,294,967,295 nodes, depending on order of insertion. You may
+ * change this compile-time constant as you wish.
+ */
+#ifndef RTEMS_BDBUF_AVL_MAX_HEIGHT
+#define RTEMS_BDBUF_AVL_MAX_HEIGHT (32)
+#endif
+
+static void
+rtems_bdbuf_fatal (rtems_bdbuf_buf_state state, uint32_t error)
+{
+ rtems_fatal_error_occurred ((((uint32_t) state) << 16) | error);
+}
+
+/**
+ * Searches for the node with specified dev/block.
+ *
+ * @param root pointer to the root node of the AVL-Tree
+ * @param dev device search key
+ * @param block block search key
+ * @retval NULL node with the specified dev/block is not found
+ * @return pointer to the node with specified dev/block
+ */
+static rtems_bdbuf_buffer *
+rtems_bdbuf_avl_search (rtems_bdbuf_buffer** root,
+ dev_t dev,
+ rtems_blkdev_bnum block)
+{
+ rtems_bdbuf_buffer* p = *root;
+
+ while ((p != NULL) && ((p->dev != dev) || (p->block != block)))
+ {
+ if ((p->dev < dev) || ((p->dev == dev) && (p->block < block)))
+ {
+ p = p->avl.right;
+ }
+ else
+ {
+ p = p->avl.left;
+ }
+ }
+
+ return p;
+}
+
+/**
+ * Inserts the specified node to the AVl-Tree.
+ *
+ * @param root pointer to the root node of the AVL-Tree
+ * @param node Pointer to the node to add.
+ * @retval 0 The node added successfully
+ * @retval -1 An error occured
+ */
+static int
+rtems_bdbuf_avl_insert(rtems_bdbuf_buffer** root,
+ rtems_bdbuf_buffer* node)
+{
+ dev_t dev = node->dev;
+ rtems_blkdev_bnum block = node->block;
+
+ rtems_bdbuf_buffer* p = *root;
+ rtems_bdbuf_buffer* q;
+ rtems_bdbuf_buffer* p1;
+ rtems_bdbuf_buffer* p2;
+ rtems_bdbuf_buffer* buf_stack[RTEMS_BDBUF_AVL_MAX_HEIGHT];
+ rtems_bdbuf_buffer** buf_prev = buf_stack;
+
+ bool modified = false;
+
+ if (p == NULL)
+ {
+ *root = node;
+ node->avl.left = NULL;
+ node->avl.right = NULL;
+ node->avl.bal = 0;
+ return 0;
+ }
+
+ while (p != NULL)
+ {
+ *buf_prev++ = p;
+
+ if ((p->dev < dev) || ((p->dev == dev) && (p->block < block)))
+ {
+ p->avl.cache = 1;
+ q = p->avl.right;
+ if (q == NULL)
+ {
+ q = node;
+ p->avl.right = q = node;
+ break;
+ }
+ }
+ else if ((p->dev != dev) || (p->block != block))
+ {
+ p->avl.cache = -1;
+ q = p->avl.left;
+ if (q == NULL)
+ {
+ q = node;
+ p->avl.left = q;
+ break;
+ }
+ }
+ else
+ {
+ return -1;
+ }
+
+ p = q;
+ }
+
+ q->avl.left = q->avl.right = NULL;
+ q->avl.bal = 0;
+ modified = true;
+ buf_prev--;
+
+ while (modified)
+ {
+ if (p->avl.cache == -1)
+ {
+ switch (p->avl.bal)
+ {
+ case 1:
+ p->avl.bal = 0;
+ modified = false;
+ break;
+
+ case 0:
+ p->avl.bal = -1;
+ break;
+
+ case -1:
+ p1 = p->avl.left;
+ if (p1->avl.bal == -1) /* simple LL-turn */
+ {
+ p->avl.left = p1->avl.right;
+ p1->avl.right = p;
+ p->avl.bal = 0;
+ p = p1;
+ }
+ else /* double LR-turn */
+ {
+ p2 = p1->avl.right;
+ p1->avl.right = p2->avl.left;
+ p2->avl.left = p1;
+ p->avl.left = p2->avl.right;
+ p2->avl.right = p;
+ if (p2->avl.bal == -1) p->avl.bal = +1; else p->avl.bal = 0;
+ if (p2->avl.bal == +1) p1->avl.bal = -1; else p1->avl.bal = 0;
+ p = p2;
+ }
+ p->avl.bal = 0;
+ modified = false;
+ break;
+
+ default:
+ break;
+ }
+ }
+ else
+ {
+ switch (p->avl.bal)
+ {
+ case -1:
+ p->avl.bal = 0;
+ modified = false;
+ break;
+
+ case 0:
+ p->avl.bal = 1;
+ break;
+
+ case 1:
+ p1 = p->avl.right;
+ if (p1->avl.bal == 1) /* simple RR-turn */
+ {
+ p->avl.right = p1->avl.left;
+ p1->avl.left = p;
+ p->avl.bal = 0;
+ p = p1;
+ }
+ else /* double RL-turn */
+ {
+ p2 = p1->avl.left;
+ p1->avl.left = p2->avl.right;
+ p2->avl.right = p1;
+ p->avl.right = p2->avl.left;
+ p2->avl.left = p;
+ if (p2->avl.bal == +1) p->avl.bal = -1; else p->avl.bal = 0;
+ if (p2->avl.bal == -1) p1->avl.bal = +1; else p1->avl.bal = 0;
+ p = p2;
+ }
+ p->avl.bal = 0;
+ modified = false;
+ break;
+
+ default:
+ break;
+ }
+ }
+ q = p;
+ if (buf_prev > buf_stack)
+ {
+ p = *--buf_prev;
+
+ if (p->avl.cache == -1)
+ {
+ p->avl.left = q;
+ }
+ else
+ {
+ p->avl.right = q;
+ }
+ }
+ else
+ {
+ *root = p;
+ break;
+ }
+ };
+
+ return 0;
+}
+
+
+/**
+ * Removes the node from the tree.
+ *
+ * @param root Pointer to pointer to the root node
+ * @param node Pointer to the node to remove
+ * @retval 0 Item removed
+ * @retval -1 No such item found
+ */
+static int
+rtems_bdbuf_avl_remove(rtems_bdbuf_buffer** root,
+ const rtems_bdbuf_buffer* node)
+{
+ dev_t dev = node->dev;
+ rtems_blkdev_bnum block = node->block;
+
+ rtems_bdbuf_buffer* p = *root;
+ rtems_bdbuf_buffer* q;
+ rtems_bdbuf_buffer* r;
+ rtems_bdbuf_buffer* s;
+ rtems_bdbuf_buffer* p1;
+ rtems_bdbuf_buffer* p2;
+ rtems_bdbuf_buffer* buf_stack[RTEMS_BDBUF_AVL_MAX_HEIGHT];
+ rtems_bdbuf_buffer** buf_prev = buf_stack;
+
+ bool modified = false;
+
+ memset (buf_stack, 0, sizeof(buf_stack));
+
+ while (p != NULL)
+ {
+ *buf_prev++ = p;
+
+ if ((p->dev < dev) || ((p->dev == dev) && (p->block < block)))
+ {
+ p->avl.cache = 1;
+ p = p->avl.right;
+ }
+ else if ((p->dev != dev) || (p->block != block))
+ {
+ p->avl.cache = -1;
+ p = p->avl.left;
+ }
+ else
+ {
+ /* node found */
+ break;
+ }
+ }
+
+ if (p == NULL)
+ {
+ /* there is no such node */
+ return -1;
+ }
+
+ q = p;
+
+ buf_prev--;
+ if (buf_prev > buf_stack)
+ {
+ p = *(buf_prev - 1);
+ }
+ else
+ {
+ p = NULL;
+ }
+
+ /* at this moment q - is a node to delete, p is q's parent */
+ if (q->avl.right == NULL)
+ {
+ r = q->avl.left;
+ if (r != NULL)
+ {
+ r->avl.bal = 0;
+ }
+ q = r;
+ }
+ else
+ {
+ rtems_bdbuf_buffer **t;
+
+ r = q->avl.right;
+
+ if (r->avl.left == NULL)
+ {
+ r->avl.left = q->avl.left;
+ r->avl.bal = q->avl.bal;
+ r->avl.cache = 1;
+ *buf_prev++ = q = r;
+ }
+ else
+ {
+ t = buf_prev++;
+ s = r;
+
+ while (s->avl.left != NULL)
+ {
+ *buf_prev++ = r = s;
+ s = r->avl.left;
+ r->avl.cache = -1;
+ }
+
+ s->avl.left = q->avl.left;
+ r->avl.left = s->avl.right;
+ s->avl.right = q->avl.right;
+ s->avl.bal = q->avl.bal;
+ s->avl.cache = 1;
+
+ *t = q = s;
+ }
+ }
+
+ if (p != NULL)
+ {
+ if (p->avl.cache == -1)
+ {
+ p->avl.left = q;
+ }
+ else
+ {
+ p->avl.right = q;
+ }
+ }
+ else
+ {
+ *root = q;
+ }
+
+ modified = true;
+
+ while (modified)
+ {
+ if (buf_prev > buf_stack)
+ {
+ p = *--buf_prev;
+ }
+ else
+ {
+ break;
+ }
+
+ if (p->avl.cache == -1)
+ {
+ /* rebalance left branch */
+ switch (p->avl.bal)
+ {
+ case -1:
+ p->avl.bal = 0;
+ break;
+ case 0:
+ p->avl.bal = 1;
+ modified = false;
+ break;
+
+ case +1:
+ p1 = p->avl.right;
+
+ if (p1->avl.bal >= 0) /* simple RR-turn */
+ {
+ p->avl.right = p1->avl.left;
+ p1->avl.left = p;
+
+ if (p1->avl.bal == 0)
+ {
+ p1->avl.bal = -1;
+ modified = false;
+ }
+ else
+ {
+ p->avl.bal = 0;
+ p1->avl.bal = 0;
+ }
+ p = p1;
+ }
+ else /* double RL-turn */
+ {
+ p2 = p1->avl.left;
+
+ p1->avl.left = p2->avl.right;
+ p2->avl.right = p1;
+ p->avl.right = p2->avl.left;
+ p2->avl.left = p;
+
+ if (p2->avl.bal == +1) p->avl.bal = -1; else p->avl.bal = 0;
+ if (p2->avl.bal == -1) p1->avl.bal = 1; else p1->avl.bal = 0;
+
+ p = p2;
+ p2->avl.bal = 0;
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+ else
+ {
+ /* rebalance right branch */
+ switch (p->avl.bal)
+ {
+ case +1:
+ p->avl.bal = 0;
+ break;
+
+ case 0:
+ p->avl.bal = -1;
+ modified = false;
+ break;
+
+ case -1:
+ p1 = p->avl.left;
+
+ if (p1->avl.bal <= 0) /* simple LL-turn */
+ {
+ p->avl.left = p1->avl.right;
+ p1->avl.right = p;
+ if (p1->avl.bal == 0)
+ {
+ p1->avl.bal = 1;
+ modified = false;
+ }
+ else
+ {
+ p->avl.bal = 0;
+ p1->avl.bal = 0;
+ }
+ p = p1;
+ }
+ else /* double LR-turn */
+ {
+ p2 = p1->avl.right;
+
+ p1->avl.right = p2->avl.left;
+ p2->avl.left = p1;
+ p->avl.left = p2->avl.right;
+ p2->avl.right = p;
+
+ if (p2->avl.bal == -1) p->avl.bal = 1; else p->avl.bal = 0;
+ if (p2->avl.bal == +1) p1->avl.bal = -1; else p1->avl.bal = 0;
+
+ p = p2;
+ p2->avl.bal = 0;
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if (buf_prev > buf_stack)
+ {
+ q = *(buf_prev - 1);
+
+ if (q->avl.cache == -1)
+ {
+ q->avl.left = p;
+ }
+ else
+ {
+ q->avl.right = p;
+ }
+ }
+ else
+ {
+ *root = p;
+ break;
+ }
+
+ }
+
+ return 0;
+}
+
+static void
+rtems_bdbuf_set_state (rtems_bdbuf_buffer *bd, rtems_bdbuf_buf_state state)
+{
+ bd->state = state;
+}
+
+/**
+ * Change the block number for the block size to the block number for the media
+ * block size. We have to use 64bit maths. There is no short cut here.
+ *
+ * @param block The logical block number in the block size terms.
+ * @param block_size The block size.
+ * @param media_block_size The block size of the media.
+ * @return rtems_blkdev_bnum The media block number.
+ */
+static rtems_blkdev_bnum
+rtems_bdbuf_media_block (rtems_blkdev_bnum block,
+ size_t block_size,
+ size_t media_block_size)
+{
+ return (rtems_blkdev_bnum)
+ ((((uint64_t) block) * block_size) / media_block_size);
+}
+
+/**
+ * Lock the mutex. A single task can nest calls.
+ *
+ * @param lock The mutex to lock.
+ * @param fatal_error_code The error code if the call fails.
+ */
+static void
+rtems_bdbuf_lock (rtems_id lock, uint32_t fatal_error_code)
+{
+ rtems_status_code sc = rtems_semaphore_obtain (lock,
+ RTEMS_WAIT,
+ RTEMS_NO_TIMEOUT);
+ if (sc != RTEMS_SUCCESSFUL)
+ rtems_fatal_error_occurred (fatal_error_code);
+}
+
+/**
+ * Unlock the mutex.
+ *
+ * @param lock The mutex to unlock.
+ * @param fatal_error_code The error code if the call fails.
+ */
+static void
+rtems_bdbuf_unlock (rtems_id lock, uint32_t fatal_error_code)
+{
+ rtems_status_code sc = rtems_semaphore_release (lock);
+ if (sc != RTEMS_SUCCESSFUL)
+ rtems_fatal_error_occurred (fatal_error_code);
+}
+
+/**
+ * Lock the cache. A single task can nest calls.
+ */
+static void
+rtems_bdbuf_lock_cache (void)
+{
+ rtems_bdbuf_lock (bdbuf_cache.lock, RTEMS_BLKDEV_FATAL_BDBUF_CACHE_LOCK);
+}
+
+/**
+ * Unlock the cache.
+ */
+static void
+rtems_bdbuf_unlock_cache (void)
+{
+ rtems_bdbuf_unlock (bdbuf_cache.lock, RTEMS_BLKDEV_FATAL_BDBUF_CACHE_UNLOCK);
+}
+
+/**
+ * Lock the cache's sync. A single task can nest calls.
+ */
+static void
+rtems_bdbuf_lock_sync (void)
+{
+ rtems_bdbuf_lock (bdbuf_cache.sync_lock, RTEMS_BLKDEV_FATAL_BDBUF_SYNC_LOCK);
+}
+
+/**
+ * Unlock the cache's sync lock. Any blocked writers are woken.
+ */
+static void
+rtems_bdbuf_unlock_sync (void)
+{
+ rtems_bdbuf_unlock (bdbuf_cache.sync_lock,
+ RTEMS_BLKDEV_FATAL_BDBUF_SYNC_UNLOCK);
+}
+
+static void
+rtems_bdbuf_group_obtain (rtems_bdbuf_buffer *bd)
+{
+ ++bd->group->users;
+}
+
+static void
+rtems_bdbuf_group_release (rtems_bdbuf_buffer *bd)
+{
+ --bd->group->users;
+}
+
+static rtems_mode
+rtems_bdbuf_disable_preemption (void)
+{
+ rtems_status_code sc = RTEMS_SUCCESSFUL;
+ rtems_mode prev_mode = 0;
+
+ sc = rtems_task_mode (RTEMS_NO_PREEMPT, RTEMS_PREEMPT_MASK, &prev_mode);
+ if (sc != RTEMS_SUCCESSFUL)
+ rtems_fatal_error_occurred (RTEMS_BLKDEV_FATAL_BDBUF_PREEMPT_DIS);
+
+ return prev_mode;
+}
+
+static void
+rtems_bdbuf_restore_preemption (rtems_mode prev_mode)
+{
+ rtems_status_code sc = RTEMS_SUCCESSFUL;
+
+ sc = rtems_task_mode (prev_mode, RTEMS_ALL_MODE_MASKS, &prev_mode);
+ if (sc != RTEMS_SUCCESSFUL)
+ rtems_fatal_error_occurred (RTEMS_BLKDEV_FATAL_BDBUF_PREEMPT_RST);
+}
+
+/**
+ * Wait until woken. Semaphores are used so a number of tasks can wait and can
+ * be woken at once. Task events would require we maintain a list of tasks to
+ * be woken and this would require storage and we do not know the number of
+ * tasks that could be waiting.
+ *
+ * While we have the cache locked we can try and claim the semaphore and
+ * therefore know when we release the lock to the cache we will block until the
+ * semaphore is released. This may even happen before we get to block.
+ *
+ * A counter is used to save the release call when no one is waiting.
+ *
+ * The function assumes the cache is locked on entry and it will be locked on
+ * exit.
+ */
+static void
+rtems_bdbuf_anonymous_wait (rtems_bdbuf_waiters *waiters)
+{
+ rtems_status_code sc;
+ rtems_mode prev_mode;
+
+ /*
+ * Indicate we are waiting.
+ */
+ ++waiters->count;
+
+ /*
+ * Disable preemption then unlock the cache and block. There is no POSIX
+ * condition variable in the core API so this is a work around.
+ *
+ * The issue is a task could preempt after the cache is unlocked because it is
+ * blocking or just hits that window, and before this task has blocked on the
+ * semaphore. If the preempting task flushes the queue this task will not see
+ * the flush and may block for ever or until another transaction flushes this
+ * semaphore.
+ */
+ prev_mode = rtems_bdbuf_disable_preemption ();
+
+ /*
+ * Unlock the cache, wait, and lock the cache when we return.
+ */
+ rtems_bdbuf_unlock_cache ();
+
+ sc = rtems_semaphore_obtain (waiters->sema, RTEMS_WAIT, RTEMS_BDBUF_WAIT_TIMEOUT);
+
+ if (sc == RTEMS_TIMEOUT)
+ rtems_fatal_error_occurred (RTEMS_BLKDEV_FATAL_BDBUF_CACHE_WAIT_TO);
+
+ if (sc != RTEMS_UNSATISFIED)
+ rtems_fatal_error_occurred (RTEMS_BLKDEV_FATAL_BDBUF_CACHE_WAIT_2);
+
+ rtems_bdbuf_lock_cache ();
+
+ rtems_bdbuf_restore_preemption (prev_mode);
+
+ --waiters->count;
+}
+
+static void
+rtems_bdbuf_wait (rtems_bdbuf_buffer *bd, rtems_bdbuf_waiters *waiters)
+{
+ rtems_bdbuf_group_obtain (bd);
+ ++bd->waiters;
+ rtems_bdbuf_anonymous_wait (waiters);
+ --bd->waiters;
+ rtems_bdbuf_group_release (bd);
+}
+
+/**
+ * Wake a blocked resource. The resource has a counter that lets us know if
+ * there are any waiters.
+ */
+static void
+rtems_bdbuf_wake (const rtems_bdbuf_waiters *waiters)
+{
+ rtems_status_code sc = RTEMS_SUCCESSFUL;
+
+ if (waiters->count > 0)
+ {
+ sc = rtems_semaphore_flush (waiters->sema);
+ if (sc != RTEMS_SUCCESSFUL)
+ rtems_fatal_error_occurred (RTEMS_BLKDEV_FATAL_BDBUF_CACHE_WAKE);
+ }
+}
+
+static void
+rtems_bdbuf_wake_swapper (void)
+{
+ rtems_status_code sc = rtems_event_send (bdbuf_cache.swapout,
+ RTEMS_BDBUF_SWAPOUT_SYNC);
+ if (sc != RTEMS_SUCCESSFUL)
+ rtems_fatal_error_occurred (RTEMS_BLKDEV_FATAL_BDBUF_SO_WAKE);
+}
+
+static bool
+rtems_bdbuf_has_buffer_waiters (void)
+{
+ return bdbuf_cache.buffer_waiters.count;
+}
+
+static void
+rtems_bdbuf_remove_from_tree (rtems_bdbuf_buffer *bd)
+{
+ if (rtems_bdbuf_avl_remove (&bdbuf_cache.tree, bd) != 0)
+ rtems_bdbuf_fatal (bd->state, RTEMS_BLKDEV_FATAL_BDBUF_TREE_RM);
+}
+
+static void
+rtems_bdbuf_remove_from_tree_and_lru_list (rtems_bdbuf_buffer *bd)
+{
+ switch (bd->state)
+ {
+ case RTEMS_BDBUF_STATE_FREE:
+ break;
+ case RTEMS_BDBUF_STATE_CACHED:
+ rtems_bdbuf_remove_from_tree (bd);
+ break;
+ default:
+ rtems_bdbuf_fatal (bd->state, RTEMS_BLKDEV_FATAL_BDBUF_STATE_10);
+ }
+
+ rtems_chain_extract (&bd->link);
+}
+
+static void
+rtems_bdbuf_make_free_and_add_to_lru_list (rtems_bdbuf_buffer *bd)
+{
+ rtems_bdbuf_set_state (bd, RTEMS_BDBUF_STATE_FREE);
+ rtems_chain_prepend (&bdbuf_cache.lru, &bd->link);
+}
+
+static void
+rtems_bdbuf_make_empty (rtems_bdbuf_buffer *bd)
+{
+ rtems_bdbuf_set_state (bd, RTEMS_BDBUF_STATE_EMPTY);
+}
+
+static void
+rtems_bdbuf_make_cached_and_add_to_lru_list (rtems_bdbuf_buffer *bd)
+{
+ rtems_bdbuf_set_state (bd, RTEMS_BDBUF_STATE_CACHED);
+ rtems_chain_append (&bdbuf_cache.lru, &bd->link);
+}
+
+static void
+rtems_bdbuf_discard_buffer (rtems_bdbuf_buffer *bd)
+{
+ rtems_bdbuf_make_empty (bd);
+
+ if (bd->waiters == 0)
+ {
+ rtems_bdbuf_remove_from_tree (bd);
+ rtems_bdbuf_make_free_and_add_to_lru_list (bd);
+ }
+}
+
+static void
+rtems_bdbuf_add_to_modified_list_after_access (rtems_bdbuf_buffer *bd)
+{
+ if (bdbuf_cache.sync_active && bdbuf_cache.sync_device == bd->dev)
+ {
+ rtems_bdbuf_unlock_cache ();
+
+ /*
+ * Wait for the sync lock.
+ */
+ rtems_bdbuf_lock_sync ();
+
+ rtems_bdbuf_unlock_sync ();
+ rtems_bdbuf_lock_cache ();
+ }
+
+ /*
+ * Only the first modified release sets the timer and any further user
+ * accesses do not change the timer value which should move down. This
+ * assumes the user's hold of the buffer is much less than the time on the
+ * modified list. Resetting the timer on each access which could result in a
+ * buffer never getting to 0 and never being forced onto disk. This raises a
+ * difficult question. Is a snapshot of a block that is changing better than
+ * nothing being written? We have tended to think we should hold changes for
+ * only a specific period of time even if still changing and get onto disk
+ * and letting the file system try and recover this position if it can.
+ */
+ if (bd->state == RTEMS_BDBUF_STATE_ACCESS_CACHED
+ || bd->state == RTEMS_BDBUF_STATE_ACCESS_EMPTY)
+ bd->hold_timer = bdbuf_config.swap_block_hold;
+
+ rtems_bdbuf_set_state (bd, RTEMS_BDBUF_STATE_MODIFIED);
+ rtems_chain_append (&bdbuf_cache.modified, &bd->link);
+
+ if (bd->waiters)
+ rtems_bdbuf_wake (&bdbuf_cache.access_waiters);
+ else if (rtems_bdbuf_has_buffer_waiters ())
+ rtems_bdbuf_wake_swapper ();
+}
+
+static void
+rtems_bdbuf_add_to_lru_list_after_access (rtems_bdbuf_buffer *bd)
+{
+ rtems_bdbuf_group_release (bd);
+ rtems_bdbuf_make_cached_and_add_to_lru_list (bd);
+
+ if (bd->waiters)
+ rtems_bdbuf_wake (&bdbuf_cache.access_waiters);
+ else
+ rtems_bdbuf_wake (&bdbuf_cache.buffer_waiters);
+}
+
+/**
+ * Compute the number of BDs per group for a given buffer size.
+ *
+ * @param size The buffer size. It can be any size and we scale up.
+ */
+static size_t
+rtems_bdbuf_bds_per_group (size_t size)
+{
+ size_t bufs_per_size;
+ size_t bds_per_size;
+
+ if (size > bdbuf_config.buffer_max)
+ return 0;
+
+ bufs_per_size = ((size - 1) / bdbuf_config.buffer_min) + 1;
+
+ for (bds_per_size = 1;
+ bds_per_size < bufs_per_size;
+ bds_per_size <<= 1)
+ ;
+
+ return bdbuf_cache.max_bds_per_group / bds_per_size;
+}
+
+static void
+rtems_bdbuf_discard_buffer_after_access (rtems_bdbuf_buffer *bd)
+{
+ rtems_bdbuf_group_release (bd);
+ rtems_bdbuf_discard_buffer (bd);
+
+ if (bd->waiters)
+ rtems_bdbuf_wake (&bdbuf_cache.access_waiters);
+ else
+ rtems_bdbuf_wake (&bdbuf_cache.buffer_waiters);
+}
+
+/**
+ * Reallocate a group. The BDs currently allocated in the group are removed
+ * from the ALV tree and any lists then the new BD's are prepended to the ready
+ * list of the cache.
+ *
+ * @param group The group to reallocate.
+ * @param new_bds_per_group The new count of BDs per group.
+ * @return A buffer of this group.
+ */
+static rtems_bdbuf_buffer *
+rtems_bdbuf_group_realloc (rtems_bdbuf_group* group, size_t new_bds_per_group)
+{
+ rtems_bdbuf_buffer* bd;
+ size_t b;
+ size_t bufs_per_bd;
+
+ if (rtems_bdbuf_tracer)
+ printf ("bdbuf:realloc: %tu: %zd -> %zd\n",
+ group - bdbuf_cache.groups, group->bds_per_group,
+ new_bds_per_group);
+
+ bufs_per_bd = bdbuf_cache.max_bds_per_group / group->bds_per_group;
+
+ for (b = 0, bd = group->bdbuf;
+ b < group->bds_per_group;
+ b++, bd += bufs_per_bd)
+ rtems_bdbuf_remove_from_tree_and_lru_list (bd);
+
+ group->bds_per_group = new_bds_per_group;
+ bufs_per_bd = bdbuf_cache.max_bds_per_group / new_bds_per_group;
+
+ for (b = 1, bd = group->bdbuf + bufs_per_bd;
+ b < group->bds_per_group;
+ b++, bd += bufs_per_bd)
+ rtems_bdbuf_make_free_and_add_to_lru_list (bd);
+
+ if (b > 1)
+ rtems_bdbuf_wake (&bdbuf_cache.buffer_waiters);
+
+ return group->bdbuf;
+}
+
+static void
+rtems_bdbuf_setup_empty_buffer (rtems_bdbuf_buffer *bd,
+ dev_t dev,
+ rtems_blkdev_bnum block)
+{
+ bd->dev = dev;
+ bd->block = block;
+ bd->avl.left = NULL;
+ bd->avl.right = NULL;
+ bd->waiters = 0;
+
+ if (rtems_bdbuf_avl_insert (&bdbuf_cache.tree, bd) != 0)
+ rtems_fatal_error_occurred (RTEMS_BLKDEV_FATAL_BDBUF_RECYCLE);
+
+ rtems_bdbuf_make_empty (bd);
+}
+
+static rtems_bdbuf_buffer *
+rtems_bdbuf_get_buffer_from_lru_list (dev_t dev,
+ rtems_blkdev_bnum block,
+ size_t bds_per_group)
+{
+ rtems_chain_node *node = rtems_chain_first (&bdbuf_cache.lru);
+
+ while (!rtems_chain_is_tail (&bdbuf_cache.lru, node))
+ {
+ rtems_bdbuf_buffer *bd = (rtems_bdbuf_buffer *) node;
+ rtems_bdbuf_buffer *empty_bd = NULL;
+
+ if (rtems_bdbuf_tracer)
+ printf ("bdbuf:next-bd: %tu (%td:%" PRId32 ") %zd -> %zd\n",
+ bd - bdbuf_cache.bds,
+ bd->group - bdbuf_cache.groups, bd->group->users,
+ bd->group->bds_per_group, bds_per_group);
+
+ /*
+ * If nobody waits for this BD, we may recycle it.
+ */
+ if (bd->waiters == 0)
+ {
+ if (bd->group->bds_per_group == bds_per_group)
+ {
+ rtems_bdbuf_remove_from_tree_and_lru_list (bd);
+
+ empty_bd = bd;
+ }
+ else if (bd->group->users == 0)
+ empty_bd = rtems_bdbuf_group_realloc (bd->group, bds_per_group);
+ }
+
+ if (empty_bd != NULL)
+ {
+ rtems_bdbuf_setup_empty_buffer (empty_bd, dev, block);
+
+ return empty_bd;
+ }
+
+ node = rtems_chain_next (node);
+ }
+
+ return NULL;
+}
+
+/**
+ * Initialise the cache.
+ *
+ * @return rtems_status_code The initialisation status.
+ */
+rtems_status_code
+rtems_bdbuf_init (void)
+{
+ rtems_bdbuf_group* group;
+ rtems_bdbuf_buffer* bd;
+ uint8_t* buffer;
+ size_t b;
+ size_t cache_aligment;
+ rtems_status_code sc;
+ rtems_mode prev_mode;
+
+ if (rtems_bdbuf_tracer)
+ printf ("bdbuf:init\n");
+
+ if (rtems_interrupt_is_in_progress())
+ return RTEMS_CALLED_FROM_ISR;
+
+ /*
+ * Check the configuration table values.
+ */
+ if ((bdbuf_config.buffer_max % bdbuf_config.buffer_min) != 0)
+ return RTEMS_INVALID_NUMBER;
+
+ /*
+ * We use a special variable to manage the initialisation incase we have
+ * completing threads doing this. You may get errors if the another thread
+ * makes a call and we have not finished initialisation.
+ */
+ prev_mode = rtems_bdbuf_disable_preemption ();
+ if (bdbuf_cache.initialised)
+ {
+ rtems_bdbuf_restore_preemption (prev_mode);
+ return RTEMS_RESOURCE_IN_USE;
+ }
+
+ memset(&bdbuf_cache, 0, sizeof(bdbuf_cache));
+ bdbuf_cache.initialised = true;
+ rtems_bdbuf_restore_preemption (prev_mode);
+
+ /*
+ * For unspecified cache alignments we use the CPU alignment.
+ */
+ cache_aligment = 32; /* FIXME rtems_cache_get_data_line_size() */
+ if (cache_aligment <= 0)
+ cache_aligment = CPU_ALIGNMENT;
+
+ bdbuf_cache.sync_device = BDBUF_INVALID_DEV;
+
+ rtems_chain_initialize_empty (&bdbuf_cache.swapout_workers);
+ rtems_chain_initialize_empty (&bdbuf_cache.lru);
+ rtems_chain_initialize_empty (&bdbuf_cache.modified);
+ rtems_chain_initialize_empty (&bdbuf_cache.sync);
+
+ /*
+ * Create the locks for the cache.
+ */
+ sc = rtems_semaphore_create (rtems_build_name ('B', 'D', 'C', 'l'),
+ 1, RTEMS_BDBUF_CACHE_LOCK_ATTRIBS, 0,
+ &bdbuf_cache.lock);
+ if (sc != RTEMS_SUCCESSFUL)
+ goto error;
+
+ rtems_bdbuf_lock_cache ();
+
+ sc = rtems_semaphore_create (rtems_build_name ('B', 'D', 'C', 's'),
+ 1, RTEMS_BDBUF_CACHE_LOCK_ATTRIBS, 0,
+ &bdbuf_cache.sync_lock);
+ if (sc != RTEMS_SUCCESSFUL)
+ goto error;
+
+ sc = rtems_semaphore_create (rtems_build_name ('B', 'D', 'C', 'a'),
+ 0, RTEMS_BDBUF_CACHE_WAITER_ATTRIBS, 0,
+ &bdbuf_cache.access_waiters.sema);
+ if (sc != RTEMS_SUCCESSFUL)
+ goto error;
+
+ sc = rtems_semaphore_create (rtems_build_name ('B', 'D', 'C', 't'),
+ 0, RTEMS_BDBUF_CACHE_WAITER_ATTRIBS, 0,
+ &bdbuf_cache.transfer_waiters.sema);
+ if (sc != RTEMS_SUCCESSFUL)
+ goto error;
+
+ sc = rtems_semaphore_create (rtems_build_name ('B', 'D', 'C', 'b'),
+ 0, RTEMS_BDBUF_CACHE_WAITER_ATTRIBS, 0,
+ &bdbuf_cache.buffer_waiters.sema);
+ if (sc != RTEMS_SUCCESSFUL)
+ goto error;
+
+ /*
+ * Compute the various number of elements in the cache.
+ */
+ bdbuf_cache.buffer_min_count =
+ bdbuf_config.size / bdbuf_config.buffer_min;
+ bdbuf_cache.max_bds_per_group =
+ bdbuf_config.buffer_max / bdbuf_config.buffer_min;
+ bdbuf_cache.group_count =
+ bdbuf_cache.buffer_min_count / bdbuf_cache.max_bds_per_group;
+
+ /*
+ * Allocate the memory for the buffer descriptors.
+ */
+ bdbuf_cache.bds = calloc (sizeof (rtems_bdbuf_buffer),
+ bdbuf_cache.buffer_min_count);
+ if (!bdbuf_cache.bds)
+ goto error;
+
+ /*
+ * Allocate the memory for the buffer descriptors.
+ */
+ bdbuf_cache.groups = calloc (sizeof (rtems_bdbuf_group),
+ bdbuf_cache.group_count);
+ if (!bdbuf_cache.groups)
+ goto error;
+
+ /*
+ * Allocate memory for buffer memory. The buffer memory will be cache
+ * aligned. It is possible to free the memory allocated by rtems_memalign()
+ * with free(). Return 0 if allocated.
+ *
+ * The memory allocate allows a
+ */
+ if (rtems_memalign ((void **) &bdbuf_cache.buffers,
+ cache_aligment,
+ bdbuf_cache.buffer_min_count * bdbuf_config.buffer_min) != 0)
+ goto error;
+
+ /*
+ * The cache is empty after opening so we need to add all the buffers to it
+ * and initialise the groups.
+ */
+ for (b = 0, group = bdbuf_cache.groups,
+ bd = bdbuf_cache.bds, buffer = bdbuf_cache.buffers;
+ b < bdbuf_cache.buffer_min_count;
+ b++, bd++, buffer += bdbuf_config.buffer_min)
+ {
+ bd->dev = BDBUF_INVALID_DEV;
+ bd->group = group;
+ bd->buffer = buffer;
+
+ rtems_chain_append (&bdbuf_cache.lru, &bd->link);
+
+ if ((b % bdbuf_cache.max_bds_per_group) ==
+ (bdbuf_cache.max_bds_per_group - 1))
+ group++;
+ }
+
+ for (b = 0,
+ group = bdbuf_cache.groups,
+ bd = bdbuf_cache.bds;
+ b < bdbuf_cache.group_count;
+ b++,
+ group++,
+ bd += bdbuf_cache.max_bds_per_group)
+ {
+ group->bds_per_group = bdbuf_cache.max_bds_per_group;
+ group->bdbuf = bd;
+ }
+
+ /*
+ * Create and start swapout task. This task will create and manage the worker
+ * threads.
+ */
+ bdbuf_cache.swapout_enabled = true;
+
+ sc = rtems_task_create (rtems_build_name('B', 'S', 'W', 'P'),
+ bdbuf_config.swapout_priority ?
+ bdbuf_config.swapout_priority :
+ RTEMS_BDBUF_SWAPOUT_TASK_PRIORITY_DEFAULT,
+ SWAPOUT_TASK_STACK_SIZE,
+ RTEMS_PREEMPT | RTEMS_NO_TIMESLICE | RTEMS_NO_ASR,
+ RTEMS_LOCAL | RTEMS_NO_FLOATING_POINT,
+ &bdbuf_cache.swapout);
+ if (sc != RTEMS_SUCCESSFUL)
+ goto error;
+
+ sc = rtems_task_start (bdbuf_cache.swapout,
+ rtems_bdbuf_swapout_task,
+ (rtems_task_argument) &bdbuf_cache);
+ if (sc != RTEMS_SUCCESSFUL)
+ goto error;
+
+ rtems_bdbuf_unlock_cache ();
+
+ return RTEMS_SUCCESSFUL;
+
+error:
+
+ if (bdbuf_cache.swapout != 0)
+ rtems_task_delete (bdbuf_cache.swapout);
+
+ free (bdbuf_cache.buffers);
+ free (bdbuf_cache.groups);
+ free (bdbuf_cache.bds);
+
+ rtems_semaphore_delete (bdbuf_cache.buffer_waiters.sema);
+ rtems_semaphore_delete (bdbuf_cache.access_waiters.sema);
+ rtems_semaphore_delete (bdbuf_cache.transfer_waiters.sema);
+ rtems_semaphore_delete (bdbuf_cache.sync_lock);
+
+ if (bdbuf_cache.lock != 0)
+ {
+ rtems_bdbuf_unlock_cache ();
+ rtems_semaphore_delete (bdbuf_cache.lock);
+ }
+
+ bdbuf_cache.initialised = false;
+
+ return RTEMS_UNSATISFIED;
+}
+
+static void
+rtems_bdbuf_wait_for_event (rtems_event_set event)
+{
+ rtems_status_code sc = RTEMS_SUCCESSFUL;
+ rtems_event_set out = 0;
+
+ sc = rtems_event_receive (event,
+ RTEMS_EVENT_ALL | RTEMS_WAIT,
+ RTEMS_NO_TIMEOUT,
+ &out);
+
+ if (sc != RTEMS_SUCCESSFUL || out != event)
+ rtems_fatal_error_occurred (RTEMS_BLKDEV_FATAL_BDBUF_WAIT_EVNT);
+}
+
+static void
+rtems_bdbuf_wait_for_access (rtems_bdbuf_buffer *bd)
+{
+ while (true)
+ {
+ switch (bd->state)
+ {
+ case RTEMS_BDBUF_STATE_MODIFIED:
+ rtems_bdbuf_group_release (bd);
+ /* Fall through */
+ case RTEMS_BDBUF_STATE_CACHED:
+ rtems_chain_extract (&bd->link);
+ /* Fall through */
+ case RTEMS_BDBUF_STATE_EMPTY:
+ return;
+ case RTEMS_BDBUF_STATE_ACCESS_CACHED:
+ case RTEMS_BDBUF_STATE_ACCESS_EMPTY:
+ case RTEMS_BDBUF_STATE_ACCESS_MODIFIED:
+ case RTEMS_BDBUF_STATE_ACCESS_PURGED:
+ rtems_bdbuf_wait (bd, &bdbuf_cache.access_waiters);
+ break;
+ case RTEMS_BDBUF_STATE_SYNC:
+ case RTEMS_BDBUF_STATE_TRANSFER:
+ case RTEMS_BDBUF_STATE_TRANSFER_PURGED:
+ rtems_bdbuf_wait (bd, &bdbuf_cache.transfer_waiters);
+ break;
+ default:
+ rtems_bdbuf_fatal (bd->state, RTEMS_BLKDEV_FATAL_BDBUF_STATE_7);
+ }
+ }
+}
+
+static void
+rtems_bdbuf_request_sync_for_modified_buffer (rtems_bdbuf_buffer *bd)
+{
+ rtems_bdbuf_set_state (bd, RTEMS_BDBUF_STATE_SYNC);
+ rtems_chain_extract (&bd->link);
+ rtems_chain_append (&bdbuf_cache.sync, &bd->link);
+ rtems_bdbuf_wake_swapper ();
+}
+
+/**
+ * @brief Waits until the buffer is ready for recycling.
+ *
+ * @retval @c true Buffer is valid and may be recycled.
+ * @retval @c false Buffer is invalid and has to searched again.
+ */
+static bool
+rtems_bdbuf_wait_for_recycle (rtems_bdbuf_buffer *bd)
+{
+ while (true)
+ {
+ switch (bd->state)
+ {
+ case RTEMS_BDBUF_STATE_FREE:
+ return true;
+ case RTEMS_BDBUF_STATE_MODIFIED:
+ rtems_bdbuf_request_sync_for_modified_buffer (bd);
+ break;
+ case RTEMS_BDBUF_STATE_CACHED:
+ case RTEMS_BDBUF_STATE_EMPTY:
+ if (bd->waiters == 0)
+ return true;
+ else
+ {
+ /*
+ * It is essential that we wait here without a special wait count and
+ * without the group in use. Otherwise we could trigger a wait ping
+ * pong with another recycle waiter. The state of the buffer is
+ * arbitrary afterwards.
+ */
+ rtems_bdbuf_anonymous_wait (&bdbuf_cache.buffer_waiters);
+ return false;
+ }
+ case RTEMS_BDBUF_STATE_ACCESS_CACHED:
+ case RTEMS_BDBUF_STATE_ACCESS_EMPTY:
+ case RTEMS_BDBUF_STATE_ACCESS_MODIFIED:
+ case RTEMS_BDBUF_STATE_ACCESS_PURGED:
+ rtems_bdbuf_wait (bd, &bdbuf_cache.access_waiters);
+ break;
+ case RTEMS_BDBUF_STATE_SYNC:
+ case RTEMS_BDBUF_STATE_TRANSFER:
+ case RTEMS_BDBUF_STATE_TRANSFER_PURGED:
+ rtems_bdbuf_wait (bd, &bdbuf_cache.transfer_waiters);
+ break;
+ default:
+ rtems_bdbuf_fatal (bd->state, RTEMS_BLKDEV_FATAL_BDBUF_STATE_8);
+ }
+ }
+}
+
+static void
+rtems_bdbuf_wait_for_sync_done (rtems_bdbuf_buffer *bd)
+{
+ while (true)
+ {
+ switch (bd->state)
+ {
+ case RTEMS_BDBUF_STATE_CACHED:
+ case RTEMS_BDBUF_STATE_EMPTY:
+ case RTEMS_BDBUF_STATE_MODIFIED:
+ case RTEMS_BDBUF_STATE_ACCESS_CACHED:
+ case RTEMS_BDBUF_STATE_ACCESS_EMPTY:
+ case RTEMS_BDBUF_STATE_ACCESS_MODIFIED:
+ case RTEMS_BDBUF_STATE_ACCESS_PURGED:
+ return;
+ case RTEMS_BDBUF_STATE_SYNC:
+ case RTEMS_BDBUF_STATE_TRANSFER:
+ case RTEMS_BDBUF_STATE_TRANSFER_PURGED:
+ rtems_bdbuf_wait (bd, &bdbuf_cache.transfer_waiters);
+ break;
+ default:
+ rtems_bdbuf_fatal (bd->state, RTEMS_BLKDEV_FATAL_BDBUF_STATE_9);
+ }
+ }
+}
+
+static void
+rtems_bdbuf_wait_for_buffer (void)
+{
+ if (!rtems_chain_is_empty (&bdbuf_cache.modified))
+ rtems_bdbuf_wake_swapper ();
+
+ rtems_bdbuf_anonymous_wait (&bdbuf_cache.buffer_waiters);
+}
+
+static void
+rtems_bdbuf_sync_after_access (rtems_bdbuf_buffer *bd)
+{
+ rtems_bdbuf_set_state (bd, RTEMS_BDBUF_STATE_SYNC);
+
+ rtems_chain_append (&bdbuf_cache.sync, &bd->link);
+
+ if (bd->waiters)
+ rtems_bdbuf_wake (&bdbuf_cache.access_waiters);
+
+ rtems_bdbuf_wake_swapper ();
+ rtems_bdbuf_wait_for_sync_done (bd);
+
+ /*
+ * We may have created a cached or empty buffer which may be recycled.
+ */
+ if (bd->waiters == 0
+ && (bd->state == RTEMS_BDBUF_STATE_CACHED
+ || bd->state == RTEMS_BDBUF_STATE_EMPTY))
+ {
+ if (bd->state == RTEMS_BDBUF_STATE_EMPTY)
+ {
+ rtems_bdbuf_remove_from_tree (bd);
+ rtems_bdbuf_make_free_and_add_to_lru_list (bd);
+ }
+ rtems_bdbuf_wake (&bdbuf_cache.buffer_waiters);
+ }
+}
+
+static rtems_bdbuf_buffer *
+rtems_bdbuf_get_buffer_for_read_ahead (dev_t dev,
+ rtems_blkdev_bnum block,
+ size_t bds_per_group)
+{
+ rtems_bdbuf_buffer *bd = NULL;
+
+ bd = rtems_bdbuf_avl_search (&bdbuf_cache.tree, dev, block);
+
+ if (bd == NULL)
+ {
+ bd = rtems_bdbuf_get_buffer_from_lru_list (dev, block, bds_per_group);
+
+ if (bd != NULL)
+ rtems_bdbuf_group_obtain (bd);
+ }
+ else
+ /*
+ * The buffer is in the cache. So it is already available or in use, and
+ * thus no need for a read ahead.
+ */
+ bd = NULL;
+
+ return bd;
+}
+
+static rtems_bdbuf_buffer *
+rtems_bdbuf_get_buffer_for_access (dev_t dev,
+ rtems_blkdev_bnum block,
+ size_t bds_per_group)
+{
+ rtems_bdbuf_buffer *bd = NULL;
+
+ do
+ {
+ bd = rtems_bdbuf_avl_search (&bdbuf_cache.tree, dev, block);
+
+ if (bd != NULL)
+ {
+ if (bd->group->bds_per_group != bds_per_group)
+ {
+ if (rtems_bdbuf_wait_for_recycle (bd))
+ {
+ rtems_bdbuf_remove_from_tree_and_lru_list (bd);
+ rtems_bdbuf_make_free_and_add_to_lru_list (bd);
+ rtems_bdbuf_wake (&bdbuf_cache.buffer_waiters);
+ }
+ bd = NULL;
+ }
+ }
+ else
+ {
+ bd = rtems_bdbuf_get_buffer_from_lru_list (dev, block, bds_per_group);
+
+ if (bd == NULL)
+ rtems_bdbuf_wait_for_buffer ();
+ }
+ }
+ while (bd == NULL);
+
+ rtems_bdbuf_wait_for_access (bd);
+ rtems_bdbuf_group_obtain (bd);
+
+ return bd;
+}
+
+static rtems_status_code
+rtems_bdbuf_obtain_disk (dev_t dev,
+ rtems_blkdev_bnum block,
+ rtems_disk_device **dd_ptr,
+ rtems_blkdev_bnum *media_block_ptr,
+ size_t *bds_per_group_ptr)
+{
+ rtems_disk_device *dd = NULL;
+
+ if (!bdbuf_cache.initialised)
+ return RTEMS_NOT_CONFIGURED;
+
+ /*
+ * Do not hold the cache lock when obtaining the disk table.
+ */
+ dd = rtems_disk_obtain (dev);
+ if (dd == NULL)
+ return RTEMS_INVALID_ID;
+
+ *dd_ptr = dd;
+
+ if (media_block_ptr != NULL)
+ {
+ /*
+ * Compute the media block number. Drivers work with media block number not
+ * the block number a BD may have as this depends on the block size set by
+ * the user.
+ */
+ rtems_blkdev_bnum mb = rtems_bdbuf_media_block (block,
+ dd->block_size,
+ dd->media_block_size);
+ if (mb >= dd->size)
+ {
+ rtems_disk_release(dd);
+ return RTEMS_INVALID_NUMBER;
+ }
+
+ *media_block_ptr = mb + dd->start;
+ }
+
+ if (bds_per_group_ptr != NULL)
+ {
+ size_t bds_per_group = rtems_bdbuf_bds_per_group (dd->block_size);
+
+ if (bds_per_group == 0)
+ {
+ rtems_disk_release (dd);
+ return RTEMS_INVALID_NUMBER;
+ }
+
+ *bds_per_group_ptr = bds_per_group;
+ }
+
+ return RTEMS_SUCCESSFUL;
+}
+
+static void
+rtems_bdbuf_release_disk (rtems_disk_device *dd)
+{
+ rtems_status_code sc = RTEMS_SUCCESSFUL;
+
+ sc = rtems_disk_release (dd);
+ if (sc != RTEMS_SUCCESSFUL)
+ rtems_fatal_error_occurred (RTEMS_BLKDEV_FATAL_BDBUF_DISK_REL);
+}
+
+rtems_status_code
+rtems_bdbuf_get (dev_t dev,
+ rtems_blkdev_bnum block,
+ rtems_bdbuf_buffer **bd_ptr)
+{
+ rtems_status_code sc = RTEMS_SUCCESSFUL;
+ rtems_disk_device *dd = NULL;
+ rtems_bdbuf_buffer *bd = NULL;
+ rtems_blkdev_bnum media_block = 0;
+ size_t bds_per_group = 0;
+
+ sc = rtems_bdbuf_obtain_disk (dev, block, &dd, &media_block, &bds_per_group);
+ if (sc != RTEMS_SUCCESSFUL)
+ return sc;
+
+ rtems_bdbuf_lock_cache ();
+
+ /*
+ * Print the block index relative to the physical disk.
+ */
+ if (rtems_bdbuf_tracer)
+ printf ("bdbuf:get: %" PRIu32 " (%" PRIu32 ") (dev = %08x)\n",
+ media_block, block, (unsigned) dev);
+
+ bd = rtems_bdbuf_get_buffer_for_access (dev, media_block, bds_per_group);
+
+ switch (bd->state)
+ {
+ case RTEMS_BDBUF_STATE_CACHED:
+ rtems_bdbuf_set_state (bd, RTEMS_BDBUF_STATE_ACCESS_CACHED);
+ break;
+ case RTEMS_BDBUF_STATE_EMPTY:
+ rtems_bdbuf_set_state (bd, RTEMS_BDBUF_STATE_ACCESS_EMPTY);
+ break;
+ case RTEMS_BDBUF_STATE_MODIFIED:
+ /*
+ * To get a modified buffer could be considered a bug in the caller
+ * because you should not be getting an already modified buffer but user
+ * may have modified a byte in a block then decided to seek the start and
+ * write the whole block and the file system will have no record of this
+ * so just gets the block to fill.
+ */
+ rtems_bdbuf_set_state (bd, RTEMS_BDBUF_STATE_ACCESS_MODIFIED);
+ break;
+ default:
+ rtems_bdbuf_fatal (bd->state, RTEMS_BLKDEV_FATAL_BDBUF_STATE_2);
+ break;
+ }
+
+ if (rtems_bdbuf_tracer)
+ {
+ rtems_bdbuf_show_users ("get", bd);
+ rtems_bdbuf_show_usage ();
+ }
+
+ rtems_bdbuf_unlock_cache ();
+
+ rtems_bdbuf_release_disk (dd);
+
+ *bd_ptr = bd;
+
+ return RTEMS_SUCCESSFUL;
+}
+
+/**
+ * Call back handler called by the low level driver when the transfer has
+ * completed. This function may be invoked from interrupt handler.
+ *
+ * @param arg Arbitrary argument specified in block device request
+ * structure (in this case - pointer to the appropriate
+ * block device request structure).
+ * @param status I/O completion status
+ */
+static void
+rtems_bdbuf_transfer_done (void* arg, rtems_status_code status)
+{
+ rtems_blkdev_request* req = (rtems_blkdev_request*) arg;
+
+ req->status = status;
+
+ rtems_event_send (req->io_task, RTEMS_BDBUF_TRANSFER_SYNC);
+}
+
+static void
+rtems_bdbuf_create_read_request (const rtems_disk_device *dd,
+ rtems_blkdev_bnum media_block,
+ size_t bds_per_group,
+ rtems_blkdev_request *req,
+ rtems_bdbuf_buffer **bd_ptr)
+{
+ rtems_bdbuf_buffer *bd = NULL;
+ rtems_blkdev_bnum media_block_end = dd->start + dd->size;
+ rtems_blkdev_bnum media_block_count = dd->block_size / dd->media_block_size;
+ dev_t dev = dd->dev;
+ uint32_t block_size = dd->block_size;
+ uint32_t transfer_index = 1;
+ uint32_t transfer_count = bdbuf_config.max_read_ahead_blocks + 1;
+
+ if (media_block_end - media_block < transfer_count)
+ transfer_count = media_block_end - media_block;
+
+ req->req = RTEMS_BLKDEV_REQ_READ;
+ req->req_done = rtems_bdbuf_transfer_done;
+ req->done_arg = req;
+ req->io_task = rtems_task_self ();
+ req->status = RTEMS_RESOURCE_IN_USE;
+ req->bufnum = 0;
+
+ bd = rtems_bdbuf_get_buffer_for_access (dev, media_block, bds_per_group);
+
+ *bd_ptr = bd;
+
+ req->bufs [0].user = bd;
+ req->bufs [0].block = media_block;
+ req->bufs [0].length = block_size;
+ req->bufs [0].buffer = bd->buffer;
+
+ if (rtems_bdbuf_tracer)
+ rtems_bdbuf_show_users ("read", bd);
+
+ switch (bd->state)
+ {
+ case RTEMS_BDBUF_STATE_CACHED:
+ case RTEMS_BDBUF_STATE_MODIFIED:
+ return;
+ case RTEMS_BDBUF_STATE_EMPTY:
+ rtems_bdbuf_set_state (bd, RTEMS_BDBUF_STATE_TRANSFER);
+ break;
+ default:
+ rtems_bdbuf_fatal (bd->state, RTEMS_BLKDEV_FATAL_BDBUF_STATE_1);
+ break;
+ }
+
+ while (transfer_index < transfer_count)
+ {
+ media_block += media_block_count;
+
+ bd = rtems_bdbuf_get_buffer_for_read_ahead (dev, media_block,
+ bds_per_group);
+
+ if (bd == NULL)
+ break;
+
+ rtems_bdbuf_set_state (bd, RTEMS_BDBUF_STATE_TRANSFER);
+
+ req->bufs [transfer_index].user = bd;
+ req->bufs [transfer_index].block = media_block;
+ req->bufs [transfer_index].length = block_size;
+ req->bufs [transfer_index].buffer = bd->buffer;
+
+ if (rtems_bdbuf_tracer)
+ rtems_bdbuf_show_users ("read-ahead", bd);
+
+ ++transfer_index;
+ }
+
+ req->bufnum = transfer_index;
+}
+
+static rtems_status_code
+rtems_bdbuf_execute_transfer_request (const rtems_disk_device *dd,
+ rtems_blkdev_request *req,
+ bool cache_locked)
+{
+ rtems_status_code sc = RTEMS_SUCCESSFUL;
+ int result = 0;
+ uint32_t transfer_index = 0;
+ bool wake_transfer_waiters = false;
+ bool wake_buffer_waiters = false;
+
+ if (cache_locked)
+ rtems_bdbuf_unlock_cache ();
+
+ result = dd->ioctl (dd->phys_dev, RTEMS_BLKIO_REQUEST, req);
+
+ if (result == 0)
+ {
+ rtems_bdbuf_wait_for_event (RTEMS_BDBUF_TRANSFER_SYNC);
+ sc = req->status;
+ }
+ else
+ sc = RTEMS_IO_ERROR;
+
+ rtems_bdbuf_lock_cache ();
+
+ for (transfer_index = 0; transfer_index < req->bufnum; ++transfer_index)
+ {
+ rtems_bdbuf_buffer *bd = req->bufs [transfer_index].user;
+ bool waiters = bd->waiters;
+
+ if (waiters)
+ wake_transfer_waiters = true;
+ else
+ wake_buffer_waiters = true;
+
+ rtems_bdbuf_group_release (bd);
+
+ if (sc == RTEMS_SUCCESSFUL && bd->state == RTEMS_BDBUF_STATE_TRANSFER)
+ rtems_bdbuf_make_cached_and_add_to_lru_list (bd);
+ else
+ rtems_bdbuf_discard_buffer (bd);
+
+ if (rtems_bdbuf_tracer)
+ rtems_bdbuf_show_users ("transfer", bd);
+ }
+
+ if (wake_transfer_waiters)
+ rtems_bdbuf_wake (&bdbuf_cache.transfer_waiters);
+
+ if (wake_buffer_waiters)
+ rtems_bdbuf_wake (&bdbuf_cache.buffer_waiters);
+
+ if (!cache_locked)
+ rtems_bdbuf_unlock_cache ();
+
+ if (sc == RTEMS_SUCCESSFUL || sc == RTEMS_UNSATISFIED)
+ return sc;
+ else
+ return RTEMS_IO_ERROR;
+}
+
+rtems_status_code
+rtems_bdbuf_read (dev_t dev,
+ rtems_blkdev_bnum block,
+ rtems_bdbuf_buffer **bd_ptr)
+{
+ rtems_status_code sc = RTEMS_SUCCESSFUL;
+ rtems_disk_device *dd = NULL;
+ rtems_blkdev_request *req = NULL;
+ rtems_bdbuf_buffer *bd = NULL;
+ rtems_blkdev_bnum media_block = 0;
+ size_t bds_per_group = 0;
+
+ sc = rtems_bdbuf_obtain_disk (dev, block, &dd, &media_block, &bds_per_group);
+ if (sc != RTEMS_SUCCESSFUL)
+ return sc;
+
+ /*
+ * TODO: This type of request structure is wrong and should be removed.
+ */
+#define bdbuf_alloc(size) __builtin_alloca (size)
+
+ req = bdbuf_alloc (sizeof (rtems_blkdev_request) +
+ sizeof (rtems_blkdev_sg_buffer) *
+ (bdbuf_config.max_read_ahead_blocks + 1));
+
+ if (rtems_bdbuf_tracer)
+ printf ("bdbuf:read: %" PRIu32 " (%" PRIu32 ") (dev = %08x)\n",
+ media_block + dd->start, block, (unsigned) dev);
+
+ rtems_bdbuf_lock_cache ();
+ rtems_bdbuf_create_read_request (dd, media_block, bds_per_group, req, &bd);
+
+ if (req->bufnum > 0)
+ {
+ sc = rtems_bdbuf_execute_transfer_request (dd, req, true);
+ if (sc == RTEMS_SUCCESSFUL)
+ {
+ rtems_chain_extract (&bd->link);
+ rtems_bdbuf_group_obtain (bd);
+ }
+ }
+
+ if (sc == RTEMS_SUCCESSFUL)
+ {
+ switch (bd->state)
+ {
+ case RTEMS_BDBUF_STATE_CACHED:
+ rtems_bdbuf_set_state (bd, RTEMS_BDBUF_STATE_ACCESS_CACHED);
+ break;
+ case RTEMS_BDBUF_STATE_MODIFIED:
+ rtems_bdbuf_set_state (bd, RTEMS_BDBUF_STATE_ACCESS_MODIFIED);
+ break;
+ default:
+ rtems_bdbuf_fatal (bd->state, RTEMS_BLKDEV_FATAL_BDBUF_STATE_4);
+ break;
+ }
+
+ if (rtems_bdbuf_tracer)
+ {
+ rtems_bdbuf_show_users ("read", bd);
+ rtems_bdbuf_show_usage ();
+ }
+
+ *bd_ptr = bd;
+ }
+ else
+ *bd_ptr = NULL;
+
+ rtems_bdbuf_unlock_cache ();
+ rtems_bdbuf_release_disk (dd);
+
+ return sc;
+}
+
+static rtems_status_code
+rtems_bdbuf_check_bd_and_lock_cache (rtems_bdbuf_buffer *bd, const char *kind)
+{
+ if (!bdbuf_cache.initialised)
+ return RTEMS_NOT_CONFIGURED;
+ if (bd == NULL)
+ return RTEMS_INVALID_ADDRESS;
+ if (rtems_bdbuf_tracer)
+ {
+ printf ("bdbuf:%s: %" PRIu32 "\n", kind, bd->block);
+ rtems_bdbuf_show_users (kind, bd);
+ }
+ rtems_bdbuf_lock_cache();
+
+ return RTEMS_SUCCESSFUL;
+}
+
+rtems_status_code
+rtems_bdbuf_release (rtems_bdbuf_buffer *bd)
+{
+ rtems_status_code sc = RTEMS_SUCCESSFUL;
+
+ sc = rtems_bdbuf_check_bd_and_lock_cache (bd, "release");
+ if (sc != RTEMS_SUCCESSFUL)
+ return sc;
+
+ switch (bd->state)
+ {
+ case RTEMS_BDBUF_STATE_ACCESS_CACHED:
+ rtems_bdbuf_add_to_lru_list_after_access (bd);
+ break;
+ case RTEMS_BDBUF_STATE_ACCESS_EMPTY:
+ case RTEMS_BDBUF_STATE_ACCESS_PURGED:
+ rtems_bdbuf_discard_buffer_after_access (bd);
+ break;
+ case RTEMS_BDBUF_STATE_ACCESS_MODIFIED:
+ rtems_bdbuf_add_to_modified_list_after_access (bd);
+ break;
+ default:
+ rtems_bdbuf_fatal (bd->state, RTEMS_BLKDEV_FATAL_BDBUF_STATE_0);
+ break;
+ }
+
+ if (rtems_bdbuf_tracer)
+ rtems_bdbuf_show_usage ();
+
+ rtems_bdbuf_unlock_cache ();
+
+ return RTEMS_SUCCESSFUL;
+}
+
+rtems_status_code
+rtems_bdbuf_release_modified (rtems_bdbuf_buffer *bd)
+{
+ rtems_status_code sc = RTEMS_SUCCESSFUL;
+
+ sc = rtems_bdbuf_check_bd_and_lock_cache (bd, "release modified");
+ if (sc != RTEMS_SUCCESSFUL)
+ return sc;
+
+ switch (bd->state)
+ {
+ case RTEMS_BDBUF_STATE_ACCESS_CACHED:
+ case RTEMS_BDBUF_STATE_ACCESS_EMPTY:
+ case RTEMS_BDBUF_STATE_ACCESS_MODIFIED:
+ rtems_bdbuf_add_to_modified_list_after_access (bd);
+ break;
+ case RTEMS_BDBUF_STATE_ACCESS_PURGED:
+ rtems_bdbuf_discard_buffer_after_access (bd);
+ break;
+ default:
+ rtems_bdbuf_fatal (bd->state, RTEMS_BLKDEV_FATAL_BDBUF_STATE_6);
+ break;
+ }
+
+ if (rtems_bdbuf_tracer)
+ rtems_bdbuf_show_usage ();
+
+ rtems_bdbuf_unlock_cache ();
+
+ return RTEMS_SUCCESSFUL;
+}
+
+rtems_status_code
+rtems_bdbuf_sync (rtems_bdbuf_buffer *bd)
+{
+ rtems_status_code sc = RTEMS_SUCCESSFUL;
+
+ sc = rtems_bdbuf_check_bd_and_lock_cache (bd, "sync");
+ if (sc != RTEMS_SUCCESSFUL)
+ return sc;
+
+ switch (bd->state)
+ {
+ case RTEMS_BDBUF_STATE_ACCESS_CACHED:
+ case RTEMS_BDBUF_STATE_ACCESS_EMPTY:
+ case RTEMS_BDBUF_STATE_ACCESS_MODIFIED:
+ rtems_bdbuf_sync_after_access (bd);
+ break;
+ case RTEMS_BDBUF_STATE_ACCESS_PURGED:
+ rtems_bdbuf_discard_buffer_after_access (bd);
+ break;
+ default:
+ rtems_bdbuf_fatal (bd->state, RTEMS_BLKDEV_FATAL_BDBUF_STATE_5);
+ break;
+ }
+
+ if (rtems_bdbuf_tracer)
+ rtems_bdbuf_show_usage ();
+
+ rtems_bdbuf_unlock_cache ();
+
+ return RTEMS_SUCCESSFUL;
+}
+
+rtems_status_code
+rtems_bdbuf_syncdev (dev_t dev)
+{
+ rtems_status_code sc = RTEMS_SUCCESSFUL;
+ rtems_disk_device *dd = NULL;
+
+ if (rtems_bdbuf_tracer)
+ printf ("bdbuf:syncdev: %08x\n", (unsigned) dev);
+
+ sc = rtems_bdbuf_obtain_disk (dev, 0, &dd, NULL, NULL);
+ if (sc != RTEMS_SUCCESSFUL)
+ return sc;
+
+ /*
+ * Take the sync lock before locking the cache. Once we have the sync lock we
+ * can lock the cache. If another thread has the sync lock it will cause this
+ * thread to block until it owns the sync lock then it can own the cache. The
+ * sync lock can only be obtained with the cache unlocked.
+ */
+ rtems_bdbuf_lock_sync ();
+ rtems_bdbuf_lock_cache ();
+
+ /*
+ * Set the cache to have a sync active for a specific device and let the swap
+ * out task know the id of the requester to wake when done.
+ *
+ * The swap out task will negate the sync active flag when no more buffers
+ * for the device are held on the "modified for sync" queues.
+ */
+ bdbuf_cache.sync_active = true;
+ bdbuf_cache.sync_requester = rtems_task_self ();
+ bdbuf_cache.sync_device = dev;
+
+ rtems_bdbuf_wake_swapper ();
+ rtems_bdbuf_unlock_cache ();
+ rtems_bdbuf_wait_for_event (RTEMS_BDBUF_TRANSFER_SYNC);
+ rtems_bdbuf_unlock_sync ();
+ rtems_bdbuf_release_disk (dd);
+
+ return RTEMS_SUCCESSFUL;
+}
+
+static int
+rtems_bdbuf_null_disk_ioctl (rtems_disk_device *dd, uint32_t req, void *arg)
+{
+ return -1;
+}
+
+/**
+ * Swapout transfer to the driver. The driver will break this I/O into groups
+ * of consecutive write requests is multiple consecutive buffers are required
+ * by the driver. The cache is not locked.
+ *
+ * @param transfer The transfer transaction.
+ */
+static void
+rtems_bdbuf_swapout_write (rtems_bdbuf_swapout_transfer* transfer)
+{
+ rtems_chain_node *node;
+ static rtems_disk_device null_disk = {
+ .phys_dev = &null_disk,
+ .capabilities = 0,
+ .ioctl = rtems_bdbuf_null_disk_ioctl
+ };
+
+ if (rtems_bdbuf_tracer)
+ printf ("bdbuf:swapout transfer: %08x\n", (unsigned) transfer->dev);
+
+ /*
+ * If there are buffers to transfer to the media transfer them.
+ */
+ if (!rtems_chain_is_empty (&transfer->bds))
+ {
+ /*
+ * The last block number used when the driver only supports
+ * continuous blocks in a single request.
+ */
+ uint32_t last_block = 0;
+
+ /*
+ * Number of buffers per bd. This is used to detect the next
+ * block.
+ */
+ uint32_t bufs_per_bd = 0;
+
+ /*
+ * Obtain the disk device. The cache's mutex has been released to avoid a
+ * dead lock.
+ */
+ rtems_disk_device *dd = rtems_disk_obtain (transfer->dev);
+
+ if (dd == NULL)
+ dd = &null_disk;
+
+ bufs_per_bd = dd->block_size / bdbuf_config.buffer_min;
+
+ /*
+ * Take as many buffers as configured and pass to the driver. Note, the
+ * API to the drivers has an array of buffers and if a chain was passed
+ * we could have just passed the list. If the driver API is updated it
+ * should be possible to make this change with little effect in this
+ * code. The array that is passed is broken in design and should be
+ * removed. Merging members of a struct into the first member is
+ * trouble waiting to happen.
+ */
+ transfer->write_req->status = RTEMS_RESOURCE_IN_USE;
+ transfer->write_req->bufnum = 0;
+
+ while ((node = rtems_chain_get(&transfer->bds)) != NULL)
+ {
+ rtems_bdbuf_buffer* bd = (rtems_bdbuf_buffer*) node;
+ bool write = false;
+
+ /*
+ * If the device only accepts sequential buffers and this is not the
+ * first buffer (the first is always sequential, and the buffer is not
+ * sequential then put the buffer back on the transfer chain and write
+ * the committed buffers.
+ */
+
+ if (rtems_bdbuf_tracer)
+ printf ("bdbuf:swapout write: bd:%" PRIu32 ", bufnum:%" PRIu32 " mode:%s\n",
+ bd->block, transfer->write_req->bufnum,
+ dd->phys_dev->capabilities &
+ RTEMS_BLKDEV_CAP_MULTISECTOR_CONT ? "MULIT" : "SCAT");
+
+ if ((dd->phys_dev->capabilities & RTEMS_BLKDEV_CAP_MULTISECTOR_CONT) &&
+ transfer->write_req->bufnum &&
+ (bd->block != (last_block + bufs_per_bd)))
+ {
+ rtems_chain_prepend (&transfer->bds, &bd->link);
+ write = true;
+ }
+ else
+ {
+ rtems_blkdev_sg_buffer* buf;
+ buf = &transfer->write_req->bufs[transfer->write_req->bufnum];
+ transfer->write_req->bufnum++;
+ buf->user = bd;
+ buf->block = bd->block;
+ buf->length = dd->block_size;
+ buf->buffer = bd->buffer;
+ last_block = bd->block;
+ }
+
+ /*
+ * Perform the transfer if there are no more buffers, or the transfer
+ * size has reached the configured max. value.
+ */
+
+ if (rtems_chain_is_empty (&transfer->bds) ||
+ (transfer->write_req->bufnum >= bdbuf_config.max_write_blocks))
+ write = true;
+
+ if (write)
+ {
+ rtems_bdbuf_execute_transfer_request (dd, transfer->write_req, false);
+
+ transfer->write_req->status = RTEMS_RESOURCE_IN_USE;
+ transfer->write_req->bufnum = 0;
+ }
+ }
+
+ if (dd != &null_disk)
+ {
+ /*
+ * If sync'ing and the deivce is capability of handling a sync IO control
+ * call perform the call.
+ */
+ if (transfer->syncing &&
+ (dd->phys_dev->capabilities & RTEMS_BLKDEV_CAP_SYNC))
+ {
+ /* int result = */ dd->ioctl (dd->phys_dev, RTEMS_BLKDEV_REQ_SYNC, NULL);
+ /* How should the error be handled ? */
+ }
+
+ rtems_disk_release (dd);
+ }
+ }
+}
+
+/**
+ * Process the modified list of buffers. There is a sync or modified list that
+ * needs to be handled so we have a common function to do the work.
+ *
+ * @param dev The device to handle. If BDBUF_INVALID_DEV no device is selected
+ * so select the device of the first buffer to be written to disk.
+ * @param chain The modified chain to process.
+ * @param transfer The chain to append buffers to be written too.
+ * @param sync_active If true this is a sync operation so expire all timers.
+ * @param update_timers If true update the timers.
+ * @param timer_delta It update_timers is true update the timers by this
+ * amount.
+ */
+static void
+rtems_bdbuf_swapout_modified_processing (dev_t* dev,
+ rtems_chain_control* chain,
+ rtems_chain_control* transfer,
+ bool sync_active,
+ bool update_timers,
+ uint32_t timer_delta)
+{
+ if (!rtems_chain_is_empty (chain))
+ {
+ rtems_chain_node* node = rtems_chain_head (chain);
+ bool sync_all;
+
+ node = node->next;
+
+ /*
+ * A sync active with no valid dev means sync all.
+ */
+ if (sync_active && (*dev == BDBUF_INVALID_DEV))
+ sync_all = true;
+ else
+ sync_all = false;
+
+ while (!rtems_chain_is_tail (chain, node))
+ {
+ rtems_bdbuf_buffer* bd = (rtems_bdbuf_buffer*) node;
+
+ /*
+ * Check if the buffer's hold timer has reached 0. If a sync is active
+ * or someone waits for a buffer written force all the timers to 0.
+ *
+ * @note Lots of sync requests will skew this timer. It should be based
+ * on TOD to be accurate. Does it matter ?
+ */
+ if (sync_all || (sync_active && (*dev == bd->dev))
+ || rtems_bdbuf_has_buffer_waiters ())
+ bd->hold_timer = 0;
+
+ if (bd->hold_timer)
+ {
+ if (update_timers)
+ {
+ if (bd->hold_timer > timer_delta)
+ bd->hold_timer -= timer_delta;
+ else
+ bd->hold_timer = 0;
+ }
+
+ if (bd->hold_timer)
+ {
+ node = node->next;
+ continue;
+ }
+ }
+
+ /*
+ * This assumes we can set dev_t to BDBUF_INVALID_DEV which is just an
+ * assumption. Cannot use the transfer list being empty the sync dev
+ * calls sets the dev to use.
+ */
+ if (*dev == BDBUF_INVALID_DEV)
+ *dev = bd->dev;
+
+ if (bd->dev == *dev)
+ {
+ rtems_chain_node* next_node = node->next;
+ rtems_chain_node* tnode = rtems_chain_tail (transfer);
+
+ /*
+ * The blocks on the transfer list are sorted in block order. This
+ * means multi-block transfers for drivers that require consecutive
+ * blocks perform better with sorted blocks and for real disks it may
+ * help lower head movement.
+ */
+
+ rtems_bdbuf_set_state (bd, RTEMS_BDBUF_STATE_TRANSFER);
+
+ rtems_chain_extract (node);
+
+ tnode = tnode->previous;
+
+ while (node && !rtems_chain_is_head (transfer, tnode))
+ {
+ rtems_bdbuf_buffer* tbd = (rtems_bdbuf_buffer*) tnode;
+
+ if (bd->block > tbd->block)
+ {
+ rtems_chain_insert (tnode, node);
+ node = NULL;
+ }
+ else
+ tnode = tnode->previous;
+ }
+
+ if (node)
+ rtems_chain_prepend (transfer, node);
+
+ node = next_node;
+ }
+ else
+ {
+ node = node->next;
+ }
+ }
+ }
+}
+
+/**
+ * Process the cache's modified buffers. Check the sync list first then the
+ * modified list extracting the buffers suitable to be written to disk. We have
+ * a device at a time. The task level loop will repeat this operation while
+ * there are buffers to be written. If the transfer fails place the buffers
+ * back on the modified list and try again later. The cache is unlocked while
+ * the buffers are being written to disk.
+ *
+ * @param timer_delta It update_timers is true update the timers by this
+ * amount.
+ * @param update_timers If true update the timers.
+ * @param transfer The transfer transaction data.
+ *
+ * @retval true Buffers where written to disk so scan again.
+ * @retval false No buffers where written to disk.
+ */
+static bool
+rtems_bdbuf_swapout_processing (unsigned long timer_delta,
+ bool update_timers,
+ rtems_bdbuf_swapout_transfer* transfer)
+{
+ rtems_bdbuf_swapout_worker* worker;
+ bool transfered_buffers = false;
+
+ rtems_bdbuf_lock_cache ();
+
+ /*
+ * If a sync is active do not use a worker because the current code does not
+ * cleaning up after. We need to know the buffers have been written when
+ * syncing to release sync lock and currently worker threads do not return to
+ * here. We do not know the worker is the last in a sequence of sync writes
+ * until after we have it running so we do not know to tell it to release the
+ * lock. The simplest solution is to get the main swap out task perform all
+ * sync operations.
+ */
+ if (bdbuf_cache.sync_active)
+ worker = NULL;
+ else
+ {
+ worker = (rtems_bdbuf_swapout_worker*)
+ rtems_chain_get (&bdbuf_cache.swapout_workers);
+ if (worker)
+ transfer = &worker->transfer;
+ }
+
+ rtems_chain_initialize_empty (&transfer->bds);
+ transfer->dev = BDBUF_INVALID_DEV;
+ transfer->syncing = bdbuf_cache.sync_active;
+
+ /*
+ * When the sync is for a device limit the sync to that device. If the sync
+ * is for a buffer handle process the devices in the order on the sync
+ * list. This means the dev is BDBUF_INVALID_DEV.
+ */
+ if (bdbuf_cache.sync_active)
+ transfer->dev = bdbuf_cache.sync_device;
+
+ /*
+ * If we have any buffers in the sync queue move them to the modified
+ * list. The first sync buffer will select the device we use.
+ */
+ rtems_bdbuf_swapout_modified_processing (&transfer->dev,
+ &bdbuf_cache.sync,
+ &transfer->bds,
+ true, false,
+ timer_delta);
+
+ /*
+ * Process the cache's modified list.
+ */
+ rtems_bdbuf_swapout_modified_processing (&transfer->dev,
+ &bdbuf_cache.modified,
+ &transfer->bds,
+ bdbuf_cache.sync_active,
+ update_timers,
+ timer_delta);
+
+ /*
+ * We have all the buffers that have been modified for this device so the
+ * cache can be unlocked because the state of each buffer has been set to
+ * TRANSFER.
+ */
+ rtems_bdbuf_unlock_cache ();
+
+ /*
+ * If there are buffers to transfer to the media transfer them.
+ */
+ if (!rtems_chain_is_empty (&transfer->bds))
+ {
+ if (worker)
+ {
+ rtems_status_code sc = rtems_event_send (worker->id,
+ RTEMS_BDBUF_SWAPOUT_SYNC);
+ if (sc != RTEMS_SUCCESSFUL)
+ rtems_fatal_error_occurred (RTEMS_BLKDEV_FATAL_BDBUF_SO_WAKE);
+ }
+ else
+ {
+ rtems_bdbuf_swapout_write (transfer);
+ }
+
+ transfered_buffers = true;
+ }
+
+ if (bdbuf_cache.sync_active && !transfered_buffers)
+ {
+ rtems_id sync_requester;
+ rtems_bdbuf_lock_cache ();
+ sync_requester = bdbuf_cache.sync_requester;
+ bdbuf_cache.sync_active = false;
+ bdbuf_cache.sync_requester = 0;
+ rtems_bdbuf_unlock_cache ();
+ if (sync_requester)
+ rtems_event_send (sync_requester, RTEMS_BDBUF_TRANSFER_SYNC);
+ }
+
+ return transfered_buffers;
+}
+
+/**
+ * Allocate the write request and initialise it for good measure.
+ *
+ * @return rtems_blkdev_request* The write reference memory.
+ */
+static rtems_blkdev_request*
+rtems_bdbuf_swapout_writereq_alloc (void)
+{
+ /*
+ * @note chrisj The rtems_blkdev_request and the array at the end is a hack.
+ * I am disappointment at finding code like this in RTEMS. The request should
+ * have been a rtems_chain_control. Simple, fast and less storage as the node
+ * is already part of the buffer structure.
+ */
+ rtems_blkdev_request* write_req =
+ malloc (sizeof (rtems_blkdev_request) +
+ (bdbuf_config.max_write_blocks * sizeof (rtems_blkdev_sg_buffer)));
+
+ if (!write_req)
+ rtems_fatal_error_occurred (RTEMS_BLKDEV_FATAL_BDBUF_SO_NOMEM);
+
+ write_req->req = RTEMS_BLKDEV_REQ_WRITE;
+ write_req->req_done = rtems_bdbuf_transfer_done;
+ write_req->done_arg = write_req;
+ write_req->io_task = rtems_task_self ();
+
+ return write_req;
+}
+
+/**
+ * The swapout worker thread body.
+ *
+ * @param arg A pointer to the worker thread's private data.
+ * @return rtems_task Not used.
+ */
+static rtems_task
+rtems_bdbuf_swapout_worker_task (rtems_task_argument arg)
+{
+ rtems_bdbuf_swapout_worker* worker = (rtems_bdbuf_swapout_worker*) arg;
+
+ while (worker->enabled)
+ {
+ rtems_bdbuf_wait_for_event (RTEMS_BDBUF_SWAPOUT_SYNC);
+
+ rtems_bdbuf_swapout_write (&worker->transfer);
+
+ rtems_bdbuf_lock_cache ();
+
+ rtems_chain_initialize_empty (&worker->transfer.bds);
+ worker->transfer.dev = BDBUF_INVALID_DEV;
+
+ rtems_chain_append (&bdbuf_cache.swapout_workers, &worker->link);
+
+ rtems_bdbuf_unlock_cache ();
+ }
+
+ free (worker->transfer.write_req);
+ free (worker);
+
+ rtems_task_delete (RTEMS_SELF);
+}
+
+/**
+ * Open the swapout worker threads.
+ */
+static void
+rtems_bdbuf_swapout_workers_open (void)
+{
+ rtems_status_code sc;
+ size_t w;
+
+ rtems_bdbuf_lock_cache ();
+
+ for (w = 0; w < bdbuf_config.swapout_workers; w++)
+ {
+ rtems_bdbuf_swapout_worker* worker;
+
+ worker = malloc (sizeof (rtems_bdbuf_swapout_worker));
+ if (!worker)
+ rtems_fatal_error_occurred (RTEMS_BLKDEV_FATAL_BDBUF_SO_NOMEM);
+
+ rtems_chain_append (&bdbuf_cache.swapout_workers, &worker->link);
+ worker->enabled = true;
+ worker->transfer.write_req = rtems_bdbuf_swapout_writereq_alloc ();
+
+ rtems_chain_initialize_empty (&worker->transfer.bds);
+ worker->transfer.dev = BDBUF_INVALID_DEV;
+
+ sc = rtems_task_create (rtems_build_name('B', 'D', 'o', 'a' + w),
+ (bdbuf_config.swapout_priority ?
+ bdbuf_config.swapout_priority :
+ RTEMS_BDBUF_SWAPOUT_TASK_PRIORITY_DEFAULT),
+ SWAPOUT_TASK_STACK_SIZE,
+ RTEMS_PREEMPT | RTEMS_NO_TIMESLICE | RTEMS_NO_ASR,
+ RTEMS_LOCAL | RTEMS_NO_FLOATING_POINT,
+ &worker->id);
+ if (sc != RTEMS_SUCCESSFUL)
+ rtems_fatal_error_occurred (RTEMS_BLKDEV_FATAL_BDBUF_SO_WK_CREATE);
+
+ sc = rtems_task_start (worker->id,
+ rtems_bdbuf_swapout_worker_task,
+ (rtems_task_argument) worker);
+ if (sc != RTEMS_SUCCESSFUL)
+ rtems_fatal_error_occurred (RTEMS_BLKDEV_FATAL_BDBUF_SO_WK_START);
+ }
+
+ rtems_bdbuf_unlock_cache ();
+}
+
+/**
+ * Close the swapout worker threads.
+ */
+static void
+rtems_bdbuf_swapout_workers_close (void)
+{
+ rtems_chain_node* node;
+
+ rtems_bdbuf_lock_cache ();
+
+ node = rtems_chain_first (&bdbuf_cache.swapout_workers);
+ while (!rtems_chain_is_tail (&bdbuf_cache.swapout_workers, node))
+ {
+ rtems_bdbuf_swapout_worker* worker = (rtems_bdbuf_swapout_worker*) node;
+ worker->enabled = false;
+ rtems_event_send (worker->id, RTEMS_BDBUF_SWAPOUT_SYNC);
+ node = rtems_chain_next (node);
+ }
+
+ rtems_bdbuf_unlock_cache ();
+}
+
+/**
+ * Body of task which takes care on flushing modified buffers to the disk.
+ *
+ * @param arg A pointer to the global cache data. Use the global variable and
+ * not this.
+ * @return rtems_task Not used.
+ */
+static rtems_task
+rtems_bdbuf_swapout_task (rtems_task_argument arg)
+{
+ rtems_bdbuf_swapout_transfer transfer;
+ uint32_t period_in_ticks;
+ const uint32_t period_in_msecs = bdbuf_config.swapout_period;;
+ uint32_t timer_delta;
+
+ transfer.write_req = rtems_bdbuf_swapout_writereq_alloc ();
+ rtems_chain_initialize_empty (&transfer.bds);
+ transfer.dev = BDBUF_INVALID_DEV;
+ transfer.syncing = false;
+
+ /*
+ * Localise the period.
+ */
+ period_in_ticks = RTEMS_MICROSECONDS_TO_TICKS (period_in_msecs * 1000);
+
+ /*
+ * This is temporary. Needs to be changed to use the real time clock.
+ */
+ timer_delta = period_in_msecs;
+
+ /*
+ * Create the worker threads.
+ */
+ rtems_bdbuf_swapout_workers_open ();
+
+ while (bdbuf_cache.swapout_enabled)
+ {
+ rtems_event_set out;
+ rtems_status_code sc;
+
+ /*
+ * Only update the timers once in the processing cycle.
+ */
+ bool update_timers = true;
+
+ /*
+ * If we write buffers to any disk perform a check again. We only write a
+ * single device at a time and the cache may have more than one device's
+ * buffers modified waiting to be written.
+ */
+ bool transfered_buffers;
+
+ do
+ {
+ transfered_buffers = false;
+
+ /*
+ * Extact all the buffers we find for a specific device. The device is
+ * the first one we find on a modified list. Process the sync queue of
+ * buffers first.
+ */
+ if (rtems_bdbuf_swapout_processing (timer_delta,
+ update_timers,
+ &transfer))
+ {
+ transfered_buffers = true;
+ }
+
+ /*
+ * Only update the timers once.
+ */
+ update_timers = false;
+ }
+ while (transfered_buffers);
+
+ sc = rtems_event_receive (RTEMS_BDBUF_SWAPOUT_SYNC,
+ RTEMS_EVENT_ALL | RTEMS_WAIT,
+ period_in_ticks,
+ &out);
+
+ if ((sc != RTEMS_SUCCESSFUL) && (sc != RTEMS_TIMEOUT))
+ rtems_fatal_error_occurred (BLKDEV_FATAL_BDBUF_SWAPOUT_RE);
+ }
+
+ rtems_bdbuf_swapout_workers_close ();
+
+ free (transfer.write_req);
+
+ rtems_task_delete (RTEMS_SELF);
+}
+
+static void
+rtems_bdbuf_purge_list (rtems_chain_control *purge_list)
+{
+ bool wake_buffer_waiters = false;
+ rtems_chain_node *node = NULL;
+
+ while ((node = rtems_chain_get (purge_list)) != NULL)
+ {
+ rtems_bdbuf_buffer *bd = (rtems_bdbuf_buffer *) node;
+
+ if (bd->waiters == 0)
+ wake_buffer_waiters = true;
+
+ rtems_bdbuf_discard_buffer (bd);
+ }
+
+ if (wake_buffer_waiters)
+ rtems_bdbuf_wake (&bdbuf_cache.buffer_waiters);
+}
+
+typedef bool (*rtems_bdbuf_purge_compare)(dev_t a, dev_t b);
+
+static void
+rtems_bdbuf_gather_for_purge (rtems_chain_control *purge_list,
+ rtems_bdbuf_purge_compare compare,
+ dev_t dev)
+{
+ rtems_bdbuf_buffer *stack [RTEMS_BDBUF_AVL_MAX_HEIGHT];
+ rtems_bdbuf_buffer **prev = stack;
+ rtems_bdbuf_buffer *cur = bdbuf_cache.tree;
+
+ *prev = NULL;
+
+ while (cur != NULL)
+ {
+ if ((*compare) (cur->dev, dev))
+ {
+ switch (cur->state)
+ {
+ case RTEMS_BDBUF_STATE_FREE:
+ case RTEMS_BDBUF_STATE_EMPTY:
+ case RTEMS_BDBUF_STATE_ACCESS_PURGED:
+ case RTEMS_BDBUF_STATE_TRANSFER_PURGED:
+ break;
+ case RTEMS_BDBUF_STATE_SYNC:
+ rtems_bdbuf_wake (&bdbuf_cache.transfer_waiters);
+ /* Fall through */
+ case RTEMS_BDBUF_STATE_MODIFIED:
+ rtems_bdbuf_group_release (cur);
+ /* Fall through */
+ case RTEMS_BDBUF_STATE_CACHED:
+ rtems_chain_extract (&cur->link);
+ rtems_chain_append (purge_list, &cur->link);
+ break;
+ case RTEMS_BDBUF_STATE_TRANSFER:
+ rtems_bdbuf_set_state (cur, RTEMS_BDBUF_STATE_TRANSFER_PURGED);
+ break;
+ case RTEMS_BDBUF_STATE_ACCESS_CACHED:
+ case RTEMS_BDBUF_STATE_ACCESS_EMPTY:
+ case RTEMS_BDBUF_STATE_ACCESS_MODIFIED:
+ rtems_bdbuf_set_state (cur, RTEMS_BDBUF_STATE_ACCESS_PURGED);
+ break;
+ default:
+ rtems_fatal_error_occurred (RTEMS_BLKDEV_FATAL_BDBUF_STATE_11);
+ }
+ }
+
+ if (cur->avl.left != NULL)
+ {
+ /* Left */
+ ++prev;
+ *prev = cur;
+ cur = cur->avl.left;
+ }
+ else if (cur->avl.right != NULL)
+ {
+ /* Right */
+ ++prev;
+ *prev = cur;
+ cur = cur->avl.right;
+ }
+ else
+ {
+ while (*prev != NULL && cur == (*prev)->avl.right)
+ {
+ /* Up */
+ cur = *prev;
+ --prev;
+ }
+ if (*prev != NULL)
+ /* Right */
+ cur = (*prev)->avl.right;
+ else
+ /* Finished */
+ cur = NULL;
+ }
+ }
+}
+
+static void
+rtems_bdbuf_purge (rtems_bdbuf_purge_compare compare, dev_t dev)
+{
+ rtems_chain_control purge_list;
+
+ rtems_chain_initialize_empty (&purge_list);
+ rtems_bdbuf_lock_cache ();
+ rtems_bdbuf_gather_for_purge (&purge_list, compare, dev);
+ rtems_bdbuf_purge_list (&purge_list);
+ rtems_bdbuf_unlock_cache ();
+}
+
+static bool
+rtems_bdbuf_purge_compare_dev (dev_t a, dev_t b)
+{
+ return a == b;
+}
+
+void
+rtems_bdbuf_purge_dev (dev_t dev)
+{
+ rtems_bdbuf_purge (rtems_bdbuf_purge_compare_dev, dev);
+}
+
+static bool
+rtems_bdbuf_purge_compare_major (dev_t a, dev_t b)
+{
+ return rtems_filesystem_dev_major_t (a) == rtems_filesystem_dev_major_t (b);
+}
+
+void
+rtems_bdbuf_purge_major (rtems_device_major_number major)
+{
+ dev_t dev = rtems_filesystem_make_dev_t (major, 0);
+
+ rtems_bdbuf_purge (rtems_bdbuf_purge_compare_major, dev);
+}
diff --git a/cpukit/libblock/src/bdpart-create.c b/cpukit/libblock/src/bdpart-create.c
new file mode 100644
index 0000000000..dc93522c19
--- /dev/null
+++ b/cpukit/libblock/src/bdpart-create.c
@@ -0,0 +1,160 @@
+/**
+ * @file
+ *
+ * @ingroup rtems_bdpart
+ *
+ * Block device partition management.
+ */
+
+/*
+ * Copyright (c) 2009, 2010
+ * embedded brains GmbH
+ * Obere Lagerstr. 30
+ * D-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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <rtems.h>
+#include <rtems/bdpart.h>
+
+rtems_status_code rtems_bdpart_create(
+ const char *disk_name,
+ const rtems_bdpart_format *format,
+ rtems_bdpart_partition *pt,
+ const unsigned *dist,
+ size_t count
+)
+{
+ rtems_status_code sc = RTEMS_SUCCESSFUL;
+ bool dos_compatibility = format != NULL
+ && format->type == RTEMS_BDPART_FORMAT_MBR
+ && format->mbr.dos_compatibility;
+ rtems_blkdev_bnum disk_end = 0;
+ rtems_blkdev_bnum pos = 0;
+ rtems_blkdev_bnum dist_sum = 0;
+ rtems_blkdev_bnum record_space =
+ dos_compatibility ? RTEMS_BDPART_MBR_CYLINDER_SIZE : 1;
+ rtems_blkdev_bnum overhead = 0;
+ rtems_blkdev_bnum free_space = 0;
+ dev_t disk = 0;
+ size_t i = 0;
+
+ /* Check if we have something to do */
+ if (count == 0) {
+ /* Nothing to do */
+ return RTEMS_SUCCESSFUL;
+ }
+
+ /* Check parameter */
+ if (format == NULL || pt == NULL || dist == NULL) {
+ return RTEMS_INVALID_ADDRESS;
+ }
+
+ /* Get disk data */
+ sc = rtems_bdpart_get_disk_data( disk_name, &disk, &disk_end);
+ if (sc != RTEMS_SUCCESSFUL) {
+ return sc;
+ }
+
+ /* Get distribution sum and check for overflow */
+ for (i = 0; i < count; ++i) {
+ unsigned prev_sum = dist_sum;
+
+ dist_sum += dist [i];
+
+ if (dist_sum < prev_sum) {
+ return RTEMS_INVALID_NUMBER;
+ }
+
+ if (dist [i] == 0) {
+ return RTEMS_INVALID_NUMBER;
+ }
+ }
+
+ /* Check format */
+ if (format->type != RTEMS_BDPART_FORMAT_MBR) {
+ return RTEMS_NOT_IMPLEMENTED;
+ }
+
+ /* Align end of disk on cylinder boundary if necessary */
+ if (dos_compatibility) {
+ disk_end -= (disk_end % record_space);
+ }
+
+ /*
+ * We need at least space for the MBR and the compatibility space for the
+ * first primary partition.
+ */
+ overhead += record_space;
+
+ /*
+ * In case we need an extended partition and logical partitions we have to
+ * account for the space of each EBR.
+ */
+ if (count > 4) {
+ overhead += (count - 3) * record_space;
+ }
+
+ /*
+ * Account space to align every partition on cylinder boundaries if
+ * necessary.
+ */
+ if (dos_compatibility) {
+ overhead += (count - 1) * record_space;
+ }
+
+ /* Check disk space */
+ if ((overhead + count) > disk_end) {
+ return RTEMS_IO_ERROR;
+ }
+
+ /* Begin of first primary partition */
+ pos = record_space;
+
+ /* Space for partitions */
+ free_space = disk_end - overhead;
+
+ for (i = 0; i < count; ++i) {
+ rtems_bdpart_partition *p = pt + i;
+
+ /* Partition size */
+ rtems_blkdev_bnum s = free_space * dist [i];
+ if (s < free_space || s < dist [i]) {
+ /* TODO: Calculate without overflow */
+ return RTEMS_INVALID_NUMBER;
+ }
+ s /= dist_sum;
+
+ /* Ensure that the partition is not empty */
+ if (s == 0) {
+ s = 1;
+ }
+
+ /* Align partition upwards */
+ s += record_space - (s % record_space);
+
+ /* Partition begin and end */
+ p->begin = pos;
+ pos += s;
+ p->end = pos;
+
+ /* Reserve space for the EBR if necessary */
+ if (count > 4 && i > 2) {
+ p->begin += record_space;
+ }
+ }
+
+ /* Expand the last partition to the disk end */
+ pt [count - 1].end = disk_end;
+
+ return RTEMS_SUCCESSFUL;
+}
diff --git a/cpukit/libblock/src/bdpart-dump.c b/cpukit/libblock/src/bdpart-dump.c
new file mode 100644
index 0000000000..abd85b6037
--- /dev/null
+++ b/cpukit/libblock/src/bdpart-dump.c
@@ -0,0 +1,97 @@
+/**
+ * @file
+ *
+ * @ingroup rtems_bdpart
+ *
+ * Block device partition management.
+ */
+
+/*
+ * Copyright (c) 2009, 2010
+ * embedded brains GmbH
+ * Obere Lagerstr. 30
+ * D-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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <inttypes.h>
+
+#include <rtems.h>
+#include <rtems/bdpart.h>
+
+static void rtems_bdpart_type_to_string(
+ const uuid_t type,
+ char str [37]
+)
+{
+ uuid_unparse_lower( type, str);
+}
+
+void rtems_bdpart_dump( const rtems_bdpart_partition *pt, size_t count)
+{
+ size_t i = 0;
+
+ printf(
+ "-------------------------------------------------------------------------------\n"
+ " PARTITION TABLE\n"
+ "------------+------------+-----------------------------------------------------\n"
+ " BEGIN | END | TYPE\n"
+ "------------+------------+-----------------------------------------------------\n"
+ );
+
+ for (i = 0; i < count; ++i) {
+ const rtems_bdpart_partition *p = pt + i;
+ const char *type = NULL;
+ char type_buffer [52];
+ uint8_t type_mbr = 0;
+
+ if (rtems_bdpart_to_mbr_partition_type( p->type, &type_mbr)) {
+ switch (type_mbr) {
+ case RTEMS_BDPART_MBR_FAT_12:
+ type = "FAT 12";
+ break;
+ case RTEMS_BDPART_MBR_FAT_16:
+ type = "FAT 16";
+ break;
+ case RTEMS_BDPART_MBR_FAT_16_LBA:
+ type = "FAT 16 LBA";
+ break;
+ case RTEMS_BDPART_MBR_FAT_32:
+ type = "FAT 32";
+ break;
+ case RTEMS_BDPART_MBR_FAT_32_LBA:
+ type = "FAT 32 LBA";
+ break;
+ case RTEMS_BDPART_MBR_DATA:
+ type = "DATA";
+ break;
+ default:
+ snprintf( type_buffer, sizeof( type_buffer), "0x%02" PRIx8, type_mbr);
+ type = type_buffer;
+ break;
+ }
+ } else {
+ rtems_bdpart_type_to_string( p->type, type_buffer);
+ type = type_buffer;
+ }
+
+ printf(
+ " %10" PRIu32 " | %10" PRIu32 " |%52s\n",
+ p->begin,
+ p->end,
+ type
+ );
+ }
+
+ puts( "------------+------------+-----------------------------------------------------");
+}
diff --git a/cpukit/libblock/src/bdpart-mount.c b/cpukit/libblock/src/bdpart-mount.c
new file mode 100644
index 0000000000..aef15dfbaa
--- /dev/null
+++ b/cpukit/libblock/src/bdpart-mount.c
@@ -0,0 +1,184 @@
+/**
+ * @file
+ *
+ * @ingroup rtems_bdpart
+ *
+ * Block device partition management.
+ */
+
+/*
+ * Copyright (c) 2009, 2010
+ * embedded brains GmbH
+ * Obere Lagerstr. 30
+ * D-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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <rtems.h>
+#include <rtems/bdpart.h>
+#include <rtems/libio.h>
+
+rtems_status_code rtems_bdpart_mount(
+ const char *disk_name,
+ const rtems_bdpart_partition *pt __attribute__((unused)),
+ size_t count,
+ const char *mount_base
+)
+{
+ rtems_status_code esc = RTEMS_SUCCESSFUL;
+ const char *disk_file_name = strrchr( disk_name, '/');
+ char *logical_disk_name = NULL;
+ char *logical_disk_marker = NULL;
+ char *mount_point = NULL;
+ char *mount_marker = NULL;
+ size_t disk_file_name_size = 0;
+ size_t disk_name_size = strlen( disk_name);
+ size_t mount_base_size = strlen( mount_base);
+ size_t i = 0;
+
+ /* Create logical disk name base */
+ logical_disk_name = malloc( disk_name_size + RTEMS_BDPART_NUMBER_SIZE);
+ if (logical_disk_name == NULL) {
+ return RTEMS_NO_MEMORY;
+ }
+ strncpy( logical_disk_name, disk_name, disk_name_size);
+
+ /* Get disk file name */
+ if (disk_file_name != NULL) {
+ disk_file_name += 1;
+ disk_file_name_size = strlen( disk_file_name);
+ } else {
+ disk_file_name = disk_name;
+ disk_file_name_size = disk_name_size;
+ }
+
+ /* Create mount point base */
+ mount_point = malloc( mount_base_size + 1 + disk_file_name_size + RTEMS_BDPART_NUMBER_SIZE);
+ if (mount_point == NULL) {
+ esc = RTEMS_NO_MEMORY;
+ goto cleanup;
+ }
+ strncpy( mount_point, mount_base, mount_base_size);
+ mount_point [mount_base_size] = '/';
+ strncpy( mount_point + mount_base_size + 1, disk_file_name, disk_file_name_size);
+
+ /* Markers */
+ logical_disk_marker = logical_disk_name + disk_name_size;
+ mount_marker = mount_point + mount_base_size + 1 + disk_file_name_size;
+
+ /* Mount supported file systems for each partition */
+ for (i = 0; i < count; ++i) {
+ /* Create logical disk name */
+ int rv = snprintf( logical_disk_marker, RTEMS_BDPART_NUMBER_SIZE, "%zu", i + 1);
+ if (rv >= RTEMS_BDPART_NUMBER_SIZE) {
+ esc = RTEMS_INVALID_NAME;
+ goto cleanup;
+ }
+
+ /* Create mount point */
+ strncpy( mount_marker, logical_disk_marker, RTEMS_BDPART_NUMBER_SIZE);
+ rv = rtems_mkdir( mount_point, S_IRWXU | S_IRWXG | S_IRWXO);
+ if (rv != 0) {
+ esc = RTEMS_IO_ERROR;
+ goto cleanup;
+ }
+
+ /* Mount */
+ rv = mount(
+ logical_disk_name,
+ mount_point,
+ "msdos",
+ 0,
+ NULL
+ );
+ if (rv != 0) {
+ rmdir( mount_point);
+ }
+ }
+
+cleanup:
+
+ free( logical_disk_name);
+ free( mount_point);
+
+ return esc;
+}
+
+rtems_status_code rtems_bdpart_unmount(
+ const char *disk_name,
+ const rtems_bdpart_partition *pt __attribute__((unused)),
+ size_t count,
+ const char *mount_base
+)
+{
+ rtems_status_code esc = RTEMS_SUCCESSFUL;
+ const char *disk_file_name = strrchr( disk_name, '/');
+ char *mount_point = NULL;
+ char *mount_marker = NULL;
+ size_t disk_file_name_size = 0;
+ size_t disk_name_size = strlen( disk_name);
+ size_t mount_base_size = strlen( mount_base);
+ size_t i = 0;
+
+ /* Get disk file name */
+ if (disk_file_name != NULL) {
+ disk_file_name += 1;
+ disk_file_name_size = strlen( disk_file_name);
+ } else {
+ disk_file_name = disk_name;
+ disk_file_name_size = disk_name_size;
+ }
+
+ /* Create mount point base */
+ mount_point = malloc( mount_base_size + 1 + disk_file_name_size + RTEMS_BDPART_NUMBER_SIZE);
+ if (mount_point == NULL) {
+ esc = RTEMS_NO_MEMORY;
+ goto cleanup;
+ }
+ strncpy( mount_point, mount_base, mount_base_size);
+ mount_point [mount_base_size] = '/';
+ strncpy( mount_point + mount_base_size + 1, disk_file_name, disk_file_name_size);
+
+ /* Marker */
+ mount_marker = mount_point + mount_base_size + 1 + disk_file_name_size;
+
+ /* Mount supported file systems for each partition */
+ for (i = 0; i < count; ++i) {
+ /* Create mount point */
+ int rv = snprintf( mount_marker, RTEMS_BDPART_NUMBER_SIZE, "%zu", i + 1);
+ if (rv >= RTEMS_BDPART_NUMBER_SIZE) {
+ esc = RTEMS_INVALID_NAME;
+ goto cleanup;
+ }
+
+ /* Unmount */
+ rv = unmount( mount_point);
+ if (rv == 0) {
+ /* Remove mount point */
+ rv = rmdir( mount_point);
+ if (rv != 0) {
+ esc = RTEMS_IO_ERROR;
+ goto cleanup;
+ }
+ }
+ }
+
+cleanup:
+
+ free( mount_point);
+
+ return esc;
+}
diff --git a/cpukit/libblock/src/bdpart-read.c b/cpukit/libblock/src/bdpart-read.c
new file mode 100644
index 0000000000..3560dbd3a3
--- /dev/null
+++ b/cpukit/libblock/src/bdpart-read.c
@@ -0,0 +1,348 @@
+/**
+ * @file
+ *
+ * @ingroup rtems_bdpart
+ *
+ * Block device partition management.
+ */
+
+/*
+ * Copyright (c) 2009, 2010
+ * embedded brains GmbH
+ * Obere Lagerstr. 30
+ * D-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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <rtems.h>
+#include <rtems/bdbuf.h>
+#include <rtems/bdpart.h>
+#include <rtems/endian.h>
+
+#define RTEMS_BDPART_MBR_PARTITION_TYPE( type) \
+ { \
+ (type), 0xa2U, 0x2eU, 0x38U, \
+ 0x38U, 0xb5U, 0xdeU, 0x11U, \
+ 0xbcU, 0x13U, 0x00U, 0x1dU, \
+ 0x09U, 0xb0U, 0x5fU, 0xa4U \
+ }
+
+static const uuid_t RTEMS_BDPART_MBR_MASTER_TYPE =
+ RTEMS_BDPART_MBR_PARTITION_TYPE( RTEMS_BDPART_MBR_EMPTY);
+
+void rtems_bdpart_to_partition_type( uint8_t mbr_type, uuid_t type)
+{
+ type [0] = mbr_type;
+ memcpy( type + 1, RTEMS_BDPART_MBR_MASTER_TYPE + 1, sizeof( uuid_t) - 1);
+}
+
+bool rtems_bdpart_to_mbr_partition_type(
+ const uuid_t type,
+ uint8_t *mbr_type
+)
+{
+ *mbr_type = rtems_bdpart_mbr_partition_type( type);
+
+ return memcmp(
+ type + 1,
+ RTEMS_BDPART_MBR_MASTER_TYPE + 1,
+ sizeof( uuid_t) - 1
+ ) == 0;
+}
+
+/*
+ * FIXME: This code should the deviceio interface and not the bdbug interface.
+ */
+rtems_status_code rtems_bdpart_get_disk_data(
+ const char *disk_name,
+ dev_t *disk,
+ rtems_blkdev_bnum *disk_end
+)
+{
+ rtems_status_code sc = RTEMS_SUCCESSFUL;
+ int rv = 0;
+ rtems_blkdev_bnum disk_begin = 0;
+ rtems_blkdev_bnum block_size = 0;
+ rtems_disk_device *dd = NULL;
+ struct stat st;
+
+ /* Get disk handle */
+ rv = stat( disk_name, &st);
+ if (rv != 0) {
+ return RTEMS_INVALID_NAME;
+ }
+ *disk = st.st_rdev;
+
+ /* Get disk begin, end and block size */
+ dd = rtems_disk_obtain( *disk);
+ if (dd == NULL) {
+ return RTEMS_INVALID_NAME;
+ }
+ disk_begin = dd->start;
+ *disk_end = dd->size;
+ block_size = dd->block_size;
+ sc = rtems_disk_release( dd);
+ if (sc != RTEMS_SUCCESSFUL) {
+ return sc;
+ }
+
+ /* Check block size */
+ if (block_size < RTEMS_BDPART_BLOCK_SIZE) {
+ return RTEMS_IO_ERROR;
+ }
+
+ /* Check that we have do not have a logical disk */
+ if (disk_begin != 0) {
+ return RTEMS_IO_ERROR;
+ }
+
+ return RTEMS_SUCCESSFUL;
+}
+
+static bool rtems_bdpart_is_valid_record( const uint8_t *data)
+{
+ return data [RTEMS_BDPART_MBR_OFFSET_SIGNATURE_0]
+ == RTEMS_BDPART_MBR_SIGNATURE_0
+ && data [RTEMS_BDPART_MBR_OFFSET_SIGNATURE_1]
+ == RTEMS_BDPART_MBR_SIGNATURE_1;
+}
+
+static rtems_blkdev_bnum rtems_bdpart_next_ebr( const uint8_t *data)
+{
+ rtems_blkdev_bnum begin =
+ rtems_uint32_from_little_endian( data + RTEMS_BDPART_MBR_OFFSET_BEGIN);
+ uint8_t type = data [RTEMS_BDPART_MBR_OFFSET_TYPE];
+
+ if (type == RTEMS_BDPART_MBR_EXTENDED) {
+ return begin;
+ } else {
+ return 0;
+ }
+}
+
+static rtems_status_code rtems_bdpart_read_mbr_partition(
+ const uint8_t *data,
+ rtems_bdpart_partition **p,
+ const rtems_bdpart_partition *p_end,
+ rtems_blkdev_bnum *ep_begin
+)
+{
+ rtems_blkdev_bnum begin =
+ rtems_uint32_from_little_endian( data + RTEMS_BDPART_MBR_OFFSET_BEGIN);
+ rtems_blkdev_bnum size =
+ rtems_uint32_from_little_endian( data + RTEMS_BDPART_MBR_OFFSET_SIZE);
+ rtems_blkdev_bnum end = begin + size;
+ uint8_t type = data [RTEMS_BDPART_MBR_OFFSET_TYPE];
+
+ if (type == RTEMS_BDPART_MBR_EMPTY) {
+ return RTEMS_SUCCESSFUL;
+ } else if (*p == p_end) {
+ return RTEMS_TOO_MANY;
+ } else if (begin >= end) {
+ return RTEMS_IO_ERROR;
+ } else if (type == RTEMS_BDPART_MBR_EXTENDED) {
+ if (ep_begin != NULL) {
+ *ep_begin = begin;
+ }
+ } else {
+ /* Increment partition index */
+ ++(*p);
+
+ /* Clear partition */
+ memset( *p, 0, sizeof( rtems_bdpart_partition));
+
+ /* Set values */
+ (*p)->begin = begin;
+ (*p)->end = end;
+ rtems_bdpart_to_partition_type( type, (*p)->type);
+ (*p)->flags = data [RTEMS_BDPART_MBR_OFFSET_FLAGS];
+ }
+
+ return RTEMS_SUCCESSFUL;
+}
+
+static rtems_status_code rtems_bdpart_read_record(
+ dev_t disk,
+ rtems_blkdev_bnum index,
+ rtems_bdbuf_buffer **block
+)
+{
+ rtems_status_code sc = RTEMS_SUCCESSFUL;
+
+ /* Release previous block if necessary */
+ if (*block != NULL) {
+ sc = rtems_bdbuf_release( *block);
+ if (sc != RTEMS_SUCCESSFUL) {
+ return sc;
+ }
+ }
+
+ /* Read the record block */
+ sc = rtems_bdbuf_read( disk, index, block);
+ if (sc != RTEMS_SUCCESSFUL) {
+ return sc;
+ }
+
+ /* just in case block did not get filled in */
+ if ( *block == NULL ) {
+ return RTEMS_INVALID_ADDRESS;
+ }
+
+ /* Check MBR signature */
+ if (!rtems_bdpart_is_valid_record( (*block)->buffer)) {
+ return RTEMS_IO_ERROR;
+ }
+
+ return RTEMS_SUCCESSFUL;
+}
+
+rtems_status_code rtems_bdpart_read(
+ const char *disk_name,
+ rtems_bdpart_format *format,
+ rtems_bdpart_partition *pt,
+ size_t *count
+)
+{
+ rtems_status_code sc = RTEMS_SUCCESSFUL;
+ rtems_status_code esc = RTEMS_SUCCESSFUL;
+ rtems_bdbuf_buffer *block = NULL;
+ rtems_bdpart_partition *p = pt - 1;
+ const rtems_bdpart_partition *p_end = pt + (count != NULL ? *count : 0);
+ rtems_blkdev_bnum ep_begin = 0; /* Extended partition begin */
+ rtems_blkdev_bnum ebr = 0; /* Extended boot record block index */
+ rtems_blkdev_bnum disk_end = 0;
+ dev_t disk = 0;
+ size_t i = 0;
+ const uint8_t *data = NULL;
+
+ /* Check parameter */
+ if (format == NULL || pt == NULL || count == NULL) {
+ return RTEMS_INVALID_ADDRESS;
+ }
+
+ /* Set count to a save value */
+ *count = 0;
+
+ /* Get disk data */
+ sc = rtems_bdpart_get_disk_data( disk_name, &disk, &disk_end);
+ if (sc != RTEMS_SUCCESSFUL) {
+ return sc;
+ }
+
+ /* Read MBR */
+ sc = rtems_bdpart_read_record( disk, 0, &block);
+ if (sc != RTEMS_SUCCESSFUL) {
+ esc = sc;
+ goto cleanup;
+ }
+
+ /* Read the first partition entry */
+ data = block->buffer + RTEMS_BDPART_MBR_OFFSET_TABLE_0;
+ sc = rtems_bdpart_read_mbr_partition( data, &p, p_end, &ep_begin);
+ if (sc != RTEMS_SUCCESSFUL) {
+ esc = sc;
+ goto cleanup;
+ }
+
+ /* Determine if we have a MBR or GPT format */
+ if (rtems_bdpart_mbr_partition_type( p->type) == RTEMS_BDPART_MBR_GPT) {
+ esc = RTEMS_NOT_IMPLEMENTED;
+ goto cleanup;
+ }
+
+ /* Set format */
+ format->type = RTEMS_BDPART_FORMAT_MBR;
+ format->mbr.disk_id = rtems_uint32_from_little_endian(
+ block->buffer + RTEMS_BDPART_MBR_OFFSET_DISK_ID
+ );
+ format->mbr.dos_compatibility = true;
+
+ /* Iterate through the rest of the primary partition table */
+ for (i = 1; i < 4; ++i) {
+ data += RTEMS_BDPART_MBR_TABLE_ENTRY_SIZE;
+
+ sc = rtems_bdpart_read_mbr_partition( data, &p, p_end, &ep_begin);
+ if (sc != RTEMS_SUCCESSFUL) {
+ esc = sc;
+ goto cleanup;
+ }
+ }
+
+ /* Iterate through the logical partitions within the extended partition */
+ ebr = ep_begin;
+ while (ebr != 0) {
+ rtems_blkdev_bnum tmp = 0;
+
+ /* Read EBR */
+ sc = rtems_bdpart_read_record( disk, ebr, &block);
+ if (sc != RTEMS_SUCCESSFUL) {
+ esc = sc;
+ goto cleanup;
+ }
+
+ /* Read first partition entry */
+ sc = rtems_bdpart_read_mbr_partition(
+ block->buffer + RTEMS_BDPART_MBR_OFFSET_TABLE_0,
+ &p,
+ p_end,
+ NULL
+ );
+ if (sc != RTEMS_SUCCESSFUL) {
+ esc = sc;
+ goto cleanup;
+ }
+
+ /* Adjust partition begin */
+ tmp = p->begin + ebr;
+ if (tmp > p->begin) {
+ p->begin = tmp;
+ } else {
+ esc = RTEMS_IO_ERROR;
+ goto cleanup;
+ }
+
+ /* Adjust partition end */
+ tmp = p->end + ebr;
+ if (tmp > p->end) {
+ p->end = tmp;
+ } else {
+ esc = RTEMS_IO_ERROR;
+ goto cleanup;
+ }
+
+ /* Read second partition entry for next EBR block */
+ ebr = rtems_bdpart_next_ebr(
+ block->buffer + RTEMS_BDPART_MBR_OFFSET_TABLE_1
+ );
+ if (ebr != 0) {
+ /* Adjust partition EBR block index */
+ tmp = ebr + ep_begin;
+ if (tmp > ebr) {
+ ebr = tmp;
+ } else {
+ esc = RTEMS_IO_ERROR;
+ goto cleanup;
+ }
+ }
+ }
+
+ /* Return partition count */
+ *count = (size_t) (p - pt + 1);
+
+cleanup:
+
+ if (block != NULL) {
+ rtems_bdbuf_release( block);
+ }
+
+ return esc;
+}
diff --git a/cpukit/libblock/src/bdpart-register.c b/cpukit/libblock/src/bdpart-register.c
new file mode 100644
index 0000000000..172c8b4964
--- /dev/null
+++ b/cpukit/libblock/src/bdpart-register.c
@@ -0,0 +1,163 @@
+/**
+ * @file
+ *
+ * @ingroup rtems_bdpart
+ *
+ * Block device partition management.
+ */
+
+/*
+ * Copyright (c) 2009, 2010
+ * embedded brains GmbH
+ * Obere Lagerstr. 30
+ * D-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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <rtems.h>
+#include <rtems/bdpart.h>
+
+rtems_status_code rtems_bdpart_register(
+ const char *disk_name,
+ const rtems_bdpart_partition *pt,
+ size_t count
+)
+{
+ rtems_status_code sc = RTEMS_SUCCESSFUL;
+ rtems_status_code esc = RTEMS_SUCCESSFUL;
+ rtems_device_major_number major = 0;
+ rtems_device_minor_number minor = 0;
+ rtems_blkdev_bnum disk_end = 0;
+ dev_t disk = 0;
+ dev_t logical_disk = 0;
+ char *logical_disk_name = NULL;
+ char *logical_disk_marker = NULL;
+ size_t disk_name_size = strlen( disk_name);
+ size_t i = 0;
+
+ /* Get disk data */
+ sc = rtems_bdpart_get_disk_data( disk_name, &disk, &disk_end);
+ if (sc != RTEMS_SUCCESSFUL) {
+ return sc;
+ }
+
+ /* Get the disk device identifier */
+ rtems_filesystem_split_dev_t( disk, major, minor);
+
+ /* Create logical disk name */
+ logical_disk_name = malloc( disk_name_size + RTEMS_BDPART_NUMBER_SIZE);
+ if (logical_disk_name == NULL) {
+ return RTEMS_NO_MEMORY;
+ }
+ strncpy( logical_disk_name, disk_name, disk_name_size);
+ logical_disk_marker = logical_disk_name + disk_name_size;
+
+ /* Create a logical disk for each partition */
+ for (i = 0; i < count; ++i) {
+ const rtems_bdpart_partition *p = pt + i;
+ int rv = 0;
+
+ /* New minor number */
+ ++minor;
+
+ /* Create a new device identifier */
+ logical_disk = rtems_filesystem_make_dev_t( major, minor);
+
+ /* Set partition number for logical disk name */
+ rv = snprintf( logical_disk_marker, RTEMS_BDPART_NUMBER_SIZE, "%zu", i + 1);
+ if (rv >= RTEMS_BDPART_NUMBER_SIZE) {
+ esc = RTEMS_INVALID_NAME;
+ goto cleanup;
+ }
+
+ /* Create logical disk */
+ sc = rtems_disk_create_log(
+ logical_disk,
+ disk,
+ p->begin,
+ p->end - p->begin,
+ logical_disk_name
+ );
+ if (sc != RTEMS_SUCCESSFUL) {
+ esc = sc;
+ goto cleanup;
+ }
+ }
+
+cleanup:
+
+ free( logical_disk_name);
+
+ return esc;
+}
+
+rtems_status_code rtems_bdpart_register_from_disk( const char *disk_name)
+{
+ rtems_status_code sc = RTEMS_SUCCESSFUL;
+ rtems_bdpart_format format;
+ rtems_bdpart_partition pt [RTEMS_BDPART_PARTITION_NUMBER_HINT];
+ size_t count = RTEMS_BDPART_PARTITION_NUMBER_HINT;
+
+ /* Read partitions */
+ sc = rtems_bdpart_read( disk_name, &format, pt, &count);
+ if (sc != RTEMS_SUCCESSFUL) {
+ return sc;
+ }
+
+ /* Register partitions */
+ return rtems_bdpart_register( disk_name, pt, count);
+}
+
+rtems_status_code rtems_bdpart_unregister(
+ const char *disk_name,
+ const rtems_bdpart_partition *pt __attribute__((unused)),
+ size_t count
+)
+{
+ rtems_status_code sc = RTEMS_SUCCESSFUL;
+ rtems_device_major_number major = 0;
+ rtems_device_minor_number minor = 0;
+ rtems_blkdev_bnum disk_end = 0;
+ dev_t disk = 0;
+ dev_t logical_disk = 0;
+ size_t i = 0;
+
+ /* Get disk data */
+ sc = rtems_bdpart_get_disk_data( disk_name, &disk, &disk_end);
+ if (sc != RTEMS_SUCCESSFUL) {
+ return sc;
+ }
+
+ /* Get the disk device identifier */
+ rtems_filesystem_split_dev_t( disk, major, minor);
+
+ /* Create a logical disk for each partition */
+ for (i = 0; i < count; ++i) {
+ /* New minor number */
+ ++minor;
+
+ /* Get the device identifier */
+ logical_disk = rtems_filesystem_make_dev_t( major, minor);
+
+ /* Delete logical disk */
+ sc = rtems_disk_delete( logical_disk);
+ if (sc != RTEMS_SUCCESSFUL) {
+ return sc;
+ }
+ }
+
+ return RTEMS_SUCCESSFUL;
+}
diff --git a/cpukit/libblock/src/bdpart-sort.c b/cpukit/libblock/src/bdpart-sort.c
new file mode 100644
index 0000000000..1114b7a492
--- /dev/null
+++ b/cpukit/libblock/src/bdpart-sort.c
@@ -0,0 +1,48 @@
+/**
+ * @file
+ *
+ * @ingroup rtems_bdpart
+ *
+ * Block device partition management.
+ */
+
+/*
+ * Copyright (c) 2009, 2010
+ * embedded brains GmbH
+ * Obere Lagerstr. 30
+ * D-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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdlib.h>
+
+#include <rtems.h>
+#include <rtems/bdpart.h>
+
+static int rtems_bdpart_partition_compare( const void *aa, const void *bb)
+{
+ const rtems_bdpart_partition *a = aa;
+ const rtems_bdpart_partition *b = bb;
+
+ if (a->begin < b->begin) {
+ return -1;
+ } else if (a->begin == b->begin) {
+ return 0;
+ } else {
+ return 1;
+ }
+}
+
+void rtems_bdpart_sort( rtems_bdpart_partition *pt, size_t count)
+{
+ qsort( pt, count, sizeof( *pt), rtems_bdpart_partition_compare);
+}
diff --git a/cpukit/libblock/src/bdpart-write.c b/cpukit/libblock/src/bdpart-write.c
new file mode 100644
index 0000000000..92e80a24ec
--- /dev/null
+++ b/cpukit/libblock/src/bdpart-write.c
@@ -0,0 +1,302 @@
+/**
+ * @file
+ *
+ * @ingroup rtems_bdpart
+ *
+ * Block device partition management.
+ */
+
+/*
+ * Copyright (c) 2009, 2010
+ * embedded brains GmbH
+ * Obere Lagerstr. 30
+ * D-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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <string.h>
+
+#include <rtems.h>
+#include <rtems/bdbuf.h>
+#include <rtems/bdpart.h>
+#include <rtems/endian.h>
+
+static void rtems_bdpart_write_mbr_partition(
+ uint8_t *data,
+ uint32_t begin,
+ uint32_t size,
+ uint8_t type,
+ uint8_t flags
+)
+{
+ rtems_uint32_to_little_endian( begin, data + RTEMS_BDPART_MBR_OFFSET_BEGIN);
+ rtems_uint32_to_little_endian( size, data + RTEMS_BDPART_MBR_OFFSET_SIZE);
+ data [RTEMS_BDPART_MBR_OFFSET_TYPE] = type;
+ data [RTEMS_BDPART_MBR_OFFSET_FLAGS] = flags;
+}
+
+static rtems_status_code rtems_bdpart_new_record(
+ dev_t disk,
+ rtems_blkdev_bnum index,
+ rtems_bdbuf_buffer **block
+)
+{
+ rtems_status_code sc = RTEMS_SUCCESSFUL;
+
+ /* Synchronize previous block if necessary */
+ if (*block != NULL) {
+ sc = rtems_bdbuf_sync( *block);
+ if (sc != RTEMS_SUCCESSFUL) {
+ return sc;
+ }
+ }
+
+ /* Read the new record block (this accounts for disk block sizes > 512) */
+ sc = rtems_bdbuf_read( disk, index, block);
+ if (sc != RTEMS_SUCCESSFUL) {
+ return sc;
+ }
+
+ /* just in case block did not get filled in */
+ if ( *block == NULL ) {
+ return RTEMS_INVALID_ADDRESS;
+ }
+
+ /* Clear record */
+ memset( (*block)->buffer, 0, RTEMS_BDPART_BLOCK_SIZE);
+
+ /* Write signature */
+ (*block)->buffer [RTEMS_BDPART_MBR_OFFSET_SIGNATURE_0] =
+ RTEMS_BDPART_MBR_SIGNATURE_0;
+ (*block)->buffer [RTEMS_BDPART_MBR_OFFSET_SIGNATURE_1] =
+ RTEMS_BDPART_MBR_SIGNATURE_1;
+
+ return RTEMS_SUCCESSFUL;
+}
+
+rtems_status_code rtems_bdpart_write(
+ const char *disk_name,
+ const rtems_bdpart_format *format,
+ const rtems_bdpart_partition *pt,
+ size_t count
+)
+{
+ rtems_status_code sc = RTEMS_SUCCESSFUL;
+ rtems_status_code esc = RTEMS_SUCCESSFUL;
+ bool dos_compatibility = format != NULL
+ && format->type == RTEMS_BDPART_FORMAT_MBR
+ && format->mbr.dos_compatibility;
+ rtems_bdbuf_buffer *block = NULL;
+ rtems_blkdev_bnum disk_end = 0;
+ rtems_blkdev_bnum record_space =
+ dos_compatibility ? RTEMS_BDPART_MBR_CYLINDER_SIZE : 1;
+ dev_t disk = 0;
+ size_t ppc = 0; /* Primary partition count */
+ size_t i = 0;
+ uint8_t *data = NULL;
+
+ /* Check if we have something to do */
+ if (count == 0) {
+ /* Nothing to do */
+ return RTEMS_SUCCESSFUL;
+ }
+
+ /* Check parameter */
+ if (format == NULL || pt == NULL) {
+ return RTEMS_INVALID_ADDRESS;
+ }
+
+ /* Get disk data */
+ sc = rtems_bdpart_get_disk_data( disk_name, &disk, &disk_end);
+ if (sc != RTEMS_SUCCESSFUL) {
+ return sc;
+ }
+
+ /* Align end of disk on cylinder boundary if necessary */
+ if (dos_compatibility) {
+ disk_end -= (disk_end % record_space);
+ }
+
+ /* Check that we have a consistent partition table */
+ for (i = 0; i < count; ++i) {
+ const rtems_bdpart_partition *p = pt + i;
+
+ /* Check that begin and end are proper within the disk */
+ if (p->begin >= disk_end || p->end > disk_end) {
+ esc = RTEMS_INVALID_NUMBER;
+ goto cleanup;
+ }
+
+ /* Check that begin and end are valid */
+ if (p->begin >= p->end) {
+ esc = RTEMS_INVALID_NUMBER;
+ goto cleanup;
+ }
+
+ /* Check that partitions do not overlap */
+ if (i > 0 && pt [i - 1].end > p->begin) {
+ esc = RTEMS_INVALID_NUMBER;
+ goto cleanup;
+ }
+ }
+
+ /* Check format */
+ if (format->type != RTEMS_BDPART_FORMAT_MBR) {
+ esc = RTEMS_NOT_IMPLEMENTED;
+ goto cleanup;
+ }
+
+ /*
+ * Set primary partition count. If we have more than four partitions we need
+ * an extended partition which will contain the partitions of number four and
+ * above as logical partitions. If we have four or less partitions we can
+ * use the primary partition table.
+ */
+ ppc = count <= 4 ? count : 3;
+
+ /*
+ * Check that the first primary partition starts at head one and sector one
+ * under the virtual one head and 63 sectors geometry if necessary.
+ */
+ if (dos_compatibility && pt [0].begin != RTEMS_BDPART_MBR_CYLINDER_SIZE) {
+ esc = RTEMS_INVALID_NUMBER;
+ goto cleanup;
+ }
+
+ /*
+ * Check that we have enough space for the EBRs. The partitions with number
+ * four and above are logical partitions if we have more than four partitions
+ * in total. The logical partitions are contained in the extended partition.
+ * Each logical partition is described via one EBR preceding the partition.
+ * The space for the EBR and maybe some space which is needed for DOS
+ * compatibility resides between the partitions. So there have to be gaps of
+ * the appropriate size between the partitions.
+ */
+ for (i = ppc; i < count; ++i) {
+ if ((pt [i].begin - pt [i - 1].end) < record_space) {
+ esc = RTEMS_INVALID_NUMBER;
+ goto cleanup;
+ }
+ }
+
+ /* Check that we can convert the parition descriptions to the MBR format */
+ for (i = 0; i < count; ++i) {
+ uint8_t type = 0;
+
+ const rtems_bdpart_partition *p = pt + i;
+
+ /* Check type */
+ if (!rtems_bdpart_to_mbr_partition_type( p->type, &type)) {
+ esc = RTEMS_INVALID_ID;
+ goto cleanup;
+ }
+
+ /* Check flags */
+ if (p->flags > 0xffU) {
+ esc = RTEMS_INVALID_ID;
+ goto cleanup;
+ }
+
+ /* Check ID */
+ /* TODO */
+ }
+
+ /* New MBR */
+ sc = rtems_bdpart_new_record( disk, 0, &block);
+ if (sc != RTEMS_SUCCESSFUL) {
+ esc = sc;
+ goto cleanup;
+ }
+
+ /* Write disk ID */
+ rtems_uint32_to_little_endian(
+ format->mbr.disk_id,
+ block->buffer + RTEMS_BDPART_MBR_OFFSET_DISK_ID
+ );
+
+ /* Write primary partition table */
+ data = block->buffer + RTEMS_BDPART_MBR_OFFSET_TABLE_0;
+ for (i = 0; i < ppc; ++i) {
+ const rtems_bdpart_partition *p = pt + i;
+
+ /* Write partition entry */
+ rtems_bdpart_write_mbr_partition(
+ data,
+ p->begin,
+ p->end - p->begin,
+ rtems_bdpart_mbr_partition_type( p->type),
+ (uint8_t) p->flags
+ );
+
+ data += RTEMS_BDPART_MBR_TABLE_ENTRY_SIZE;
+ }
+
+ /* Write extended partition with logical partitions if necessary */
+ if (ppc != count) {
+ rtems_blkdev_bnum ebr = 0; /* Extended boot record block index */
+
+ /* Begin of extended partition */
+ rtems_blkdev_bnum ep_begin = pt [ppc].begin - record_space;
+
+ /* Write extended partition */
+ rtems_bdpart_write_mbr_partition(
+ data,
+ ep_begin,
+ disk_end - ep_begin,
+ RTEMS_BDPART_MBR_EXTENDED,
+ 0
+ );
+
+ /* Write logical partitions */
+ for (i = ppc; i < count; ++i) {
+ const rtems_bdpart_partition *p = pt + i;
+
+ /* Write second partition entry */
+ if (i > ppc) {
+ rtems_blkdev_bnum begin = p->begin - record_space;
+
+ rtems_bdpart_write_mbr_partition(
+ block->buffer + RTEMS_BDPART_MBR_OFFSET_TABLE_1,
+ begin - ep_begin,
+ disk_end - begin,
+ RTEMS_BDPART_MBR_EXTENDED,
+ 0
+ );
+ }
+
+ /* New EBR */
+ ebr = p->begin - record_space;
+ sc = rtems_bdpart_new_record( disk, ebr, &block);
+ if (sc != RTEMS_SUCCESSFUL) {
+ esc = sc;
+ goto cleanup;
+ }
+
+ /* Write first partition entry */
+ rtems_bdpart_write_mbr_partition(
+ block->buffer + RTEMS_BDPART_MBR_OFFSET_TABLE_0,
+ record_space,
+ p->end - p->begin,
+ rtems_bdpart_mbr_partition_type( p->type),
+ (uint8_t) p->flags
+ );
+ }
+ }
+
+cleanup:
+
+ if (block != NULL) {
+ rtems_bdbuf_sync( block);
+ }
+
+ return esc;
+}
diff --git a/cpukit/libblock/src/blkdev-ops.c b/cpukit/libblock/src/blkdev-ops.c
new file mode 100644
index 0000000000..6cc0c3009e
--- /dev/null
+++ b/cpukit/libblock/src/blkdev-ops.c
@@ -0,0 +1,32 @@
+/**
+ * @file
+ *
+ * @ingroup rtems_blkdev
+ *
+ * @brief Block device management.
+ */
+
+/*
+ * Copyright (c) 2011 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.
+ */
+
+#include <rtems/blkdev.h>
+
+const rtems_driver_address_table rtems_blkdev_generic_ops = {
+ .initialization_entry = NULL,
+ .open_entry = rtems_blkdev_generic_open,
+ .close_entry = rtems_blkdev_generic_close,
+ .read_entry = rtems_blkdev_generic_read,
+ .write_entry = rtems_blkdev_generic_write,
+ .control_entry = rtems_blkdev_generic_ioctl
+};
diff --git a/cpukit/libblock/src/blkdev.c b/cpukit/libblock/src/blkdev.c
new file mode 100644
index 0000000000..10c14dfaf2
--- /dev/null
+++ b/cpukit/libblock/src/blkdev.c
@@ -0,0 +1,263 @@
+/**
+ * @file
+ *
+ * @ingroup rtems_blkdev
+ *
+ * Block device management.
+ */
+
+/*
+ * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia
+ * Author: Victor V. Vengerov <vvv@oktet.ru>
+ *
+ * @(#) $Id$
+ */
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <errno.h>
+#include <string.h>
+
+#include <rtems.h>
+#include <rtems/libio.h>
+#include <sys/ioctl.h>
+
+#include "rtems/diskdevs.h"
+#include "rtems/bdbuf.h"
+
+/* rtems_blkdev_generic_read --
+ * Generic block device read primitive. Implemented using block device
+ * buffer management primitives.
+ */
+rtems_device_driver
+rtems_blkdev_generic_read(
+ rtems_device_major_number major __attribute__((unused)),
+ rtems_device_minor_number minor __attribute__((unused)),
+ void * arg)
+{
+ rtems_status_code rc = RTEMS_SUCCESSFUL;
+ rtems_libio_rw_args_t *args = arg;
+ rtems_libio_t *iop = args->iop;
+ rtems_disk_device *dd = iop->data1;
+ uint32_t block_size = dd->block_size;
+ char *buf = args->buffer;
+ uint32_t count = args->count;
+ rtems_blkdev_bnum block = (rtems_blkdev_bnum) (args->offset / block_size);
+ uint32_t blkofs = (uint32_t) (args->offset % block_size);
+ dev_t dev = dd->dev;
+
+ args->bytes_moved = 0;
+
+ while (count > 0)
+ {
+ rtems_bdbuf_buffer *diskbuf;
+ uint32_t copy;
+
+ rc = rtems_bdbuf_read(dev, block, &diskbuf);
+ if (rc != RTEMS_SUCCESSFUL)
+ break;
+ copy = block_size - blkofs;
+ if (copy > count)
+ copy = count;
+ memcpy(buf, (char *)diskbuf->buffer + blkofs, copy);
+ rc = rtems_bdbuf_release(diskbuf);
+ args->bytes_moved += copy;
+ if (rc != RTEMS_SUCCESSFUL)
+ break;
+ count -= copy;
+ buf += copy;
+ blkofs = 0;
+ block++;
+ }
+
+ return rc;
+}
+
+/* rtems_blkdev_generic_write --
+ * Generic block device write primitive. Implemented using block device
+ * buffer management primitives.
+ */
+rtems_device_driver
+rtems_blkdev_generic_write(
+ rtems_device_major_number major __attribute__((unused)),
+ rtems_device_minor_number minor __attribute__((unused)),
+ void * arg)
+{
+ rtems_status_code rc = RTEMS_SUCCESSFUL;
+ rtems_libio_rw_args_t *args = arg;
+ rtems_libio_t *iop = args->iop;
+ rtems_disk_device *dd = iop->data1;
+ uint32_t block_size = dd->block_size;
+ char *buf = args->buffer;
+ uint32_t count = args->count;
+ rtems_blkdev_bnum block = (rtems_blkdev_bnum) (args->offset / block_size);
+ uint32_t blkofs = (uint32_t) (args->offset % block_size);
+ dev_t dev = dd->dev;
+
+ args->bytes_moved = 0;
+
+ while (count > 0)
+ {
+ rtems_bdbuf_buffer *diskbuf;
+ uint32_t copy;
+
+ if ((blkofs == 0) && (count >= block_size))
+ rc = rtems_bdbuf_get(dev, block, &diskbuf);
+ else
+ rc = rtems_bdbuf_read(dev, block, &diskbuf);
+ if (rc != RTEMS_SUCCESSFUL)
+ break;
+
+ copy = block_size - blkofs;
+ if (copy > count)
+ copy = count;
+ memcpy((char *)diskbuf->buffer + blkofs, buf, copy);
+ args->bytes_moved += copy;
+
+ rc = rtems_bdbuf_release_modified(diskbuf);
+ if (rc != RTEMS_SUCCESSFUL)
+ break;
+
+ count -= copy;
+ buf += copy;
+ blkofs = 0;
+ block++;
+ }
+
+ return rc;
+}
+
+/* blkdev_generic_open --
+ * Generic block device open primitive.
+ */
+rtems_device_driver
+rtems_blkdev_generic_open(
+ rtems_device_major_number major,
+ rtems_device_minor_number minor,
+ void * arg)
+{
+ rtems_libio_open_close_args_t *oc = arg;
+ rtems_libio_t *iop = oc->iop;
+ dev_t dev = rtems_filesystem_make_dev_t(major, minor);
+ rtems_disk_device *dd = rtems_disk_obtain(dev);
+
+ iop->data1 = dd;
+
+ if (dd != NULL)
+ return RTEMS_SUCCESSFUL;
+ else
+ return RTEMS_UNSATISFIED;
+}
+
+
+/* blkdev_generic_close --
+ * Generic block device close primitive.
+ */
+rtems_device_driver
+rtems_blkdev_generic_close(
+ rtems_device_major_number major __attribute__((unused)),
+ rtems_device_minor_number minor __attribute__((unused)),
+ void * arg)
+{
+ rtems_libio_open_close_args_t *oc = arg;
+ rtems_libio_t *iop = oc->iop;
+ rtems_disk_device *dd = iop->data1;
+
+ rtems_disk_release(dd);
+
+ return RTEMS_SUCCESSFUL;
+}
+
+/* blkdev_generic_ioctl --
+ * Generic block device ioctl primitive.
+ */
+rtems_device_driver
+rtems_blkdev_generic_ioctl(
+ rtems_device_major_number major __attribute__((unused)),
+ rtems_device_minor_number minor __attribute__((unused)),
+ void * arg)
+{
+ rtems_libio_ioctl_args_t *args = arg;
+ rtems_libio_t *iop = args->iop;
+ rtems_disk_device *dd = iop->data1;
+ int rc;
+
+ switch (args->command)
+ {
+ case RTEMS_BLKIO_GETMEDIABLKSIZE:
+ *((uint32_t *) args->buffer) = dd->media_block_size;
+ args->ioctl_return = 0;
+ break;
+
+ case RTEMS_BLKIO_GETBLKSIZE:
+ *((uint32_t *) args->buffer) = dd->block_size;
+ args->ioctl_return = 0;
+ break;
+
+ case RTEMS_BLKIO_SETBLKSIZE:
+ dd->block_size = *((uint32_t *) args->buffer);
+ args->ioctl_return = 0;
+ break;
+
+ case RTEMS_BLKIO_GETSIZE:
+ *((rtems_blkdev_bnum *) args->buffer) = dd->size;
+ args->ioctl_return = 0;
+ break;
+
+ case RTEMS_BLKIO_SYNCDEV:
+ rc = rtems_bdbuf_syncdev(dd->dev);
+ args->ioctl_return = (uint32_t) (rc == RTEMS_SUCCESSFUL ? 0 : -1);
+ break;
+
+ case RTEMS_BLKIO_REQUEST:
+ /*
+ * It is not allowed to directly access the driver circumventing
+ * the cache.
+ */
+ args->ioctl_return = (uint32_t) -1;
+ break;
+
+ default:
+ args->ioctl_return = (uint32_t) dd->ioctl(dd->phys_dev,
+ args->command,
+ args->buffer);
+ break;
+ }
+
+ return RTEMS_SUCCESSFUL;
+}
+
+int
+rtems_blkdev_ioctl(rtems_disk_device *dd, uint32_t req, void *argp)
+{
+ size_t *arg_size = argp;
+ int rc = 0;
+
+ switch (req)
+ {
+ case RTEMS_BLKIO_GETMEDIABLKSIZE:
+ *arg_size = dd->media_block_size;
+ break;
+
+ case RTEMS_BLKIO_GETBLKSIZE:
+ *arg_size = dd->block_size;
+ break;
+
+ case RTEMS_BLKIO_SETBLKSIZE:
+ dd->block_size = *arg_size;
+ break;
+
+ case RTEMS_BLKIO_GETSIZE:
+ *arg_size = dd->size;
+ break;
+
+ default:
+ errno = EINVAL;
+ rc = -1;
+ break;
+ }
+
+ return rc;
+}
diff --git a/cpukit/libblock/src/diskdevs.c b/cpukit/libblock/src/diskdevs.c
new file mode 100644
index 0000000000..08a6a9bf8c
--- /dev/null
+++ b/cpukit/libblock/src/diskdevs.c
@@ -0,0 +1,589 @@
+/**
+ * @file
+ *
+ * @ingroup rtems_disk
+ *
+ * @brief Block device disk management implementation.
+ */
+
+/*
+ * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia
+ * Author: Victor V. Vengerov <vvv@oktet.ru>
+ *
+ * Copyright (c) 2009 embedded brains GmbH.
+ *
+ * @(#) $Id$
+ */
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+#include <rtems.h>
+#include <rtems/libio.h>
+#include <rtems/diskdevs.h>
+#include <rtems/blkdev.h>
+#include <rtems/bdbuf.h>
+
+#define DISKTAB_INITIAL_SIZE 8
+
+/* Table of disk devices having the same major number */
+typedef struct rtems_disk_device_table {
+ rtems_disk_device **minor; /* minor-indexed disk device table */
+ rtems_device_minor_number size; /* Number of entries in the table */
+} rtems_disk_device_table;
+
+/* Pointer to [major].minor[minor] indexed array of disk devices */
+static rtems_disk_device_table *disktab;
+
+/* Number of allocated entries in disktab table */
+static rtems_device_major_number disktab_size;
+
+/* Mutual exclusion semaphore for disk devices table */
+static rtems_id diskdevs_mutex;
+
+/* diskdevs data structures protection flag.
+ * Normally, only table lookup operations performed. It is quite fast, so
+ * it is possible to done lookups when interrupts are disabled, avoiding
+ * obtaining the semaphore. This flags sets immediately after entering in
+ * mutex-protected section and cleared before leaving this section in
+ * "big" primitives like add/delete new device etc. Lookup function first
+ * disable interrupts and check this flag. If it is set, lookup function
+ * will be blocked on semaphore and lookup operation will be performed in
+ * semaphore-protected code. If it is not set (very-very frequent case),
+ * we can do lookup safely, enable interrupts and return result.
+ */
+static volatile bool diskdevs_protected;
+
+static rtems_status_code
+disk_lock(void)
+{
+ rtems_status_code sc = RTEMS_SUCCESSFUL;
+
+ sc = rtems_semaphore_obtain(diskdevs_mutex, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
+ if (sc == RTEMS_SUCCESSFUL) {
+ diskdevs_protected = true;
+
+ return RTEMS_SUCCESSFUL;
+ } else {
+ return RTEMS_NOT_CONFIGURED;
+ }
+}
+
+static void
+disk_unlock(void)
+{
+ rtems_status_code sc = RTEMS_SUCCESSFUL;
+
+ diskdevs_protected = false;
+
+ sc = rtems_semaphore_release(diskdevs_mutex);
+ if (sc != RTEMS_SUCCESSFUL) {
+ /* FIXME: Error number */
+ rtems_fatal_error_occurred(0xdeadbeef);
+ }
+}
+
+static rtems_disk_device *
+get_disk_entry(dev_t dev, bool lookup_only)
+{
+ rtems_device_major_number major = 0;
+ rtems_device_minor_number minor = 0;
+
+ rtems_filesystem_split_dev_t(dev, major, minor);
+
+ if (major < disktab_size && disktab != NULL) {
+ rtems_disk_device_table *dtab = disktab + major;
+
+ if (minor < dtab->size && dtab->minor != NULL) {
+ rtems_disk_device *dd = dtab->minor [minor];
+
+ if (dd != NULL && !lookup_only) {
+ if (!dd->deleted) {
+ ++dd->uses;
+ } else {
+ dd = NULL;
+ }
+ }
+
+ return dd;
+ }
+ }
+
+ return NULL;
+}
+
+static rtems_disk_device **
+create_disk_table_entry(dev_t dev)
+{
+ rtems_device_major_number major = 0;
+ rtems_device_minor_number minor = 0;
+
+ rtems_filesystem_split_dev_t(dev, major, minor);
+
+ if (major >= disktab_size) {
+ rtems_disk_device_table *table = disktab;
+ rtems_device_major_number old_size = disktab_size;
+ rtems_device_major_number new_size = 2 * old_size;
+
+ if (major >= new_size) {
+ new_size = major + 1;
+ }
+
+ table = realloc(table, new_size * sizeof(*table));
+ if (table == NULL) {
+ return NULL;
+ }
+
+ memset(table + old_size, 0, (new_size - old_size) * sizeof(*table));
+ disktab = table;
+ disktab_size = new_size;
+ }
+
+ if (disktab [major].minor == NULL || minor >= disktab[major].size) {
+ rtems_disk_device **table = disktab [major].minor;
+ rtems_device_minor_number old_size = disktab [major].size;
+ rtems_device_minor_number new_size = 0;
+
+ if (old_size == 0) {
+ new_size = DISKTAB_INITIAL_SIZE;
+ } else {
+ new_size = 2 * old_size;
+ }
+ if (minor >= new_size) {
+ new_size = minor + 1;
+ }
+
+ table = realloc(table, new_size * sizeof(*table));
+ if (table == NULL) {
+ return NULL;
+ }
+
+ memset(table + old_size, 0, (new_size - old_size) * sizeof(*table));
+ disktab [major].minor = table;
+ disktab [major].size = new_size;
+ }
+
+ return disktab [major].minor + minor;
+}
+
+static rtems_status_code
+create_disk(dev_t dev, const char *name, rtems_disk_device **dd_ptr)
+{
+ rtems_disk_device **dd_entry = create_disk_table_entry(dev);
+ rtems_disk_device *dd = NULL;
+ char *alloc_name = NULL;
+
+ if (dd_entry == NULL) {
+ return RTEMS_NO_MEMORY;
+ }
+
+ if (*dd_entry != NULL) {
+ return RTEMS_RESOURCE_IN_USE;
+ }
+
+ dd = malloc(sizeof(*dd));
+ if (dd == NULL) {
+ return RTEMS_NO_MEMORY;
+ }
+
+ if (name != NULL) {
+ alloc_name = strdup(name);
+
+ if (alloc_name == NULL) {
+ free(dd);
+
+ return RTEMS_NO_MEMORY;
+ }
+ }
+
+ if (name != NULL) {
+ if (mknod(alloc_name, 0777 | S_IFBLK, dev) < 0) {
+ free(alloc_name);
+ free(dd);
+ return RTEMS_UNSATISFIED;
+ }
+ }
+
+ dd->dev = dev;
+ dd->name = alloc_name;
+ dd->uses = 0;
+ dd->deleted = false;
+
+ *dd_entry = dd;
+ *dd_ptr = dd;
+
+ return RTEMS_SUCCESSFUL;
+}
+
+rtems_status_code rtems_disk_create_phys(
+ dev_t dev,
+ uint32_t block_size,
+ rtems_blkdev_bnum block_count,
+ rtems_block_device_ioctl handler,
+ void *driver_data,
+ const char *name
+)
+{
+ rtems_disk_device *dd = NULL;
+ rtems_status_code sc = RTEMS_SUCCESSFUL;
+
+ if (handler == NULL) {
+ return RTEMS_INVALID_ADDRESS;
+ }
+
+ if (block_size == 0) {
+ return RTEMS_INVALID_NUMBER;
+ }
+
+ sc = disk_lock();
+ if (sc != RTEMS_SUCCESSFUL) {
+ return sc;
+ }
+
+ sc = create_disk(dev, name, &dd);
+ if (sc != RTEMS_SUCCESSFUL) {
+ disk_unlock();
+
+ return sc;
+ }
+
+ dd->phys_dev = dd;
+ dd->start = 0;
+ dd->size = block_count;
+ dd->block_size = dd->media_block_size = block_size;
+ dd->ioctl = handler;
+ dd->driver_data = driver_data;
+
+ if ((*handler)(dd, RTEMS_BLKIO_CAPABILITIES, &dd->capabilities) < 0) {
+ dd->capabilities = 0;
+ }
+
+ disk_unlock();
+
+ return RTEMS_SUCCESSFUL;
+}
+
+static bool
+is_physical_disk(const rtems_disk_device *dd)
+{
+ return dd->phys_dev == dd;
+}
+
+rtems_status_code rtems_disk_create_log(
+ dev_t dev,
+ dev_t phys,
+ rtems_blkdev_bnum begin_block,
+ rtems_blkdev_bnum block_count,
+ const char *name
+)
+{
+ rtems_status_code sc = RTEMS_SUCCESSFUL;
+ rtems_disk_device *physical_disk = NULL;
+ rtems_disk_device *dd = NULL;
+ rtems_blkdev_bnum end_block = begin_block + block_count;
+
+ sc = disk_lock();
+ if (sc != RTEMS_SUCCESSFUL) {
+ return sc;
+ }
+
+ physical_disk = get_disk_entry(phys, true);
+ if (physical_disk == NULL || !is_physical_disk(physical_disk)) {
+ disk_unlock();
+
+ return RTEMS_INVALID_ID;
+ }
+
+ if (
+ begin_block >= physical_disk->size
+ || end_block <= begin_block
+ || end_block > physical_disk->size
+ ) {
+ disk_unlock();
+
+ return RTEMS_INVALID_NUMBER;
+ }
+
+ sc = create_disk(dev, name, &dd);
+ if (sc != RTEMS_SUCCESSFUL) {
+ disk_unlock();
+
+ return sc;
+ }
+
+ dd->phys_dev = physical_disk;
+ dd->start = begin_block;
+ dd->size = block_count;
+ dd->block_size = dd->media_block_size = physical_disk->block_size;
+ dd->ioctl = physical_disk->ioctl;
+ dd->driver_data = physical_disk->driver_data;
+
+ ++physical_disk->uses;
+
+ disk_unlock();
+
+ return RTEMS_SUCCESSFUL;
+}
+
+static void
+free_disk_device(rtems_disk_device *dd)
+{
+ if (is_physical_disk(dd)) {
+ (*dd->ioctl)(dd, RTEMS_BLKIO_DELETED, NULL);
+ }
+ if (dd->name != NULL) {
+ unlink(dd->name);
+ free(dd->name);
+ }
+ free(dd);
+}
+
+static void
+rtems_disk_cleanup(rtems_disk_device *disk_to_remove)
+{
+ rtems_disk_device *const physical_disk = disk_to_remove->phys_dev;
+ rtems_device_major_number major = 0;
+ rtems_device_minor_number minor = 0;
+
+ if (physical_disk->deleted) {
+ dev_t dev = physical_disk->dev;
+ unsigned deleted_count = 0;
+
+ for (major = 0; major < disktab_size; ++major) {
+ rtems_disk_device_table *dtab = disktab + major;
+
+ for (minor = 0; minor < dtab->size; ++minor) {
+ rtems_disk_device *dd = dtab->minor [minor];
+
+ if (dd != NULL && dd->phys_dev->dev == dev && dd != physical_disk) {
+ if (dd->uses == 0) {
+ ++deleted_count;
+ dtab->minor [minor] = NULL;
+ free_disk_device(dd);
+ } else {
+ dd->deleted = true;
+ }
+ }
+ }
+ }
+
+ physical_disk->uses -= deleted_count;
+ if (physical_disk->uses == 0) {
+ rtems_filesystem_split_dev_t(physical_disk->dev, major, minor);
+ disktab [major].minor [minor] = NULL;
+ free_disk_device(physical_disk);
+ }
+ } else {
+ if (disk_to_remove->uses == 0) {
+ --physical_disk->uses;
+ rtems_filesystem_split_dev_t(disk_to_remove->dev, major, minor);
+ disktab [major].minor [minor] = NULL;
+ free_disk_device(disk_to_remove);
+ }
+ }
+}
+
+rtems_status_code
+rtems_disk_delete(dev_t dev)
+{
+ rtems_status_code sc = RTEMS_SUCCESSFUL;
+ rtems_disk_device *dd = NULL;
+
+ sc = disk_lock();
+ if (sc != RTEMS_SUCCESSFUL) {
+ return sc;
+ }
+
+ dd = get_disk_entry(dev, true);
+ if (dd == NULL) {
+ disk_unlock();
+
+ return RTEMS_INVALID_ID;
+ }
+
+ dd->deleted = true;
+ rtems_disk_cleanup(dd);
+
+ disk_unlock();
+
+ return RTEMS_SUCCESSFUL;
+}
+
+rtems_disk_device *
+rtems_disk_obtain(dev_t dev)
+{
+ rtems_status_code sc = RTEMS_SUCCESSFUL;
+ rtems_disk_device *dd = NULL;
+ rtems_interrupt_level level;
+
+ rtems_interrupt_disable(level);
+ if (!diskdevs_protected) {
+ /* Frequent and quickest case */
+ dd = get_disk_entry(dev, false);
+ rtems_interrupt_enable(level);
+ } else {
+ rtems_interrupt_enable(level);
+
+ sc = disk_lock();
+ if (sc == RTEMS_SUCCESSFUL) {
+ dd = get_disk_entry(dev, false);
+ disk_unlock();
+ }
+ }
+
+ return dd;
+}
+
+rtems_status_code
+rtems_disk_release(rtems_disk_device *dd)
+{
+ rtems_interrupt_level level;
+ dev_t dev = dd->dev;
+ unsigned uses = 0;
+ bool deleted = false;
+
+ rtems_interrupt_disable(level);
+ uses = --dd->uses;
+ deleted = dd->deleted;
+ rtems_interrupt_enable(level);
+
+ if (uses == 0 && deleted) {
+ rtems_disk_delete(dev);
+ }
+
+ return RTEMS_SUCCESSFUL;
+}
+
+rtems_disk_device *
+rtems_disk_next(dev_t dev)
+{
+ rtems_status_code sc = RTEMS_SUCCESSFUL;
+ rtems_disk_device_table *dtab = NULL;
+ rtems_device_major_number major = 0;
+ rtems_device_minor_number minor = 0;
+
+ if (dev != (dev_t) -1) {
+ rtems_filesystem_split_dev_t(dev, major, minor);
+
+ /* If minor wraps around */
+ if ((minor + 1) < minor) {
+ /* If major wraps around */
+ if ((major + 1) < major) {
+ return NULL;
+ }
+ ++major;
+ minor = 0;
+ } else {
+ ++minor;
+ }
+ }
+
+ sc = disk_lock();
+ if (sc != RTEMS_SUCCESSFUL) {
+ return NULL;
+ }
+
+ if (major >= disktab_size) {
+ disk_unlock();
+
+ return NULL;
+ }
+
+ dtab = disktab + major;
+ while (true) {
+ if (dtab->minor == NULL || minor >= dtab->size) {
+ minor = 0;
+ ++major;
+ if (major >= disktab_size) {
+ disk_unlock();
+
+ return NULL;
+ }
+ dtab = disktab + major;
+ } else if (dtab->minor [minor] == NULL) {
+ ++minor;
+ } else {
+ ++dtab->minor [minor]->uses;
+ disk_unlock();
+
+ return dtab->minor [minor];
+ }
+ }
+}
+
+rtems_status_code
+rtems_disk_io_initialize(void)
+{
+ rtems_status_code sc = RTEMS_SUCCESSFUL;
+ rtems_device_major_number size = DISKTAB_INITIAL_SIZE;
+
+ if (disktab_size > 0) {
+ return RTEMS_SUCCESSFUL;
+ }
+
+ disktab = calloc(size, sizeof(rtems_disk_device_table));
+ if (disktab == NULL) {
+ return RTEMS_NO_MEMORY;
+ }
+
+ diskdevs_protected = false;
+ sc = rtems_semaphore_create(
+ rtems_build_name('D', 'D', 'E', 'V'),
+ 1,
+ RTEMS_FIFO | RTEMS_BINARY_SEMAPHORE | RTEMS_NO_INHERIT_PRIORITY
+ | RTEMS_NO_PRIORITY_CEILING | RTEMS_LOCAL,
+ 0,
+ &diskdevs_mutex
+ );
+ if (sc != RTEMS_SUCCESSFUL) {
+ free(disktab);
+
+ return RTEMS_NO_MEMORY;
+ }
+
+ sc = rtems_bdbuf_init();
+ if (sc != RTEMS_SUCCESSFUL) {
+ rtems_semaphore_delete(diskdevs_mutex);
+ free(disktab);
+
+ return RTEMS_UNSATISFIED;
+ }
+
+ disktab_size = size;
+
+ return RTEMS_SUCCESSFUL;
+}
+
+rtems_status_code
+rtems_disk_io_done(void)
+{
+ rtems_device_major_number major = 0;
+ rtems_device_minor_number minor = 0;
+
+ for (major = 0; major < disktab_size; ++major) {
+ rtems_disk_device_table *dtab = disktab + major;
+
+ for (minor = 0; minor < dtab->size; ++minor) {
+ rtems_disk_device *dd = dtab->minor [minor];
+
+ if (dd != NULL) {
+ free_disk_device(dd);
+ }
+ }
+ free(dtab->minor);
+ }
+ free(disktab);
+
+ rtems_semaphore_delete(diskdevs_mutex);
+
+ diskdevs_mutex = RTEMS_ID_NONE;
+ disktab = NULL;
+ disktab_size = 0;
+
+ return RTEMS_SUCCESSFUL;
+}
diff --git a/cpukit/libblock/src/flashdisk.c b/cpukit/libblock/src/flashdisk.c
new file mode 100644
index 0000000000..20a9eb46b9
--- /dev/null
+++ b/cpukit/libblock/src/flashdisk.c
@@ -0,0 +1,2603 @@
+/*
+ * flashdisk.c -- Flash disk block device implementation
+ *
+ * Copyright (C) 2007 Chris Johns
+ *
+ * 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.
+ *
+ * $Id$
+ */
+/**
+ * @file
+ *
+ * Flash disk driver for RTEMS provides support for block based
+ * file systems on flash devices. The driver is not a flash file
+ * system nor does it try to compete with flash file systems. It
+ * currently does not journal how-ever block sequence numbering
+ * could be added to allow recovery of a past positions if
+ * a power down occurred while being updated.
+ *
+ * This flash driver provides block device support for most flash
+ * devices. The driver has been tested on NOR type devices such
+ * as the AMLV160 or M28W160. Support for NAND type devices may
+ * require driver changes to allow speedy recover of the block
+ * mapping data and to also handle the current use of word programming.
+ * Currently the page descriptors are stored in the first few pages
+ * of each segment.
+ *
+ * The driver supports devices, segments and pages. You provide
+ * to the driver the device descriptions as a table of device
+ * descriptors. Each device descriptor contain a table of
+ * segment descriptions or segment descriptors. The driver uses
+ * this information to manage the devices.
+ *
+ * A device is made up of segments. These are also called
+ * sectors or blocks. It is the smallest erasable part of a device.
+ * A device can have differing size segments at different
+ * offsets in the device. The segment descriptors support repeating
+ * segments that are continous in the device. The driver breaks the
+ * segments up into pages. The first pages of a segment contain
+ * the page descriptors. A page descriptor hold the page flags,
+ * a CRC for the page of data and the block number the page
+ * holds. The block can appear in any order in the devices. A
+ * page is active if it hold a current block of data. If the
+ * used bit is set the page is counted as used. A page moves
+ * from erased to active to used then back to erased. If a block
+ * is written that is already in a page, the block is written to
+ * a new page the old page is flagged as used.
+ *
+ * At initialisation time each segment's page descriptors are
+ * read into memory and scanned to determine the active pages,
+ * the used pages and the bad pages. If a segment has any erased
+ * pages it is queue on the available queue. If the segment has
+ * no erased pages it is queue on the used queue.
+ *
+ * The available queue is sorted from the least number available
+ * to the most number of available pages. A segment that has just
+ * been erased will placed at the end of the queue. A segment that
+ * has only a few available pages will be used sooner and once
+ * there are no available pages it is queued on the used queue.
+ * The used queue hold segments that have no available pages and
+ * is sorted from the least number of active pages to the most
+ * number of active pages.
+ *
+ * The driver is required to compact segments. Compacting takes
+ * the segment with the most number of available pages from the
+ * available queue then takes segments with the least number of
+ * active pages from the used queue until it has enough pages
+ * to fill the empty segment. As the active pages are moved
+ * they flagged as used and once the segment has only used pages
+ * it is erased.
+ *
+ * A flash block driver like this never knows if a page is not
+ * being used by the file-system. A typical file system is not
+ * design with the idea of erasing a block on a disk once it is
+ * not being used. The file-system will normally use a flag
+ * or a location as a marker to say that part of the disk is
+ * no longer in use. This means a number of blocks could be
+ * held in active pages but are no in use by the file system.
+ * The file system may also read blocks that have never been
+ * written to disk. This complicates the driver and may make
+ * the wear, usage and erase patterns harsher than a flash
+ * file system. The driver may also suffer from problems if
+ * power is lost.
+ *
+ * @note
+ *
+ * The use of pages can vary. The rtems_fdisk_seg_*_page set
+ * routines use an absolute page number relative to the segment
+ * while all other page numbera are relative to the number of
+ * page descriptor pages a segment has. You need to add the
+ * number of page descriptor pages (pages_desc) to the page number
+ * when call the rtems_fdisk_seg_*_page functions.
+ *
+ * You must always show the page number as relative in any trace
+ * or error message as device-segment-page and if you have to
+ * the page number as absolute use device-segment~page. This
+ * can be seen in the page copy routine.
+ *
+ * The code is like this to avoid needing the pass the pages_desc
+ * value around. It is only used in selected places and so the
+ * extra parameter was avoided.
+ */
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <rtems.h>
+#include <rtems/libio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+
+#include "rtems/blkdev.h"
+#include "rtems/diskdevs.h"
+#include "rtems/flashdisk.h"
+
+/**
+ * Control tracing. It can be compiled out of the code for small
+ * footprint targets. Leave in by default.
+ */
+#if !defined (RTEMS_FDISK_TRACE)
+#define RTEMS_FDISK_TRACE 1
+#endif
+
+/**
+ * The start of a segment has a segment control table. This hold the CRC and
+ * block number for the page.
+ *
+ * @todo A sequence number for the block could be added. This would
+ * mean a larger descriptor size. Need to make the sequence
+ * large like 20+ bits so a large file system would not have
+ * more blocks available than the sequence number.
+ */
+typedef struct rtems_fdisk_page_desc
+{
+ uint16_t crc; /**< The page's checksum. */
+ uint16_t flags; /**< The flags for the page. */
+ uint32_t block; /**< The block number. */
+} rtems_fdisk_page_desc;
+
+/**
+ * Flag the page as active.
+ */
+#define RTEMS_FDISK_PAGE_ACTIVE (1 << 0)
+
+/**
+ * Flag the page as used.
+ */
+#define RTEMS_FDISK_PAGE_USED (1 << 1)
+
+/**
+ * Flash Segment Control holds the pointer to the segment, number of
+ * pages, various page stats and the memory copy of the page descriptors.
+ */
+typedef struct rtems_fdisk_segment_ctl
+{
+ /**
+ * Segments with available pages are maintained as a linked list.
+ */
+ struct rtems_fdisk_segment_ctl* next;
+
+ /**
+ * The descriptor provided by the low-level driver.
+ */
+ const rtems_fdisk_segment_desc* descriptor;
+
+ /**
+ * The device this segment resides on.
+ */
+ uint32_t device;
+
+ /**
+ * The segment in the device. This must be within the
+ * segment descriptor.
+ */
+ uint32_t segment;
+
+ /**
+ * The in-memory ocpy of the page descriptors found at
+ * the start of the segment in the flash device.
+ */
+ rtems_fdisk_page_desc* page_descriptors;
+
+ /*
+ * Page stats.
+ *
+ * A bad page does not checksum or is not erased or has invalid flags.
+ */
+ uint32_t pages; /**< Total number of pages in the segment. */
+ uint32_t pages_desc; /**< Number of pages used for page descriptors. */
+ uint32_t pages_active; /**< Number of pages flagged as active. */
+ uint32_t pages_used; /**< Number of pages flagged as used. */
+ uint32_t pages_bad; /**< Number of pages detected as bad. */
+
+ uint32_t failed; /**< The segment has failed. */
+
+ uint32_t erased; /**< Counter to debugging. Wear support would
+ remove this. */
+} rtems_fdisk_segment_ctl;
+
+/**
+ * Segment control table queue.
+ */
+typedef struct rtems_fdisk_segment_ctl_queue
+{
+ rtems_fdisk_segment_ctl* head;
+ rtems_fdisk_segment_ctl* tail;
+ uint32_t count;
+} rtems_fdisk_segment_ctl_queue;
+
+/**
+ * Flash Device Control holds the segment controls
+ */
+typedef struct rtems_fdisk_device_ctl
+{
+ rtems_fdisk_segment_ctl* segments; /**< Segment controls. */
+ uint32_t segment_count; /**< Segment control count. */
+ const rtems_fdisk_device_desc* descriptor; /**< Device descriptor. */
+} rtems_fdisk_device_ctl;
+
+/**
+ * The Block control holds the segment and page with the data.
+ */
+typedef struct rtems_fdisk_block_ctl
+{
+ rtems_fdisk_segment_ctl* segment; /**< The segment with the block. */
+ uint32_t page; /**< The page in the segment. */
+} rtems_fdisk_block_ctl;
+
+/**
+ * The virtual block table holds the mapping for blocks as seen by the disk
+ * drivers to the device, segment and page numbers of the physical device.
+ */
+typedef struct rtems_flashdisk
+{
+ rtems_device_major_number major; /**< The driver's major number. */
+ rtems_device_minor_number minor; /**< The driver's minor number. */
+
+ uint32_t flags; /**< configuration flags. */
+
+ uint32_t compact_segs; /**< Max segs to compact at once. */
+ uint32_t avail_compact_segs; /**< The number of segments when
+ compaction occurs when writing. */
+
+ uint32_t block_size; /**< The block size for this disk. */
+ rtems_fdisk_block_ctl* blocks; /**< The block to segment-page
+ mappings. */
+ uint32_t block_count; /**< The number of avail. blocks. */
+ uint32_t unavail_blocks; /**< The number of unavail blocks. */
+
+ rtems_fdisk_device_ctl* devices; /**< The flash devices for this
+ disk. */
+ uint32_t device_count; /**< The number of flash devices. */
+
+ rtems_fdisk_segment_ctl_queue available; /**< The queue of segments with
+ available pages. */
+ rtems_fdisk_segment_ctl_queue used; /**< The list of segments with all
+ pages used. */
+ rtems_fdisk_segment_ctl_queue erase; /**< The list of segments to be
+ erased. */
+ rtems_fdisk_segment_ctl_queue failed; /**< The list of segments that failed
+ when being erased. */
+ rtems_id lock; /**< Mutex for threading protection.*/
+
+ uint8_t* copy_buffer; /**< Copy buf used during compacting */
+
+ uint32_t info_level; /**< The info trace level. */
+} rtems_flashdisk;
+
+/**
+ * The array of flash disks we support.
+ */
+static rtems_flashdisk* rtems_flashdisks;
+
+/**
+ * The number of flash disks we have.
+ */
+static uint32_t rtems_flashdisk_count;
+
+/**
+ * The CRC16 factor table. Created during initialisation.
+ */
+static uint16_t* rtems_fdisk_crc16_factor;
+
+/**
+ * Calculate the CRC16 checksum.
+ *
+ * @param _b The byte to checksum.
+ * @param _c The current checksum.
+ */
+#define rtems_fdisk_calc_crc16(_b, _c) \
+ rtems_fdisk_crc16_factor[((_b) ^ ((_c) & 0xff)) & 0xff] ^ (((_c) >> 8) & 0xff)
+
+/**
+ * Generate the CRC table.
+ *
+ * @param pattern The seed pattern for the table of factors.
+ * @relval RTEMS_SUCCESSFUL The table was generated.
+ * @retval RTEMS_NO_MEMORY The table could not be allocated from the heap.
+ */
+rtems_status_code
+rtems_fdisk_crc16_gen_factors (uint16_t pattern)
+{
+ uint32_t b;
+
+ rtems_fdisk_crc16_factor = malloc (sizeof (uint16_t) * 256);
+ if (!rtems_fdisk_crc16_factor)
+ return RTEMS_NO_MEMORY;
+
+ for (b = 0; b < 256; b++)
+ {
+ uint32_t i;
+ uint16_t v = b;
+ for (i = 8; i--;)
+ v = v & 1 ? (v >> 1) ^ pattern : v >> 1;
+ rtems_fdisk_crc16_factor[b] = v & 0xffff;
+ }
+ return RTEMS_SUCCESSFUL;
+}
+
+#if RTEMS_FDISK_TRACE
+/**
+ * Print a message to the flash disk output and flush it.
+ *
+ * @param fd The flashdisk control structure.
+ * @param format The format string. See printf for details.
+ * @param ... The arguments for the format text.
+ * @return int The number of bytes written to the output.
+ */
+static int
+rtems_fdisk_printf (const rtems_flashdisk* fd, const char *format, ...)
+{
+ int ret = 0;
+ if (fd->info_level >= 3)
+ {
+ va_list args;
+ va_start (args, format);
+ fprintf (stdout, "fdisk:");
+ ret = vfprintf (stdout, format, args);
+ fprintf (stdout, "\n");
+ fflush (stdout);
+ va_end (args);
+ }
+ return ret;
+}
+
+/**
+ * Print a info message to the flash disk output and flush it.
+ *
+ * @param fd The flashdisk control structure.
+ * @param format The format string. See printf for details.
+ * @param ... The arguments for the format text.
+ * @return int The number of bytes written to the output.
+ */
+static int
+rtems_fdisk_info (const rtems_flashdisk* fd, const char *format, ...)
+{
+ int ret = 0;
+ if (fd->info_level >= 2)
+ {
+ va_list args;
+ va_start (args, format);
+ fprintf (stdout, "fdisk:");
+ ret = vfprintf (stdout, format, args);
+ fprintf (stdout, "\n");
+ fflush (stdout);
+ va_end (args);
+ }
+ return ret;
+}
+
+/**
+ * Print a warning to the flash disk output and flush it.
+ *
+ * @param fd The flashdisk control structure.
+ * @param format The format string. See printf for details.
+ * @param ... The arguments for the format text.
+ * @return int The number of bytes written to the output.
+ */
+static int
+rtems_fdisk_warning (const rtems_flashdisk* fd, const char *format, ...)
+{
+ int ret = 0;
+ if (fd->info_level >= 1)
+ {
+ va_list args;
+ va_start (args, format);
+ fprintf (stdout, "fdisk:warning:");
+ ret = vfprintf (stdout, format, args);
+ fprintf (stdout, "\n");
+ fflush (stdout);
+ va_end (args);
+ }
+ return ret;
+}
+#endif
+
+/**
+ * Print an error to the flash disk output and flush it.
+ *
+ * @param format The format string. See printf for details.
+ * @param ... The arguments for the format text.
+ * @return int The number of bytes written to the output.
+ */
+static int
+rtems_fdisk_error (const char *format, ...)
+{
+ int ret;
+ va_list args;
+ va_start (args, format);
+ fprintf (stderr, "fdisk:error:");
+ ret = vfprintf (stderr, format, args);
+ fprintf (stderr, "\n");
+ fflush (stderr);
+ va_end (args);
+ return ret;
+}
+
+/**
+ * Print an abort message, flush it then abort the program.
+ *
+ * @param format The format string. See printf for details.
+ * @param ... The arguments for the format text.
+ */
+static void
+rtems_fdisk_abort (const char *format, ...)
+{
+ va_list args;
+ va_start (args, format);
+ fprintf (stderr, "fdisk:abort:");
+ vfprintf (stderr, format, args);
+ fprintf (stderr, "\n");
+ fflush (stderr);
+ va_end (args);
+ exit (1);
+}
+
+/**
+ * Initialise the segment control queue.
+ */
+static void
+rtems_fdisk_segment_queue_init (rtems_fdisk_segment_ctl_queue* queue)
+{
+ queue->head = queue->tail = 0;
+ queue->count = 0;
+}
+
+/**
+ * Push to the head of the segment control queue.
+ */
+static void
+rtems_fdisk_segment_queue_push_head (rtems_fdisk_segment_ctl_queue* queue,
+ rtems_fdisk_segment_ctl* sc)
+{
+ if (sc)
+ {
+ sc->next = queue->head;
+ queue->head = sc;
+
+ if (queue->tail == 0)
+ queue->tail = sc;
+ queue->count++;
+ }
+}
+
+/**
+ * Pop the head of the segment control queue.
+ */
+static rtems_fdisk_segment_ctl*
+rtems_fdisk_segment_queue_pop_head (rtems_fdisk_segment_ctl_queue* queue)
+{
+ if (queue->head)
+ {
+ rtems_fdisk_segment_ctl* sc = queue->head;
+
+ queue->head = sc->next;
+ if (!queue->head)
+ queue->tail = 0;
+
+ queue->count--;
+
+ sc->next = 0;
+
+ return sc;
+ }
+
+ return 0;
+}
+
+/**
+ * Push to the tail of the segment control queue.
+ */
+static void
+rtems_fdisk_segment_queue_push_tail (rtems_fdisk_segment_ctl_queue* queue,
+ rtems_fdisk_segment_ctl* sc)
+{
+ if (sc)
+ {
+ sc->next = 0;
+
+ if (queue->head)
+ {
+ queue->tail->next = sc;
+ queue->tail = sc;
+ }
+ else
+ {
+ queue->head = queue->tail = sc;
+ }
+
+ queue->count++;
+ }
+}
+
+/**
+ * Remove from the segment control queue.
+ */
+static void
+rtems_fdisk_segment_queue_remove (rtems_fdisk_segment_ctl_queue* queue,
+ rtems_fdisk_segment_ctl* sc)
+{
+ rtems_fdisk_segment_ctl* prev = 0;
+ rtems_fdisk_segment_ctl* it = queue->head;
+
+ /*
+ * Do not change sc->next as sc could be on another queue.
+ */
+
+ while (it)
+ {
+ if (sc == it)
+ {
+ if (prev == 0)
+ {
+ queue->head = sc->next;
+ if (queue->head == 0)
+ queue->tail = 0;
+ }
+ else
+ {
+ prev->next = sc->next;
+ if (queue->tail == sc)
+ queue->tail = prev;
+ }
+ sc->next = 0;
+ queue->count--;
+ break;
+ }
+
+ prev = it;
+ it = it->next;
+ }
+}
+
+/**
+ * Insert into the segment control queue before the specific
+ * segment control item.
+ */
+static void
+rtems_fdisk_segment_queue_insert_before (rtems_fdisk_segment_ctl_queue* queue,
+ rtems_fdisk_segment_ctl* item,
+ rtems_fdisk_segment_ctl* sc)
+{
+ if (item)
+ {
+ rtems_fdisk_segment_ctl** prev = &queue->head;
+ rtems_fdisk_segment_ctl* it = queue->head;
+
+ while (it)
+ {
+ if (item == it)
+ {
+ sc->next = item;
+ *prev = sc;
+ queue->count++;
+ return;
+ }
+
+ prev = &it->next;
+ it = it->next;
+ }
+ }
+
+ rtems_fdisk_segment_queue_push_tail (queue, sc);
+}
+
+/**
+ * Count the number of elements on the list.
+ */
+static uint32_t
+rtems_fdisk_segment_queue_count (rtems_fdisk_segment_ctl_queue* queue)
+{
+ return queue->count;
+}
+
+/**
+ * Count the number of elements on the list.
+ */
+static uint32_t
+rtems_fdisk_segment_count_queue (rtems_fdisk_segment_ctl_queue* queue)
+{
+ rtems_fdisk_segment_ctl* sc = queue->head;
+ uint32_t count = 0;
+
+ while (sc)
+ {
+ count++;
+ sc = sc->next;
+ }
+
+ return count;
+}
+
+/**
+ * See if a segment control is present on this queue.
+ */
+static bool
+rtems_fdisk_segment_queue_present (rtems_fdisk_segment_ctl_queue* queue,
+ rtems_fdisk_segment_ctl* sc)
+{
+ rtems_fdisk_segment_ctl* it = queue->head;
+
+ while (it)
+ {
+ if (it == sc)
+ return true;
+ it = it->next;
+ }
+
+ return false;
+}
+
+/**
+ * Format a string with the queue status.
+ */
+static void
+rtems_fdisk_queue_status (rtems_flashdisk* fd,
+ rtems_fdisk_segment_ctl* sc,
+ char queues[5])
+{
+ queues[0] = rtems_fdisk_segment_queue_present (&fd->available, sc) ? 'A' : '-';
+ queues[1] = rtems_fdisk_segment_queue_present (&fd->used, sc) ? 'U' : '-';
+ queues[2] = rtems_fdisk_segment_queue_present (&fd->erase, sc) ? 'E' : '-';
+ queues[3] = rtems_fdisk_segment_queue_present (&fd->failed, sc) ? 'F' : '-';
+ queues[4] = '\0';
+}
+
+/**
+ * Check if the page descriptor is erased.
+ */
+static bool
+rtems_fdisk_page_desc_erased (const rtems_fdisk_page_desc* pd)
+{
+ return ((pd->crc == 0xffff) &&
+ (pd->flags == 0xffff) &&
+ (pd->block == 0xffffffff)) ? true : false;
+}
+
+/**
+ * Check if the flags are set. The flags are inverted as we can
+ * only set a flag by changing it from 1 to 0.
+ */
+static bool
+rtems_fdisk_page_desc_flags_set (rtems_fdisk_page_desc* pd, uint16_t flags)
+{
+ return (pd->flags & flags) == 0 ? true : false;
+}
+
+/**
+ * Check if the flags are clear. The flags are inverted as we can
+ * only set a flag by changing it from 1 to 0.
+ */
+static bool
+rtems_fdisk_page_desc_flags_clear (rtems_fdisk_page_desc* pd, uint16_t flags)
+{
+ return (pd->flags & flags) == flags ? true : false;
+}
+
+/**
+ * Set the flags. Setting means clear the bit to 0.
+ */
+static void
+rtems_fdisk_page_desc_set_flags (rtems_fdisk_page_desc* pd, uint16_t flags)
+{
+ pd->flags &= ~flags;
+}
+
+/**
+ * Get the segment descriptor for a device and segment. There are
+ * no range checks.
+ */
+static const rtems_fdisk_segment_desc*
+rtems_fdisk_seg_descriptor (const rtems_flashdisk* fd,
+ uint32_t device,
+ uint32_t segment)
+{
+ return fd->devices[device].segments[segment].descriptor;
+}
+
+/**
+ * Count the segments for a device.
+ */
+static uint32_t
+rtems_fdisk_count_segments (const rtems_fdisk_device_desc* dd)
+{
+ uint32_t count = 0;
+ uint32_t segment;
+ for (segment = 0; segment < dd->segment_count; segment++)
+ count += dd->segments[segment].count;
+ return count;
+}
+
+/**
+ * Calculate the pages in a segment give the segment size and the
+ * page size.
+ *
+ * @param sd The segment descriptor.
+ * @param page_size The page size in bytes.
+ */
+static uint32_t
+rtems_fdisk_pages_in_segment (const rtems_fdisk_segment_desc* sd,
+ uint32_t page_size)
+{
+ return sd->size / page_size;
+}
+
+/**
+ * Calculate the number of pages needed to hold the page descriptors.
+ * The calculation need to round up.
+ *
+ * The segment control contains the number of pages used as descriptors
+ * and should be used rather than this call where possible.
+ */
+static uint32_t
+rtems_fdisk_page_desc_pages (const rtems_fdisk_segment_desc* sd,
+ uint32_t page_size)
+{
+ uint32_t pages = rtems_fdisk_pages_in_segment (sd, page_size);
+ uint32_t bytes = pages * sizeof (rtems_fdisk_page_desc);
+ return ((bytes - 1) / page_size) + 1;
+}
+
+/**
+ * The number of available pages is the total pages less the
+ * active, used and bad pages.
+ */
+static uint32_t
+rtems_fdisk_seg_pages_available (const rtems_fdisk_segment_ctl* sc)
+{
+ return sc->pages - (sc->pages_active + sc->pages_used + sc->pages_bad);
+}
+/**
+ * Find the next available page in a segment.
+ */
+static uint32_t
+rtems_fdisk_seg_next_available_page (rtems_fdisk_segment_ctl* sc)
+{
+ rtems_fdisk_page_desc* pd = &sc->page_descriptors[0];
+ uint32_t page;
+
+ for (page = 0; page < sc->pages; page++, pd++)
+ if (rtems_fdisk_page_desc_erased (pd))
+ break;
+
+ return page;
+}
+
+/**
+ * Find the segment on the queue that has the most free pages.
+ */
+static rtems_fdisk_segment_ctl*
+rtems_fdisk_seg_most_available (const rtems_fdisk_segment_ctl_queue* queue)
+{
+ rtems_fdisk_segment_ctl* sc = queue->head;
+ rtems_fdisk_segment_ctl* biggest = queue->head;
+
+ while (sc)
+ {
+ if (rtems_fdisk_seg_pages_available (sc) >
+ rtems_fdisk_seg_pages_available (biggest))
+ biggest = sc;
+ sc = sc->next;
+ }
+
+ return biggest;
+}
+
+/**
+ * Is the segment all used ?
+ */
+#if 0
+static bool
+rtems_fdisk_seg_pages_all_used (const rtems_fdisk_segment_ctl* sc)
+{
+ return sc->pages == (sc->pages_used + sc->pages_bad) ? true : false;
+}
+#endif
+
+/**
+ * Calculate the blocks in a device. This is the number of
+ * pages less the pages hold page descriptors. This call be used
+ * early in the initialisation process and does not rely on
+ * the system being fully initialised.
+ *
+ * @param dd The device descriptor.
+ * @param page_size The page size in bytes.
+ */
+static uint32_t
+rtems_fdisk_blocks_in_device (const rtems_fdisk_device_desc* dd,
+ uint32_t page_size)
+{
+ uint32_t count = 0;
+ uint32_t s;
+ for (s = 0; s < dd->segment_count; s++)
+ {
+ const rtems_fdisk_segment_desc* sd = &dd->segments[s];
+ count +=
+ (rtems_fdisk_pages_in_segment (sd, page_size) -
+ rtems_fdisk_page_desc_pages (sd, page_size)) * sd->count;
+ }
+ return count;
+}
+
+/**
+ * Read a block of data from a segment.
+ */
+static int
+rtems_fdisk_seg_read (const rtems_flashdisk* fd,
+ uint32_t device,
+ uint32_t segment,
+ uint32_t offset,
+ void* buffer,
+ uint32_t size)
+{
+ const rtems_fdisk_segment_desc* sd;
+ const rtems_fdisk_driver_handlers* ops;
+ sd = rtems_fdisk_seg_descriptor (fd, device, segment);
+ ops = fd->devices[device].descriptor->flash_ops;
+#if RTEMS_FDISK_TRACE
+ rtems_fdisk_printf (fd, " seg-read: %02d-%03d: o=%08x s=%d",
+ device, segment, offset, size);
+#endif
+ return ops->read (sd, device, segment, offset, buffer, size);
+}
+
+/**
+ * Write a block of data to a segment. It is assumed the
+ * location in the segment is erased and able to take the
+ * data.
+ */
+static int
+rtems_fdisk_seg_write (const rtems_flashdisk* fd,
+ uint32_t device,
+ uint32_t segment,
+ uint32_t offset,
+ const void* buffer,
+ uint32_t size)
+{
+ const rtems_fdisk_segment_desc* sd;
+ const rtems_fdisk_driver_handlers* ops;
+ sd = rtems_fdisk_seg_descriptor (fd, device, segment);
+ ops = fd->devices[device].descriptor->flash_ops;
+#if RTEMS_FDISK_TRACE
+ rtems_fdisk_printf (fd, " seg-write: %02d-%03d: o=%08x s=%d",
+ device, segment, offset, size);
+#endif
+ return ops->write (sd, device, segment, offset, buffer, size);
+}
+
+/**
+ * Blank check the area of a segment.
+ */
+static int
+rtems_fdisk_seg_blank_check (const rtems_flashdisk* fd,
+ uint32_t device,
+ uint32_t segment,
+ uint32_t offset,
+ uint32_t size)
+{
+ const rtems_fdisk_segment_desc* sd;
+ const rtems_fdisk_driver_handlers* ops;
+ sd = rtems_fdisk_seg_descriptor (fd, device, segment);
+ ops = fd->devices[device].descriptor->flash_ops;
+#if RTEMS_FDISK_TRACE
+ rtems_fdisk_printf (fd, " seg-blank: %02d-%03d: o=%08x s=%d",
+ device, segment, offset, size);
+#endif
+ return ops->blank (sd, device, segment, offset, size);
+}
+/**
+ * Verify the data with the data in a segment.
+ */
+static int
+rtems_fdisk_seg_verify (const rtems_flashdisk* fd,
+ uint32_t device,
+ uint32_t segment,
+ uint32_t offset,
+ const void* buffer,
+ uint32_t size)
+{
+ const rtems_fdisk_segment_desc* sd;
+ const rtems_fdisk_driver_handlers* ops;
+ sd = rtems_fdisk_seg_descriptor (fd, device, segment);
+ ops = fd->devices[device].descriptor->flash_ops;
+#if RTEMS_FDISK_TRACE
+ rtems_fdisk_printf (fd, " seg-verify: %02d-%03d: o=%08x s=%d",
+ device, segment, offset, size);
+#endif
+ return ops->verify (sd, device, segment, offset, buffer, size);
+}
+
+/**
+ * Blank check a page of data in a segment.
+ */
+static int
+rtems_fdisk_seg_blank_check_page (const rtems_flashdisk* fd,
+ uint32_t device,
+ uint32_t segment,
+ uint32_t page)
+{
+ return rtems_fdisk_seg_blank_check (fd, device, segment,
+ page * fd->block_size, fd->block_size);
+}
+
+/**
+ * Read a page of data from a segment.
+ */
+static int
+rtems_fdisk_seg_read_page (const rtems_flashdisk* fd,
+ uint32_t device,
+ uint32_t segment,
+ uint32_t page,
+ void* buffer)
+{
+ return rtems_fdisk_seg_read (fd, device, segment,
+ page * fd->block_size, buffer, fd->block_size);
+}
+
+/**
+ * Write a page of data to a segment.
+ */
+static int
+rtems_fdisk_seg_write_page (const rtems_flashdisk* fd,
+ uint32_t device,
+ uint32_t segment,
+ uint32_t page,
+ const void* buffer)
+{
+ if ((fd->flags & RTEMS_FDISK_BLANK_CHECK_BEFORE_WRITE))
+ {
+ int ret = rtems_fdisk_seg_blank_check_page (fd, device, segment, page);
+ if (ret)
+ return ret;
+ }
+ return rtems_fdisk_seg_write (fd, device, segment,
+ page * fd->block_size, buffer, fd->block_size);
+}
+
+/**
+ * Verify a page of data with the data in the segment.
+ */
+static int
+rtems_fdisk_seg_verify_page (const rtems_flashdisk* fd,
+ uint32_t device,
+ uint32_t segment,
+ uint32_t page,
+ const void* buffer)
+{
+ return rtems_fdisk_seg_verify (fd, device, segment,
+ page * fd->block_size, buffer, fd->block_size);
+}
+
+/**
+ * Copy a page of data from one segment to another segment.
+ */
+static int
+rtems_fdisk_seg_copy_page (const rtems_flashdisk* fd,
+ uint32_t src_device,
+ uint32_t src_segment,
+ uint32_t src_page,
+ uint32_t dst_device,
+ uint32_t dst_segment,
+ uint32_t dst_page)
+{
+ int ret;
+#if RTEMS_FDISK_TRACE
+ rtems_fdisk_printf (fd, " seg-copy-page: %02d-%03d~%03d=>%02d-%03d~%03d",
+ src_device, src_segment, src_page,
+ dst_device, dst_segment, dst_page);
+#endif
+ ret = rtems_fdisk_seg_read_page (fd, src_device, src_segment, src_page,
+ fd->copy_buffer);
+ if (ret)
+ return ret;
+ return rtems_fdisk_seg_write_page (fd, dst_device, dst_segment, dst_page,
+ fd->copy_buffer);
+}
+
+/**
+ * Write the page descriptor to a segment. This code assumes the page
+ * descriptors are located at offset 0 in the segment.
+ */
+static int
+rtems_fdisk_seg_write_page_desc (const rtems_flashdisk* fd,
+ uint32_t device,
+ uint32_t segment,
+ uint32_t page,
+ const rtems_fdisk_page_desc* page_desc)
+{
+ uint32_t offset = page * sizeof (rtems_fdisk_page_desc);
+ if ((fd->flags & RTEMS_FDISK_BLANK_CHECK_BEFORE_WRITE))
+ {
+ int ret = rtems_fdisk_seg_blank_check (fd, device, segment,
+ offset,
+ sizeof (rtems_fdisk_page_desc));
+ if (ret)
+ return ret;
+ }
+ return rtems_fdisk_seg_write (fd, device, segment, offset,
+ page_desc, sizeof (rtems_fdisk_page_desc));
+}
+
+/**
+ * Write the page descriptor flags to a segment. This code assumes the page
+ * descriptors are located at offset 0 in the segment.
+ */
+static int
+rtems_fdisk_seg_write_page_desc_flags (const rtems_flashdisk* fd,
+ uint32_t device,
+ uint32_t segment,
+ uint32_t page,
+ const rtems_fdisk_page_desc* page_desc)
+{
+ uint32_t offset = ((page * sizeof (rtems_fdisk_page_desc)) +
+ ((uint8_t*) &page_desc->flags) - ((uint8_t*) page_desc));
+ if ((fd->flags & RTEMS_FDISK_BLANK_CHECK_BEFORE_WRITE))
+ {
+ uint16_t flash_flags;
+ int ret;
+ ret = rtems_fdisk_seg_read (fd, device, segment, offset,
+ &flash_flags, sizeof (flash_flags));
+ if (ret)
+ return ret;
+ if ((flash_flags & page_desc->flags) != page_desc->flags)
+ {
+ rtems_fdisk_error (" seg-write-page-flags: %02d-%03d-%03d: " \
+ "flags not erased: 0x%04 -> 0x%04x",
+ device, segment, page, flash_flags, page_desc->flags);
+ return ret;
+ }
+ }
+ return rtems_fdisk_seg_write (fd, device, segment, offset,
+ &page_desc->flags, sizeof (page_desc->flags));
+}
+
+/**
+ * Erase a segment.
+ */
+static int
+rtems_fdisk_seg_erase (const rtems_flashdisk* fd,
+ uint32_t device,
+ uint32_t segment)
+{
+ const rtems_fdisk_segment_desc* sd;
+ const rtems_fdisk_driver_handlers* ops;
+ sd = rtems_fdisk_seg_descriptor (fd, device, segment);
+ ops = fd->devices[device].descriptor->flash_ops;
+#if RTEMS_FDISK_TRACE
+ rtems_fdisk_printf (fd, " seg-erase: %02d-%03d", device, segment);
+#endif
+ return ops->erase (sd, device, segment);
+}
+
+/**
+ * Erase a device.
+ */
+static int
+rtems_fdisk_device_erase (const rtems_flashdisk* fd, uint32_t device)
+{
+ const rtems_fdisk_driver_handlers* ops;
+ ops = fd->devices[device].descriptor->flash_ops;
+#if RTEMS_FDISK_TRACE
+ rtems_fdisk_printf (fd, " device-erase: %02d", device);
+#endif
+ return ops->erase_device (fd->devices[device].descriptor, device);
+}
+
+/**
+ * Erase all flash.
+ */
+static int
+rtems_fdisk_erase_flash (const rtems_flashdisk* fd)
+{
+ uint32_t device;
+ for (device = 0; device < fd->device_count; device++)
+ {
+ int ret;
+
+#if RTEMS_FDISK_TRACE
+ rtems_fdisk_info (fd, " erase-flash:%02d", device);
+#endif
+
+ ret = rtems_fdisk_device_erase (fd, device);
+
+ if (ret != 0)
+ return ret;
+ }
+ return 0;
+}
+
+/**
+ * Calculate the checksum of a page in a segment.
+ */
+static uint16_t
+rtems_fdisk_page_checksum (const uint8_t* buffer, uint32_t page_size)
+{
+ uint16_t cs = 0xffff;
+ uint32_t i;
+
+ for (i = 0; i < page_size; i++, buffer++)
+ cs = rtems_fdisk_calc_crc16 (cs, *buffer);
+
+ return cs;
+}
+
+/**
+ * Erase the segment.
+ */
+static int
+rtems_fdisk_erase_segment (rtems_flashdisk* fd, rtems_fdisk_segment_ctl* sc)
+{
+ int ret = rtems_fdisk_seg_erase (fd, sc->device, sc->segment);
+ if (ret)
+ {
+ rtems_fdisk_error (" erase-segment:%02d-%03d: " \
+ "segment erase failed: %s (%d)",
+ sc->device, sc->segment, strerror (ret), ret);
+ sc->failed = true;
+ if (!rtems_fdisk_segment_queue_present (&fd->failed, sc))
+ rtems_fdisk_segment_queue_push_tail (&fd->failed, sc);
+ return ret;
+ }
+
+ sc->erased++;
+
+ memset (sc->page_descriptors, 0xff, sc->pages_desc * fd->block_size);
+
+ sc->pages_active = 0;
+ sc->pages_used = 0;
+ sc->pages_bad = 0;
+
+ sc->failed = false;
+
+ /*
+ * Push to the tail of the available queue. It is a very
+ * simple type of wear reduction. Every other available
+ * segment will now get a go.
+ */
+ rtems_fdisk_segment_queue_push_tail (&fd->available, sc);
+
+ return 0;
+}
+
+/**
+ * Erase used segment.
+ */
+static int
+rtems_fdisk_erase_used (rtems_flashdisk* fd)
+{
+ rtems_fdisk_segment_ctl* sc;
+ int latched_ret = 0;
+
+ while ((sc = rtems_fdisk_segment_queue_pop_head (&fd->erase)))
+ {
+ /*
+ * The segment will either end up on the available queue or
+ * the failed queue.
+ */
+ int ret = rtems_fdisk_erase_segment (fd, sc);
+ if (ret && !latched_ret)
+ latched_ret = ret;
+ }
+
+ return latched_ret;
+}
+
+/**
+ * Queue a segment. This is done after some of the stats for the segment
+ * have been changed and this may effect the order the segment pages have in
+ * the queue of available pages.
+ *
+ * @param fd The flash disk control table.
+ * @param sc The segment control table to be reallocated
+ */
+static void
+rtems_fdisk_queue_segment (rtems_flashdisk* fd, rtems_fdisk_segment_ctl* sc)
+{
+#if RTEMS_FDISK_TRACE
+ rtems_fdisk_info (fd, " queue-seg:%02d-%03d: p=%d a=%d u=%d b=%d f=%s n=%s",
+ sc->device, sc->segment,
+ sc->pages, sc->pages_active, sc->pages_used, sc->pages_bad,
+ sc->failed ? "FAILED" : "no", sc->next ? "set" : "null");
+#endif
+
+ /*
+ * If the segment has failed then check the failed queue and append
+ * if not failed.
+ */
+ if (sc->failed)
+ {
+ if (!rtems_fdisk_segment_queue_present (&fd->failed, sc))
+ rtems_fdisk_segment_queue_push_tail (&fd->failed, sc);
+ return;
+ }
+
+ /*
+ * Remove the queue from the available or used queue.
+ */
+ rtems_fdisk_segment_queue_remove (&fd->available, sc);
+ rtems_fdisk_segment_queue_remove (&fd->used, sc);
+
+ /*
+ * Are all the pages in the segment used ?
+ * If they are and the driver has been configured to background
+ * erase place the segment on the used queue. If not configured
+ * to background erase perform the erase now.
+ *
+ */
+ if (rtems_fdisk_seg_pages_available (sc) == 0)
+ {
+ if (sc->pages_active)
+ {
+ /*
+ * Keep the used queue sorted by the most number of used
+ * pages. When we compact we want to move the pages into
+ * a new segment and cover more than one segment.
+ */
+ rtems_fdisk_segment_ctl* seg = fd->used.head;
+
+ while (seg)
+ {
+ if (sc->pages_used > seg->pages_used)
+ break;
+ seg = seg->next;
+ }
+
+ if (seg)
+ rtems_fdisk_segment_queue_insert_before (&fd->used, seg, sc);
+ else
+ rtems_fdisk_segment_queue_push_tail (&fd->used, sc);
+ }
+ else
+ {
+ if ((fd->flags & RTEMS_FDISK_BACKGROUND_ERASE))
+ rtems_fdisk_segment_queue_push_tail (&fd->erase, sc);
+ else
+ rtems_fdisk_erase_segment (fd, sc);
+ }
+ }
+ else
+ {
+ /*
+ * The segment has pages available so place back onto the
+ * available list. The list is sorted from the least number
+ * of available pages to the most. This approach means
+ * the pages of a partially filled segment will be filled
+ * before moving onto another emptier segment. This keeps
+ * empty segments longer aiding compaction.
+ *
+ * The down side is the wear effect as a single segment
+ * could be used more than segment. This will not be
+ * addressed until wear support is added.
+ *
+ * @note Wear support can be added by having counts for
+ * for the number of times a segment is erased. This
+ * available list is then sorted on the least number
+ * of available pages then empty segments are sorted
+ * on the least number of erases the segment has.
+ *
+ * The erase count can be stored in specially flaged
+ * pages and contain a counter (32bits?) and 32 bits
+ * for each segment. When a segment is erased a
+ * bit is cleared for that segment. When 32 erasers
+ * has occurred the page is re-written to the flash
+ * with all the counters updated with the number of
+ * bits cleared and all bits set back to 1.
+ */
+ rtems_fdisk_segment_ctl* seg = fd->available.head;
+
+ while (seg)
+ {
+ if (rtems_fdisk_seg_pages_available (sc) <
+ rtems_fdisk_seg_pages_available (seg))
+ break;
+ seg = seg->next;
+ }
+
+ if (seg)
+ rtems_fdisk_segment_queue_insert_before (&fd->available, seg, sc);
+ else
+ rtems_fdisk_segment_queue_push_tail (&fd->available, sc);
+ }
+}
+
+/**
+ * Compact the used segments to free what is available. Find the segment
+ * with the most avalable number of pages and see if we have
+ * used segments that will fit. The used queue is sorted on the least
+ * number of active pages.
+ */
+static int
+rtems_fdisk_compact (rtems_flashdisk* fd)
+{
+ uint32_t compacted_segs = 0;
+
+ while (fd->used.head)
+ {
+ rtems_fdisk_segment_ctl* dsc;
+ rtems_fdisk_segment_ctl* ssc;
+ uint32_t dst_pages;
+ uint32_t segments;
+ uint32_t pages;
+
+#if RTEMS_FDISK_TRACE
+ rtems_fdisk_printf (fd, " compacting");
+#endif
+
+ dsc = rtems_fdisk_seg_most_available (&fd->available);
+
+ if (dsc == 0)
+ {
+ rtems_fdisk_error ("compacting: no available segments to compact too");
+ return EIO;
+ }
+
+ ssc = fd->used.head;
+ dst_pages = rtems_fdisk_seg_pages_available (dsc);
+ segments = 0;
+ pages = 0;
+
+#if RTEMS_FDISK_TRACE
+ rtems_fdisk_printf (fd, " dsc:%02d-%03d: most available",
+ dsc->device, dsc->segment);
+#endif
+
+ /*
+ * Count the number of segments that have active pages that fit into
+ * the destination segment. Also limit the number of segments that
+ * we handle during one compaction. A lower number means less aggressive
+ * compaction or less delay when compacting but it may mean the disk
+ * will fill.
+ */
+
+ while (ssc &&
+ ((pages + ssc->pages_active) < dst_pages) &&
+ ((compacted_segs + segments) < fd->compact_segs))
+ {
+ pages += ssc->pages_active;
+ segments++;
+ ssc = ssc->next;
+ }
+
+ /*
+ * We need a source segment and have pages to copy and
+ * compacting one segment to another is silly. Compaction needs
+ * to free at least one more segment.
+ */
+
+ if (!ssc || (pages == 0) || ((compacted_segs + segments) == 1))
+ break;
+
+#if RTEMS_FDISK_TRACE
+ rtems_fdisk_printf (fd, " ssc scan: %d-%d: p=%ld, seg=%ld",
+ ssc->device, ssc->segment,
+ pages, segments);
+#endif
+
+ rtems_fdisk_segment_queue_remove (&fd->available, dsc);
+
+ /*
+ * We now copy the pages to the new segment.
+ */
+
+ while (pages)
+ {
+ uint32_t spage;
+ int ret;
+
+ ssc = rtems_fdisk_segment_queue_pop_head (&fd->used);
+
+ if (ssc)
+ {
+ uint32_t used = 0;
+ uint32_t active = 0;
+ for (spage = 0; spage < ssc->pages; spage++)
+ {
+ rtems_fdisk_page_desc* spd = &ssc->page_descriptors[spage];
+
+ if (rtems_fdisk_page_desc_flags_set (spd, RTEMS_FDISK_PAGE_ACTIVE) &&
+ !rtems_fdisk_page_desc_flags_set (spd, RTEMS_FDISK_PAGE_USED))
+ {
+ rtems_fdisk_page_desc* dpd;
+ uint32_t dpage;
+
+ dpage = rtems_fdisk_seg_next_available_page (dsc);
+ dpd = &dsc->page_descriptors[dpage];
+
+ active++;
+
+ if (dpage >= dsc->pages)
+ {
+ rtems_fdisk_error ("compacting: %02d-%03d: " \
+ "no page desc available: %d",
+ dsc->device, dsc->segment,
+ rtems_fdisk_seg_pages_available (dsc));
+ dsc->failed = true;
+ rtems_fdisk_queue_segment (fd, dsc);
+ rtems_fdisk_segment_queue_push_head (&fd->used, ssc);
+ return EIO;
+ }
+
+#if RTEMS_FDISK_TRACE
+ rtems_fdisk_info (fd, "compacting: %02d-%03d-%03d=>%02d-%03d-%03d",
+ ssc->device, ssc->segment, spage,
+ dsc->device, dsc->segment, dpage);
+#endif
+ ret = rtems_fdisk_seg_copy_page (fd, ssc->device, ssc->segment,
+ spage + ssc->pages_desc,
+ dsc->device, dsc->segment,
+ dpage + dsc->pages_desc);
+ if (ret)
+ {
+ rtems_fdisk_error ("compacting: %02d-%03d-%03d=>" \
+ "%02d-%03d-%03d: " \
+ "copy page failed: %s (%d)",
+ ssc->device, ssc->segment, spage,
+ dsc->device, dsc->segment, dpage,
+ strerror (ret), ret);
+ dsc->failed = true;
+ rtems_fdisk_queue_segment (fd, dsc);
+ rtems_fdisk_segment_queue_push_head (&fd->used, ssc);
+ return ret;
+ }
+
+ *dpd = *spd;
+
+ ret = rtems_fdisk_seg_write_page_desc (fd,
+ dsc->device, dsc->segment,
+ dpage, dpd);
+
+ if (ret)
+ {
+ rtems_fdisk_error ("compacting: %02d-%03d-%03d=>" \
+ "%02d-%03d-%03d: copy pd failed: %s (%d)",
+ ssc->device, ssc->segment, spage,
+ dsc->device, dsc->segment, dpage,
+ strerror (ret), ret);
+ dsc->failed = true;
+ rtems_fdisk_queue_segment (fd, dsc);
+ rtems_fdisk_segment_queue_push_head (&fd->used, ssc);
+ return ret;
+ }
+
+ dsc->pages_active++;
+
+ /*
+ * No need to set the used bit on the source page as the
+ * segment will be erased. Power down could be a problem.
+ * We do the stats to make sure everything is as it should
+ * be.
+ */
+
+ ssc->pages_active--;
+ ssc->pages_used++;
+
+ fd->blocks[spd->block].segment = dsc;
+ fd->blocks[spd->block].page = dpage;
+
+ /*
+ * Place the segment on to the correct queue.
+ */
+ rtems_fdisk_queue_segment (fd, dsc);
+
+ pages--;
+ }
+ else
+ used++;
+ }
+
+#if RTEMS_FDISK_TRACE
+ rtems_fdisk_printf (fd, "ssc end: %d-%d: p=%ld, a=%ld, u=%ld",
+ ssc->device, ssc->segment,
+ pages, active, used);
+#endif
+ if (ssc->pages_active != 0)
+ {
+ rtems_fdisk_error ("compacting: ssc pages not 0: %d",
+ ssc->pages_active);
+ }
+
+ ret = rtems_fdisk_erase_segment (fd, ssc);
+
+ if (ret)
+ return ret;
+ }
+ }
+
+ compacted_segs += segments;
+ }
+
+ return 0;
+}
+
+/**
+ * Recover the block mappings from the devices.
+ */
+static int
+rtems_fdisk_recover_block_mappings (rtems_flashdisk* fd)
+{
+ uint32_t device;
+
+ /*
+ * Clear the queues.
+ */
+ rtems_fdisk_segment_queue_init (&fd->available);
+ rtems_fdisk_segment_queue_init (&fd->used);
+ rtems_fdisk_segment_queue_init (&fd->erase);
+ rtems_fdisk_segment_queue_init (&fd->failed);
+
+ /*
+ * Clear the lock mappings.
+ */
+ memset (fd->blocks, 0, fd->block_count * sizeof (rtems_fdisk_block_ctl));
+
+ /*
+ * Scan each segment or each device recovering the valid pages.
+ */
+ for (device = 0; device < fd->device_count; device++)
+ {
+ uint32_t segment;
+ for (segment = 0; segment < fd->devices[device].segment_count; segment++)
+ {
+ rtems_fdisk_segment_ctl* sc = &fd->devices[device].segments[segment];
+ const rtems_fdisk_segment_desc* sd = sc->descriptor;
+ rtems_fdisk_page_desc* pd;
+ uint32_t page;
+ int ret;
+
+#if RTEMS_FDISK_TRACE
+ rtems_fdisk_info (fd, "recover-block-mappings:%02d-%03d", device, segment);
+#endif
+
+ sc->pages_desc = rtems_fdisk_page_desc_pages (sd, fd->block_size);
+ sc->pages =
+ rtems_fdisk_pages_in_segment (sd, fd->block_size) - sc->pages_desc;
+
+ sc->pages_active = 0;
+ sc->pages_used = 0;
+ sc->pages_bad = 0;
+
+ sc->failed = false;
+
+ if (!sc->page_descriptors)
+ sc->page_descriptors = malloc (sc->pages_desc * fd->block_size);
+
+ if (!sc->page_descriptors)
+ rtems_fdisk_abort ("no memory for page descriptors");
+
+ pd = sc->page_descriptors;
+
+ /*
+ * The page descriptors are always at the start of the segment. Read
+ * the descriptors off the device into the segment control page
+ * descriptors.
+ *
+ * @todo It may be better to ask the driver to get these value
+ * so NAND flash could be better supported.
+ */
+ ret = rtems_fdisk_seg_read (fd, device, segment, 0, (void*) pd,
+ sc->pages_desc * fd->block_size);
+
+ if (ret)
+ {
+ rtems_fdisk_error ("recover-block-mappings:%02d-%03d: " \
+ "read page desc failed: %s (%d)",
+ device, segment, strerror (ret), ret);
+ return ret;
+ }
+
+ /*
+ * Check each page in the segement for valid pages.
+ * Update the stats for the segment so we know how many pages
+ * are active and how many are used.
+ *
+ * If the page is active see if the block is with-in range and
+ * if the block is a duplicate.
+ */
+ for (page = 0; page < sc->pages; page++, pd++)
+ {
+ if (rtems_fdisk_page_desc_erased (pd))
+ {
+ /*
+ * Is the page erased ?
+ */
+ ret = rtems_fdisk_seg_blank_check_page (fd, device, segment,
+ page + sc->pages_desc);
+
+ if (ret)
+ {
+#if RTEMS_FDISK_TRACE
+ rtems_fdisk_warning (fd, "page not blank: %d-%d-%d",
+ device, segment, page, pd->block);
+#endif
+ rtems_fdisk_page_desc_set_flags (pd, RTEMS_FDISK_PAGE_USED);
+
+ ret = rtems_fdisk_seg_write_page_desc (fd, device, segment,
+ page, pd);
+
+ if (ret)
+ {
+ rtems_fdisk_error ("forcing page to used failed: %d-%d-%d",
+ device, segment, page);
+ sc->failed = true;
+ }
+
+ sc->pages_used++;
+ }
+ }
+ else
+ {
+ if (rtems_fdisk_page_desc_flags_set (pd, RTEMS_FDISK_PAGE_USED))
+ {
+ sc->pages_used++;
+ }
+ else if (rtems_fdisk_page_desc_flags_set (pd, RTEMS_FDISK_PAGE_ACTIVE))
+ {
+ if (pd->block >= fd->block_count)
+ {
+#if RTEMS_FDISK_TRACE
+ rtems_fdisk_warning (fd,
+ "invalid block number: %d-%d-%d: block: %d",
+ device, segment, page, pd->block);
+#endif
+ sc->pages_bad++;
+ }
+ else if (fd->blocks[pd->block].segment)
+ {
+ /**
+ * @todo
+ * This may need more work later. Maybe a counter is stored with
+ * each block so we can tell which is the later block when
+ * duplicates appear. A power down with a failed wirte could cause
+ * a duplicate.
+ */
+ const rtems_fdisk_segment_ctl* bsc = fd->blocks[pd->block].segment;
+ rtems_fdisk_error ("duplicate block: %d-%d-%d: " \
+ "duplicate: %d-%d-%d",
+ bsc->device, bsc->segment,
+ fd->blocks[pd->block].page,
+ device, segment, page);
+ sc->pages_bad++;
+ }
+ else
+ {
+ /**
+ * @todo
+ * Add start up crc checks here.
+ */
+ fd->blocks[pd->block].segment = sc;
+ fd->blocks[pd->block].page = page;
+
+ /*
+ * The page is active.
+ */
+ sc->pages_active++;
+ }
+ }
+ else
+ sc->pages_bad++;
+ }
+ }
+
+ /*
+ * Place the segment on to the correct queue.
+ */
+ rtems_fdisk_queue_segment (fd, sc);
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * Read a block. The block is checked to see if the page referenced
+ * is valid and the page has a valid crc.
+ *
+ * @param fd The rtems_flashdisk control table.
+ * @param block The block number to read.
+ * @param buffer The buffer to write the data into.
+ * @return 0 No error.
+ * @return EIO Invalid block size, block number, segment pointer, crc,
+ * page flags.
+ */
+static bool
+rtems_fdisk_read_block (rtems_flashdisk* fd,
+ uint32_t block,
+ uint8_t* buffer)
+{
+ rtems_fdisk_block_ctl* bc;
+ rtems_fdisk_segment_ctl* sc;
+ rtems_fdisk_page_desc* pd;
+
+#if RTEMS_FDISK_TRACE
+ rtems_fdisk_info (fd, "read-block:%d", block);
+#endif
+
+ /*
+ * Broken out to allow info messages when testing.
+ */
+
+ if (block >= (fd->block_count - fd->unavail_blocks))
+ {
+ rtems_fdisk_error ("read-block: block out of range: %d", block);
+ return EIO;
+ }
+
+ bc = &fd->blocks[block];
+
+ if (!bc->segment)
+ {
+#if RTEMS_FDISK_TRACE
+ rtems_fdisk_info (fd, "read-block: no segment mapping: %d", block);
+#endif
+ memset (buffer, fd->block_size, 0xff);
+ return 0;
+ }
+
+ sc = fd->blocks[block].segment;
+ pd = &sc->page_descriptors[bc->page];
+
+#if RTEMS_FDISK_TRACE
+ rtems_fdisk_info (fd,
+ " read:%d=>%02d-%03d-%03d: p=%d a=%d u=%d b=%d n=%s: " \
+ "f=%04x c=%04x b=%d",
+ block, sc->device, sc->segment, bc->page,
+ sc->pages, sc->pages_active, sc->pages_used, sc->pages_bad,
+ sc->next ? "set" : "null",
+ pd->flags, pd->crc, pd->block);
+#endif
+
+ if (rtems_fdisk_page_desc_flags_set (pd, RTEMS_FDISK_PAGE_ACTIVE))
+ {
+ if (rtems_fdisk_page_desc_flags_clear (pd, RTEMS_FDISK_PAGE_USED))
+ {
+ uint16_t cs;
+
+ /*
+ * We use the segment page offset not the page number used in the
+ * driver. This skips the page descriptors.
+ */
+ int ret = rtems_fdisk_seg_read_page (fd, sc->device, sc->segment,
+ bc->page + sc->pages_desc, buffer);
+
+ if (ret)
+ {
+#if RTEMS_FDISK_TRACE
+ rtems_fdisk_info (fd,
+ "read-block:%02d-%03d-%03d: read page failed: %s (%d)",
+ sc->device, sc->segment, bc->page,
+ strerror (ret), ret);
+#endif
+ return ret;
+ }
+
+ cs = rtems_fdisk_page_checksum (buffer, fd->block_size);
+
+ if (cs == pd->crc)
+ return 0;
+
+ rtems_fdisk_error ("read-block: crc failure: %d: buffer:%04x page:%04x",
+ block, cs, pd->crc);
+ }
+ else
+ {
+ rtems_fdisk_error ("read-block: block points to used page: %d: %d-%d-%d",
+ block, sc->device, sc->segment, bc->page);
+ }
+ }
+ else
+ {
+ rtems_fdisk_error ("read-block: block page not active: %d: %d-%d-%d",
+ block, sc->device, sc->segment, bc->page);
+ }
+
+ return EIO;
+}
+
+/**
+ * Write a block. The block:
+ *
+ * # May never have existed in flash before this write.
+ * # Exists and needs to be moved to a new page.
+ *
+ * If the block does not exist in flash we need to get the next
+ * segment available to place the page into. The segments with
+ * available pages are held on the avaliable list sorted on least
+ * number of available pages as the primary key. Currently there
+ * is no secondary key. Empty segments are at the end of the list.
+ *
+ * If the block already exists we need to set the USED bit in the
+ * current page's flags. This is a single byte which changes a 1 to
+ * a 0 and can be done with a single 16 bit write. The driver for
+ * 8 bit devices should only attempt the write on the changed bit.
+ *
+ * @param fd The rtems_flashdisk control table.
+ * @param block The block number to read.
+ * @param block_size The size of the block. Must match what we have.
+ * @param buffer The buffer to write the data into.
+ * @return 0 No error.
+ * @return EIO Invalid block size, block number, segment pointer, crc,
+ * page flags.
+ */
+static int
+rtems_fdisk_write_block (rtems_flashdisk* fd,
+ uint32_t block,
+ const uint8_t* buffer)
+{
+ rtems_fdisk_block_ctl* bc;
+ rtems_fdisk_segment_ctl* sc;
+ rtems_fdisk_page_desc* pd;
+ uint32_t page;
+ int ret;
+
+#if RTEMS_FDISK_TRACE
+ rtems_fdisk_info (fd, "write-block:%d", block);
+#endif
+
+ /*
+ * Broken out to allow info messages when testing.
+ */
+
+ if (block >= (fd->block_count - fd->unavail_blocks))
+ {
+ rtems_fdisk_error ("write-block: block out of range: %d", block);
+ return EIO;
+ }
+
+ bc = &fd->blocks[block];
+
+ /*
+ * Does the page exist in flash ?
+ */
+ if (bc->segment)
+ {
+ sc = bc->segment;
+ pd = &sc->page_descriptors[bc->page];
+
+#if RTEMS_FDISK_TRACE
+ rtems_fdisk_info (fd, " write:%02d-%03d-%03d: flag used",
+ sc->device, sc->segment, bc->page);
+#endif
+
+ /*
+ * The page exists in flash so see if the page has been changed.
+ */
+ if (rtems_fdisk_seg_verify_page (fd, sc->device, sc->segment,
+ bc->page + sc->pages_desc, buffer) == 0)
+ {
+#if RTEMS_FDISK_TRACE
+ rtems_fdisk_info (fd, "write-block:%d=>%02d-%03d-%03d: page verified",
+ block, sc->device, sc->segment, bc->page);
+#endif
+ return 0;
+ }
+
+ /*
+ * The page exists in flash so we need to set the used flag
+ * in the page descriptor. The descriptor is in memory with the
+ * segment control block. We can assume this memory copy
+ * matches the flash device.
+ */
+
+ rtems_fdisk_page_desc_set_flags (pd, RTEMS_FDISK_PAGE_USED);
+
+ ret = rtems_fdisk_seg_write_page_desc_flags (fd, sc->device, sc->segment,
+ bc->page, pd);
+
+ if (ret)
+ {
+#if RTEMS_FDISK_TRACE
+ rtems_fdisk_info (fd, " write:%02d-%03d-%03d: " \
+ "write used page desc failed: %s (%d)",
+ sc->device, sc->segment, bc->page,
+ strerror (ret), ret);
+#endif
+ sc->failed = true;
+ }
+ else
+ {
+ sc->pages_active--;
+ sc->pages_used++;
+ }
+
+ /*
+ * If possible reuse this segment. This will mean the segment
+ * needs to be removed from the available list and placed
+ * back if space is still available.
+ */
+ rtems_fdisk_queue_segment (fd, sc);
+
+ /*
+ * If no background compacting then compact in the forground.
+ * If we compact we ignore the error as there is little we
+ * can do from here. The write may will work.
+ */
+ if ((fd->flags & RTEMS_FDISK_BACKGROUND_COMPACT) == 0)
+ rtems_fdisk_compact (fd);
+ }
+
+ /*
+ * Is it time to compact the disk ?
+ *
+ * We override the background compaction configruation.
+ */
+ if (rtems_fdisk_segment_count_queue (&fd->available) <=
+ fd->avail_compact_segs)
+ rtems_fdisk_compact (fd);
+
+ /*
+ * Get the next avaliable segment.
+ */
+ sc = rtems_fdisk_segment_queue_pop_head (&fd->available);
+
+ /*
+ * Is the flash disk full ?
+ */
+ if (!sc)
+ {
+ /*
+ * If compacting is configured for the background do it now
+ * to see if we can get some space back.
+ */
+ if ((fd->flags & RTEMS_FDISK_BACKGROUND_COMPACT))
+ rtems_fdisk_compact (fd);
+
+ /*
+ * Try again for some free space.
+ */
+ sc = rtems_fdisk_segment_queue_pop_head (&fd->available);
+
+ if (!sc)
+ {
+ rtems_fdisk_error ("write-block: no available pages");
+ return ENOSPC;
+ }
+ }
+
+#if RTEMS_FDISK_TRACE
+ if (fd->info_level >= 3)
+ {
+ char queues[5];
+ rtems_fdisk_queue_status (fd, sc, queues);
+ rtems_fdisk_info (fd, " write:%d=>%02d-%03d: queue check: %s",
+ block, sc->device, sc->segment, queues);
+ }
+#endif
+
+ /*
+ * Find the next avaliable page in the segment.
+ */
+
+ pd = sc->page_descriptors;
+
+ for (page = 0; page < sc->pages; page++, pd++)
+ {
+ if (rtems_fdisk_page_desc_erased (pd))
+ {
+ pd->crc = rtems_fdisk_page_checksum (buffer, fd->block_size);
+ pd->block = block;
+
+ bc->segment = sc;
+ bc->page = page;
+
+ rtems_fdisk_page_desc_set_flags (pd, RTEMS_FDISK_PAGE_ACTIVE);
+
+#if RTEMS_FDISK_TRACE
+ rtems_fdisk_info (fd, " write:%d=>%02d-%03d-%03d: write: " \
+ "p=%d a=%d u=%d b=%d n=%s: f=%04x c=%04x b=%d",
+ block, sc->device, sc->segment, page,
+ sc->pages, sc->pages_active, sc->pages_used,
+ sc->pages_bad, sc->next ? "set" : "null",
+ pd->flags, pd->crc, pd->block);
+#endif
+
+ /*
+ * We use the segment page offset not the page number used in the
+ * driver. This skips the page descriptors.
+ */
+ ret = rtems_fdisk_seg_write_page (fd, sc->device, sc->segment,
+ page + sc->pages_desc, buffer);
+ if (ret)
+ {
+#if RTEMS_FDISK_TRACE
+ rtems_fdisk_info (fd, "write-block:%02d-%03d-%03d: write page failed: " \
+ "%s (%d)", sc->device, sc->segment, page,
+ strerror (ret), ret);
+#endif
+ sc->failed = true;
+ }
+ else
+ {
+ ret = rtems_fdisk_seg_write_page_desc (fd, sc->device, sc->segment,
+ page, pd);
+ if (ret)
+ {
+#if RTEMS_FDISK_TRACE
+ rtems_fdisk_info (fd, "write-block:%02d-%03d-%03d: " \
+ "write page desc failed: %s (%d)",
+ sc->device, sc->segment, bc->page,
+ strerror (ret), ret);
+#endif
+ sc->failed = true;
+ }
+ else
+ {
+ sc->pages_active++;
+ }
+ }
+
+ rtems_fdisk_queue_segment (fd, sc);
+ return ret;
+ }
+ }
+
+ rtems_fdisk_error ("write-block: no erased page descs in segment: %d-%d",
+ sc->device, sc->segment);
+
+ sc->failed = true;
+ rtems_fdisk_queue_segment (fd, sc);
+
+ return EIO;
+}
+
+/**
+ * Disk READ request handler. This primitive copies data from the
+ * flash disk to the supplied buffer and invoke the callout function
+ * to inform upper layer that reading is completed.
+ *
+ * @param req Pointer to the READ block device request info.
+ * @retval int The ioctl return value.
+ */
+static int
+rtems_fdisk_read (rtems_flashdisk* fd, rtems_blkdev_request* req)
+{
+ rtems_blkdev_sg_buffer* sg = req->bufs;
+ uint32_t buf;
+ int ret = 0;
+
+ for (buf = 0; (ret == 0) && (buf < req->bufnum); buf++, sg++)
+ {
+ uint8_t* data;
+ uint32_t fb;
+ uint32_t b;
+ fb = sg->length / fd->block_size;
+ data = sg->buffer;
+ for (b = 0; b < fb; b++, data += fd->block_size)
+ {
+ ret = rtems_fdisk_read_block (fd, sg->block + b, data);
+ if (ret)
+ break;
+ }
+ }
+
+ req->status = ret ? RTEMS_IO_ERROR : RTEMS_SUCCESSFUL;
+ req->req_done (req->done_arg, req->status);
+
+ return ret;
+}
+
+/**
+ * Flash disk WRITE request handler. This primitive copies data from
+ * supplied buffer to flash disk and invoke the callout function to inform
+ * upper layer that writing is completed.
+ *
+ * @param req Pointers to the WRITE block device request info.
+ * @retval int The ioctl return value.
+ */
+static int
+rtems_fdisk_write (rtems_flashdisk* fd, rtems_blkdev_request* req)
+{
+ rtems_blkdev_sg_buffer* sg = req->bufs;
+ uint32_t buf;
+ int ret = 0;
+
+ for (buf = 0; (ret == 0) && (buf < req->bufnum); buf++, sg++)
+ {
+ uint8_t* data;
+ uint32_t fb;
+ uint32_t b;
+ fb = sg->length / fd->block_size;
+ data = sg->buffer;
+ for (b = 0; b < fb; b++, data += fd->block_size)
+ {
+ ret = rtems_fdisk_write_block (fd, sg->block + b, data);
+ if (ret)
+ break;
+ }
+ }
+
+ req->status = ret ? RTEMS_IO_ERROR : RTEMS_SUCCESSFUL;
+ req->req_done (req->done_arg, req->status);
+
+ return 0;
+}
+
+/**
+ * Flash disk erase disk.
+ *
+ * @param fd The flashdisk data.
+ * @retval int The ioctl return value.
+ */
+static int
+rtems_fdisk_erase_disk (rtems_flashdisk* fd)
+{
+ uint32_t device;
+ int ret;
+
+#if RTEMS_FDISK_TRACE
+ rtems_fdisk_info (fd, "erase-disk");
+#endif
+
+ ret = rtems_fdisk_erase_flash (fd);
+
+ if (ret == 0)
+ {
+ for (device = 0; device < fd->device_count; device++)
+ {
+ if (!fd->devices[device].segments)
+ return ENOMEM;
+
+ ret = rtems_fdisk_recover_block_mappings (fd);
+ if (ret)
+ break;
+ }
+ }
+
+ return ret;
+}
+
+/**
+ * Flash Disk Monitoring data is return in the monitoring data
+ * structure.
+ */
+static int
+rtems_fdisk_monitoring_data (rtems_flashdisk* fd,
+ rtems_fdisk_monitor_data* data)
+{
+ uint32_t i;
+ uint32_t j;
+
+ data->block_size = fd->block_size;
+ data->block_count = fd->block_count;
+ data->unavail_blocks = fd->unavail_blocks;
+ data->device_count = fd->device_count;
+
+ data->blocks_used = 0;
+ for (i = 0; i < fd->block_count; i++)
+ if (fd->blocks[i].segment)
+ data->blocks_used++;
+
+ data->segs_available = rtems_fdisk_segment_count_queue (&fd->available);
+ data->segs_used = rtems_fdisk_segment_count_queue (&fd->used);
+ data->segs_failed = rtems_fdisk_segment_count_queue (&fd->failed);
+
+ data->segment_count = 0;
+ data->page_count = 0;
+ data->pages_desc = 0;
+ data->pages_active = 0;
+ data->pages_used = 0;
+ data->pages_bad = 0;
+ data->seg_erases = 0;
+
+ for (i = 0; i < fd->device_count; i++)
+ {
+ data->segment_count += fd->devices[i].segment_count;
+
+ for (j = 0; j < fd->devices[i].segment_count; j++)
+ {
+ rtems_fdisk_segment_ctl* sc = &fd->devices[i].segments[j];
+
+ data->page_count += sc->pages;
+ data->pages_desc += sc->pages_desc;
+ data->pages_active += sc->pages_active;
+ data->pages_used += sc->pages_used;
+ data->pages_bad += sc->pages_bad;
+ data->seg_erases += sc->erased;
+ }
+ }
+
+ data->info_level = fd->info_level;
+ return 0;
+}
+
+/**
+ * Print to stdout the status of the driver. This is a debugging aid.
+ */
+static int
+rtems_fdisk_print_status (rtems_flashdisk* fd)
+{
+#if RTEMS_FDISK_TRACE
+ uint32_t current_info_level = fd->info_level;
+ uint32_t total;
+ uint32_t count;
+ uint32_t device;
+
+ fd->info_level = 3;
+
+ rtems_fdisk_printf (fd,
+ "Flash Disk Driver Status : %d.%d", fd->major, fd->minor);
+
+ rtems_fdisk_printf (fd, "Block count\t%d", fd->block_count);
+ rtems_fdisk_printf (fd, "Unavail blocks\t%d", fd->unavail_blocks);
+ count = rtems_fdisk_segment_count_queue (&fd->available);
+ total = count;
+ rtems_fdisk_printf (fd, "Available queue\t%ld (%ld)",
+ count, rtems_fdisk_segment_queue_count (&fd->available));
+ count = rtems_fdisk_segment_count_queue (&fd->used);
+ total += count;
+ rtems_fdisk_printf (fd, "Used queue\t%ld (%ld)",
+ count, rtems_fdisk_segment_queue_count (&fd->used));
+ count = rtems_fdisk_segment_count_queue (&fd->erase);
+ total += count;
+ rtems_fdisk_printf (fd, "Erase queue\t%ld (%ld)",
+ count, rtems_fdisk_segment_queue_count (&fd->erase));
+ count = rtems_fdisk_segment_count_queue (&fd->failed);
+ total += count;
+ rtems_fdisk_printf (fd, "Failed queue\t%ld (%ld)",
+ count, rtems_fdisk_segment_queue_count (&fd->failed));
+
+ count = 0;
+ for (device = 0; device < fd->device_count; device++)
+ count += fd->devices[device].segment_count;
+
+ rtems_fdisk_printf (fd, "Queue total\t%ld of %ld, %s", total, count,
+ total == count ? "ok" : "MISSING");
+
+ rtems_fdisk_printf (fd, "Device count\t%d", fd->device_count);
+
+ for (device = 0; device < fd->device_count; device++)
+ {
+ uint32_t block;
+ uint32_t seg;
+
+ rtems_fdisk_printf (fd, " Device\t\t%ld", device);
+ rtems_fdisk_printf (fd, " Segment count\t%ld",
+ fd->devices[device].segment_count);
+
+ for (seg = 0; seg < fd->devices[device].segment_count; seg++)
+ {
+ rtems_fdisk_segment_ctl* sc = &fd->devices[device].segments[seg];
+ uint32_t page;
+ uint32_t erased = 0;
+ uint32_t active = 0;
+ uint32_t used = 0;
+ bool is_active = false;
+ char queues[5];
+
+ rtems_fdisk_queue_status (fd, sc, queues);
+
+ for (page = 0; page < sc->pages; page++)
+ {
+ if (rtems_fdisk_page_desc_erased (&sc->page_descriptors[page]))
+ erased++;
+ else if (rtems_fdisk_page_desc_flags_set (&sc->page_descriptors[page],
+ RTEMS_FDISK_PAGE_ACTIVE))
+ {
+ if (rtems_fdisk_page_desc_flags_set (&sc->page_descriptors[page],
+ RTEMS_FDISK_PAGE_USED))
+ used++;
+ else
+ {
+ active++;
+ is_active = true;
+ }
+ }
+
+ for (block = 0; block < fd->block_count; block++)
+ {
+ if ((fd->blocks[block].segment == sc) &&
+ (fd->blocks[block].page == page) && !is_active)
+ rtems_fdisk_printf (fd,
+ " %ld\t not active when mapped by block %ld",
+ page, block);
+ }
+ }
+
+ count = 0;
+ for (block = 0; block < fd->block_count; block++)
+ {
+ if (fd->blocks[block].segment == sc)
+ count++;
+ }
+
+ rtems_fdisk_printf (fd, " %3ld %s p:%3ld a:%3ld/%3ld" \
+ " u:%3ld/%3ld e:%3ld/%3ld br:%ld",
+ seg, queues,
+ sc->pages, sc->pages_active, active,
+ sc->pages_used, used, erased,
+ sc->pages - (sc->pages_active +
+ sc->pages_used + sc->pages_bad),
+ count);
+ }
+ }
+
+ {
+ rtems_fdisk_segment_ctl* sc = fd->used.head;
+ int count = 0;
+ rtems_fdisk_printf (fd, "Used List:");
+ while (sc)
+ {
+ rtems_fdisk_printf (fd, " %3d %02d:%03d u:%3ld",
+ count, sc->device, sc->segment, sc->pages_used);
+ sc = sc->next;
+ count++;
+ }
+ }
+ fd->info_level = current_info_level;
+
+ return 0;
+#else
+ return ENOSYS;
+#endif
+}
+
+/**
+ * Flash disk IOCTL handler.
+ *
+ * @param dd Disk device.
+ * @param req IOCTL request code.
+ * @param argp IOCTL argument.
+ * @retval The IOCTL return value
+ */
+static int
+rtems_fdisk_ioctl (rtems_disk_device *dd, uint32_t req, void* argp)
+{
+ dev_t dev = rtems_disk_get_device_identifier (dd);
+ rtems_device_minor_number minor = rtems_filesystem_dev_minor_t (dev);
+ rtems_blkdev_request* r = argp;
+ rtems_status_code sc;
+
+ errno = 0;
+
+ sc = rtems_semaphore_obtain (rtems_flashdisks[minor].lock, RTEMS_WAIT, 0);
+ if (sc != RTEMS_SUCCESSFUL)
+ errno = EIO;
+ else
+ {
+ errno = 0;
+ switch (req)
+ {
+ case RTEMS_BLKIO_REQUEST:
+ if ((minor >= rtems_flashdisk_count) ||
+ (rtems_flashdisks[minor].device_count == 0))
+ {
+ errno = ENODEV;
+ }
+ else
+ {
+ switch (r->req)
+ {
+ case RTEMS_BLKDEV_REQ_READ:
+ errno = rtems_fdisk_read (&rtems_flashdisks[minor], r);
+ break;
+
+ case RTEMS_BLKDEV_REQ_WRITE:
+ errno = rtems_fdisk_write (&rtems_flashdisks[minor], r);
+ break;
+
+ default:
+ errno = EINVAL;
+ break;
+ }
+ }
+ break;
+
+ case RTEMS_FDISK_IOCTL_ERASE_DISK:
+ errno = rtems_fdisk_erase_disk (&rtems_flashdisks[minor]);
+ break;
+
+ case RTEMS_FDISK_IOCTL_COMPACT:
+ errno = rtems_fdisk_compact (&rtems_flashdisks[minor]);
+ break;
+
+ case RTEMS_FDISK_IOCTL_ERASE_USED:
+ errno = rtems_fdisk_erase_used (&rtems_flashdisks[minor]);
+ break;
+
+ case RTEMS_FDISK_IOCTL_MONITORING:
+ errno = rtems_fdisk_monitoring_data (&rtems_flashdisks[minor],
+ (rtems_fdisk_monitor_data*) argp);
+ break;
+
+ case RTEMS_FDISK_IOCTL_INFO_LEVEL:
+ rtems_flashdisks[minor].info_level = (uintptr_t) argp;
+ break;
+
+ case RTEMS_FDISK_IOCTL_PRINT_STATUS:
+ errno = rtems_fdisk_print_status (&rtems_flashdisks[minor]);
+ break;
+
+ default:
+ rtems_blkdev_ioctl (dd, req, argp);
+ break;
+ }
+
+ sc = rtems_semaphore_release (rtems_flashdisks[minor].lock);
+ if (sc != RTEMS_SUCCESSFUL)
+ errno = EIO;
+ }
+
+ return errno == 0 ? 0 : -1;
+}
+
+/**
+ * Flash disk device driver initialization.
+ *
+ * @todo Memory clean up on error is really badly handled.
+ *
+ * @param major Flash disk major device number.
+ * @param minor Minor device number, not applicable.
+ * @param arg Initialization argument, not applicable.
+ */
+rtems_device_driver
+rtems_fdisk_initialize (rtems_device_major_number major,
+ rtems_device_minor_number minor,
+ void* arg __attribute__((unused)))
+{
+ const rtems_flashdisk_config* c = rtems_flashdisk_configuration;
+ rtems_flashdisk* fd;
+ rtems_status_code sc;
+
+ sc = rtems_disk_io_initialize ();
+ if (sc != RTEMS_SUCCESSFUL)
+ return sc;
+
+ sc = rtems_fdisk_crc16_gen_factors (0x8408);
+ if (sc != RTEMS_SUCCESSFUL)
+ return sc;
+
+ rtems_flashdisks = calloc (rtems_flashdisk_configuration_size,
+ sizeof (rtems_flashdisk));
+
+ if (!rtems_flashdisks)
+ return RTEMS_NO_MEMORY;
+
+ for (minor = 0; minor < rtems_flashdisk_configuration_size; minor++, c++)
+ {
+ char name[] = RTEMS_FLASHDISK_DEVICE_BASE_NAME "a";
+ dev_t dev = rtems_filesystem_make_dev_t (major, minor);
+ uint32_t device;
+ uint32_t blocks = 0;
+ int ret;
+
+ fd = &rtems_flashdisks[minor];
+
+ name [sizeof(RTEMS_FLASHDISK_DEVICE_BASE_NAME)] += minor;
+
+ fd->major = major;
+ fd->minor = minor;
+ fd->flags = c->flags;
+ fd->compact_segs = c->compact_segs;
+ fd->avail_compact_segs = c->avail_compact_segs;
+ fd->block_size = c->block_size;
+ fd->unavail_blocks = c->unavail_blocks;
+ fd->info_level = c->info_level;
+
+ for (device = 0; device < c->device_count; device++)
+ blocks += rtems_fdisk_blocks_in_device (&c->devices[device],
+ c->block_size);
+
+ /*
+ * One copy buffer of a page size.
+ */
+ fd->copy_buffer = malloc (c->block_size);
+ if (!fd->copy_buffer)
+ return RTEMS_NO_MEMORY;
+
+ fd->blocks = calloc (blocks, sizeof (rtems_fdisk_block_ctl));
+ if (!fd->blocks)
+ return RTEMS_NO_MEMORY;
+
+ fd->block_count = blocks;
+
+ fd->devices = calloc (c->device_count, sizeof (rtems_fdisk_device_ctl));
+ if (!fd->devices)
+ return RTEMS_NO_MEMORY;
+
+ sc = rtems_semaphore_create (rtems_build_name ('F', 'D', 'S', 'K'), 1,
+ RTEMS_PRIORITY | RTEMS_BINARY_SEMAPHORE |
+ RTEMS_INHERIT_PRIORITY, 0, &fd->lock);
+ if (sc != RTEMS_SUCCESSFUL)
+ {
+ rtems_fdisk_error ("disk lock create failed");
+ free (fd->copy_buffer);
+ free (fd->blocks);
+ free (fd->devices);
+ return sc;
+ }
+
+ sc = rtems_disk_create_phys(dev, c->block_size,
+ blocks - fd->unavail_blocks,
+ rtems_fdisk_ioctl, NULL, name);
+ if (sc != RTEMS_SUCCESSFUL)
+ {
+ rtems_semaphore_delete (fd->lock);
+ rtems_disk_delete (dev);
+ free (fd->copy_buffer);
+ free (fd->blocks);
+ free (fd->devices);
+ rtems_fdisk_error ("disk create phy failed");
+ return sc;
+ }
+
+ for (device = 0; device < c->device_count; device++)
+ {
+ rtems_fdisk_segment_ctl* sc;
+ uint32_t segment_count;
+ uint32_t segment;
+
+ segment_count = rtems_fdisk_count_segments (&c->devices[device]);
+
+ fd->devices[device].segments = calloc (segment_count,
+ sizeof (rtems_fdisk_segment_ctl));
+ if (!fd->devices[device].segments)
+ {
+ rtems_disk_delete (dev);
+ rtems_semaphore_delete (fd->lock);
+ free (fd->copy_buffer);
+ free (fd->blocks);
+ free (fd->devices);
+ return RTEMS_NO_MEMORY;
+ }
+
+ sc = fd->devices[device].segments;
+
+ for (segment = 0; segment < c->devices[device].segment_count; segment++)
+ {
+ const rtems_fdisk_segment_desc* sd;
+ uint32_t seg_segment;
+
+ sd = &c->devices[device].segments[segment];
+
+ for (seg_segment = 0; seg_segment < sd->count; seg_segment++, sc++)
+ {
+ sc->descriptor = sd;
+ sc->device = device;
+ sc->segment = seg_segment;
+ sc->erased = 0;
+ }
+ }
+
+ fd->devices[device].segment_count = segment_count;
+ fd->devices[device].descriptor = &c->devices[device];
+ }
+
+ fd->device_count = c->device_count;
+
+ ret = rtems_fdisk_recover_block_mappings (fd);
+ if (ret)
+ {
+ rtems_disk_delete (dev);
+ rtems_semaphore_delete (fd->lock);
+ free (fd->copy_buffer);
+ free (fd->blocks);
+ free (fd->devices);
+ rtems_fdisk_error ("recovery of disk failed: %s (%d)",
+ strerror (ret), ret);
+ return ret;
+ }
+
+ ret = rtems_fdisk_compact (fd);
+ if (ret)
+ {
+ rtems_disk_delete (dev);
+ rtems_semaphore_delete (fd->lock);
+ free (fd->copy_buffer);
+ free (fd->blocks);
+ free (fd->devices);
+ rtems_fdisk_error ("compacting of disk failed: %s (%d)",
+ strerror (ret), ret);
+ return ret;
+ }
+ }
+
+ rtems_flashdisk_count = rtems_flashdisk_configuration_size;
+
+ return RTEMS_SUCCESSFUL;
+}
diff --git a/cpukit/libblock/src/ide_part_table.c b/cpukit/libblock/src/ide_part_table.c
new file mode 100644
index 0000000000..3543284dfc
--- /dev/null
+++ b/cpukit/libblock/src/ide_part_table.c
@@ -0,0 +1,594 @@
+/*****************************************************************************
+ *
+ * ide_part_table.c
+ *
+ * The implementation of library supporting "MS-DOS-style" partition table
+ *
+ *
+ * Copyright (C) 2002 OKTET Ltd., St.-Petersburg, Russia
+ *
+ * Author: Konstantin Abramenko <Konstantin.Abramenko@oktet.ru>
+ * Alexander Kukuta <Alexander.Kukuta@oktet.ru>
+ *
+ * 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.
+ *
+ * $Id$
+ *
+ *****************************************************************************/
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <string.h>
+
+#include <rtems/ide_part_table.h>
+
+/*
+ * get_sector --
+ * gets sector from the disk
+ *
+ * PARAMETERS:
+ * dev - device number
+ * sector_num - number of sector to read
+ * sector - returned pointer to pointer to allocated
+ * sector_data_t structure
+ *
+ * RETURNS:
+ * RTEMS_SUCCESSFUL, if success;
+ * RTEMS_NO_MEMORY, if canot allocate memory for sector data;
+ * other error codes returned by rtems_bdbuf_read().
+ *
+ * NOTES:
+ * get_sector() operates with device via bdbuf library,
+ * and does not support devices with sector size other than 512 bytes
+ */
+static rtems_status_code
+get_sector(dev_t dev, uint32_t sector_num, rtems_sector_data_t **sector)
+{
+ rtems_sector_data_t *s;
+ rtems_bdbuf_buffer *buf;
+ rtems_status_code rc;
+
+ if (sector == NULL)
+ {
+ return RTEMS_INTERNAL_ERROR;
+ }
+
+ s = (rtems_sector_data_t *) malloc(sizeof(rtems_sector_data_t) + RTEMS_IDE_SECTOR_SIZE);
+ if (s == NULL)
+ {
+ return RTEMS_NO_MEMORY;
+ }
+
+ rc = rtems_bdbuf_read(dev, sector_num, &buf);
+ if (rc != RTEMS_SUCCESSFUL)
+ {
+ free(s);
+ return rc;
+ }
+
+ memcpy(s->data, buf->buffer, RTEMS_IDE_SECTOR_SIZE);
+ s->sector_num = sector_num;
+
+ *sector = s;
+
+ rtems_bdbuf_release(buf);
+
+ return RTEMS_SUCCESSFUL;
+}
+
+
+/*
+ * msdos_signature_check --
+ * checks if the partition table sector has msdos signature
+ *
+ * PARAMETERS:
+ * sector - sector to check
+ *
+ * RETURNS:
+ * true if sector has msdos signature, false otherwise
+ */
+static bool
+msdos_signature_check (rtems_sector_data_t *sector)
+{
+ uint8_t *p = sector->data + RTEMS_IDE_PARTITION_MSDOS_SIGNATURE_OFFSET;
+
+ return ((p[0] == RTEMS_IDE_PARTITION_MSDOS_SIGNATURE_DATA1) &&
+ (p[1] == RTEMS_IDE_PARTITION_MSDOS_SIGNATURE_DATA2));
+}
+
+
+/*
+ * is_extended --
+ * checks if the partition type is extended
+ *
+ * PARAMETERS:
+ * type - type of partition to check
+ *
+ * RETURNS:
+ * true if partition type is extended, false otherwise
+ */
+static bool
+is_extended(uint8_t type)
+{
+ return ((type == EXTENDED_PARTITION) || (type == LINUX_EXTENDED));
+}
+
+/*
+ * is_fat_partition --
+ * checks if the partition type is defined for FAT
+ *
+ * PARAMETERS:
+ * type - type of partition to check
+ *
+ * RETURNS:
+ * true if partition type is extended, false otherwise
+ */
+static bool
+is_fat_partition(uint8_t type)
+{
+ static const uint8_t fat_part_types[] = {
+ DOS_FAT12_PARTITION,DOS_FAT16_PARTITION,
+ DOS_P32MB_PARTITION,
+ FAT32_PARTITION ,FAT32_LBA_PARTITION,
+ FAT16_LBA_PARTITION
+ };
+
+ return (NULL != memchr(fat_part_types,type,sizeof(fat_part_types)));
+}
+
+
+/*
+ * data_to_part_desc --
+ * parses raw partition table sector data
+ * to partition description structure
+ *
+ * PARAMETERS:
+ * data - raw partition table sector data
+ * new_part_desc - pointer to returned partition description structure
+ *
+ * RETURNS:
+ * RTEMS_SUCCESSFUL, if success;
+ * RTEMS_NO_MEMOTY, if cannot allocate memory for part_desc_t strucure;
+ * RTEMS_INTERNAL_ERROR, if other error occurs.
+ */
+static rtems_status_code
+data_to_part_desc(uint8_t *data, rtems_part_desc_t **new_part_desc)
+{
+ rtems_part_desc_t *part_desc;
+ uint32_t temp;
+
+ if (new_part_desc == NULL)
+ {
+ return RTEMS_INTERNAL_ERROR;
+ }
+
+ *new_part_desc = NULL;
+
+ if ((part_desc = calloc(1, sizeof(rtems_part_desc_t))) == NULL)
+ {
+ return RTEMS_NO_MEMORY;
+ }
+
+ part_desc->bootable = *(data + RTEMS_IDE_PARTITION_BOOTABLE_OFFSET);
+ part_desc->sys_type = *(data + RTEMS_IDE_PARTITION_SYS_TYPE_OFFSET);
+
+ /* read the offset start position and partition size in sectors */
+
+ /* due to incorrect data alignment one have to align data first */
+ memcpy(&temp, data + RTEMS_IDE_PARTITION_START_OFFSET, sizeof(uint32_t));
+ part_desc->start = LE_TO_CPU_U32(temp);
+
+ memcpy(&temp, data + RTEMS_IDE_PARTITION_SIZE_OFFSET, sizeof(uint32_t));
+ part_desc->size = LE_TO_CPU_U32(temp);
+
+ /*
+ * use partitions that are
+ * - extended
+ * or
+ * - FAT type and non-zero
+ */
+ if (is_extended(part_desc->sys_type) ||
+ ((is_fat_partition(part_desc->sys_type)) && (part_desc->size != 0))) {
+ *new_part_desc = part_desc;
+ }
+ else {
+ /* empty partition */
+ free(part_desc);
+ }
+ return RTEMS_SUCCESSFUL;
+}
+
+
+/*
+ * read_extended_partition --
+ * recursively reads extended partition sector from the device
+ * and constructs the partition table tree
+ *
+ * PARAMETERS:
+ * start - start sector of primary extended partition, used for
+ * calculation of absolute partition sector address
+ * ext_part - description of extended partition to process
+ *
+ * RETURNS:
+ * RTEMS_SUCCESSFUL if success,
+ * RTEMS_NO_MEMOTY if cannot allocate memory for part_desc_t strucure,
+ * RTEMS_INTERNAL_ERROR if other error occurs.
+ */
+static rtems_status_code
+read_extended_partition(uint32_t start, rtems_part_desc_t *ext_part)
+{
+ int i;
+ dev_t dev;
+ rtems_sector_data_t *sector = NULL;
+ uint32_t here;
+ uint8_t *data;
+ rtems_part_desc_t *new_part_desc;
+ rtems_status_code rc;
+
+ if ((ext_part == NULL) || (ext_part->disk_desc == NULL))
+ {
+ return RTEMS_INTERNAL_ERROR;
+ }
+
+ dev = ext_part->disk_desc->dev;
+
+ /* get start sector of current extended partition */
+ here = ext_part->start;
+
+ /* get first extended partition sector */
+
+ rc = get_sector(dev, here, &sector);
+ if (rc != RTEMS_SUCCESSFUL)
+ {
+ if (sector)
+ free(sector);
+ return rc;
+ }
+
+ if (!msdos_signature_check(sector))
+ {
+ free(sector);
+ return RTEMS_INTERNAL_ERROR;
+ }
+
+ /* read and process up to 4 logical partition descriptors */
+
+ data = sector->data + RTEMS_IDE_PARTITION_TABLE_OFFSET;
+
+ for (i = 0; i < RTEMS_IDE_PARTITION_MAX_SUB_PARTITION_NUMBER; i++)
+ {
+ /* if data_to_part_desc fails skip this partition
+ * and parse the next one
+ */
+ rc = data_to_part_desc(data, &new_part_desc);
+ if (rc != RTEMS_SUCCESSFUL)
+ {
+ free(sector);
+ return rc;
+ }
+
+ if (new_part_desc == NULL)
+ {
+ data += RTEMS_IDE_PARTITION_DESCRIPTOR_SIZE;
+ continue;
+ }
+
+ ext_part->sub_part[i] = new_part_desc;
+ new_part_desc->ext_part = ext_part;
+ new_part_desc->disk_desc = ext_part->disk_desc;
+
+ if (is_extended(new_part_desc->sys_type))
+ {
+ new_part_desc->log_id = EMPTY_PARTITION;
+ new_part_desc->start += start;
+ read_extended_partition(start, new_part_desc);
+ }
+ else
+ {
+ rtems_disk_desc_t *disk_desc = new_part_desc->disk_desc;
+ disk_desc->partitions[disk_desc->last_log_id] = new_part_desc;
+ new_part_desc->log_id = ++disk_desc->last_log_id;
+ new_part_desc->start += here;
+ new_part_desc->end = new_part_desc->start + new_part_desc->size - 1;
+ }
+ data += RTEMS_IDE_PARTITION_DESCRIPTOR_SIZE;
+ }
+
+ free(sector);
+
+ return RTEMS_SUCCESSFUL;
+}
+
+
+/*
+ * read_mbr --
+ * reads Master Boot Record (sector 0) of physical device and
+ * constructs disk description structure
+ *
+ * PARAMETERS:
+ * disk_desc - returned disc description structure
+ *
+ * RETURNS:
+ * RTEMS_SUCCESSFUL if success,
+ * RTEMS_INTERNAL_ERROR otherwise
+ */
+static rtems_status_code
+read_mbr(rtems_disk_desc_t *disk_desc)
+{
+ int part_num;
+ rtems_sector_data_t *sector = NULL;
+ rtems_part_desc_t *part_desc;
+ uint8_t *data;
+ rtems_status_code rc;
+ dev_t dev = disk_desc->dev;
+
+ /* get MBR sector */
+ rc = get_sector(dev, 0, &sector);
+ if (rc != RTEMS_SUCCESSFUL)
+ {
+ if (sector)
+ free(sector);
+ return rc;
+ }
+
+ /* check if the partition table structure is MS-DOS style */
+ if (!msdos_signature_check(sector))
+ {
+ free(sector);
+ return RTEMS_INTERNAL_ERROR;
+ }
+
+ /* read and process 4 primary partition descriptors */
+
+ data = sector->data + RTEMS_IDE_PARTITION_TABLE_OFFSET;
+
+ for (part_num = 0;
+ part_num < RTEMS_IDE_PARTITION_MAX_SUB_PARTITION_NUMBER;
+ part_num++)
+ {
+ rc = data_to_part_desc(data, &part_desc);
+ if (rc != RTEMS_SUCCESSFUL)
+ {
+ free(sector);
+ return rc;
+ }
+
+ if (part_desc != NULL)
+ {
+ part_desc->log_id = part_num + 1;
+ part_desc->disk_desc = disk_desc;
+ part_desc->end = part_desc->start + part_desc->size - 1;
+ disk_desc->partitions[part_num] = part_desc;
+ }
+ else
+ {
+ disk_desc->partitions[part_num] = NULL;
+ }
+
+ data += RTEMS_IDE_PARTITION_DESCRIPTOR_SIZE;
+ }
+
+ free(sector);
+
+ disk_desc->last_log_id = RTEMS_IDE_PARTITION_MAX_SUB_PARTITION_NUMBER;
+
+ /* There cannot be more than one extended partition,
+ but we are to process each primary partition */
+ for (part_num = 0;
+ part_num < RTEMS_IDE_PARTITION_MAX_SUB_PARTITION_NUMBER;
+ part_num++)
+ {
+ part_desc = disk_desc->partitions[part_num];
+ if (part_desc != NULL && is_extended(part_desc->sys_type))
+ {
+ read_extended_partition(part_desc->start, part_desc);
+ }
+ }
+
+ return RTEMS_SUCCESSFUL;
+}
+
+
+/*
+ * partition free --
+ * frees partition description structure
+ *
+ * PARAMETERS:
+ * part_desc - returned disc description structure
+ *
+ * RETURNS:
+ * N/A
+ */
+static void
+partition_free(rtems_part_desc_t *part_desc)
+{
+ int part_num;
+
+ if (part_desc == NULL)
+ return;
+
+ if (is_extended(part_desc->sys_type))
+ {
+ for (part_num = 0;
+ part_num < RTEMS_IDE_PARTITION_MAX_SUB_PARTITION_NUMBER;
+ part_num++)
+ {
+ partition_free(part_desc->sub_part[part_num]);
+ }
+ }
+
+ free(part_desc);
+}
+
+
+/*
+ * partition_table_free - frees disk descriptor structure
+ *
+ * PARAMETERS:
+ * disk_desc - disc descriptor structure to free
+ *
+ * RETURNS:
+ * N/A
+ */
+static void
+partition_table_free(rtems_disk_desc_t *disk_desc)
+{
+ int part_num;
+
+ for (part_num = 0;
+ part_num < RTEMS_IDE_PARTITION_MAX_SUB_PARTITION_NUMBER;
+ part_num++)
+ {
+ partition_free(disk_desc->partitions[part_num]);
+ }
+
+ free(disk_desc);
+}
+
+
+/*
+ * partition_table_get - reads partition table structure from the device
+ * and creates disk description structure
+ *
+ * PARAMETERS:
+ * dev_name - path to physical device in /dev filesystem
+ * disk_desc - returned disc description structure
+ *
+ * RETURNS:
+ * RTEMS_SUCCESSFUL if success,
+ * RTEMS_INTERNAL_ERROR otherwise
+ */
+static rtems_status_code
+partition_table_get(const char *dev_name, rtems_disk_desc_t *disk_desc)
+{
+ struct stat dev_stat;
+ rtems_status_code rc;
+
+ rc = stat(dev_name, &dev_stat);
+ if (rc != RTEMS_SUCCESSFUL)
+ {
+ return RTEMS_INTERNAL_ERROR;
+ }
+
+ strncpy (disk_desc->dev_name, dev_name, 15);
+ disk_desc->dev = dev_stat.st_rdev;
+ disk_desc->sector_size = (dev_stat.st_blksize) ? dev_stat.st_blksize :
+ RTEMS_IDE_SECTOR_SIZE;
+
+ rc = read_mbr(disk_desc);
+
+ return rc;
+}
+
+
+/*
+ * rtems_ide_part_table_free - frees disk descriptor structure
+ *
+ * PARAMETERS:
+ * disk_desc - disc descriptor structure to free
+ *
+ * RETURNS:
+ * N/A
+ */
+void
+rtems_ide_part_table_free(rtems_disk_desc_t *disk_desc)
+{
+ partition_table_free( disk_desc );
+}
+
+
+/*
+ * rtems_ide_part_table_get - reads partition table structure from the device
+ * and creates disk description structure
+ *
+ * PARAMETERS:
+ * dev_name - path to physical device in /dev filesystem
+ * disk_desc - returned disc description structure
+ *
+ * RETURNS:
+ * RTEMS_SUCCESSFUL if success,
+ * RTEMS_INTERNAL_ERROR otherwise
+ */
+rtems_status_code
+rtems_ide_part_table_get(const char *dev_name, rtems_disk_desc_t *disk_desc)
+{
+ return partition_table_get( dev_name, disk_desc );
+}
+
+
+/*
+ * rtems_ide_part_table_initialize - initializes logical devices
+ * on the physical IDE drive
+ *
+ * PARAMETERS:
+ * dev_name - path to physical device in /dev filesystem
+ *
+ * RETURNS:
+ * RTEMS_SUCCESSFUL if success,
+ * RTEMS_NO_MEMOTY if cannot have not enough memory,
+ * RTEMS_INTERNAL_ERROR if other error occurs.
+ */
+rtems_status_code
+rtems_ide_part_table_initialize(char *dev_name)
+{
+ int part_num;
+ dev_t dev;
+ rtems_disk_desc_t *disk_desc;
+ rtems_device_major_number major;
+ rtems_device_minor_number minor;
+ rtems_status_code rc;
+ rtems_part_desc_t *part_desc;
+
+ /* logical device name /dev/hdxyy */
+ char name[RTEMS_IDE_PARTITION_DEV_NAME_LENGTH_MAX];
+
+ disk_desc = (rtems_disk_desc_t *) calloc(1, sizeof(rtems_disk_desc_t));
+ if (disk_desc == NULL)
+ {
+ return RTEMS_NO_MEMORY;
+ }
+
+ /* get partition table */
+ rc = partition_table_get(dev_name, disk_desc);
+ if (rc != RTEMS_SUCCESSFUL)
+ {
+ free(disk_desc);
+ return rc;
+ }
+
+ /* To avoid device numbers conflicts we have to use for logic disk the same
+ * device major number as ATA device has, and minor number that equals to
+ * sum of logic disk partition number and the minor number of physical disk
+ */
+
+ rtems_filesystem_split_dev_t (disk_desc->dev, major, minor);
+
+ /* create logical disks on the physical one */
+ for (part_num = 0; part_num < disk_desc->last_log_id; part_num++)
+ {
+ sprintf(name, "%s%d", dev_name, part_num + 1);
+ dev = rtems_filesystem_make_dev_t(major, ++minor);
+
+ part_desc = disk_desc->partitions[part_num];
+ if (part_desc == NULL)
+ {
+ continue;
+ }
+
+ rc = rtems_disk_create_log(dev, disk_desc->dev, part_desc->start,
+ part_desc->size, name);
+ if (rc != RTEMS_SUCCESSFUL)
+ {
+ fprintf(stdout,"Cannot create device %s, error code %d\n", name, rc);
+ continue;
+ }
+ }
+
+ partition_table_free(disk_desc);
+
+ return RTEMS_SUCCESSFUL;
+}
diff --git a/cpukit/libblock/src/media-desc.c b/cpukit/libblock/src/media-desc.c
new file mode 100644
index 0000000000..005f0f7329
--- /dev/null
+++ b/cpukit/libblock/src/media-desc.c
@@ -0,0 +1,72 @@
+/**
+ * @file
+ *
+ * @ingroup RTEMSMedia
+ *
+ * @brief Media implementation.
+ */
+
+/*
+ * Copyright (c) 2009, 2010 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.
+ */
+
+#include <rtems/media.h>
+
+static const char *const rtems_media_event_desc_table [] = {
+ [RTEMS_MEDIA_EVENT_DISK_ATTACH] = "DISK ATTACH",
+ [RTEMS_MEDIA_EVENT_DISK_DETACH] = "DISK DETACH",
+ [RTEMS_MEDIA_EVENT_MOUNT] = "MOUNT",
+ [RTEMS_MEDIA_EVENT_UNMOUNT] = "UNMOUNT",
+ [RTEMS_MEDIA_EVENT_PARTITION_INQUIRY] = "PARTITION INQUIRY",
+ [RTEMS_MEDIA_EVENT_PARTITION_ATTACH] = "PARTITION ATTACH",
+ [RTEMS_MEDIA_EVENT_PARTITION_DETACH] = "PARTITION DETACH"
+};
+
+static const char *const rtems_media_state_desc_table [] = {
+ [RTEMS_MEDIA_STATE_INQUIRY] = "INQUIRY",
+ [RTEMS_MEDIA_STATE_ABORTED] = "ABORTED",
+ [RTEMS_MEDIA_STATE_READY] = "SUCCESS",
+ [RTEMS_MEDIA_STATE_FAILED] = "FAILED",
+ [RTEMS_MEDIA_STATE_SUCCESS] = "SUCCESS",
+ [RTEMS_MEDIA_ERROR_DISK_UNKNOWN] = "ERROR DISK UNKNOWN",
+ [RTEMS_MEDIA_ERROR_DISK_EXISTS] = "ERROR DISK EXISTS",
+ [RTEMS_MEDIA_ERROR_DISK_OR_PARTITION_UNKNOWN] = "ERROR DISK OR PARTITION UNKNOWN",
+ [RTEMS_MEDIA_ERROR_DISK_OR_PARTITION_EXISTS] = "ERROR DISK OR PARTITION EXISTS",
+ [RTEMS_MEDIA_ERROR_PARTITION_UNKNOWN] = "ERROR PARTITION UNKNOWN",
+ [RTEMS_MEDIA_ERROR_PARTITION_ORPHAN] = "ERROR PARTITION ORPHAN",
+ [RTEMS_MEDIA_ERROR_PARTITION_DETACH_WITH_MOUNT] = "ERROR PARTITION DETACH WITH MOUNT",
+ [RTEMS_MEDIA_ERROR_PARTITION_WITH_UNKNOWN_DISK] = "ERROR PARTITION WITH UNKNOWN DISK",
+ [RTEMS_MEDIA_ERROR_MOUNT_POINT_UNKNOWN] = "ERROR MOUNT POINT UNKNOWN",
+ [RTEMS_MEDIA_ERROR_MOUNT_POINT_EXISTS] = "ERROR MOUNT POINT EXISTS",
+ [RTEMS_MEDIA_ERROR_MOUNT_POINT_ORPHAN] = "ERROR MOUNT POINT ORPHAN"
+};
+
+#define TC(table) (sizeof(table) / sizeof(table [0]))
+
+const char *rtems_media_event_description(rtems_media_event event)
+{
+ if ((size_t) event < TC(rtems_media_event_desc_table)) {
+ return rtems_media_event_desc_table [event];
+ } else {
+ return "INVALID";
+ }
+}
+
+const char *rtems_media_state_description(rtems_media_state state)
+{
+ if ((size_t) state < TC(rtems_media_state_desc_table)) {
+ return rtems_media_state_desc_table [state];
+ } else {
+ return "INVALID";
+ }
+}
diff --git a/cpukit/libblock/src/media-dev-ident.c b/cpukit/libblock/src/media-dev-ident.c
new file mode 100644
index 0000000000..7c0f298424
--- /dev/null
+++ b/cpukit/libblock/src/media-dev-ident.c
@@ -0,0 +1,47 @@
+/**
+ * @file
+ *
+ * @ingroup RTEMSMedia
+ *
+ * @brief Media implementation.
+ */
+
+/*
+ * Copyright (c) 2009, 2010 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.
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <rtems.h>
+
+#include <rtems/media.h>
+
+rtems_status_code rtems_media_get_device_identifier(
+ const char *device_path,
+ dev_t *device_identifier
+)
+{
+ int rv = 0;
+ struct stat st;
+
+ rv = stat(device_path, &st);
+ if (rv != 0) {
+ return RTEMS_INVALID_ID;
+ }
+
+ *device_identifier = st.st_rdev;
+
+ return RTEMS_SUCCESSFUL;
+}
diff --git a/cpukit/libblock/src/media-path.c b/cpukit/libblock/src/media-path.c
new file mode 100644
index 0000000000..1668145294
--- /dev/null
+++ b/cpukit/libblock/src/media-path.c
@@ -0,0 +1,86 @@
+/**
+ * @file
+ *
+ * @ingroup RTEMSMedia
+ *
+ * @brief Media implementation.
+ */
+
+/*
+ * Copyright (c) 2009, 2010 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.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <inttypes.h>
+#include <assert.h>
+
+#include <rtems/media.h>
+
+char *rtems_media_create_path(
+ const char *prefix,
+ const char *name,
+ rtems_device_major_number major
+)
+{
+ size_t const size = strlen(prefix) + 1 + strlen(name) + 1 + 10 + 1;
+ char *const s = malloc(size);
+
+ if (s != NULL) {
+#ifndef NDEBUG
+ int rv =
+#endif
+ snprintf(s, size, "%s/%s-%" PRIu32, prefix, name, major);
+ assert(rv < (int) size);
+ }
+
+ return s;
+}
+
+char *rtems_media_replace_prefix(const char *new_prefix, const char *path)
+{
+ const char *const name_try = strrchr(path, '/');
+ const char *const name = (name_try == NULL) ? path : name_try + 1;
+ size_t const new_prefix_len = strlen(new_prefix);
+ size_t const name_size = strlen(name) + 1;
+ size_t const size = new_prefix_len + 1 + name_size;
+ char *const s = malloc(size);
+
+ if (s != NULL) {
+ memcpy(s, new_prefix, new_prefix_len);
+ s [new_prefix_len] = '/';
+ memcpy(s + new_prefix_len + 1, name, name_size);
+ }
+
+ return s;
+}
+
+char *rtems_media_append_minor(
+ const char *path,
+ rtems_device_minor_number minor
+)
+{
+ size_t const size = strlen(path) + 1 + 10 + 1;
+ char *const s = malloc(size);
+
+ if (s != NULL) {
+#ifndef NDEBUG
+ int rv =
+#endif
+ snprintf(s, size, "%s-%" PRIu32, path, minor);
+ assert(rv < (int) size);
+ }
+
+ return s;
+}
diff --git a/cpukit/libblock/src/media-server.c b/cpukit/libblock/src/media-server.c
new file mode 100644
index 0000000000..66f46abee9
--- /dev/null
+++ b/cpukit/libblock/src/media-server.c
@@ -0,0 +1,152 @@
+/**
+ * @file
+ *
+ * @ingroup RTEMSMedia
+ *
+ * @brief Media implementation.
+ */
+
+/*
+ * Copyright (c) 2009, 2010 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.
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#include <rtems.h>
+#include <rtems/chain.h>
+#include <rtems/media.h>
+
+#define EVENT RTEMS_EVENT_13
+
+typedef struct {
+ rtems_chain_node node;
+ rtems_media_event event;
+ const char *src;
+ rtems_media_worker worker;
+ void *worker_arg;
+} message;
+
+static RTEMS_CHAIN_DEFINE_EMPTY(message_chain);
+
+static rtems_id server_id = RTEMS_ID_NONE;
+
+static void media_server(rtems_task_argument arg __attribute__((unused)))
+{
+ rtems_status_code sc = RTEMS_SUCCESSFUL;
+
+ while (true) {
+ message *msg = NULL;
+
+ sc = rtems_chain_get_with_wait(
+ &message_chain,
+ EVENT,
+ RTEMS_NO_TIMEOUT,
+ (rtems_chain_node **) &msg
+ );
+ assert(sc == RTEMS_SUCCESSFUL);
+ assert(msg != NULL);
+
+ rtems_media_post_event(
+ msg->event,
+ msg->src,
+ NULL,
+ msg->worker,
+ msg->worker_arg
+ );
+
+ free(msg);
+ }
+}
+
+rtems_status_code rtems_media_server_initialize(
+ rtems_task_priority priority,
+ size_t stack_size,
+ rtems_mode modes,
+ rtems_attribute attributes
+)
+{
+ rtems_status_code sc = RTEMS_SUCCESSFUL;
+
+ if (server_id == RTEMS_ID_NONE) {
+ sc = rtems_media_initialize();
+ if (sc != RTEMS_SUCCESSFUL) {
+ goto error;
+ }
+
+ sc = rtems_task_create(
+ rtems_build_name('M', 'D', 'I', 'A'),
+ priority,
+ stack_size,
+ modes,
+ attributes,
+ &server_id
+ );
+ if (sc != RTEMS_SUCCESSFUL) {
+ goto error;
+ }
+
+ sc = rtems_task_start(server_id, media_server, 0);
+ if (sc != RTEMS_SUCCESSFUL) {
+ goto error;
+ }
+ }
+
+ return RTEMS_SUCCESSFUL;
+
+error:
+
+ if (server_id != RTEMS_ID_NONE) {
+ rtems_task_delete(server_id);
+ }
+
+ return RTEMS_NO_MEMORY;
+}
+
+rtems_status_code rtems_media_server_post_event(
+ rtems_media_event event,
+ const char *src,
+ rtems_media_worker worker,
+ void *worker_arg
+)
+{
+ rtems_status_code sc = RTEMS_SUCCESSFUL;
+ size_t src_size = strlen(src) + 1;
+ message *msg = malloc(sizeof(*msg) + src_size);
+
+ if (msg != NULL) {
+ char *s = (char *) msg + sizeof(*msg);
+
+ memcpy(s, src, src_size);
+
+ msg->event = event;
+ msg->src = s;
+ msg->worker = worker;
+ msg->worker_arg = worker_arg;
+
+ sc = rtems_chain_append_with_notification(
+ &message_chain,
+ &msg->node,
+ server_id,
+ EVENT
+ );
+ if (sc != RTEMS_SUCCESSFUL) {
+ sc = RTEMS_NOT_CONFIGURED;
+ }
+ } else {
+ sc = RTEMS_NO_MEMORY;
+ }
+
+ return sc;
+}
diff --git a/cpukit/libblock/src/media.c b/cpukit/libblock/src/media.c
new file mode 100644
index 0000000000..b4ddd6ad7c
--- /dev/null
+++ b/cpukit/libblock/src/media.c
@@ -0,0 +1,1012 @@
+/**
+ * @file
+ *
+ * @ingroup RTEMSMedia
+ *
+ * @brief Media implementation.
+ */
+
+/*
+ * Copyright (c) 2009, 2010 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.
+ */
+
+#include <string.h>
+#include <assert.h>
+
+#include <rtems.h>
+#include <rtems/bdbuf.h>
+#include <rtems/diskdevs.h>
+#include <rtems/bdpart.h>
+#include <rtems/libio.h>
+
+#include <rtems/media.h>
+
+typedef struct {
+ rtems_bdpart_partition *partitions;
+ size_t *count;
+} partition_table;
+
+typedef struct {
+ dev_t physical_disk;
+ dev_t logical_disk;
+ rtems_blkdev_bnum begin;
+ rtems_blkdev_bnum count;
+} partition;
+
+typedef struct media_item {
+ rtems_chain_node node;
+ struct media_item *parent;
+ char *disk_path;
+ char *mount_path;
+} media_item;
+
+typedef struct listener_item {
+ rtems_chain_node node;
+ rtems_media_listener listener;
+ void *listener_arg;
+} listener_item;
+
+static RTEMS_CHAIN_DEFINE_EMPTY(listener_item_chain);
+
+static RTEMS_CHAIN_DEFINE_EMPTY(media_item_chain);
+
+static rtems_id media_mutex = RTEMS_ID_NONE;
+
+static rtems_status_code lock(void)
+{
+ rtems_status_code sc = RTEMS_SUCCESSFUL;
+
+ sc = rtems_semaphore_obtain(media_mutex, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
+ if (sc != RTEMS_SUCCESSFUL) {
+ sc = RTEMS_NOT_CONFIGURED;
+ }
+
+ return sc;
+}
+
+static void unlock(void)
+{
+ rtems_status_code sc = RTEMS_SUCCESSFUL;
+
+ sc = rtems_semaphore_release(media_mutex);
+ assert(sc == RTEMS_SUCCESSFUL);
+}
+
+static listener_item *find_listener(
+ rtems_media_listener listener,
+ void *listener_arg
+)
+{
+ rtems_chain_node *node = rtems_chain_first(&listener_item_chain);
+
+ while (!rtems_chain_is_tail(&listener_item_chain, node)) {
+ listener_item *item = (listener_item *) node;
+
+ if (item->listener == listener && item->listener_arg == listener_arg) {
+ return item;
+ }
+
+ node = rtems_chain_next(node);
+ }
+
+ return NULL;
+}
+
+rtems_status_code rtems_media_listener_add(
+ rtems_media_listener listener,
+ void *listener_arg
+)
+{
+ rtems_status_code sc = RTEMS_SUCCESSFUL;
+
+ sc = lock();
+ if (sc == RTEMS_SUCCESSFUL) {
+ listener_item *item = find_listener(listener, listener_arg);
+
+ if (item == NULL) {
+ item = malloc(sizeof(*item));
+ if (item != NULL) {
+ item->listener = listener;
+ item->listener_arg = listener_arg;
+ rtems_chain_append_unprotected(&listener_item_chain, &item->node);
+ } else {
+ sc = RTEMS_NO_MEMORY;
+ }
+ } else {
+ sc = RTEMS_TOO_MANY;
+ }
+
+ unlock();
+ }
+
+ return sc;
+}
+
+rtems_status_code rtems_media_listener_remove(
+ rtems_media_listener listener,
+ void *listener_arg
+)
+{
+ rtems_status_code sc = RTEMS_SUCCESSFUL;
+
+ sc = lock();
+ if (sc == RTEMS_SUCCESSFUL) {
+ listener_item *item = find_listener(listener, listener_arg);
+
+ if (item != NULL) {
+ rtems_chain_extract_unprotected(&item->node);
+ free(item);
+ } else {
+ sc = RTEMS_INVALID_ID;
+ }
+
+ unlock();
+ }
+
+ return sc;
+}
+
+static rtems_status_code notify(
+ rtems_media_event event,
+ rtems_media_state state,
+ const char *src,
+ const char *dest
+)
+{
+ rtems_status_code sc = RTEMS_SUCCESSFUL;
+ rtems_status_code rsc = RTEMS_SUCCESSFUL;
+ rtems_chain_node *node = rtems_chain_first(&listener_item_chain);
+
+ while (!rtems_chain_is_tail(&listener_item_chain, node)) {
+ listener_item *item = (listener_item *) node;
+
+ sc = (*item->listener)(event, state, src, dest, item->listener_arg);
+ if (sc != RTEMS_SUCCESSFUL) {
+ rsc = sc;
+ }
+
+ node = rtems_chain_next(node);
+ }
+
+ return rsc;
+}
+
+static void error(
+ rtems_media_state state,
+ const char *src,
+ const char *dest
+)
+{
+ notify(RTEMS_MEDIA_EVENT_ERROR, state, src, dest);
+}
+
+static media_item *get_media_item(
+ const char *disk_path,
+ const char *mount_path
+)
+{
+ rtems_chain_node *node = rtems_chain_first(&media_item_chain);
+
+ while (!rtems_chain_is_tail(&media_item_chain, node)) {
+ media_item *item = (media_item *) node;
+
+ if (
+ (disk_path == NULL || strcmp(disk_path, item->disk_path) == 0)
+ && (mount_path == NULL || strcmp(mount_path, item->mount_path) == 0)
+ ) {
+ return item;
+ }
+
+ node = rtems_chain_next(node);
+ }
+
+ return NULL;
+}
+
+static void free_item(media_item *item)
+{
+ rtems_chain_extract(&item->node);
+ free(item->mount_path);
+ free(item);
+}
+
+static void create_item(
+ media_item *parent,
+ const char *disk_path,
+ const char *mount_path
+)
+{
+ size_t disk_path_size = strlen(disk_path) + 1;
+ media_item *item = malloc(sizeof(*item) + disk_path_size);
+
+ if (item != NULL) {
+ if (mount_path != NULL) {
+ item->mount_path = strdup(mount_path);
+
+ if (item->mount_path == NULL) {
+ free(item);
+
+ return;
+ }
+ } else {
+ item->mount_path = NULL;
+ }
+
+ item->parent = parent;
+ item->disk_path = (char *) item + sizeof(*item);
+ memcpy(item->disk_path, disk_path, disk_path_size);
+ rtems_chain_append(&media_item_chain, &item->node);
+ }
+}
+
+static void remove_mount_point(const char *mount_path)
+{
+ media_item *item = get_media_item(NULL, mount_path);
+
+ if (item != NULL) {
+ free(item->mount_path);
+ item->mount_path = NULL;
+ } else {
+ error(RTEMS_MEDIA_ERROR_MOUNT_POINT_UNKNOWN, mount_path, NULL);
+ }
+}
+
+static void remove_partition(const char *partition_path)
+{
+ media_item *item = get_media_item(partition_path, NULL);
+
+ if (item != NULL) {
+ if (item->mount_path != NULL) {
+ error(
+ RTEMS_MEDIA_ERROR_PARTITION_DETACH_WITH_MOUNT,
+ partition_path,
+ item->mount_path
+ );
+ }
+ free_item(item);
+ } else {
+ error(RTEMS_MEDIA_ERROR_PARTITION_UNKNOWN, partition_path, NULL);
+ }
+}
+
+static void remove_disk(const char *disk_path)
+{
+ media_item *item = get_media_item(disk_path, NULL);
+
+ if (item != NULL) {
+ rtems_chain_node *node = rtems_chain_first(&media_item_chain);
+
+ while (!rtems_chain_is_tail(&media_item_chain, node)) {
+ media_item *child = (media_item *) node;
+
+ node = rtems_chain_next(node);
+
+ if (child->parent == item) {
+ if (child->mount_path != NULL) {
+ error(
+ RTEMS_MEDIA_ERROR_MOUNT_POINT_ORPHAN,
+ child->mount_path,
+ disk_path
+ );
+ }
+ error(RTEMS_MEDIA_ERROR_PARTITION_ORPHAN, child->disk_path, disk_path);
+ free_item(child);
+ }
+ }
+
+ free_item(item);
+ } else {
+ error(RTEMS_MEDIA_ERROR_DISK_UNKNOWN, disk_path, NULL);
+ }
+}
+
+static void add_disk(const char *disk_path)
+{
+ media_item *item = get_media_item(disk_path, NULL);
+
+ if (item != NULL) {
+ error(RTEMS_MEDIA_ERROR_DISK_EXISTS, disk_path, NULL);
+ remove_disk(disk_path);
+ }
+
+ create_item(NULL, disk_path, NULL);
+}
+
+static void add_partition(const char *disk_path, const char *partition_path)
+{
+ media_item *item = get_media_item(partition_path, NULL);
+ media_item *parent = get_media_item(disk_path, NULL);
+
+ if (item != NULL) {
+ error(RTEMS_MEDIA_ERROR_DISK_OR_PARTITION_EXISTS, partition_path, NULL);
+ remove_disk(partition_path);
+ }
+
+ if (parent != NULL) {
+ create_item(parent, partition_path, NULL);
+ } else {
+ error(
+ RTEMS_MEDIA_ERROR_PARTITION_WITH_UNKNOWN_DISK,
+ partition_path,
+ disk_path
+ );
+ }
+}
+
+static void add_mount_point(const char *disk_path, const char *mount_path)
+{
+ media_item *item = get_media_item(disk_path, NULL);
+
+ if (item != NULL) {
+ if (item->mount_path != NULL) {
+ error(RTEMS_MEDIA_ERROR_MOUNT_POINT_EXISTS, item->mount_path, NULL);
+ free(item->mount_path);
+ }
+ item->mount_path = strdup(mount_path);
+ } else {
+ error(RTEMS_MEDIA_ERROR_DISK_OR_PARTITION_UNKNOWN, disk_path, NULL);
+ }
+}
+
+static bool is_add_state(rtems_media_state state)
+{
+ return state == RTEMS_MEDIA_STATE_SUCCESS;
+}
+
+static bool is_remove_state(rtems_media_state state)
+{
+ return state == RTEMS_MEDIA_STATE_SUCCESS
+ || state == RTEMS_MEDIA_STATE_FAILED;
+}
+
+static rtems_status_code remember_event(
+ rtems_media_event event,
+ rtems_media_state state,
+ const char *src,
+ const char *dest
+)
+{
+ switch (event) {
+ case RTEMS_MEDIA_EVENT_DISK_ATTACH:
+ if (is_add_state(state)) {
+ add_disk(dest);
+ }
+ break;
+ case RTEMS_MEDIA_EVENT_PARTITION_ATTACH:
+ if (is_add_state(state)) {
+ add_partition(src, dest);
+ }
+ break;
+ case RTEMS_MEDIA_EVENT_MOUNT:
+ if (is_add_state(state)) {
+ add_mount_point(src, dest);
+ }
+ break;
+ case RTEMS_MEDIA_EVENT_UNMOUNT:
+ if (is_remove_state(state)) {
+ remove_mount_point(src);
+ }
+ break;
+ case RTEMS_MEDIA_EVENT_PARTITION_DETACH:
+ if (is_remove_state(state)) {
+ remove_partition(src);
+ }
+ break;
+ case RTEMS_MEDIA_EVENT_DISK_DETACH:
+ if (is_remove_state(state)) {
+ remove_disk(src);
+ }
+ break;
+ default:
+ break;
+ }
+
+ return RTEMS_SUCCESSFUL;
+}
+
+static rtems_status_code process_event(
+ rtems_media_event event,
+ const char *src,
+ char **dest_ptr,
+ rtems_media_worker worker,
+ void *worker_arg
+)
+{
+ rtems_status_code sc = RTEMS_SUCCESSFUL;
+ rtems_media_state state = RTEMS_MEDIA_STATE_FAILED;
+ char *dest = NULL;
+
+ sc = notify(event, RTEMS_MEDIA_STATE_INQUIRY, src, NULL);
+ if (sc == RTEMS_SUCCESSFUL) {
+ state = RTEMS_MEDIA_STATE_READY;
+ } else {
+ state = RTEMS_MEDIA_STATE_ABORTED;
+ }
+
+ sc = (*worker)(state, src, &dest, worker_arg);
+ if (state == RTEMS_MEDIA_STATE_READY) {
+ if (sc == RTEMS_SUCCESSFUL) {
+ state = RTEMS_MEDIA_STATE_SUCCESS;
+ } else {
+ state = RTEMS_MEDIA_STATE_FAILED;
+ }
+ }
+
+ notify(event, state, src, dest);
+ remember_event(event, state, src, dest);
+
+ if (state == RTEMS_MEDIA_STATE_SUCCESS) {
+ sc = RTEMS_SUCCESSFUL;
+ } else if (state == RTEMS_MEDIA_STATE_ABORTED) {
+ sc = RTEMS_UNSATISFIED;
+ } else {
+ sc = RTEMS_IO_ERROR;
+ }
+
+ if (dest_ptr != NULL && sc == RTEMS_SUCCESSFUL) {
+ *dest_ptr = dest;
+ } else {
+ free(dest);
+ }
+
+ return sc;
+}
+
+static rtems_status_code mount_worker(
+ rtems_media_state state,
+ const char *src,
+ char **dest,
+ void *worker_arg
+)
+{
+ int rv = 0;
+
+ if (state == RTEMS_MEDIA_STATE_READY) {
+ char *mount_path = NULL;
+
+ if (worker_arg == NULL) {
+ mount_path = rtems_media_replace_prefix(RTEMS_MEDIA_MOUNT_BASE, src);
+ } else {
+ mount_path = strdup(worker_arg);
+ }
+
+ if (mount_path == NULL) {
+ return RTEMS_IO_ERROR;
+ }
+
+ rv = rtems_mkdir(mount_path, S_IRWXU | S_IRWXG | S_IRWXO);
+ if (rv != 0) {
+ free(mount_path);
+
+ return RTEMS_IO_ERROR;
+ }
+
+ rv = mount(
+ src,
+ mount_path,
+ RTEMS_FILESYSTEM_TYPE_DOSFS,
+ RTEMS_FILESYSTEM_READ_WRITE,
+ NULL
+ );
+ if (rv != 0) {
+ rmdir(mount_path);
+ free(mount_path);
+
+ return RTEMS_IO_ERROR;
+ }
+
+ *dest = mount_path;
+ }
+
+ return RTEMS_SUCCESSFUL;
+}
+
+static rtems_status_code do_mount(
+ const char *src,
+ char **dest_ptr,
+ rtems_media_worker worker,
+ void *worker_arg
+)
+{
+ if (worker == NULL) {
+ worker = mount_worker;
+ }
+
+ return process_event(
+ RTEMS_MEDIA_EVENT_MOUNT,
+ src,
+ dest_ptr,
+ worker,
+ worker_arg
+ );
+}
+
+static rtems_status_code do_partition_attach(
+ const char *src,
+ char **dest_ptr,
+ rtems_media_worker worker,
+ void *worker_arg
+)
+{
+ rtems_status_code sc = RTEMS_SUCCESSFUL;
+ char *part_path = NULL;
+
+ if (worker != NULL) {
+ sc = process_event(
+ RTEMS_MEDIA_EVENT_PARTITION_ATTACH,
+ src,
+ &part_path,
+ worker,
+ worker_arg
+ );
+
+ if (sc == RTEMS_SUCCESSFUL) {
+ sc = do_mount(part_path, NULL, NULL, NULL);
+ }
+ } else {
+ sc = RTEMS_INVALID_ADDRESS;
+ }
+
+ if (dest_ptr != NULL && sc == RTEMS_SUCCESSFUL) {
+ *dest_ptr = part_path;
+ } else {
+ free(part_path);
+ }
+
+ return sc;
+}
+
+static rtems_status_code partition_attach_worker(
+ rtems_media_state state,
+ const char *src,
+ char **dest,
+ void *worker_arg
+)
+{
+ rtems_status_code sc = RTEMS_SUCCESSFUL;
+
+ if (state == RTEMS_MEDIA_STATE_READY) {
+ partition *part = worker_arg;
+ rtems_device_minor_number minor =
+ rtems_filesystem_dev_minor_t(part->logical_disk);
+ char *part_path = rtems_media_append_minor(src, minor);
+
+ if (part_path == NULL) {
+ return RTEMS_IO_ERROR;
+ }
+
+ sc = rtems_disk_create_log(
+ part->logical_disk,
+ part->physical_disk,
+ part->begin,
+ part->count,
+ part_path
+ );
+ if (sc != RTEMS_SUCCESSFUL) {
+ free(part_path);
+
+ return RTEMS_IO_ERROR;
+ }
+
+ *dest = part_path;
+ }
+
+ return RTEMS_SUCCESSFUL;
+}
+
+static rtems_status_code attach_and_mount_partitions(
+ const char *disk_path,
+ rtems_bdpart_partition *partitions,
+ size_t count
+)
+{
+ rtems_status_code sc = RTEMS_SUCCESSFUL;
+ rtems_device_major_number major = 0;
+ rtems_device_minor_number minor = 0;
+ dev_t dev = 0;
+ size_t i = 0;
+
+ sc = rtems_media_get_device_identifier(disk_path, &dev);
+ if (sc != RTEMS_SUCCESSFUL) {
+ return RTEMS_INVALID_ID;
+ }
+
+ major = rtems_filesystem_dev_major_t(dev);
+ minor = rtems_filesystem_dev_minor_t(dev) + 1;
+
+ for (i = 0; i < count; ++i, ++minor) {
+ partition part_desc = {
+ .physical_disk = dev,
+ .logical_disk = rtems_filesystem_make_dev_t(major, minor),
+ .begin = partitions [i].begin,
+ .count = partitions [i].end - partitions [i].begin
+ };
+ char *part_path = NULL;
+
+ sc = process_event(
+ RTEMS_MEDIA_EVENT_PARTITION_ATTACH,
+ disk_path,
+ &part_path,
+ partition_attach_worker,
+ &part_desc
+ );
+
+ if (sc == RTEMS_SUCCESSFUL) {
+ sc = do_mount(part_path, NULL, NULL, NULL);
+ }
+
+ free(part_path);
+ }
+
+ return sc;
+}
+
+static rtems_status_code partition_inquiry_worker(
+ rtems_media_state state,
+ const char *src,
+ char **dest __attribute__((unused)),
+ void *worker_arg
+)
+{
+ rtems_status_code sc = RTEMS_SUCCESSFUL;
+
+ if (state == RTEMS_MEDIA_STATE_READY) {
+ partition_table *pt = worker_arg;
+ rtems_bdpart_format format;
+
+ sc = rtems_bdpart_read(src, &format, pt->partitions, pt->count);
+ if (sc != RTEMS_SUCCESSFUL || *pt->count == 0) {
+ return RTEMS_IO_ERROR;
+ }
+ }
+
+ return RTEMS_SUCCESSFUL;
+}
+
+static rtems_status_code do_partition_inquiry(
+ const char *src,
+ char **dest_ptr,
+ rtems_media_worker worker,
+ void *worker_arg
+)
+{
+ rtems_status_code sc = RTEMS_SUCCESSFUL;
+
+ if (worker == NULL) {
+ rtems_bdpart_partition partitions [RTEMS_BDPART_PARTITION_NUMBER_HINT];
+ size_t count = RTEMS_BDPART_PARTITION_NUMBER_HINT;
+ partition_table pt = {
+ .partitions = partitions,
+ .count = &count
+ };
+
+ sc = process_event(
+ RTEMS_MEDIA_EVENT_PARTITION_INQUIRY,
+ src,
+ dest_ptr,
+ partition_inquiry_worker,
+ &pt
+ );
+
+ if (sc == RTEMS_SUCCESSFUL) {
+ sc = attach_and_mount_partitions(src, partitions, count);
+ }
+ } else {
+ sc = process_event(
+ RTEMS_MEDIA_EVENT_PARTITION_INQUIRY,
+ src,
+ dest_ptr,
+ worker,
+ worker_arg
+ );
+ }
+
+ return sc;
+}
+
+static rtems_status_code do_disk_attach(
+ const char *src,
+ char **dest_ptr,
+ rtems_media_worker worker,
+ void *worker_arg
+)
+{
+ rtems_status_code sc = RTEMS_SUCCESSFUL;
+ rtems_status_code rsc = RTEMS_SUCCESSFUL;
+ char *disk_path = NULL;
+
+ if (worker != NULL) {
+ rsc = process_event(
+ RTEMS_MEDIA_EVENT_DISK_ATTACH,
+ src,
+ &disk_path,
+ worker,
+ worker_arg
+ );
+
+ if (rsc == RTEMS_SUCCESSFUL) {
+ sc = do_mount(disk_path, NULL, NULL, NULL);
+
+ if (sc != RTEMS_SUCCESSFUL) {
+ do_partition_inquiry(disk_path, NULL, NULL, NULL);
+ }
+ }
+ } else {
+ rsc = RTEMS_INVALID_ADDRESS;
+ }
+
+ if (dest_ptr != NULL && rsc == RTEMS_SUCCESSFUL) {
+ *dest_ptr = disk_path;
+ } else {
+ free(disk_path);
+ }
+
+ return rsc;
+}
+
+static rtems_status_code unmount_worker(
+ rtems_media_state state,
+ const char *src,
+ char **dest __attribute__((unused)),
+ void *worker_arg __attribute__((unused))
+)
+{
+ rtems_status_code sc = RTEMS_SUCCESSFUL;
+
+ if (state == RTEMS_MEDIA_STATE_READY) {
+ int rv = unmount(src);
+
+ if (rv == 0) {
+ rv = rmdir(src);
+ if (rv != 0) {
+ sc = RTEMS_IO_ERROR;
+ }
+ } else {
+ sc = RTEMS_IO_ERROR;
+ }
+ }
+
+ return sc;
+}
+
+static rtems_status_code do_unmount(
+ const char *src,
+ char **dest_ptr,
+ rtems_media_worker worker,
+ void *worker_arg
+)
+{
+ if (worker == NULL) {
+ worker = unmount_worker;
+ worker_arg = NULL;
+ }
+
+ return process_event(
+ RTEMS_MEDIA_EVENT_UNMOUNT,
+ src,
+ dest_ptr,
+ worker,
+ worker_arg
+ );
+}
+
+static rtems_status_code disk_detach_worker(
+ rtems_media_state state,
+ const char *src,
+ char **dest __attribute__((unused)),
+ void *worker_arg __attribute__((unused))
+)
+{
+ rtems_status_code sc = RTEMS_SUCCESSFUL;
+ rtems_status_code rsc = RTEMS_SUCCESSFUL;
+
+ if (state == RTEMS_MEDIA_STATE_READY) {
+ dev_t dev = 0;
+
+ sc = rtems_media_get_device_identifier(src, &dev);
+ if (sc != RTEMS_SUCCESSFUL) {
+ return RTEMS_IO_ERROR;
+ }
+
+ sc = rtems_bdbuf_syncdev(dev);
+ if (sc != RTEMS_SUCCESSFUL) {
+ rsc = RTEMS_IO_ERROR;
+ }
+
+ sc = rtems_disk_delete(dev);
+ if (sc != RTEMS_SUCCESSFUL) {
+ rsc = RTEMS_IO_ERROR;
+ }
+
+ rtems_bdbuf_purge_dev(dev);
+
+ if (rtems_filesystem_dev_minor_t(dev) == 0) {
+ sc = rtems_io_unregister_driver(rtems_filesystem_dev_major_t(dev));
+ if (sc != RTEMS_SUCCESSFUL) {
+ rsc = RTEMS_IO_ERROR;
+ }
+ }
+ }
+
+ return rsc;
+}
+
+static rtems_status_code detach_item(rtems_media_event event, media_item *item)
+{
+ rtems_status_code sc = RTEMS_SUCCESSFUL;
+ rtems_status_code rsc = RTEMS_SUCCESSFUL;
+
+ if (item->mount_path != NULL) {
+ sc = do_unmount(item->mount_path, NULL, NULL, NULL);
+ if (sc != RTEMS_SUCCESSFUL) {
+ rsc = RTEMS_IO_ERROR;
+ }
+ }
+
+ sc = process_event(event, item->disk_path, NULL, disk_detach_worker, NULL);
+ if (sc != RTEMS_SUCCESSFUL) {
+ rsc = RTEMS_IO_ERROR;
+ }
+
+ return rsc;
+}
+
+static rtems_status_code detach_parent_item(media_item *parent)
+{
+ rtems_status_code sc = RTEMS_SUCCESSFUL;
+ rtems_status_code rsc = RTEMS_SUCCESSFUL;
+
+ rtems_chain_node *node = rtems_chain_first(&media_item_chain);
+
+ while (!rtems_chain_is_tail(&media_item_chain, node)) {
+ media_item *child = (media_item *) node;
+
+ node = rtems_chain_next(node);
+
+ if (child->parent == parent) {
+ sc = detach_item(RTEMS_MEDIA_EVENT_PARTITION_DETACH, child);
+ if (sc != RTEMS_SUCCESSFUL) {
+ rsc = RTEMS_IO_ERROR;
+ }
+ }
+ }
+
+ sc = detach_item(RTEMS_MEDIA_EVENT_DISK_DETACH, parent);
+ if (sc != RTEMS_SUCCESSFUL) {
+ rsc = RTEMS_IO_ERROR;
+ }
+
+ return rsc;
+}
+
+static rtems_status_code do_disk_detach(
+ const char *src,
+ char **dest_ptr,
+ rtems_media_worker worker,
+ void *worker_arg
+)
+{
+ if (worker == NULL) {
+ media_item *parent = get_media_item(src, NULL);
+
+ if (parent != NULL) {
+ return detach_parent_item(parent);
+ }
+
+ worker = disk_detach_worker;
+ worker_arg = NULL;
+ }
+
+ return process_event(
+ RTEMS_MEDIA_EVENT_DISK_DETACH,
+ src,
+ dest_ptr,
+ worker,
+ worker_arg
+ );
+}
+
+static rtems_status_code do_partition_detach(
+ const char *src,
+ char **dest_ptr,
+ rtems_media_worker worker,
+ void *worker_arg
+)
+{
+ if (worker == NULL) {
+ media_item *item = get_media_item(src, NULL);
+
+ if (item != NULL) {
+ return detach_item(RTEMS_MEDIA_EVENT_PARTITION_DETACH, item);
+ }
+
+ worker = disk_detach_worker;
+ worker_arg = NULL;
+ }
+
+ return process_event(
+ RTEMS_MEDIA_EVENT_PARTITION_DETACH,
+ src,
+ dest_ptr,
+ worker,
+ worker_arg
+ );
+}
+
+rtems_status_code rtems_media_post_event(
+ rtems_media_event event,
+ const char *src,
+ char **dest_ptr,
+ rtems_media_worker worker,
+ void *worker_arg
+)
+{
+ rtems_status_code sc = RTEMS_SUCCESSFUL;
+
+ sc = lock();
+ if (sc != RTEMS_SUCCESSFUL) {
+ return sc;
+ }
+
+ switch (event) {
+ case RTEMS_MEDIA_EVENT_DISK_ATTACH:
+ sc = do_disk_attach(src, dest_ptr, worker, worker_arg);
+ break;
+ case RTEMS_MEDIA_EVENT_DISK_DETACH:
+ sc = do_disk_detach(src, dest_ptr, worker, worker_arg);
+ break;
+ case RTEMS_MEDIA_EVENT_MOUNT:
+ sc = do_mount(src, dest_ptr, worker, worker_arg);
+ break;
+ case RTEMS_MEDIA_EVENT_UNMOUNT:
+ sc = do_unmount(src, dest_ptr, worker, worker_arg);
+ break;
+ case RTEMS_MEDIA_EVENT_PARTITION_INQUIRY:
+ sc = do_partition_inquiry(src, dest_ptr, worker, worker_arg);
+ break;
+ case RTEMS_MEDIA_EVENT_PARTITION_ATTACH:
+ sc = do_partition_attach(src, dest_ptr, worker, worker_arg);
+ break;
+ case RTEMS_MEDIA_EVENT_PARTITION_DETACH:
+ sc = do_partition_detach(src, dest_ptr, worker, worker_arg);
+ break;
+ default:
+ sc = RTEMS_INVALID_ID;
+ break;
+ }
+
+ unlock();
+
+ return sc;
+}
+
+rtems_status_code rtems_media_initialize(void)
+{
+ rtems_status_code sc = RTEMS_SUCCESSFUL;
+
+ if (media_mutex == RTEMS_ID_NONE) {
+ sc = rtems_semaphore_create(
+ rtems_build_name('M', 'D', 'I', 'A'),
+ 1,
+ RTEMS_LOCAL | RTEMS_PRIORITY
+ | RTEMS_INHERIT_PRIORITY | RTEMS_BINARY_SEMAPHORE,
+ 0,
+ &media_mutex
+ );
+ if (sc != RTEMS_SUCCESSFUL) {
+ sc = RTEMS_NO_MEMORY;
+ }
+ }
+
+ return sc;
+}
diff --git a/cpukit/libblock/src/nvdisk-sram.c b/cpukit/libblock/src/nvdisk-sram.c
new file mode 100644
index 0000000000..b31f83cfed
--- /dev/null
+++ b/cpukit/libblock/src/nvdisk-sram.c
@@ -0,0 +1,68 @@
+/*
+ * $Id$
+ *
+ * RTEMS Project (http://www.rtems.org/)
+ *
+ * Copyright 2007 Chris Johns (chrisj@rtems.org)
+ */
+/**
+ * Provide SRAM support for the NV Disk.
+ */
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+
+#include <rtems.h>
+
+#include <rtems/nvdisk-sram.h>
+
+#ifndef NVDISK_SRAM_ERROR_TRACE
+#define NVDISK_SRAM_ERROR_TRACE (0)
+#endif
+
+static int
+rtems_nvdisk_sram_read (uint32_t device __attribute__((unused)),
+ uint32_t flags __attribute__((unused)),
+ void* base,
+ uint32_t offset,
+ void* buffer,
+ size_t size)
+{
+ memcpy (buffer, (base + offset), size);
+ return 0;
+}
+
+static int
+rtems_nvdisk_sram_write (uint32_t device __attribute__((unused)),
+ uint32_t flags __attribute__((unused)),
+ void* base,
+ uint32_t offset,
+ const void* buffer,
+ size_t size)
+{
+ memcpy ((base + offset), buffer, size);
+ return 0;
+}
+
+static int
+rtems_nvdisk_sram_verify (uint32_t device __attribute__((unused)),
+ uint32_t flags __attribute__((unused)),
+ void* base,
+ uint32_t offset,
+ const void* buffer,
+ size_t size)
+{
+ return memcmp ((base + offset), buffer, size) == 0 ? 0 : EIO;
+}
+
+
+const rtems_nvdisk_driver_handlers rtems_nvdisk_sram_handlers =
+{
+ read: rtems_nvdisk_sram_read,
+ write: rtems_nvdisk_sram_write,
+ verify: rtems_nvdisk_sram_verify
+};
diff --git a/cpukit/libblock/src/nvdisk.c b/cpukit/libblock/src/nvdisk.c
new file mode 100644
index 0000000000..26c0f40309
--- /dev/null
+++ b/cpukit/libblock/src/nvdisk.c
@@ -0,0 +1,845 @@
+/*
+ * nvdisk.c -- Non-volatile disk block device implementation
+ *
+ * Copyright (C) 2007 Chris Johns
+ *
+ * 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.
+ *
+ * $Id$
+ */
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <rtems.h>
+#include <rtems/libio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+
+#include "rtems/blkdev.h"
+#include "rtems/diskdevs.h"
+#include "rtems/nvdisk.h"
+
+/**
+ * @note
+ *
+ * The use of pages can vary. The rtems_nvdisk_*_page set
+ * routines use an absolute page number relative to the segment
+ * while all other page numbera are relative to the number of
+ * page descriptor pages a segment has. You need to add the
+ * number of page descriptor pages (pages_desc) to the page number
+ * when call the rtems_nvdisk_*_page functions.
+ *
+ * You must always show the page number as relative in any trace
+ * or error message as device-page and if you have to
+ * the page number as absolute use device~page. This
+ * can be seen in the page copy routine.
+ *
+ * The code is like this to avoid needing the pass the pages_desc
+ * value around. It is only used in selected places and so the
+ * extra parameter was avoided.
+ */
+
+/**
+ * Control tracing. It can be compiled out of the code for small
+ * footprint targets. Leave in by default.
+ */
+#if !defined (RTEMS_NVDISK_TRACE)
+#define RTEMS_NVDISK_TRACE 0
+#endif
+
+/**
+ * NV Device Control holds the segment controls
+ */
+typedef struct rtems_nvdisk_device_ctl
+{
+ /**
+ * The device this segment resides on.
+ */
+ uint32_t device;
+
+ /**
+ * Total number of pages in the device.
+ */
+ uint32_t pages;
+
+ /**
+ * Number of pages used for page checksums.
+ */
+ uint32_t pages_desc;
+
+ /**
+ * First block number for this device.
+ */
+ uint32_t block_base;
+
+ /**
+ * Device descriptor.
+ */
+ const rtems_nvdisk_device_desc* descriptor;
+} rtems_nvdisk_device_ctl;
+
+/**
+ * The NV disk control structure for a single disk. There is one
+ * for each minor disk in the system.
+ */
+typedef struct rtems_mvdisk
+{
+ rtems_device_major_number major; /**< The driver's major number. */
+ rtems_device_minor_number minor; /**< The driver's minor number. */
+ uint32_t flags; /**< configuration flags. */
+ uint32_t block_size; /**< The block size for this disk. */
+ uint32_t block_count; /**< The number of available blocks. */
+ rtems_nvdisk_device_ctl* devices; /**< The NV devices for this disk. */
+ uint32_t device_count; /**< The number of NV devices. */
+ uint32_t cs_pages; /**< The num of pages of checksums. */
+ rtems_id lock; /**< Mutex for threading protection.*/
+ uint32_t info_level; /**< The info trace level. */
+} rtems_nvdisk;
+
+/**
+ * The array of NV disks we support.
+ */
+static rtems_nvdisk* rtems_nvdisks;
+
+/**
+ * The number of NV disks we have.
+ */
+static uint32_t rtems_nvdisk_count;
+
+/**
+ * The CRC16 factor table. Created during initialisation.
+ */
+static uint16_t* rtems_nvdisk_crc16_factor;
+
+/**
+ * Calculate the CRC16 checksum.
+ *
+ * @param _b The byte to checksum.
+ * @param _c The current checksum.
+ */
+#define rtems_nvdisk_calc_crc16(_b, _c) \
+ rtems_nvdisk_crc16_factor[((_b) ^ ((_c) & 0xff)) & 0xff] ^ (((_c) >> 8) & 0xff)
+
+/**
+ * Generate the CRC table.
+ *
+ * @param pattern The seed pattern for the table of factors.
+ * @relval RTEMS_SUCCESSFUL The table was generated.
+ * @retval RTEMS_NO_MEMORY The table could not be allocated from the heap.
+ */
+rtems_status_code
+rtems_nvdisk_crc16_gen_factors (uint16_t pattern)
+{
+ uint32_t b;
+
+ rtems_nvdisk_crc16_factor = malloc (sizeof (uint16_t) * 256);
+ if (!rtems_nvdisk_crc16_factor)
+ return RTEMS_NO_MEMORY;
+
+ for (b = 0; b < 256; b++)
+ {
+ uint32_t i;
+ uint16_t v = b;
+ for (i = 8; i--;)
+ v = v & 1 ? (v >> 1) ^ pattern : v >> 1;
+ rtems_nvdisk_crc16_factor[b] = v & 0xffff;
+ }
+ return RTEMS_SUCCESSFUL;
+}
+
+#if RTEMS_NVDISK_TRACE
+/**
+ * Print a message to the nvdisk output and flush it.
+ *
+ * @param nvd The nvdisk control structure.
+ * @param format The format string. See printf for details.
+ * @param ... The arguments for the format text.
+ * @return int The number of bytes written to the output.
+ */
+static int
+rtems_nvdisk_printf (const rtems_nvdisk* nvd, const char *format, ...)
+{
+ int ret = 0;
+ if (nvd->info_level >= 3)
+ {
+ va_list args;
+ va_start (args, format);
+ fprintf (stdout, "nvdisk:");
+ ret = vfprintf (stdout, format, args);
+ fprintf (stdout, "\n");
+ fflush (stdout);
+ va_end (args);
+ }
+ return ret;
+}
+
+/**
+ * Print a info message to the nvdisk output and flush it.
+ *
+ * @param nvd The nvdisk control structure.
+ * @param format The format string. See printf for details.
+ * @param ... The arguments for the format text.
+ * @return int The number of bytes written to the output.
+ */
+static int
+rtems_nvdisk_info (const rtems_nvdisk* nvd, const char *format, ...)
+{
+ int ret = 0;
+ if (nvd->info_level >= 2)
+ {
+ va_list args;
+ va_start (args, format);
+ fprintf (stdout, "nvdisk:");
+ ret = vfprintf (stdout, format, args);
+ fprintf (stdout, "\n");
+ fflush (stdout);
+ va_end (args);
+ }
+ return ret;
+}
+
+/**
+ * Print a warning to the nvdisk output and flush it.
+ *
+ * @param nvd The nvdisk control structure.
+ * @param format The format string. See printf for details.
+ * @param ... The arguments for the format text.
+ * @return int The number of bytes written to the output.
+ */
+static int
+rtems_nvdisk_warning (const rtems_nvdisk* nvd, const char *format, ...)
+{
+ int ret = 0;
+ if (nvd->info_level >= 1)
+ {
+ va_list args;
+ va_start (args, format);
+ fprintf (stdout, "nvdisk:warning:");
+ ret = vfprintf (stdout, format, args);
+ fprintf (stdout, "\n");
+ fflush (stdout);
+ va_end (args);
+ }
+ return ret;
+}
+#endif
+
+/**
+ * Print an error to the nvdisk output and flush it.
+ *
+ * @param format The format string. See printf for details.
+ * @param ... The arguments for the format text.
+ * @return int The number of bytes written to the output.
+ */
+static int
+rtems_nvdisk_error (const char *format, ...)
+{
+ int ret;
+ va_list args;
+ va_start (args, format);
+ fprintf (stderr, "nvdisk:error:");
+ ret = vfprintf (stderr, format, args);
+ fprintf (stderr, "\n");
+ fflush (stderr);
+ va_end (args);
+ return ret;
+}
+
+/**
+ * Get the descriptor for a device.
+ */
+static const rtems_nvdisk_device_desc*
+rtems_nvdisk_device_descriptor (const rtems_nvdisk* nvd, uint32_t device)
+{
+ return nvd->devices[device].descriptor;
+}
+
+/**
+ * Read a block of data from a device.
+ */
+static int
+rtems_nvdisk_device_read (const rtems_nvdisk* nvd,
+ uint32_t device,
+ uint32_t offset,
+ void* buffer,
+ uint32_t size)
+{
+ const rtems_nvdisk_device_desc* dd;
+ const rtems_nvdisk_driver_handlers* ops;
+ dd = rtems_nvdisk_device_descriptor (nvd, device);
+ ops = nvd->devices[device].descriptor->nv_ops;
+#if RTEMS_NVDISK_TRACE
+ rtems_nvdisk_printf (nvd, " dev-read: %02d-%08x: s=%d",
+ device, offset, size);
+#endif
+ return ops->read (device, dd->flags, dd->base, offset, buffer, size);
+}
+
+/**
+ * Write a block of data to a device.
+ */
+static int
+rtems_nvdisk_device_write (const rtems_nvdisk* nvd,
+ uint32_t device,
+ uint32_t offset,
+ const void* buffer,
+ uint32_t size)
+{
+ const rtems_nvdisk_device_desc* dd;
+ const rtems_nvdisk_driver_handlers* ops;
+ dd = rtems_nvdisk_device_descriptor (nvd, device);
+ ops = nvd->devices[device].descriptor->nv_ops;
+#if RTEMS_NVDISK_TRACE
+ rtems_nvdisk_printf (nvd, " dev-write: %02d-%08x: s=%d",
+ device, offset, size);
+#endif
+ return ops->write (device, dd->flags, dd->base, offset, buffer, size);
+}
+
+#if NOT_USED
+/**
+ * Verify the data with the data in a segment.
+ */
+static int
+rtems_nvdisk_device_verify (const rtems_nvdisk* nvd,
+ uint32_t device,
+ uint32_t offset,
+ const void* buffer,
+ uint32_t size)
+{
+ const rtems_nvdisk_device_desc* dd;
+ const rtems_nvdisk_driver_handlers* ops;
+ dd = rtems_nvdisk_device_descriptor (nvd, device);
+ ops = nvd->devices[device].descriptor->nv_ops;
+#if RTEMS_NVDISK_TRACE
+ rtems_nvdisk_printf (nvd, " seg-verify: %02d-%08x: s=%d",
+ device, offset, size);
+#endif
+ return ops->verify (device, dd->flags, dd->base, offset, buffer, size);
+}
+#endif
+
+/**
+ * Read a page of data from the device.
+ */
+static int
+rtems_nvdisk_read_page (const rtems_nvdisk* nvd,
+ uint32_t device,
+ uint32_t page,
+ void* buffer)
+{
+ return rtems_nvdisk_device_read (nvd, device,
+ page * nvd->block_size, buffer,
+ nvd->block_size);
+}
+
+/**
+ * Write a page of data to a device.
+ */
+static int
+rtems_nvdisk_write_page (const rtems_nvdisk* nvd,
+ uint32_t device,
+ uint32_t page,
+ const void* buffer)
+{
+ return rtems_nvdisk_device_write (nvd, device,
+ page * nvd->block_size,
+ buffer, nvd->block_size);
+}
+
+/**
+ * Read the checksum from the device.
+ */
+static int
+rtems_nvdisk_read_checksum (const rtems_nvdisk* nvd,
+ uint32_t device,
+ uint32_t page,
+ uint16_t* cs)
+{
+ return rtems_nvdisk_device_read (nvd, device,
+ page * sizeof (uint16_t),
+ cs, sizeof (uint16_t));
+}
+
+/**
+ * Write the checksum to the device.
+ */
+static int
+rtems_nvdisk_write_checksum (const rtems_nvdisk* nvd,
+ uint32_t device,
+ uint32_t page,
+ const uint16_t cs)
+{
+ return rtems_nvdisk_device_write (nvd, device,
+ page * sizeof (uint16_t),
+ &cs, sizeof (uint16_t));
+}
+
+/**
+ * Calculate the pages in a device give the device descriptor and the
+ * page size.
+ *
+ * @param dd The device descriptor.
+ * @param page_size The page size in bytes.
+ */
+static uint32_t
+rtems_nvdisk_pages_in_device (const rtems_nvdisk* nvd,
+ const rtems_nvdisk_device_desc* dd)
+{
+ return dd->size / nvd->block_size;
+}
+
+/**
+ * Calculate the number of pages needed to hold the page descriptors.
+ * The calculation need to round up.
+ */
+static uint32_t
+rtems_nvdisk_page_desc_pages (const rtems_nvdisk* nvd,
+ const rtems_nvdisk_device_desc* dd)
+{
+ uint32_t pages = rtems_nvdisk_pages_in_device (nvd, dd);
+ uint32_t bytes = pages * sizeof (uint16_t);
+ return ((bytes - 1) / nvd->block_size) + 1;
+}
+
+/**
+ * Calculate the checksum of a page.
+ */
+static uint16_t
+rtems_nvdisk_page_checksum (const uint8_t* buffer, uint32_t page_size)
+{
+ uint16_t cs = 0xffff;
+ uint32_t i;
+
+ for (i = 0; i < page_size; i++, buffer++)
+ cs = rtems_nvdisk_calc_crc16 (cs, *buffer);
+
+ return cs;
+}
+
+/**
+ * Map a block to a device.
+ */
+static rtems_nvdisk_device_ctl*
+rtems_nvdisk_get_device (rtems_nvdisk* nvd, uint32_t block)
+{
+ uint32_t device;
+
+ if (block >= nvd->block_count)
+ {
+ rtems_nvdisk_error ("read-block: bad block: %d", block);
+ return NULL;
+ }
+
+ for (device = 0; device < nvd->device_count; device++)
+ {
+ rtems_nvdisk_device_ctl* dc = &nvd->devices[device];
+ if ((block >= dc->block_base) &&
+ (block < (dc->block_base + dc->pages - dc->pages_desc)))
+ return dc;
+ }
+
+ rtems_nvdisk_error ("map-block:%d: no device/page map found", block);
+
+ return NULL;
+}
+
+/**
+ * Get the page for a block in a device.
+ */
+static uint32_t
+rtems_nvdisk_get_page (rtems_nvdisk_device_ctl* dc,
+ uint32_t block)
+{
+ return block - dc->block_base;
+}
+
+/**
+ * Read a block. The block is checked to see if the page referenced
+ * is valid and the page has a valid crc.
+ *
+ * @param nvd The rtems_nvdisk control table.
+ * @param block The block number to read.
+ * @param buffer The buffer to write the data into.
+ * @return 0 No error.
+ * @return EIO Invalid block number or crc.
+ */
+static int
+rtems_nvdisk_read_block (rtems_nvdisk* nvd, uint32_t block, uint8_t* buffer)
+{
+ rtems_nvdisk_device_ctl* dc;
+ uint32_t page;
+ uint16_t crc;
+ uint16_t cs;
+ int ret;
+
+ dc = rtems_nvdisk_get_device (nvd, block);
+
+ if (!dc)
+ return EIO;
+
+ page = rtems_nvdisk_get_page (dc, block);
+
+#if RTEMS_NVDISK_TRACE
+ rtems_nvdisk_info (nvd, " read-block:%d=>%02d-%03d, cs:%04x",
+ block, dc->device, page, crc);
+#endif
+
+ ret = rtems_nvdisk_read_checksum (nvd, dc->device, page, &crc);
+
+ if (ret)
+ return ret;
+
+ if (crc == 0xffff)
+ {
+#if RTEMS_NVDISK_TRACE
+ rtems_nvdisk_warning (nvd, "read-block: crc not set: %d", block);
+#endif
+ memset (buffer, 0, nvd->block_size);
+ return 0;
+ }
+
+ ret = rtems_nvdisk_read_page (nvd, dc->device, page + dc->pages_desc, buffer);
+
+ if (ret)
+ return ret;
+
+ cs = rtems_nvdisk_page_checksum (buffer, nvd->block_size);
+
+ if (cs != crc)
+ {
+ rtems_nvdisk_error ("read-block: crc failure: %d: buffer:%04x page:%04x",
+ block, cs, crc);
+ return EIO;
+ }
+
+ return 0;
+}
+
+/**
+ * Write a block.
+ *
+ * @param nvd The rtems_nvdisk control table.
+ * @param block The block number to read.
+ * @param block_size The size of the block. Must match what we have.
+ * @param buffer The buffer to write the data into.
+ * @return 0 No error.
+ * @return EIO Invalid block size, block number, segment pointer, crc,
+ * page flags.
+ */
+static int
+rtems_nvdisk_write_block (rtems_nvdisk* nvd,
+ uint32_t block,
+ const unsigned char* buffer)
+{
+ rtems_nvdisk_device_ctl* dc;
+ uint32_t page;
+ uint16_t cs;
+ int ret;
+
+ dc = rtems_nvdisk_get_device (nvd, block);
+
+ if (!dc)
+ return EIO;
+
+ page = rtems_nvdisk_get_page (dc, block);
+
+ cs = rtems_nvdisk_page_checksum (buffer, nvd->block_size);
+
+#if RTEMS_NVDISK_TRACE
+ rtems_nvdisk_info (nvd, " write-block:%d=>%02d-%03d", block, dc->device, page);
+#endif
+
+ ret = rtems_nvdisk_write_page (nvd, dc->device, page + dc->pages_desc, buffer);
+
+ if (ret)
+ return ret;
+
+ return rtems_nvdisk_write_checksum (nvd, dc->device, page, cs);
+}
+
+/**
+ * Disk READ request handler. This primitive copies data from the
+ * flash disk to the supplied buffer and invoke the callout function
+ * to inform upper layer that reading is completed.
+ *
+ * @param req Pointer to the READ block device request info.
+ * @retval int The ioctl return value.
+ */
+static int
+rtems_nvdisk_read (rtems_nvdisk* nvd, rtems_blkdev_request* req)
+{
+ rtems_blkdev_sg_buffer* sg = req->bufs;
+ uint32_t bufs;
+ int ret = 0;
+
+#if RTEMS_NVDISK_TRACE
+ rtems_nvdisk_info (nvd, "read: blocks=%d", req->bufnum);
+#endif
+
+ for (bufs = 0; (ret == 0) && (bufs < req->bufnum); bufs++, sg++)
+ {
+ uint8_t* data;
+ uint32_t nvb;
+ uint32_t b;
+ nvb = sg->length / nvd->block_size;
+ data = sg->buffer;
+ for (b = 0; b < nvb; b++, data += nvd->block_size)
+ {
+ ret = rtems_nvdisk_read_block (nvd, sg->block + b, data);
+ if (ret)
+ break;
+ }
+ }
+
+ req->status = ret ? RTEMS_IO_ERROR : RTEMS_SUCCESSFUL;
+ req->req_done (req->done_arg, req->status);
+
+ return ret;
+}
+
+/**
+ * Flash disk WRITE request handler. This primitive copies data from
+ * supplied buffer to NV disk and invoke the callout function to inform
+ * upper layer that writing is completed.
+ *
+ * @param req Pointers to the WRITE block device request info.
+ * @retval int The ioctl return value.
+ */
+static int
+rtems_nvdisk_write (rtems_nvdisk* nvd, rtems_blkdev_request* req)
+{
+ rtems_blkdev_sg_buffer* sg = req->bufs;
+ uint32_t bufs;
+ int ret = 0;
+
+#if RTEMS_NVDISK_TRACE
+ rtems_nvdisk_info (nvd, "write: blocks=%d", req->bufnum);
+#endif
+
+ for (bufs = 0; (ret == 0) && (bufs < req->bufnum); bufs++, sg++)
+ {
+ uint8_t* data;
+ uint32_t nvb;
+ uint32_t b;
+ nvb = sg->length / nvd->block_size;
+ data = sg->buffer;
+ for (b = 0; b < nvb; b++, data += nvd->block_size)
+ {
+ ret = rtems_nvdisk_write_block (nvd, sg->block + b, data);
+ if (ret)
+ break;
+ }
+ }
+
+ req->status = ret ? RTEMS_IO_ERROR : RTEMS_SUCCESSFUL;
+ req->req_done (req->done_arg, req->status);
+
+ return 0;
+}
+
+/**
+ * NV disk erase disk sets all the checksums for 0xffff.
+ *
+ * @param nvd The nvdisk data.
+ * @retval int The ioctl return value.
+ */
+static int
+rtems_nvdisk_erase_disk (rtems_nvdisk* nvd)
+{
+ uint32_t device;
+
+#if RTEMS_NVDISK_TRACE
+ rtems_nvdisk_info (nvd, "erase-disk");
+#endif
+
+ for (device = 0; device < nvd->device_count; device++)
+ {
+ rtems_nvdisk_device_ctl* dc = &nvd->devices[device];
+ uint32_t page;
+ for (page = 0; page < (dc->pages - dc->pages_desc); page++)
+ {
+ int ret = rtems_nvdisk_write_checksum (nvd, dc->device, page, 0xffff);
+ if (ret)
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * NV disk IOCTL handler.
+ *
+ * @param dd Disk device.
+ * @param req IOCTL request code.
+ * @param argp IOCTL argument.
+ * @retval The IOCTL return value
+ */
+static int
+rtems_nvdisk_ioctl (rtems_disk_device *dd, uint32_t req, void* argp)
+{
+ dev_t dev = rtems_disk_get_device_identifier (dd);
+ rtems_device_minor_number minor = rtems_filesystem_dev_minor_t (dev);
+ rtems_blkdev_request* r = argp;
+ rtems_status_code sc;
+
+ if (minor >= rtems_nvdisk_count)
+ {
+ errno = ENODEV;
+ return -1;
+ }
+
+ if (rtems_nvdisks[minor].device_count == 0)
+ {
+ errno = ENODEV;
+ return -1;
+ }
+
+ errno = 0;
+
+ sc = rtems_semaphore_obtain (rtems_nvdisks[minor].lock, RTEMS_WAIT, 0);
+ if (sc != RTEMS_SUCCESSFUL)
+ errno = EIO;
+ else
+ {
+ errno = 0;
+ switch (req)
+ {
+ case RTEMS_BLKIO_REQUEST:
+ switch (r->req)
+ {
+ case RTEMS_BLKDEV_REQ_READ:
+ errno = rtems_nvdisk_read (&rtems_nvdisks[minor], r);
+ break;
+
+ case RTEMS_BLKDEV_REQ_WRITE:
+ errno = rtems_nvdisk_write (&rtems_nvdisks[minor], r);
+ break;
+
+ default:
+ errno = EINVAL;
+ break;
+ }
+ break;
+
+ case RTEMS_NVDISK_IOCTL_ERASE_DISK:
+ errno = rtems_nvdisk_erase_disk (&rtems_nvdisks[minor]);
+ break;
+
+ case RTEMS_NVDISK_IOCTL_INFO_LEVEL:
+ rtems_nvdisks[minor].info_level = (uintptr_t) argp;
+ break;
+
+ default:
+ rtems_blkdev_ioctl (dd, req, argp);
+ break;
+ }
+
+ sc = rtems_semaphore_release (rtems_nvdisks[minor].lock);
+ if (sc != RTEMS_SUCCESSFUL)
+ errno = EIO;
+ }
+
+ return errno == 0 ? 0 : -1;
+}
+
+/**
+ * NV disk device driver initialization.
+ *
+ * @todo Memory clean up on error is really badly handled.
+ *
+ * @param major NV disk major device number.
+ * @param minor Minor device number, not applicable.
+ * @param arg Initialization argument, not applicable.
+ */
+rtems_device_driver
+rtems_nvdisk_initialize (rtems_device_major_number major,
+ rtems_device_minor_number minor,
+ void* arg __attribute__((unused)))
+{
+ const rtems_nvdisk_config* c = rtems_nvdisk_configuration;
+ rtems_nvdisk* nvd;
+ rtems_status_code sc;
+
+ sc = rtems_disk_io_initialize ();
+ if (sc != RTEMS_SUCCESSFUL)
+ return sc;
+
+ sc = rtems_nvdisk_crc16_gen_factors (0x8408);
+ if (sc != RTEMS_SUCCESSFUL)
+ return sc;
+
+ rtems_nvdisks = calloc (rtems_nvdisk_configuration_size,
+ sizeof (rtems_nvdisk));
+
+ if (!rtems_nvdisks)
+ return RTEMS_NO_MEMORY;
+
+ for (minor = 0; minor < rtems_nvdisk_configuration_size; minor++, c++)
+ {
+ char name[] = RTEMS_NVDISK_DEVICE_BASE_NAME "a";
+ dev_t dev = rtems_filesystem_make_dev_t (major, minor);
+ uint32_t device;
+ uint32_t blocks = 0;
+
+ nvd = &rtems_nvdisks[minor];
+
+ name [sizeof(RTEMS_NVDISK_DEVICE_BASE_NAME)] += minor;
+
+ nvd->major = major;
+ nvd->minor = minor;
+ nvd->flags = c->flags;
+ nvd->block_size = c->block_size;
+ nvd->info_level = c->info_level;
+
+ nvd->devices = calloc (c->device_count, sizeof (rtems_nvdisk_device_ctl));
+ if (!nvd->devices)
+ return RTEMS_NO_MEMORY;
+
+ for (device = 0; device < c->device_count; device++)
+ {
+ rtems_nvdisk_device_ctl* dc = &nvd->devices[device];
+
+ dc->device = device;
+ dc->pages = rtems_nvdisk_pages_in_device (nvd, &c->devices[device]);
+ dc->pages_desc = rtems_nvdisk_page_desc_pages (nvd, &c->devices[device]);
+ dc->block_base = blocks;
+
+ blocks += dc->pages - dc->pages_desc;
+
+ dc->descriptor = &c->devices[device];
+ }
+
+ nvd->block_count = blocks;
+ nvd->device_count = c->device_count;
+
+ sc = rtems_disk_create_phys(dev, c->block_size, blocks,
+ rtems_nvdisk_ioctl, NULL, name);
+ if (sc != RTEMS_SUCCESSFUL)
+ {
+ rtems_nvdisk_error ("disk create phy failed");
+ return sc;
+ }
+
+ sc = rtems_semaphore_create (rtems_build_name ('N', 'V', 'D', 'K'), 1,
+ RTEMS_PRIORITY | RTEMS_BINARY_SEMAPHORE |
+ RTEMS_INHERIT_PRIORITY, 0, &nvd->lock);
+ if (sc != RTEMS_SUCCESSFUL)
+ {
+ rtems_nvdisk_error ("disk lock create failed");
+ return sc;
+ }
+ }
+
+ rtems_nvdisk_count = rtems_nvdisk_configuration_size;
+
+ return RTEMS_SUCCESSFUL;
+}
diff --git a/cpukit/libblock/src/ramdisk-config.c b/cpukit/libblock/src/ramdisk-config.c
new file mode 100644
index 0000000000..255240668b
--- /dev/null
+++ b/cpukit/libblock/src/ramdisk-config.c
@@ -0,0 +1,90 @@
+/**
+ * @file
+ *
+ * @ingroup rtems_ramdisk
+ *
+ * @brief RAM disk block device implementation.
+ */
+
+/*
+ * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia
+ * Author: Victor V. Vengerov <vvv@oktet.ru>
+ *
+ * @(#) $Id$
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdlib.h>
+
+#include <rtems.h>
+#include <rtems/libio.h>
+#include <rtems/ramdisk.h>
+
+rtems_device_driver
+ramdisk_initialize(
+ rtems_device_major_number major,
+ rtems_device_minor_number minor __attribute__((unused)),
+ void *arg __attribute__((unused)))
+{
+ rtems_device_minor_number i;
+ rtems_ramdisk_config *c = rtems_ramdisk_configuration;
+ struct ramdisk *r;
+ rtems_status_code rc;
+
+ rc = rtems_disk_io_initialize();
+ if (rc != RTEMS_SUCCESSFUL)
+ return rc;
+
+ /*
+ * Coverity Id 27 notes that this calloc() is a resource leak.
+ *
+ * This is allocating memory for a RAM disk which will persist for
+ * the life of the system. RTEMS has no "de-initialize" driver call
+ * so there is no corresponding free(r). Coverity is correct that
+ * it is never freed but this is not a problem.
+ */
+ r = calloc(rtems_ramdisk_configuration_size, sizeof(struct ramdisk));
+ r->trace = false;
+ for (i = 0; i < rtems_ramdisk_configuration_size; i++, c++, r++)
+ {
+ dev_t dev = rtems_filesystem_make_dev_t(major, i);
+ char name [] = RAMDISK_DEVICE_BASE_NAME "a";
+ name [sizeof(RAMDISK_DEVICE_BASE_NAME)] += i;
+ r->block_size = c->block_size;
+ r->block_num = c->block_num;
+ if (c->location == NULL)
+ {
+ r->malloced = true;
+ r->area = malloc(r->block_size * r->block_num);
+ if (r->area == NULL) /* No enough memory for this disk */
+ {
+ r->initialized = false;
+ continue;
+ }
+ else
+ {
+ r->initialized = true;
+ }
+ }
+ else
+ {
+ r->malloced = false;
+ r->initialized = true;
+ r->area = c->location;
+ }
+ rc = rtems_disk_create_phys(dev, c->block_size, c->block_num,
+ ramdisk_ioctl, r, name);
+ if (rc != RTEMS_SUCCESSFUL)
+ {
+ if (r->malloced)
+ {
+ free(r->area);
+ }
+ r->initialized = false;
+ }
+ }
+ return RTEMS_SUCCESSFUL;
+}
diff --git a/cpukit/libblock/src/ramdisk-driver.c b/cpukit/libblock/src/ramdisk-driver.c
new file mode 100644
index 0000000000..2bc8d27962
--- /dev/null
+++ b/cpukit/libblock/src/ramdisk-driver.c
@@ -0,0 +1,134 @@
+/**
+ * @file
+ *
+ * @ingroup rtems_ramdisk
+ *
+ * @brief RAM disk block device implementation.
+ */
+
+/*
+ * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia
+ * Author: Victor V. Vengerov <vvv@oktet.ru>
+ *
+ * @(#) $Id$
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+/* FIXME: How to set this define? */
+#if !defined(RTEMS_RAMDISK_TRACE)
+ #define RTEMS_RAMDISK_TRACE 0
+#endif
+
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+
+#if RTEMS_RAMDISK_TRACE
+ #include <stdio.h>
+#endif
+
+#include <rtems.h>
+#include <rtems/ramdisk.h>
+
+#if RTEMS_RAMDISK_TRACE
+ static void
+ rtems_ramdisk_printf (const ramdisk *rd, const char *format, ...)
+ {
+ if (rd->trace)
+ {
+ va_list args;
+ va_start (args, format);
+ printf ("ramdisk:");
+ vprintf (format, args);
+ printf ("\n");
+ }
+ }
+#endif
+
+static int
+ramdisk_read(struct ramdisk *rd, rtems_blkdev_request *req)
+{
+ uint8_t *from = rd->area;
+ uint32_t i;
+ rtems_blkdev_sg_buffer *sg;
+
+#if RTEMS_RAMDISK_TRACE
+ rtems_ramdisk_printf (rd, "ramdisk read: start=%d, blocks=%d",
+ req->bufs[0].block, req->bufnum);
+#endif
+
+ for (i = 0, sg = req->bufs; i < req->bufnum; i++, sg++)
+ {
+#if RTEMS_RAMDISK_TRACE
+ rtems_ramdisk_printf (rd, "ramdisk read: buf=%d block=%d length=%d off=%d addr=%p",
+ i, sg->block, sg->length, sg->block * rd->block_size,
+ from + (sg->block * rd->block_size));
+#endif
+ memcpy(sg->buffer, from + (sg->block * rd->block_size), sg->length);
+ }
+ req->status = RTEMS_SUCCESSFUL;
+ req->req_done(req->done_arg, RTEMS_SUCCESSFUL);
+ return 0;
+}
+
+static int
+ramdisk_write(struct ramdisk *rd, rtems_blkdev_request *req)
+{
+ uint8_t *to = rd->area;
+ uint32_t i;
+ rtems_blkdev_sg_buffer *sg;
+
+#if RTEMS_RAMDISK_TRACE
+ rtems_ramdisk_printf (rd, "ramdisk write: start=%d, blocks=%d",
+ req->bufs[0].block, req->bufnum);
+#endif
+ for (i = 0, sg = req->bufs; i < req->bufnum; i++, sg++)
+ {
+#if RTEMS_RAMDISK_TRACE
+ rtems_ramdisk_printf (rd, "ramdisk write: buf=%d block=%d length=%d off=%d addr=%p",
+ i, sg->block, sg->length, sg->block * rd->block_size,
+ to + (sg->block * rd->block_size));
+#endif
+ memcpy(to + (sg->block * rd->block_size), sg->buffer, sg->length);
+ }
+ req->status = RTEMS_SUCCESSFUL;
+ req->req_done(req->done_arg, RTEMS_SUCCESSFUL);
+ return 0;
+}
+
+int
+ramdisk_ioctl(rtems_disk_device *dd, uint32_t req, void *argp)
+{
+ switch (req)
+ {
+ case RTEMS_BLKIO_REQUEST:
+ {
+ rtems_blkdev_request *r = argp;
+ struct ramdisk *rd = rtems_disk_get_driver_data(dd);
+
+ switch (r->req)
+ {
+ case RTEMS_BLKDEV_REQ_READ:
+ return ramdisk_read(rd, r);
+
+ case RTEMS_BLKDEV_REQ_WRITE:
+ return ramdisk_write(rd, r);
+
+ default:
+ errno = EINVAL;
+ return -1;
+ }
+ break;
+ }
+
+ default:
+ return rtems_blkdev_ioctl (dd, req, argp);
+ break;
+ }
+
+ errno = EINVAL;
+ return -1;
+}
diff --git a/cpukit/libblock/src/ramdisk-init.c b/cpukit/libblock/src/ramdisk-init.c
new file mode 100644
index 0000000000..fb8def0d83
--- /dev/null
+++ b/cpukit/libblock/src/ramdisk-init.c
@@ -0,0 +1,126 @@
+/**
+ * @file
+ *
+ * @ingroup rtems_ramdisk
+ *
+ * @brief RAM disk block device implementation.
+ */
+
+/*
+ * Copyright (c) 2009
+ * embedded brains GmbH
+ * Obere Lagerstr. 30
+ * D-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.
+ *
+ * $Id$
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdlib.h>
+
+#include <rtems.h>
+#include <rtems/ramdisk.h>
+
+const rtems_driver_address_table ramdisk_ops = {
+ .initialization_entry = NULL,
+ RTEMS_GENERIC_BLOCK_DEVICE_DRIVER_ENTRIES
+};
+
+ramdisk *ramdisk_allocate(
+ void *area_begin,
+ uint32_t block_size,
+ rtems_blkdev_bnum block_count,
+ bool trace
+)
+{
+ struct ramdisk *rd = malloc(sizeof(struct ramdisk));
+
+ if (rd == NULL) {
+ return NULL;
+ }
+
+ if (area_begin == NULL) {
+ area_begin = calloc(block_count, block_size);
+ if (area_begin == NULL) {
+ free(rd);
+
+ return NULL;
+ }
+ rd->malloced = true;
+ } else {
+ rd->malloced = false;
+ }
+ rd->block_size = block_size;
+ rd->block_num = block_count;
+ rd->area = area_begin;
+ rd->trace = trace;
+ rd->initialized = true;
+
+ return rd;
+}
+
+void ramdisk_free(ramdisk *rd)
+{
+ if (rd != NULL) {
+ if (rd->malloced) {
+ free(rd->area);
+ }
+ free(rd);
+ }
+}
+
+rtems_status_code ramdisk_register(
+ uint32_t block_size,
+ rtems_blkdev_bnum block_count,
+ bool trace,
+ const char *disk,
+ dev_t *dev_ptr
+)
+{
+ rtems_status_code sc = RTEMS_SUCCESSFUL;
+ rtems_device_major_number major = 0;
+ ramdisk *rd = NULL;
+ dev_t dev = 0;
+
+ sc = rtems_io_register_driver(0, &ramdisk_ops, &major);
+ if (sc != RTEMS_SUCCESSFUL) {
+ return RTEMS_UNSATISFIED;
+ }
+
+ rd = ramdisk_allocate(NULL, block_size, block_count, trace);
+ if (rd == NULL) {
+ rtems_io_unregister_driver(major);
+
+ return RTEMS_UNSATISFIED;
+ }
+
+ dev = rtems_filesystem_make_dev_t(major, 0);
+
+ sc = rtems_disk_create_phys(
+ dev,
+ block_size,
+ block_count,
+ ramdisk_ioctl,
+ rd,
+ disk
+ );
+ if (sc != RTEMS_SUCCESSFUL) {
+ ramdisk_free(rd);
+ rtems_io_unregister_driver(major);
+
+ return RTEMS_UNSATISFIED;
+ }
+
+ *dev_ptr = dev;
+
+ return RTEMS_SUCCESSFUL;
+}
diff --git a/cpukit/libblock/src/show_bdbuf.c b/cpukit/libblock/src/show_bdbuf.c
new file mode 100644
index 0000000000..74ff9922aa
--- /dev/null
+++ b/cpukit/libblock/src/show_bdbuf.c
@@ -0,0 +1,909 @@
+/*===============================================================*\
+| Project: RTEMS bdbuf inspector |
++-----------------------------------------------------------------+
+| File: show_bdbuf.c
++-----------------------------------------------------------------+
+| Copyright (c) 2005 |
+| Embedded Brains GmbH |
+| Obere Lagerstr. 30 |
+| D-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. |
+| |
++-----------------------------------------------------------------+
+| this file contains functions to enable the monitor |
+| to show bdbuf information |
+| |
+| XXX!!! ATTETION!!! XXX!!! |
+| |
+| This module inspects the bdbuf data structures, |
+| assuming they are static, but in fact they are used very |
+| dynamically. Therefore the results show MAY BE INCORRECT in |
+| some cases. And, to cure this a bit, this module may block |
+| preemption for a rather long time and therefore it may |
+| BREAK THE REALTIME BEHAVIOUR OF YOUR SYSTEM (when in use) |
++-----------------------------------------------------------------+
+| date history ID |
+| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
+| 26.09.06 creation doe |
+|*****************************************************************|
+\*===============================================================*/
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <rtems.h>
+#include <rtems/monitor.h>
+#include <rtems/bdbuf.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <rtems/libio.h>
+#include <inttypes.h>
+
+typedef struct {
+ bool bdbuf_modified;
+ bool bdbuf_in_progress;
+ bool bdbuf_actual;
+ bool bdbuf_used;
+ bool bdbuf_all;
+ rtems_bdpool_id pool_id;
+} show_bdbuf_filter_t;
+
+typedef struct {
+ bool show_all;
+ bool show_node_chain;
+ bool show_dev;
+ bool show_blocknum;
+ bool show_error;
+ bool show_state;
+ bool show_use_count;
+ bool show_pool_id;
+ bool show_sema;
+} show_bdbuf_selector_t;
+
+typedef enum {bdbuf_chain_ident_none,
+ bdbuf_chain_ident_free,
+ bdbuf_chain_ident_lru,
+ bdbuf_chain_ident_mod} bdbuf_chain_identifier_t;
+
+typedef struct {
+ rtems_bdpool_id pool_id;
+ int index;
+ bdbuf_chain_identifier_t in_chain;
+ dev_t dev;
+ blkdev_bnum blknum;
+ rtems_status_code status;
+ int error;
+ bool modified;
+ bool in_progress;
+ bool actual;
+ int use_count;
+ const CORE_mutex_Control *sema;
+} show_bdbuf_bdbuf_info_t;
+
+typedef rtems_mode preemption_key_t;
+#define DISABLE_PREEMPTION(key) \
+ do { \
+ rtems_task_mode(RTEMS_NO_PREEMPT, RTEMS_PREEMPT_MASK, &(key)); \
+ } while (0)
+
+#define ENABLE_PREEMPTION(key) \
+ do { \
+ rtems_mode temp; \
+ rtems_task_mode((key), RTEMS_PREEMPT_MASK, &temp); \
+ } while (0)
+
+/*=========================================================================*\
+| Function: |
+\*-------------------------------------------------------------------------*/
+rtems_status_code rtems_bdbuf_show_follow_chain_node_to_head
+(
+/*-------------------------------------------------------------------------*\
+| Purpose: |
+| follow a given chain to its head |
+| XXX: this is executed with preemption disabled |
++---------------------------------------------------------------------------+
+| Input Parameters: |
+\*-------------------------------------------------------------------------*/
+ const Chain_Node *the_node, /* input: node to track to its head */
+ Chain_Control **the_head /* storage for pointer to chain head */
+)
+/*-------------------------------------------------------------------------*\
+| Return Value: |
+| rtems_status_code |
+\*=========================================================================*/
+{
+ rtems_status_code rc = RTEMS_SUCCESSFUL;
+ preemption_key_t preempt_key;
+ bool preempt_disabled = false;
+ /*
+ * disable preemption
+ */
+ if (rc == RTEMS_SUCCESSFUL) {
+ DISABLE_PREEMPTION(preempt_key);
+ }
+ /*
+ * follow node to its head
+ * XXX: this is highly dependent on the chain implementation
+ * in score/src/chain.c and friends
+ */
+ while (the_node->previous != NULL) {
+ the_node = the_node->previous;
+ }
+ /*
+ * reenable preemption, if disabled
+ */
+ if (preempt_disabled) {
+ ENABLE_PREEMPTION(preempt_key);
+ }
+ /*
+ * XXX: this depends n the chain implementation in
+ * score/include/rtems/score/chain.h:
+ * Chain_Control is overlayed by two Cohain_Nodes
+ */
+ *the_head = (Chain_Control *)the_node;
+
+ return rc;
+}
+
+/*=========================================================================*\
+| Function: |
+\*-------------------------------------------------------------------------*/
+rtems_status_code rtems_bdbuf_show_determine_chain_of_bdbuf
+(
+/*-------------------------------------------------------------------------*\
+| Purpose: |
+| find out, which chain this bdbuf is linked in |
++---------------------------------------------------------------------------+
+| Input Parameters: |
+\*-------------------------------------------------------------------------*/
+ const bdbuf_buffer *the_bdbuf, /* this is the bdbuf structure */
+ const bdbuf_pool *curr_pool, /* the pool this buffer belongs to */
+ bdbuf_chain_identifier_t *chn_ident /* result: identifier for chain */
+)
+/*-------------------------------------------------------------------------*\
+| Return Value: |
+| rtems_status_code |
+\*=========================================================================*/
+{
+ rtems_status_code rc = RTEMS_SUCCESSFUL;
+ Chain_Control *the_chain_control;
+
+
+ *chn_ident = bdbuf_chain_ident_none;
+ if (rc == RTEMS_SUCCESSFUL) {
+ rc = rtems_bdbuf_show_follow_chain_node_to_head(&(the_bdbuf->link),
+ &(the_chain_control));
+ }
+ if (rc == RTEMS_SUCCESSFUL) {
+ if (the_chain_control == &(curr_pool->free)) {
+ *chn_ident = bdbuf_chain_ident_free;
+ }
+ else if (the_chain_control == &(curr_pool->lru)) {
+ *chn_ident = bdbuf_chain_ident_lru;
+ }
+ else if (the_chain_control == &(rtems_bdbuf_ctx.mod)) {
+ *chn_ident = bdbuf_chain_ident_mod;
+ }
+ }
+ return rc;
+}
+
+/*=========================================================================*\
+| Function: |
+\*-------------------------------------------------------------------------*/
+rtems_status_code rtems_bdbuf_show_getargs
+(
+/*-------------------------------------------------------------------------*\
+| Purpose: |
+| analyze cmd arguments |
++---------------------------------------------------------------------------+
+| Input Parameters: |
+\*-------------------------------------------------------------------------*/
+ int argc,
+ char **argv,
+ show_bdbuf_filter_t *filter,
+ show_bdbuf_selector_t *selector
+)
+/*-------------------------------------------------------------------------*\
+| Return Value: |
+| rtems_status_code |
+\*=========================================================================*/
+{
+ rtems_status_code sc = RTEMS_SUCCESSFUL;
+ int arg_error = 0;
+ int i;
+ char *tmp_ptr;
+ int nm_argc = 0;
+ /*
+ * set filter and selector to default
+ */
+ memset(filter,0,sizeof(*filter));
+ filter->bdbuf_all = true;
+ memset(selector,0,sizeof(*selector));
+ selector->show_all = true;
+
+ /*
+ * scan arguments
+ */
+ for (i = 1;
+ (i < argc) && (arg_error == 0);
+ i++) {
+ if (argv[i][0] == '-') {
+ /*
+ * modifier arguments
+ */
+ switch(tolower(argv[i][1])) {
+ /*
+ * selection, which bdbufs to show
+ */
+ case 'm': /* only show bdbufs modified */
+ filter->bdbuf_modified = true ;
+ filter->bdbuf_all = false;
+ break;
+ case 'i': /* only show bdbufs in progress*/
+ filter->bdbuf_in_progress = true ;
+ filter->bdbuf_all = false;
+ break;
+ case 'v': /* only show bdbufs, which have valid data*/
+ filter->bdbuf_actual = true ;
+ filter->bdbuf_all = false;
+ break;
+ case 'u': /* only show bdbufs, which are in use */
+ filter->bdbuf_used = true ;
+ filter->bdbuf_all = false;
+ break;
+ case 'p': /* only show bdbufs, which belong to pool <n> */
+ filter->pool_id = strtol(argv[i]+2,&tmp_ptr,0);
+ if (tmp_ptr == argv[i]+2) { /* no conversion performed... */
+ arg_error = i;
+ }
+ filter->bdbuf_all = false;
+ break;
+ /*
+ * selection, what fields to show
+ */
+ case 'n': /* show bdbuf node_chain */
+ selector->show_node_chain = true ;
+ selector->show_all = false;
+ break;
+ case 'd': /* show device */
+ selector->show_dev = true ;
+ selector->show_all = false;
+ break;
+ case 'b': /* show blocknum */
+ selector->show_blocknum = true ;
+ selector->show_all = false;
+ break;
+ case 'e': /* show bdbuf error status */
+ selector->show_error = true ;
+ selector->show_all = false;
+ break;
+ case 's': /* show bdbuf state */
+ selector->show_state = true ;
+ selector->show_all = false;
+ break;
+ case 'c': /* show bdbuf use count */
+ selector->show_use_count = true ;
+ selector->show_all = false;
+ break;
+ case 'l': /* show bdbuf pool id */
+ selector->show_pool_id = true ;
+ selector->show_all = false;
+ break;
+ case 't': /* show bdbuf transfer sema */
+ selector->show_sema = true ;
+ break;
+ default:
+ arg_error = i;
+ break;
+ }
+ }
+ else {
+ /*
+ * non-modifier arguments
+ */
+ switch(++nm_argc) {
+ default: /* no further arguments defined */
+ arg_error = i;
+ break;
+ }
+ }
+ }
+ if (arg_error) {
+ printf("%s: unknown argument %s\n",argv[0],argv[arg_error]);
+ sc = RTEMS_NOT_DEFINED;
+ }
+ return sc;
+}
+
+/*=========================================================================*\
+| Function: |
+\*-------------------------------------------------------------------------*/
+rtems_status_code rtems_bdbuf_show_get_bufpool
+(
+/*-------------------------------------------------------------------------*\
+| Purpose: |
+| get buffer pool information |
+| XXX: this should be coupled closer to the bdbuf.c module |
++---------------------------------------------------------------------------+
+| Input Parameters: |
+\*-------------------------------------------------------------------------*/
+ struct bdbuf_pool **pool_base_pptr,
+ int *pool_cnt_ptr
+)
+/*-------------------------------------------------------------------------*\
+| Return Value: |
+| rtems_status_code |
+\*=========================================================================*/
+{
+ rtems_status_code rc = RTEMS_SUCCESSFUL;
+#if 0
+ rtems_status_code pool_rc = RTEMS_SUCCESSFUL;
+ struct bdbuf_pool *curr_pool,*pool_base, *pool_top;
+ int pool_cnt;
+ int pool_probe_size;
+ /*
+ * get first buffer pool
+ * XXX: this is highly dependent on how pools are defined
+ * and maintained in bdbuf.c
+ */
+ if (rc == RTEMS_SUCCESSFUL) {
+ /*
+ * try all possible pool sizes, get highest/lowest pool address
+ */
+ pool_base = NULL;
+ pool_top = NULL;
+ curr_pool = NULL;
+ for (pool_probe_size = 1;
+ pool_probe_size < (INT_MAX>>1) && (pool_rc == RTEMS_SUCCESSFUL);
+ pool_probe_size <<= 1) {
+ pool_rc = rtems_bdbuf_find_pool(pool_probe_size,&curr_pool);
+ if (pool_rc == RTEMS_SUCCESSFUL) {
+ if (pool_base > curr_pool) {
+ pool_base = curr_pool;
+ }
+ if (pool_top < curr_pool) {
+ pool_top = curr_pool;
+ }
+ }
+ }
+ if (pool_base == NULL) {
+ rc = RTEMS_UNSATISFIED;
+ }
+ else {
+ pool_cnt = (pool_top - pool_base) + 1;
+ }
+ }
+ if (rc == RTEMS_SUCCESSFUL) {
+ *pool_base_pptr = pool_base;
+ *pool_cnt_ptr = pool_cnt;
+ }
+#else
+ if (rc == RTEMS_SUCCESSFUL) {
+ *pool_base_pptr = rtems_bdbuf_ctx.pool;
+ *pool_cnt_ptr = rtems_bdbuf_ctx.npools;
+ }
+#endif
+ return rc;
+}
+
+/*=========================================================================*\
+| Function: |
+\*-------------------------------------------------------------------------*/
+rtems_status_code rtems_bdbuf_show_pool_header
+(
+/*-------------------------------------------------------------------------*\
+| Purpose: |
+| print buffer pool information |
++---------------------------------------------------------------------------+
+| Input Parameters: |
+\*-------------------------------------------------------------------------*/
+ int pool_idx,
+ bdbuf_pool *pool_ptr
+)
+/*-------------------------------------------------------------------------*\
+| Return Value: |
+| rtems_status_code |
+\*=========================================================================*/
+{
+
+ rtems_status_code rc = RTEMS_SUCCESSFUL;
+
+ if (rc == RTEMS_SUCCESSFUL) {
+ printf("------------------------------------------------------------------------------\n");
+ printf(" pool #%03d: blksize=%5u nblks=%5u buf_mem=0x%08" PRIxPTR " bdbuf_mem=0x%08" PRIxPTR "\n",
+ pool_idx,
+ pool_ptr->blksize,
+ pool_ptr->nblks,
+ (intptr_t) pool_ptr->mallocd_bufs,
+ (intptr_t) pool_ptr->bdbufs);
+ printf("------------------------------------------------------------------------------\n");
+ }
+ return rc;
+}
+
+/*=========================================================================*\
+| Function: |
+\*-------------------------------------------------------------------------*/
+rtems_status_code rtems_show_bdbuf_get_bdbuf_info
+(
+/*-------------------------------------------------------------------------*\
+| Purpose: |
+| get buffer pool information |
+| XXX: this should be coupled closer to the bdbuf.c module |
++---------------------------------------------------------------------------+
+| Input Parameters: |
+\*-------------------------------------------------------------------------*/
+ const bdbuf_buffer *the_bdbuf, /* this is the bdbuf structure */
+ int bdbuf_idx, /* index of bdbuf */
+ const bdbuf_pool *curr_pool, /* the pool this buffer belongs to */
+ show_bdbuf_bdbuf_info_t *bdbuf_info /* struct to store info of bdbuf */
+)
+/*-------------------------------------------------------------------------*\
+| Return Value: |
+| rtems_status_code |
+\*=========================================================================*/
+{
+
+ rtems_status_code rc = RTEMS_SUCCESSFUL;
+
+ /*
+ * determine the chain we are in
+ */
+ if (rc == RTEMS_SUCCESSFUL) {
+ rc = rtems_bdbuf_show_determine_chain_of_bdbuf(the_bdbuf,curr_pool,
+ &(bdbuf_info->in_chain));
+ if (rc != RTEMS_SUCCESSFUL) {
+ bdbuf_info->in_chain = bdbuf_chain_ident_none;
+ rc = RTEMS_SUCCESSFUL;
+ }
+ }
+
+ if (rc == RTEMS_SUCCESSFUL) {
+ bdbuf_info->index = bdbuf_idx;
+ bdbuf_info->dev = the_bdbuf->dev;
+ bdbuf_info->blknum = the_bdbuf->block;
+ bdbuf_info->status = the_bdbuf->status;
+ bdbuf_info->error = the_bdbuf->error;
+ bdbuf_info->modified = the_bdbuf->modified;
+ bdbuf_info->in_progress = the_bdbuf->in_progress;
+ bdbuf_info->actual = the_bdbuf->actual;
+ bdbuf_info->use_count = the_bdbuf->use_count;
+ bdbuf_info->sema = &(the_bdbuf->transfer_sema);
+ bdbuf_info->pool_id = the_bdbuf->pool;
+ }
+ return rc;
+}
+
+/*=========================================================================*\
+| Function: |
+\*-------------------------------------------------------------------------*/
+rtems_status_code rtems_show_bdbuf_match_filter
+(
+/*-------------------------------------------------------------------------*\
+| Purpose: |
+| match bdbuf info with given filter |
++---------------------------------------------------------------------------+
+| Input Parameters: |
+\*-------------------------------------------------------------------------*/
+ const show_bdbuf_bdbuf_info_t *bdbuf_info, /* struct to store info of bdbuf */
+ const show_bdbuf_filter_t *filter,
+ bool *is_match
+)
+/*-------------------------------------------------------------------------*\
+| Return Value: |
+| rtems_status_code |
+\*=========================================================================*/
+{
+
+ rtems_status_code rc = RTEMS_SUCCESSFUL;
+ bool unmatch = false;
+
+ if (rc == RTEMS_SUCCESSFUL) {
+ if (filter->bdbuf_all) {
+ unmatch = false;
+ }
+ else {
+ unmatch = ((filter->bdbuf_modified && !bdbuf_info->modified) ||
+ (filter->bdbuf_in_progress && !bdbuf_info->in_progress) ||
+ (filter->bdbuf_actual && !bdbuf_info->actual) ||
+ (filter->bdbuf_used && !(bdbuf_info->use_count > 0)));
+
+ }
+ *is_match = !unmatch;
+ }
+ return rc;
+}
+
+/*=========================================================================*\
+| Function: |
+\*-------------------------------------------------------------------------*/
+rtems_status_code rtems_show_bdbuf_print_wait_chain
+(
+/*-------------------------------------------------------------------------*\
+| Purpose: |
+| list tasks waiting in "transfer_sema" chain |
++---------------------------------------------------------------------------+
+| Input Parameters: |
+\*-------------------------------------------------------------------------*/
+ bdbuf_buffer *the_bdbuf /* this is the bdbuf structure */
+)
+/*-------------------------------------------------------------------------*\
+| Return Value: |
+| rtems_status_code |
+\*=========================================================================*/
+{
+ rtems_status_code rc = RTEMS_SUCCESSFUL;
+ Chain_Control *the_chain_head;
+ const Chain_Node *the_chain_node;
+ int thread_cnt = 0;
+ const Thread_Control *the_thread;
+ Objects_Id thread_id;
+ Objects_Name thread_name;
+ uint32_t thread_name_nonstring;
+ /*
+ * get head of (fifo) wait chain
+ */
+ if (rc == RTEMS_SUCCESSFUL) {
+ the_chain_head = &(the_bdbuf->transfer_sema.Wait_queue.Queues.Fifo);
+ the_chain_node = _Chain_First(the_chain_head);
+ }
+ /*
+ * walk through thread chain
+ */
+ while ((rc == RTEMS_SUCCESSFUL) &&
+ (the_chain_node != _Chain_Tail( the_chain_head ))) {
+ thread_cnt++;
+ the_thread = (const Thread_Control *)the_chain_node;
+
+ thread_id = the_thread->Object.id;
+ thread_name = the_thread->Object.name;
+ thread_name_nonstring = (uint32_t)thread_name.name_u32;
+ printf("%20s %3d (0x%08" PRIx32 ") %c%c%c%c\n",
+ ((thread_cnt == 1) ? "Threads waiting:" : ""),
+ thread_cnt,thread_id,
+ (char)((thread_name_nonstring >> 24) & 0xff),
+ (char)((thread_name_nonstring >> 16) & 0xff),
+ (char)((thread_name_nonstring >> 8) & 0xff),
+ (char)((thread_name_nonstring >> 0) & 0xff));
+
+ the_chain_node = the_chain_node->next;
+ }
+
+ return rc;
+}
+
+/*=========================================================================*\
+| Function: |
+\*-------------------------------------------------------------------------*/
+rtems_status_code rtems_show_bdbuf_print
+(
+/*-------------------------------------------------------------------------*\
+| Purpose: |
+| print requested bdbuffer information |
++---------------------------------------------------------------------------+
+| Input Parameters: |
+\*-------------------------------------------------------------------------*/
+ const show_bdbuf_bdbuf_info_t *bdbuf_info, /* info of bdbuf */
+ show_bdbuf_selector_t * selector, /* selector, what to show */
+ bool print_header /* true: print header, not info */
+)
+/*-------------------------------------------------------------------------*\
+| Return Value: |
+| rtems_status_code |
+\*=========================================================================*/
+{
+
+ rtems_status_code rc = RTEMS_SUCCESSFUL;
+
+ /*
+ * 6 chars: print index of buffer
+ */
+ if (rc == RTEMS_SUCCESSFUL) {
+ if (print_header) {
+ printf("INDEX ");
+ }
+ else {
+ printf("%5u ",bdbuf_info->index);
+ }
+ }
+ /*
+ * 3 chars: print info about the pool id of this buffer
+ */
+ if ((rc == RTEMS_SUCCESSFUL) &&
+ ((selector->show_all) ||
+ (selector->show_use_count))) {
+ if (print_header) {
+ printf("PL ");
+ }
+ else {
+ printf("%2u ",bdbuf_info->pool_id);
+ }
+ }
+
+ /*
+ * 4 chars: print info about chain (lru/free/mod) of this buffer
+ */
+ if ((rc == RTEMS_SUCCESSFUL) &&
+ ((selector->show_all) ||
+ (selector->show_node_chain))) {
+ if (print_header) {
+ printf("CHN ");
+ }
+ else {
+ printf("%3s ",
+ ((bdbuf_info->in_chain == bdbuf_chain_ident_free) ? "FRE"
+ : (bdbuf_info->in_chain == bdbuf_chain_ident_lru) ? "LRU"
+ : (bdbuf_info->in_chain == bdbuf_chain_ident_mod) ? "MOD"
+ : "???"));
+ }
+ }
+
+ /*
+ * 7 chars: print info about device of this buffer
+ */
+ if ((rc == RTEMS_SUCCESSFUL) &&
+ ((selector->show_all) ||
+ (selector->show_dev))) {
+ if (print_header) {
+ printf("DEVICE ");
+ }
+ else {
+ printf("%3" PRIu32 "%2" PRIu32,
+ ((bdbuf_info->dev == -1)
+ ? 0 : rtems_filesystem_dev_major_t(bdbuf_info->dev)),
+ ((bdbuf_info->dev == -1)
+ ? 0 : rtems_filesystem_dev_minor_t(bdbuf_info->dev)));
+ }
+ }
+
+ /*
+ * 7 chars: print info about block number of this buffer
+ */
+ if ((rc == RTEMS_SUCCESSFUL) &&
+ ((selector->show_all) ||
+ (selector->show_blocknum))) {
+ if (print_header) {
+ printf("BLOCK ");
+ }
+ else {
+ printf("%6" PRIu32,bdbuf_info->blknum);
+ }
+ }
+
+ /*
+ * 4 chars: print info about use count of this buffer
+ */
+ if ((rc == RTEMS_SUCCESSFUL) &&
+ ((selector->show_all) ||
+ (selector->show_use_count))) {
+ if (print_header) {
+ printf("USE ");
+ }
+ else {
+ printf("%3u ",bdbuf_info->use_count);
+ }
+ }
+
+ /*
+ * 4 chars: print info about state of this buffer
+ */
+ if ((rc == RTEMS_SUCCESSFUL) &&
+ ((selector->show_all) ||
+ (selector->show_state))) {
+ if (print_header) {
+ printf("STA ");
+ }
+ else {
+ printf("%c%c%c ",
+ (bdbuf_info->modified ? 'M' : '.'),
+ (bdbuf_info->in_progress ? 'P' : '.'),
+ (bdbuf_info->actual ? 'A' : '.'));
+ }
+ }
+
+ /*
+ * 42 chars: print info about error of this buffer
+ */
+ if ((rc == RTEMS_SUCCESSFUL) &&
+ ((selector->show_all) ||
+ (selector->show_error))) {
+ if (print_header) {
+ printf("%20s:%-10s ","RTEMS STATUS","ERRNO");
+ }
+ else {
+ printf("%20s:%-10s ",
+ ((bdbuf_info->status == RTEMS_SUCCESSFUL)
+ ? "SUCCESSFUL" : rtems_status_text(bdbuf_info->status)),
+ ((bdbuf_info->status == RTEMS_SUCCESSFUL)
+ ? "" : strerror(bdbuf_info->error)));
+ }
+ }
+ /*
+ * FIXME: add info about waiting chain
+ */
+ printf("\n");
+ return rc;
+}
+
+/*=========================================================================*\
+| Function: |
+\*-------------------------------------------------------------------------*/
+void rtems_bdbuf_show_fnc
+(
+/*-------------------------------------------------------------------------*\
+| Purpose: |
+| list all bdbufs with their content |
++---------------------------------------------------------------------------+
+| Input Parameters: |
+\*-------------------------------------------------------------------------*/
+ int argc,
+ char **argv,
+ rtems_monitor_command_arg_t* command_arg,
+ bool verbose
+)
+/*-------------------------------------------------------------------------*\
+| Return Value: |
+| rtems_status_code |
+\*=========================================================================*/
+{
+ rtems_status_code rc = RTEMS_SUCCESSFUL;
+ show_bdbuf_filter_t filter;
+ show_bdbuf_selector_t selector;
+ show_bdbuf_bdbuf_info_t bdbuf_info;
+
+ bdbuf_pool *curr_pool,*pool_base;
+ int pool_cnt,pool_idx;
+ int bdbuf_idx;
+ bool bdbuf_matches;
+ int matched_cnt,un_matched_cnt;
+
+ /*
+ * analyze command line options
+ */
+ if (rc == RTEMS_SUCCESSFUL) {
+ rc = rtems_bdbuf_show_getargs (argc,argv,
+ &filter,&selector);
+ }
+
+ /*
+ * get buffer pool information
+ */
+ if (rc == RTEMS_SUCCESSFUL) {
+ rc = rtems_bdbuf_show_get_bufpool(&pool_base,&pool_cnt);
+ if (rc != RTEMS_SUCCESSFUL) {
+ printf("%s: ERROR: no buffer pool found\n",argv[0]);
+ }
+ }
+ /*
+ * for all or selected buffer pool(s)
+ */
+ for (pool_idx = 0;
+ (rc == RTEMS_SUCCESSFUL) && (pool_idx < pool_cnt);
+ pool_idx++) {
+ if ((filter.pool_id < 0) ||
+ (filter.pool_id == pool_idx)) {
+ curr_pool = pool_base + pool_idx;
+ /*
+ * print pool header
+ */
+ if (rc == RTEMS_SUCCESSFUL) {
+ rc = rtems_bdbuf_show_pool_header(pool_idx,curr_pool);
+ }
+ if (rc == RTEMS_SUCCESSFUL) {
+ matched_cnt = 0;
+ un_matched_cnt = 0;
+ /*
+ * print header for bdbuf
+ */
+ if (rc == RTEMS_SUCCESSFUL) {
+ rc = rtems_show_bdbuf_print(NULL,&selector,
+ true);
+ }
+ /*
+ * for all bdbufs in this pool
+ */
+ for (bdbuf_idx = 0;
+ ((rc == RTEMS_SUCCESSFUL) &&
+ (bdbuf_idx < curr_pool->nblks));
+ bdbuf_idx++) {
+ /*
+ * get infos about bdbuf
+ */
+ if (rc == RTEMS_SUCCESSFUL) {
+ rc = rtems_show_bdbuf_get_bdbuf_info
+ (&(curr_pool->bdbufs[bdbuf_idx]),
+ bdbuf_idx,
+ curr_pool,
+ &bdbuf_info);
+ }
+ /*
+ * check, if bdbuf matches selection criteria
+ */
+ if (rc == RTEMS_SUCCESSFUL) {
+ rc = rtems_show_bdbuf_match_filter(&bdbuf_info,&filter,
+ &bdbuf_matches);
+ }
+ /*
+ * print info about bdbuf
+ */
+ if (rc == RTEMS_SUCCESSFUL) {
+ if (bdbuf_matches) {
+ rc = rtems_show_bdbuf_print(&bdbuf_info,&selector,
+ false);
+ if ((rc == RTEMS_SUCCESSFUL) &&
+ selector.show_sema) {
+ rc = rtems_show_bdbuf_print_wait_chain(&(curr_pool->bdbufs[bdbuf_idx]));
+ }
+ matched_cnt++;
+ }
+ else {
+ un_matched_cnt++;
+ }
+ }
+ }
+ /*
+ * print match statistics and footer
+ */
+ if (rc == RTEMS_SUCCESSFUL) {
+ printf("%d bdbufs printed, %d bdbufs suppressed\n",
+ matched_cnt,un_matched_cnt);
+ }
+ }
+ }
+ }
+}
+
+static rtems_monitor_command_entry_t rtems_show_bdbuf_cmds[] = {
+ {
+ "bdbuf_show",
+ "usage: bdbuf_show\n",
+ 0,
+ rtems_bdbuf_show_fnc,
+ { 0 },
+ 0
+ }
+};
+
+#ifndef ARRAY_CNT
+#define ARRAY_CNT(arr) (sizeof((arr))/sizeof((arr)[0]))
+#endif
+
+/*=========================================================================*\
+| Function: |
+\*-------------------------------------------------------------------------*/
+rtems_status_code rtems_bdbuf_show_init
+(
+/*-------------------------------------------------------------------------*\
+| Purpose: |
+| add command(s) to monitor |
++---------------------------------------------------------------------------+
+| Input Parameters: |
+\*-------------------------------------------------------------------------*/
+ void /* none up to now */
+)
+/*-------------------------------------------------------------------------*\
+| Return Value: |
+| rtems_status_code |
+\*=========================================================================*/
+{
+ rtems_status_code sc = RTEMS_SUCCESSFUL;
+ int item;
+
+ for (item = 0;
+ (sc == RTEMS_SUCCESSFUL) && (item < ARRAY_CNT(rtems_show_bdbuf_cmds));
+ item++) {
+ if (0 == rtems_monitor_insert_cmd (&rtems_show_bdbuf_cmds[item])) {
+ sc = RTEMS_INVALID_NAME;
+ }
+ }
+ return sc;
+}