summaryrefslogtreecommitdiffstats
path: root/c/src/exec/libblock
diff options
context:
space:
mode:
Diffstat (limited to 'c/src/exec/libblock')
-rw-r--r--c/src/exec/libblock/.cvsignore14
-rw-r--r--c/src/exec/libblock/ChangeLog25
-rw-r--r--c/src/exec/libblock/Makefile.am13
-rw-r--r--c/src/exec/libblock/README14
-rw-r--r--c/src/exec/libblock/configure.ac32
-rw-r--r--c/src/exec/libblock/include/.cvsignore2
-rw-r--r--c/src/exec/libblock/include/Makefile.am23
-rw-r--r--c/src/exec/libblock/include/rtems/.cvsignore2
-rw-r--r--c/src/exec/libblock/include/rtems/bdbuf.h282
-rw-r--r--c/src/exec/libblock/include/rtems/blkdev.h147
-rw-r--r--c/src/exec/libblock/include/rtems/diskdevs.h206
-rw-r--r--c/src/exec/libblock/include/rtems/ramdisk.h52
-rw-r--r--c/src/exec/libblock/src/.cvsignore2
-rw-r--r--c/src/exec/libblock/src/Makefile.am33
-rw-r--r--c/src/exec/libblock/src/bdbuf.c1876
-rw-r--r--c/src/exec/libblock/src/blkdev.c243
-rw-r--r--c/src/exec/libblock/src/diskdevs.c631
-rw-r--r--c/src/exec/libblock/src/ramdisk.c224
18 files changed, 3821 insertions, 0 deletions
diff --git a/c/src/exec/libblock/.cvsignore b/c/src/exec/libblock/.cvsignore
new file mode 100644
index 0000000000..d29e5050f5
--- /dev/null
+++ b/c/src/exec/libblock/.cvsignore
@@ -0,0 +1,14 @@
+Makefile
+Makefile.in
+aclocal.m4
+autom4te.cache
+config.cache
+config.guess
+config.log
+config.status
+config.sub
+configure
+depcomp
+install-sh
+missing
+mkinstalldirs
diff --git a/c/src/exec/libblock/ChangeLog b/c/src/exec/libblock/ChangeLog
new file mode 100644
index 0000000000..bc000d4a46
--- /dev/null
+++ b/c/src/exec/libblock/ChangeLog
@@ -0,0 +1,25 @@
+2002-02-28 Joel Sherrill <joel@OARcorp.com>
+
+ * Submitted by Victor V. Vengerov <vvv@oktet.ru> and merged
+ into the RTEMS source.
+ * ChangeLog, Makefile.am, README, configure.ac, include/Makefile.am,
+ include/rtems/bdbuf.h, include/rtems/blkdev.h, include/rtems/diskdevs.h,
+ include/rtems/ramdisk.h, include/rtems/.cvsignore, include/.cvsignore,
+ src/Makefile.am, src/bdbuf.c, src/blkdev.c, src/diskdevs.c,
+ src/ramdisk.c, src/.cvsignore, .cvsignore: New files.
+
+2001-11-29 Victor V. Vengerov <vvv@oktet.ru>
+ * AVL trees implementation added.
+
+2001-11-16 Victor V. Vengerov <vvv@oktet.ru>
+ * include/rtems/bdbuf.h, src/bdbuf.c(rtems_bdbuf_syncdev): New.
+
+2001-11-07 Victor V. Vengerov <vvv@oktet.ru>
+
+ * ChangeLog: New file.
+ * src/, include/, include/rtems/: New directories.
+ * README, configure.ac, Makefile.am, src/Makefile.am,
+ include/Makefile.am: New files.
+ * include/rtems/bdbuf.h include/rtems/blkdev.h
+ include/rtems/diskdevs.h include/rtems/ramdisk.h
+ src/bdbuf.c src/blkdev.c src/diskdevs.c src/ramdisk.c: New files.
diff --git a/c/src/exec/libblock/Makefile.am b/c/src/exec/libblock/Makefile.am
new file mode 100644
index 0000000000..970205bc1f
--- /dev/null
+++ b/c/src/exec/libblock/Makefile.am
@@ -0,0 +1,13 @@
+##
+## $Id$
+##
+
+AUTOMAKE_OPTIONS = foreign 1.4
+ACLOCAL_AMFLAGS = -I ../../../aclocal
+
+SUBDIRS = include src
+
+EXTRA_DIST = README
+
+include $(top_srcdir)/../../../automake/subdirs.am
+include $(top_srcdir)/../../../automake/local.am
diff --git a/c/src/exec/libblock/README b/c/src/exec/libblock/README
new file mode 100644
index 0000000000..671cc7f645
--- /dev/null
+++ b/c/src/exec/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/c/src/exec/libblock/configure.ac b/c/src/exec/libblock/configure.ac
new file mode 100644
index 0000000000..d0e5d61fb6
--- /dev/null
+++ b/c/src/exec/libblock/configure.ac
@@ -0,0 +1,32 @@
+## Process this file with autoconf to produce a configure script.
+##
+## $Id$
+
+AC_PREREQ(2.52)
+AC_INIT
+AC_CONFIG_SRCDIR([src/bdbuf.c])
+RTEMS_TOP(../../..)
+AC_CONFIG_AUX_DIR(../../..)
+
+RTEMS_CANONICAL_TARGET_CPU
+RTEMS_CANONICAL_HOST
+
+AM_INIT_AUTOMAKE(rtems-c-src-libblock,$RTEMS_VERSION,no)
+AM_MAINTAINER_MODE
+
+RTEMS_ENV_RTEMSBSP
+RTEMS_CHECK_CPU
+RTEMS_CHECK_CUSTOM_BSP(RTEMS_BSP)
+RTEMS_PROG_CC_FOR_TARGET
+RTEMS_ENABLE_BARE
+
+RTEMS_CANONICALIZE_TOOLS
+
+RTEMS_PROJECT_ROOT
+
+# Explicitly list all Makefiles here
+AC_CONFIG_FILES([Makefile
+include/Makefile
+src/Makefile
+])
+AC_OUTPUT
diff --git a/c/src/exec/libblock/include/.cvsignore b/c/src/exec/libblock/include/.cvsignore
new file mode 100644
index 0000000000..282522db03
--- /dev/null
+++ b/c/src/exec/libblock/include/.cvsignore
@@ -0,0 +1,2 @@
+Makefile
+Makefile.in
diff --git a/c/src/exec/libblock/include/Makefile.am b/c/src/exec/libblock/include/Makefile.am
new file mode 100644
index 0000000000..298ae2974d
--- /dev/null
+++ b/c/src/exec/libblock/include/Makefile.am
@@ -0,0 +1,23 @@
+##
+## $Id$
+##
+
+AUTOMAKE_OPTIONS = foreign 1.5
+
+include_rtemsdir = $(includedir)/rtems
+
+$(PROJECT_INCLUDE)/%.h: %.h
+ $(INSTALL_DATA) $< $@
+
+$(PROJECT_INCLUDE)/rtems:
+ @$(mkinstalldirs) $@
+
+include_rtems_HEADERS = \
+ rtems/bdbuf.h rtems/blkdev.h rtems/diskdevs.h rtems/ramdisk.h
+
+PREINSTALL_FILES = $(PROJECT_INCLUDE)/rtems \
+ $(include_rtems_HEADERS:%=$(PROJECT_INCLUDE)/%)
+
+all-local: $(PREINSTALL_FILES)
+
+include $(top_srcdir)/../../../automake/local.am
diff --git a/c/src/exec/libblock/include/rtems/.cvsignore b/c/src/exec/libblock/include/rtems/.cvsignore
new file mode 100644
index 0000000000..282522db03
--- /dev/null
+++ b/c/src/exec/libblock/include/rtems/.cvsignore
@@ -0,0 +1,2 @@
+Makefile
+Makefile.in
diff --git a/c/src/exec/libblock/include/rtems/bdbuf.h b/c/src/exec/libblock/include/rtems/bdbuf.h
new file mode 100644
index 0000000000..270b69598b
--- /dev/null
+++ b/c/src/exec/libblock/include/rtems/bdbuf.h
@@ -0,0 +1,282 @@
+/* bdbuf.h -- block device buffer management
+ *
+ * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia
+ * Author: Victor V. Vengerov <vvv@oktet.ru>
+ *
+ * @(#) $Id$
+ */
+
+#ifndef __RTEMS_LIBBLOCK_BDBUF_H__
+#define __RTEMS_LIBBLOCK_BDBUF_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <rtems.h>
+#include <rtems/libio.h>
+#include <chain.h>
+
+#include "rtems/blkdev.h"
+#include "rtems/diskdevs.h"
+
+
+/*
+ * To manage buffers we using Buffer Descriptors.
+ * To speed-up buffer lookup descriptors are organized in AVL-Tree.
+ * The fields 'dev' and 'block' are search key.
+ */
+
+/* Buffer descriptors
+ * Descriptors organized in AVL-tree to speedup buffer lookup.
+ * dev and block fields are search key in AVL-tree.
+ * Modified buffers, free buffers and used buffers linked in 'mod', 'free' and
+ * 'lru' chains appropriately.
+ */
+
+typedef struct bdbuf_buffer {
+ Chain_Node link; /* Link in the lru, mod or free chains */
+
+#ifdef BINARY_TREE
+ struct bdbuf_avl_node {
+ struct bdbuf_buffer *left; /* link to the left sub-tree */
+ struct bdbuf_buffer *right; /* link to the right sub-tree */
+
+ int bf; /* AVL tree node balance factor */
+ } avl; /* AVL-tree links */
+#else /* AVL TREE */
+ struct bdbuf_avl_node {
+ char cache; /* Cache */
+
+ struct bdbuf_buffer* link[2]; /* Left and Right Kids */
+
+ char bal; /* The balance of the sub-tree */
+ } avl;
+#endif
+ dev_t dev; /* device number */
+ blkdev_bnum block; /* block number on the device */
+
+ char *buffer; /* Pointer to the buffer memory area */
+ rtems_status_code status; /* Last I/O operation completion status */
+ int error; /* If status != RTEMS_SUCCESSFUL, this field contains
+ errno value which can be used by user later */
+ boolean modified:1; /* =1 if buffer was modified */
+ boolean in_progress:1; /* =1 if exchange with disk is in progress;
+ need to wait on semaphore */
+ boolean actual:1; /* Buffer contains actual data */
+ int use_count; /* Usage counter; incremented when somebody use
+ this buffer; decremented when buffer released
+ without modification or when buffer is flushed
+ by swapout task */
+
+ rtems_bdpool_id pool; /* Identifier of buffer pool to which this buffer
+ belongs */
+ CORE_mutex_Control transfer_sema;
+ /* Transfer operation semaphore */
+} bdbuf_buffer;
+
+
+
+/* bdbuf_config structure describes block configuration (size,
+ * amount, memory location) for buffering layer
+ */
+typedef struct rtems_bdbuf_config {
+ int size; /* Size of block */
+ int num; /* Number of blocks of appropriate size */
+ char *mem_area; /* Pointer to the blocks location or NULL, in this
+ case memory for blocks will be allocated by
+ Buffering Layer with the help of RTEMS partition
+ manager */
+} rtems_bdbuf_config;
+
+extern rtems_bdbuf_config rtems_bdbuf_configuration[];
+extern int rtems_bdbuf_configuration_size;
+
+/* rtems_bdbuf_init --
+ * Prepare buffering layer to work - initialize buffer descritors
+ * and (if it is neccessary) buffers. Buffers will be allocated accoriding
+ * to the configuration table, each entry describes kind of block and
+ * amount requested. After initialization all blocks is placed into
+ * free elements lists.
+ *
+ * PARAMETERS:
+ * conf_table - pointer to the buffers configuration table
+ * size - number of entries in configuration table
+ *
+ * RETURNS:
+ * RTEMS status code (RTEMS_SUCCESSFUL if operation completed successfully
+ * or error code if error is occured)
+ */
+rtems_status_code
+rtems_bdbuf_init(rtems_bdbuf_config *conf_table, int size);
+
+
+/* rtems_bdbuf_get --
+ * Obtain block buffer. If specified block already cached (i.e. there's
+ * block in the _modified_, or _recently_used_), return address
+ * of appropriate buffer descriptor and increment reference counter to 1.
+ * If block is not cached, allocate new buffer and return it. Data
+ * shouldn't be read to the buffer from media; buffer may contains
+ * arbitrary data. This primitive may be blocked if there are no free
+ * buffer descriptors available and there are no unused non-modified
+ * (or synchronized with media) buffers available.
+ *
+ * PARAMETERS:
+ * device - device number (constructed of major and minor device number)
+ * block - linear media block number
+ * bd - address of variable to store pointer to the buffer descriptor
+ *
+ * RETURNS:
+ * RTEMS status code (RTEMS_SUCCESSFUL if operation completed successfully
+ * or error code if error is occured)
+ *
+ * SIDE EFFECTS:
+ * bufget_sema semaphore obtained by this primitive.
+ */
+rtems_status_code
+rtems_bdbuf_get(dev_t device, blkdev_bnum block, bdbuf_buffer **bdb_ptr);
+
+/* rtems_bdbuf_read --
+ * (Similar to the rtems_bdbuf_get, except reading data from media)
+ * Obtain block buffer. If specified block already cached, return address
+ * of appropriate buffer and increment reference counter to 1. If block is
+ * not cached, allocate new buffer and read data to it from the media.
+ * This primitive may be blocked on waiting until data to be read from
+ * media, if there are no free buffer descriptors available and there are
+ * no unused non-modified (or synchronized with media) buffers available.
+ *
+ * PARAMETERS:
+ * device - device number (consists of major and minor device number)
+ * block - linear media block number
+ * bd - address of variable to store pointer to the buffer descriptor
+ *
+ * RETURNS:
+ * RTEMS status code (RTEMS_SUCCESSFUL if operation completed successfully
+ * or error code if error is occured)
+ *
+ * SIDE EFFECTS:
+ * bufget_sema and transfer_sema semaphores obtained by this primitive.
+ */
+rtems_status_code
+rtems_bdbuf_read(dev_t device, blkdev_bnum block, bdbuf_buffer **bdb_ptr);
+
+/* rtems_bdbuf_release --
+ * Release buffer allocated before. This primitive decrease the
+ * usage counter. If it is zero, further destiny of buffer depends on
+ * 'modified' status. If buffer was modified, it is placed to the end of
+ * mod list and flush task waken up. If buffer was not modified,
+ * it is placed to the end of lru list, and bufget_sema released, allowing
+ * to reuse this buffer.
+ *
+ * PARAMETERS:
+ * bd_buf - pointer to the bdbuf_buffer structure previously obtained using
+ * get/read primitive.
+ *
+ * RETURNS:
+ * RTEMS status code (RTEMS_SUCCESSFUL if operation completed successfully
+ * or error code if error is occured)
+ *
+ * SIDE EFFECTS:
+ * flush_sema and bufget_sema semaphores may be released by this primitive.
+ */
+rtems_status_code
+rtems_bdbuf_release(bdbuf_buffer *bd_buf);
+
+/* rtems_bdbuf_release_modified --
+ * Release buffer allocated before, assuming that it is _modified_ by
+ * it's owner. This primitive decrease usage counter for buffer, mark
+ * buffer descriptor as modified. If usage counter is 0, insert it at
+ * end of mod chain and release flush_sema semaphore to activate the
+ * flush task.
+ *
+ * PARAMETERS:
+ * bd_buf - pointer to the bdbuf_buffer structure previously obtained using
+ * get/read primitive.
+ *
+ * RETURNS:
+ * RTEMS status code (RTEMS_SUCCESSFUL if operation completed successfully
+ * or error code if error is occured)
+ *
+ * SIDE EFFECTS:
+ * flush_sema semaphore may be released by this primitive.
+ */
+rtems_status_code
+rtems_bdbuf_release_modified(bdbuf_buffer *bd_buf);
+
+/* rtems_bdbuf_sync --
+ * Wait until specified buffer synchronized with disk. Invoked on exchanges
+ * critical for data consistency on the media. This primitive mark owned
+ * block as modified, decrease usage counter. If usage counter is 0,
+ * block inserted to the mod chain and flush_sema semaphore released.
+ * Finally, primitives blocked on transfer_sema semaphore.
+ *
+ * PARAMETERS:
+ * bd_buf - pointer to the bdbuf_buffer structure previously obtained using
+ * get/read primitive.
+ *
+ * RETURNS:
+ * RTEMS status code (RTEMS_SUCCESSFUL if operation completed successfully
+ * or error code if error is occured)
+ *
+ * SIDE EFFECTS:
+ * Primitive may be blocked on transfer_sema semaphore.
+ */
+rtems_status_code
+rtems_bdbuf_sync(bdbuf_buffer *bd_buf);
+
+/* rtems_bdbuf_syncdev --
+ * Synchronize with disk all buffers containing the blocks belonging to
+ * specified device.
+ *
+ * PARAMETERS:
+ * dev - block device number
+ *
+ * RETURNS:
+ * 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);
+
+/* rtems_bdbuf_find_pool --
+ * Find first appropriate buffer pool. This primitive returns the index
+ * of first buffer pool which block size is greater than or equal to
+ * specified size.
+ *
+ * PARAMETERS:
+ * block_size - requested block size
+ * pool - placeholder for result
+ *
+ * RETURNS:
+ * RTEMS status code: RTEMS_SUCCESSFUL if operation completed successfully,
+ * RTEMS_INVALID_SIZE if specified block size is invalid (not a power
+ * of 2), RTEMS_NOT_DEFINED if buffer pool for this or greater block size
+ * is not configured.
+ */
+rtems_status_code
+rtems_bdbuf_find_pool(int block_size, rtems_bdpool_id *pool);
+
+/* rtems_bdbuf_get_pool_info --
+ * Obtain characteristics of buffer pool with specified number.
+ *
+ * PARAMETERS:
+ * pool - buffer pool number
+ * block_size - block size for which buffer pool is configured returned
+ * there
+ * blocks - number of buffers in buffer pool returned there
+ *
+ * RETURNS:
+ * RTEMS status code: RTEMS_SUCCESSFUL if operation completed successfully,
+ * RTEMS_INVALID_NUMBER if appropriate buffer pool is not configured.
+ *
+ * NOTE:
+ * Buffer pools enumerated contiguously starting from 0.
+ */
+rtems_status_code
+rtems_bdbuf_get_pool_info(rtems_bdpool_id pool, int *block_size, int *blocks);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/c/src/exec/libblock/include/rtems/blkdev.h b/c/src/exec/libblock/include/rtems/blkdev.h
new file mode 100644
index 0000000000..ca3b5ba5e2
--- /dev/null
+++ b/c/src/exec/libblock/include/rtems/blkdev.h
@@ -0,0 +1,147 @@
+/*
+ * blkdev.h - block device driver interface definitions
+ *
+ * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia
+ * Author: Victor V. Vengerov <vvv@oktet.ru>
+ *
+ * @(#) $Id$
+ */
+
+#ifndef __RTEMS_LIBBLOCK_BLKDEV_H__
+#define __RTEMS_LIBBLOCK_BLKDEV_H__
+
+#include <rtems.h>
+#include <sys/ioctl.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Interface with device drivers
+ * Block device looks, initialized and behaves like traditional RTEMS device
+ * driver. Heart of the block device driver is in BIOREQUEST ioctl. This call
+ * puts I/O request to the block device queue, in priority order, for
+ * asynchronous processing. When driver executes request, req_done
+ * function invoked, so callee knows about it. Look for details below.
+ */
+
+
+/* Block device block number datatype */
+typedef rtems_unsigned32 blkdev_bnum;
+
+/* Block device request type */
+typedef enum blkdev_request_op {
+ BLKDEV_REQ_READ, /* Read operation */
+ BLKDEV_REQ_WRITE /* Write operation */
+} blkdev_request_op;
+
+/* Type for block device request done callback function.
+ *
+ * PARAMETERS:
+ * arg - argument supplied in blkdev_request
+ * status - rtems status code for this operation
+ * errno - errno value to be passed to the user when
+ * status != RTEMS_SUCCESSFUL
+ */
+typedef void (* blkdev_request_cb)(void *arg,
+ rtems_status_code status,
+ int error);
+
+/* blkdev_sg_buffer
+ * Block device scatter/gather buffer structure
+ */
+typedef struct blkdev_sg_buffer {
+ rtems_unsigned32 length; /* Buffer length */
+ void *buffer; /* Buffer pointer */
+} blkdev_sg_buffer;
+
+/* blkdev_request (Block Device Request) structure is
+ * used to read/write a number of blocks from/to device.
+ */
+typedef struct blkdev_request {
+ blkdev_request_op req; /* Block device operation (read or write) */
+ blkdev_request_cb req_done; /* Callback function */
+ void *done_arg; /* Argument to be passed to callback function*/
+ blkdev_bnum start; /* Start block number */
+ rtems_unsigned32 count; /* Number of blocks to be exchanged */
+ rtems_unsigned32 bufnum; /* Number of buffers provided */
+
+ blkdev_sg_buffer bufs[0];/* List of scatter/gather buffers */
+} blkdev_request;
+
+/* Block device IOCTL request codes */
+#define BLKIO_REQUEST _IOWR('B', 1, blkdev_request)
+#define BLKIO_GETBLKSIZE _IO('B', 2)
+#define BLKIO_GETSIZE _IO('B', 3)
+#define BLKIO_SYNCDEV _IO('B', 4)
+
+/* Device driver interface conventions suppose that driver may
+ * contain initialize/open/close/read/write/ioctl entry points. These
+ * primitives (except initialize) can be implemented in generic fashion,
+ * based upon supplied block device driver ioctl handler. Every block
+ * device driver should provide initialize entry point, which is register
+ * all block devices and appropriate ioctl handlers.
+ */
+
+#define 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
+
+/* 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,
+ rtems_device_minor_number minor,
+ void * arg
+);
+
+/* blkdev_generic_write --
+ * Generic block device driver 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
+);
+
+/* 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
+);
+
+/* blkdev_generic_close --
+ * Generic block device close primitive.
+ */
+rtems_device_driver
+rtems_blkdev_generic_close(
+ rtems_device_major_number major,
+ rtems_device_minor_number minor,
+ void * arg
+);
+
+/* blkdev_generic_ioctl --
+ * Generic block device ioctl primitive.
+ */
+rtems_device_driver
+rtems_blkdev_generic_ioctl(
+ rtems_device_major_number major,
+ rtems_device_minor_number minor,
+ void * arg
+);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/c/src/exec/libblock/include/rtems/diskdevs.h b/c/src/exec/libblock/include/rtems/diskdevs.h
new file mode 100644
index 0000000000..fab3231e62
--- /dev/null
+++ b/c/src/exec/libblock/include/rtems/diskdevs.h
@@ -0,0 +1,206 @@
+/*
+ * logdisk.h - Physical and logical block devices (disks) support
+ *
+ * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia
+ * Author: Victor V. Vengerov <vvv@oktet.ru>
+ *
+ * @(#) $Id$
+ */
+
+#ifndef __RTEMS_LIBBLOCK_LOGDISK_H__
+#define __RTEMS_LIBBLOCK_LOGDISK_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <rtems.h>
+#include <rtems/libio.h>
+#include <stdlib.h>
+
+#include "rtems/blkdev.h"
+
+/* Buffer pool identifier */
+typedef int rtems_bdpool_id;
+
+/* Block device ioctl handler */
+typedef int (* block_device_ioctl) (dev_t dev, int req, void *argp);
+
+/* disk_device: Entry of this type created for every disk device (both for
+ * logical and physical disks).
+ * Array of arrays of pointers to disk_device structures maintained. First
+ * table indexed by major number and second table indexed by minor number.
+ * Such data organization allow quick lookup using data structure of
+ * moderated size.
+ */
+typedef struct disk_device {
+ dev_t dev; /* Device ID (major + minor) */
+ struct disk_device *phys_dev; /* Physical device ID (the same
+ as dev if this entry specifies
+ the physical device) */
+ char *name; /* Disk device name */
+ int uses; /* Use counter. Device couldn't be
+ removed if it is in use. */
+ int start; /* Starting block number (0 for
+ physical devices, block offset
+ on the related physical device
+ for logical device) */
+ int size; /* Size of physical or logical disk
+ in disk blocks */
+ int block_size; /* Size of device block (minimum
+ transfer unit) in bytes
+ (must be power of 2) */
+ int block_size_log2; /* log2 of block_size */
+ rtems_bdpool_id pool; /* Buffer pool assigned to this
+ device */
+ block_device_ioctl ioctl; /* ioctl handler for this block
+ device */
+} disk_device;
+
+/* rtems_disk_create_phys --
+ * Create physical disk entry. This function usually invoked from
+ * block device driver initialization code when physical device
+ * detected in the system. Device driver should provide ioctl handler
+ * to allow block device access operations. This primitive will register
+ * device in rtems (invoke rtems_io_register_name).
+ *
+ * PARAMETERS:
+ * dev - device identifier (major, minor numbers)
+ * block_size - size of disk block (minimum data transfer unit); must be
+ * power of 2
+ * disk_size - number of blocks on device
+ * handler - IOCTL handler (function providing basic block input/output
+ * request handling BIOREQUEST and other device management
+ * operations)
+ * name - character name of device (e.g. /dev/hda)
+ *
+ * RETURNS:
+ * RTEMS_SUCCESSFUL if information about new physical disk added, or
+ * error code if error occured (device already registered, wrong block
+ * size value, no memory available).
+ */
+rtems_status_code
+rtems_disk_create_phys(dev_t dev, int block_size, int disk_size,
+ block_device_ioctl handler,
+ char *name);
+
+/* rtems_disk_create_log --
+ * Create logical disk entry. Logical disk is contiguous area on physical
+ * disk. Disk may be splitted to several logical disks in several ways:
+ * manually or using information stored in blocks on physical disk
+ * (DOS-like partition table, BSD disk label, etc). This function usually
+ * invoked from application when application-specific splitting are in use,
+ * or from generic code which handle different logical disk organizations.
+ * This primitive will register device in rtems (invoke
+ * rtems_io_register_name).
+ *
+ * PARAMETERS:
+ * dev - logical device identifier (major, minor numbers)
+ * phys - physical device (block device which holds this logical disk)
+ * identifier
+ * start - starting block number on the physical device
+ * size - logical disk size in blocks
+ * name - logical disk name
+ *
+ * RETURNS:
+ * RTEMS_SUCCESSFUL if logical device successfully added, or error code
+ * if error occured (device already registered, no physical device
+ * exists, logical disk is out of physical disk boundaries, no memory
+ * available).
+ */
+rtems_status_code
+rtems_disk_create_log(dev_t dev, dev_t phys, int start, int size, char *name);
+
+/* rtems_disk_delete --
+ * Delete physical or logical disk device. Device may be deleted if its
+ * use counter (and use counters of all logical devices - if it is
+ * physical device) equal to 0. When physical device deleted,
+ * all logical devices deleted inherently. Appropriate devices removed
+ * from "/dev" filesystem.
+ *
+ * PARAMETERS:
+ * dev - device identifier (major, minor numbers)
+ *
+ * RETURNS:
+ * RTEMS_SUCCESSFUL if block device successfully deleted, or error code
+ * if error occured (device is not defined, device is in use).
+ */
+rtems_status_code
+rtems_disk_delete(dev_t dev);
+
+/* rtems_disk_lookup --
+ * Find block device descriptor by its device identifier. This function
+ * increment usage counter to 1. User should release disk_device structure
+ * by invoking rtems_disk_release primitive.
+ *
+ * PARAMETERS:
+ * dev - device identifier (major, minor numbers)
+ *
+ * RETURNS:
+ * pointer to the block device descriptor, or NULL if no such device
+ * exists.
+ */
+disk_device *
+rtems_disk_lookup(dev_t dev);
+
+/* rtems_disk_release --
+ * Release disk_device structure (decrement usage counter to 1).
+ *
+ * PARAMETERS:
+ * dd - pointer to disk device structure
+ *
+ * RETURNS:
+ * RTEMS_SUCCESSFUL
+ *
+ * NOTE:
+ * It should be implemented as inline function.
+ */
+rtems_status_code
+rtems_disk_release(disk_device *dd);
+
+/* rtems_disk_next --
+ * Disk device enumerator. Looking for device having device number larger
+ * than dev and return disk device descriptor for it. If there are no
+ * such device, NULL value returned.
+ *
+ * PARAMETERS:
+ * dev - device number (use -1 to start search)
+ *
+ * RETURNS:
+ * Pointer to the disk descriptor for next disk device, or NULL if all
+ * devices enumerated. */
+disk_device *
+rtems_disk_next(dev_t dev);
+
+/* rtems_diskio_initialize --
+ * Initialization of disk device library (initialize all data structures,
+ * etc.)
+ *
+ * PARAMETERS:
+ * none
+ *
+ * RETURNS:
+ * RTEMS_SUCCESSFUL if library initialized, or error code if error
+ * occured.
+ */
+rtems_status_code
+rtems_disk_io_initialize(void);
+
+/* rtems_diskio_done --
+ * Release all resources allocated for disk device interface.
+ *
+ * PARAMETERS:
+ * none
+ *
+ * RETURNS:
+ * RTEMS_SUCCESSFUL if all resources released, or error code if error
+ * occured.
+ */
+rtems_status_code
+rtems_disk_io_done(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/c/src/exec/libblock/include/rtems/ramdisk.h b/c/src/exec/libblock/include/rtems/ramdisk.h
new file mode 100644
index 0000000000..b9e8c238b8
--- /dev/null
+++ b/c/src/exec/libblock/include/rtems/ramdisk.h
@@ -0,0 +1,52 @@
+/* ramdisk.c -- RAM disk block device implementation
+ *
+ * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia
+ * Author: Victor V. Vengerov <vvv@oktet.ru>
+ *
+ * @(#) $Id$
+ */
+
+#ifndef __RTEMS_LIBBLOCK_RAMDISK_H__
+#define __RTEMS_LIBBLOCK_RAMDISK_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <rtems.h>
+
+#include "rtems/blkdev.h"
+
+/* RAM disk configuration table entry */
+typedef struct rtems_ramdisk_config {
+ int block_size; /* RAM disk block size */
+ int block_num; /* Number of blocks on this RAM disk */
+ void *location; /* RAM disk permanent location (out of RTEMS controlled
+ memory), or NULL if RAM disk memory should be
+ allocated dynamically */
+} rtems_ramdisk_config;
+
+/* If application want to use RAM disk, it should specify configuration of
+ * available RAM disks.
+ * The following is definitions for RAM disk configuration table
+ */
+extern rtems_ramdisk_config rtems_ramdisk_configuration[];
+extern int rtems_ramdisk_configuration_size;
+
+/* ramdisk_initialize --
+ * RAM disk driver initialization entry point.
+ */
+rtems_device_driver
+ramdisk_initialize(
+ rtems_device_major_number major,
+ rtems_device_minor_number minor,
+ void *arg);
+
+#define RAMDISK_DRIVER_TABLE_ENTRY \
+ { ramdisk_initialize, GENERIC_BLOCK_DEVICE_DRIVER_ENTRIES }
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/c/src/exec/libblock/src/.cvsignore b/c/src/exec/libblock/src/.cvsignore
new file mode 100644
index 0000000000..282522db03
--- /dev/null
+++ b/c/src/exec/libblock/src/.cvsignore
@@ -0,0 +1,2 @@
+Makefile
+Makefile.in
diff --git a/c/src/exec/libblock/src/Makefile.am b/c/src/exec/libblock/src/Makefile.am
new file mode 100644
index 0000000000..4b563282df
--- /dev/null
+++ b/c/src/exec/libblock/src/Makefile.am
@@ -0,0 +1,33 @@
+##
+## $Id$
+##
+
+AUTOMAKE_OPTIONS = foreign 1.5
+
+LIBNAME = libblock
+LIB = ${ARCH}/${LIBNAME}.a
+
+C_FILES = bdbuf.c blkdev.c diskdevs.c ramdisk.c
+
+C_O_FILES = $(C_FILES:%.c=${ARCH}/%.o)
+
+SRCS = $(C_FILES)
+OBJS = $(C_O_FILES)
+
+include $(RTEMS_ROOT)/make/custom/@RTEMS_BSP@.cfg
+include $(top_srcdir)/../../../automake/compile.am
+include $(top_srcdir)/../../../automake/lib.am
+
+AM_CFLAGS += $(LIBC_DEFINES)
+
+TMPINSTALL_FILES += $(PROJECT_RELEASE)/lib/$(LIBNAME)$(LIB_VARIANT).a
+
+$(LIB): ${OBJS}
+ $(make-library)
+
+$(PROJECT_RELEASE)/lib/$(LIBNAME)$(LIB_VARIANT).a: $(LIB)
+ $(INSTALL_DATA) $< $@
+
+all-local: ${ARCH} $(PREINSTALL_FILES) $(TMPINSTALL_FILES)
+
+include $(top_srcdir)/../../../automake/local.am
diff --git a/c/src/exec/libblock/src/bdbuf.c b/c/src/exec/libblock/src/bdbuf.c
new file mode 100644
index 0000000000..806ad35243
--- /dev/null
+++ b/c/src/exec/libblock/src/bdbuf.c
@@ -0,0 +1,1876 @@
+/*
+ * 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>
+ *
+ * @(#) $Id$
+ */
+
+#define __RTEMS_VIOLATE_KERNEL_VISIBILITY__
+#include <rtems.h>
+#include <limits.h>
+#include <errno.h>
+#include <assert.h>
+
+#include "rtems/bdbuf.h"
+
+/* Fatal errors: */
+#define BLKDEV_FATAL_ERROR(n) (('B' << 24) | ((n) & 0x00FFFFFF))
+#define BLKDEV_FATAL_BDBUF_CONSISTENCY BLKDEV_FATAL_ERROR(1)
+#define BLKDEV_FATAL_BDBUF_SWAPOUT BLKDEV_FATAL_ERROR(2)
+
+enum balance_factor {BF_LEFT = -1,
+ BF_NONE = 0,
+ BF_RIGHT = 1};
+
+
+#define SWAPOUT_PRIORITY 15
+#define SWAPOUT_STACK_SIZE (RTEMS_MINIMUM_STACK_SIZE * 2)
+
+static rtems_task bdbuf_swapout_task(rtems_task_argument unused);
+
+/*
+ * The groups of the blocks with the same size are collected in the
+ * bd_pool. Note that a several of the buffer's groups with the
+ * same size can exists.
+ */
+typedef struct bdbuf_pool
+{
+ bdbuf_buffer *tree; /* Buffer descriptor lookup AVL tree root */
+
+ Chain_Control free; /* Free buffers list */
+ Chain_Control lru; /* Last recently used list */
+
+ int blksize; /* The size of the blocks (in bytes) */
+ int nblks; /* Number of blocks in this pool */
+ rtems_id bufget_sema; /* Buffer obtain counting semaphore */
+ void *mallocd_bufs; /* Pointer to the malloc'd buffer memory,
+ or NULL, if buffer memory provided in
+ buffer configuration */
+ bdbuf_buffer *bdbufs; /* Pointer to table of buffer descriptors
+ allocated for this buffer pool. */
+} bdbuf_pool;
+
+/* Buffering layer context definition */
+struct bdbuf_context {
+ bdbuf_pool *pool; /* Table of buffer pools */
+ int npools; /* Number of entries in pool table */
+
+ Chain_Control mod; /* Modified buffers list */
+ rtems_id flush_sema; /* Buffer flush semaphore; counting
+ semaphore; incremented when buffer
+ flushed to the disk; decremented when
+ buffer modified */
+ rtems_id swapout_task; /* Swapout task ID */
+};
+
+/* Block device request with a single buffer provided */
+typedef struct blkdev_request1 {
+ blkdev_request req;
+ blkdev_sg_buffer sg[1];
+} blkdev_request1;
+
+/* The static context of buffering layer */
+static struct bdbuf_context bd_ctx;
+
+#define SAFE
+#ifdef SAFE
+typedef rtems_mode preemption_key;
+
+#define DISABLE_PREEMPTION(key) \
+ do { \
+ rtems_task_mode(RTEMS_PREEMPT_MASK, RTEMS_NO_PREEMPT, &(key)); \
+ } while (0)
+
+#define ENABLE_PREEMPTION(key) \
+ do { \
+ rtems_mode temp; \
+ rtems_task_mode(RTEMS_PREEMPT_MASK, (key), &temp); \
+ } while (0)
+
+#else
+
+typedef boolean preemption_key;
+
+#define DISABLE_PREEMPTION(key) \
+ do { \
+ (key) = _Thread_Executing->is_preemptible; \
+ _Thread_Executing->is_preemptible = 0; \
+ } while (0)
+
+#define ENABLE_PREEMPTION(key) \
+ do { \
+ _Thread_Executing->is_preemptible = (key); \
+ if (_Thread_Evaluate_mode()) \
+ _Thread_Dispatch(); \
+ } while (0)
+
+#endif
+
+
+#ifdef BINARY_TREE
+static bdbuf_buffer *
+avl_search(bdbuf_buffer **root, dev_t dev, blkdev_bnum block)
+{
+ 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;
+}
+
+static bdbuf_buffer *
+avl_search_for_sync(bdbuf_buffer **root, disk_device *dd)
+{
+ bdbuf_buffer *p = *root;
+ bdbuf_buffer *s[32];
+ bdbuf_buffer **sp = s;
+ dev_t dev = dd->phys_dev->dev;
+ blkdev_bnum b = dd->start;
+ blkdev_bnum e = b + dd->size - 1;
+
+ while (p != NULL)
+ {
+ if ((p->dev < dev) || ((p->dev == dev) && (p->block < b)))
+ p = p->avl.right;
+ else if ((p->dev > dev) || ((p->dev == dev) && (p->block > e)))
+ p = p->avl.left;
+ else if (p->modified)
+ return p;
+ else
+ {
+ if (p->avl.right != NULL)
+ *sp++ = p->avl.right;
+ p = p->avl.left;
+ }
+ if ((p == NULL) && (sp > s))
+ p = *--sp;
+ }
+ return p;
+}
+
+static int
+avl_insert(bdbuf_buffer **root, bdbuf_buffer *node)
+{
+ bdbuf_buffer **r = root;
+ node->avl.left = node->avl.right = NULL;
+ while (TRUE)
+ {
+ bdbuf_buffer *rr = *r;
+ if (rr == NULL)
+ {
+ *r = node;
+ return 0;
+ }
+ else if ((rr->dev < node->dev) || ((rr->dev == node->dev) &&
+ (rr->block < node->block)))
+ {
+ r = &rr->avl.right;
+ }
+ else if ((rr->dev == node->dev) && (rr->block == node->block))
+ {
+ return -1;
+ }
+ else
+ {
+ r = &rr->avl.left;
+ }
+ }
+}
+
+static int
+avl_remove(bdbuf_buffer **root, bdbuf_buffer *node)
+{
+ bdbuf_buffer **p = root;
+ dev_t dev = node->dev;
+ blkdev_bnum block = node->block;
+
+ while ((*p != NULL) && (*p != node))
+ {
+ if (((*p)->dev < dev) || (((*p)->dev == dev) && ((*p)->block < block)))
+ p = &(*p)->avl.right;
+ else
+ p = &(*p)->avl.left;
+ }
+ if (*p == NULL)
+ return -1;
+
+ *p = node->avl.left;
+ while (*p != NULL)
+ p = &(*p)->avl.right;
+ *p = node->avl.right;
+ node->avl.left = node->avl.right = NULL;
+ return 0;
+}
+
+#else
+
+/* 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 AVL_MAX_HEIGHT
+#define AVL_MAX_HEIGHT 32
+#endif
+
+
+
+/*
+ * avl_cmp_node_node --
+ * Compares two avl nodes. Function compares dev/block pairs of
+ * the node1 and node2.
+ *
+ * PARAMETERS:
+ * node1 - Pointer to the first node to compare
+ * node2 - Pointer to the second node to compare
+ *
+ * RETURNS:
+ * 0 - dev/block of the nodes are equal
+ * 1 - dev/block of the second node are less
+ * -1 - dev/block of the first node are less
+ */
+static inline int
+avl_cmp_node_node(const bdbuf_buffer *const node1,
+ const bdbuf_buffer *const node2)
+{
+ if (node1->dev < node2->dev)
+ return -1;
+ else if (node1->dev > node2->dev)
+ return +1;
+ else if (node1->block < node2->block)
+ return -1;
+ else if (node1->block > node2->block)
+ return +1;
+ else
+ return 0;
+}
+
+/*
+ * avl_cmp_node_pattern -
+ * compares the dev/block of the node with specified two.
+ *
+ * PARAMETERS:
+ * node1 - Pointer to the node to compare
+ * dev/block - The pattern to compare with
+ *
+ * RETURNS:
+ * 0 - dev/block of the node and specified are equal
+ * 1 - dev/block specified are less
+ * -1 - dev/block of the first node are less
+ *
+ */
+static inline int
+avl_cmp_node_pattern(const bdbuf_buffer *const node1,
+ dev_t dev,
+ blkdev_bnum block)
+{
+ if (node1->dev < dev)
+ return -1;
+ else if (node1->dev > dev)
+ return +1;
+ else if (node1->block < block)
+ return -1;
+ else if (node1->block > block)
+ return +1;
+ else
+ return 0;
+}
+
+
+/*
+ * avl_search --
+ * Searches for the node with specified dev/block.
+ *
+ * PARAMETERS:
+ * root - pointer to the root node of the AVL-Tree.
+ * dev, block - search key
+ *
+ * RETURNS:
+ * NULL if node with specified dev/block not found
+ * non-NULL - pointer to the node with specified dev/block
+ */
+static bdbuf_buffer *
+avl_search(bdbuf_buffer **root, dev_t dev, blkdev_bnum block)
+{
+
+ 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.link[1];
+ else
+ p = p->avl.link[0];
+ }
+ return p;
+}
+
+/* avl_search_for_sync --
+ * Search in AVL tree for first modified buffer belongs to specified
+ * disk device.
+ *
+ * PARAMETERS:
+ * root - pointer to tree root
+ * dd - disk device descriptor
+ *
+ * RETURNS:
+ * Block buffer, or NULL if no modified blocks on specified device
+ * exists.
+ */
+static bdbuf_buffer *
+avl_search_for_sync(bdbuf_buffer **root, disk_device *dd)
+{
+ bdbuf_buffer *p = *root;
+ bdbuf_buffer *s[AVL_MAX_HEIGHT];
+ bdbuf_buffer **sp = s;
+ dev_t dev = dd->phys_dev->dev;
+ blkdev_bnum b = dd->start;
+ blkdev_bnum e = b + dd->size - 1;
+
+ while (p != NULL)
+ {
+ if ((p->dev < dev) || ((p->dev == dev) && (p->block < b)))
+ p = p->avl.link[1];
+ else if ((p->dev > dev) || ((p->dev == dev) && (p->block > e)))
+ p = p->avl.link[0];
+ else if (p->modified)
+ return p;
+ else
+ {
+ if (p->avl.link[1] != NULL)
+ *sp++ = p->avl.link[1];
+ p = p->avl.link[0];
+ }
+ if ((p == NULL) && (sp > s))
+ p = *--sp;
+ }
+ return p;
+}
+
+
+/*
+ * avl_insert --
+ * Inserts the specified node to the AVl-Tree.
+ *
+ * PARAMETERS:
+ * root_addr - Pointer to pointer to the root node
+ * node - Pointer to the node to add.
+ *
+ * RETURNS:
+ * 0 - The node added successfully
+ * -1 - An error occured
+ */
+static int
+avl_insert (bdbuf_buffer **root_addr, bdbuf_buffer *node)
+{
+ /* Uses Knuth's Algorithm 6.2.3A (balanced tree search and
+ insertion), but caches results of comparisons. In empirical
+ tests this eliminates about 25% of the comparisons seen under
+ random insertions. */
+
+ /* A1. */
+ int t_modified = 0;
+ bdbuf_buffer *t;
+ bdbuf_buffer *s, *p, *q, *r;
+
+ bdbuf_buffer *root_link = *root_addr;;
+
+ t = root_link;
+ s = p = t;
+
+ if (s == NULL)
+ {
+ q = t = node;
+ q->avl.link[0] = q->avl.link[1] = NULL;
+ q->avl.bal = 0;
+ *root_addr = t;
+ return 0;
+ }
+
+ for (;;)
+ {
+ /* A2. */
+ int diff = avl_cmp_node_node(node, p);
+
+ /* A3. */
+ if (diff < 0)
+ {
+ p->avl.cache = 0;
+ q = p->avl.link[0];
+ if (q == NULL)
+ {
+ p->avl.link[0] = q = node;
+ break;
+ }
+ }
+ /* A4. */
+ else
+ if (diff > 0)
+ {
+ p->avl.cache = 1;
+ q = p->avl.link[1];
+ if (q == NULL)
+ {
+ p->avl.link[1] = q = node;
+ break;
+ }
+ }
+ else
+ /* A2. */
+ {
+ /*
+ * The item found. Nothing changed. Have not to update
+ * root_adr*/
+
+ return -1;
+ }
+
+ /* A3, A4. */
+ if (q->avl.bal != 0)
+ {
+ t = p, s = q;
+ t_modified = 1;
+ }
+ p = q;
+ }
+
+ /* A5. */
+ q->avl.link[0] = q->avl.link[1] = NULL;
+ q->avl.bal = 0;
+
+ /* A6. */
+ r = p = s->avl.link[(int) s->avl.cache];
+ while (p != q)
+ {
+ p->avl.bal = p->avl.cache * 2 - 1;
+ p = p->avl.link[(int) p->avl.cache];
+ }
+
+ /* A7. */
+ if (s->avl.cache == 0)
+ {
+ /* a = -1. */
+ if (s->avl.bal == 0)
+ {
+ s->avl.bal = -1;
+ *root_addr = root_link;
+ return 0;
+ }
+ else if (s->avl.bal == +1)
+ {
+ s->avl.bal = 0;
+ *root_addr = root_link;
+ return 0;
+ }
+
+ assert (s->avl.bal == -1);
+ if (r->avl.bal == -1)
+ {
+ /* A8. */
+ p = r;
+ s->avl.link[0] = r->avl.link[1];
+ r->avl.link[1] = s;
+ s->avl.bal = r->avl.bal = 0;
+ }
+ else
+ {
+ /* A9. */
+ assert(r->avl.bal == +1);
+ p = r->avl.link[1];
+ r->avl.link[1] = p->avl.link[0];
+ p->avl.link[0] = r;
+ s->avl.link[0] = p->avl.link[1];
+ p->avl.link[1] = s;
+ if (p->avl.bal == -1)
+ s->avl.bal = 1, r->avl.bal = 0;
+ else
+ {
+ if (p->avl.bal == 0)
+ {
+ s->avl.bal = r->avl.bal = 0;
+ }
+ else
+ {
+ assert (p->avl.bal == +1);
+ s->avl.bal = 0, r->avl.bal = -1;
+ }
+ }
+ p->avl.bal = 0;
+ }
+ }
+ else
+ {
+ /* a == +1. */
+ if (s->avl.bal == 0)
+ {
+ s->avl.bal = 1;
+ *root_addr = root_link;
+ return 0;
+ }
+ else if (s->avl.bal == -1)
+ {
+ s->avl.bal = 0;
+ *root_addr = root_link;
+ return 0;
+ }
+
+ assert(s->avl.bal == +1);
+ if (r->avl.bal == +1)
+ {
+ /* A8. */
+ p = r;
+ s->avl.link[1] = r->avl.link[0];
+ r->avl.link[0] = s;
+ s->avl.bal = r->avl.bal = 0;
+ }
+ else
+ {
+ /* A9. */
+ assert(r->avl.bal == -1);
+ p = r->avl.link[0];
+ r->avl.link[0] = p->avl.link[1];
+ p->avl.link[1] = r;
+ s->avl.link[1] = p->avl.link[0];
+ p->avl.link[0] = s;
+ if (p->avl.bal == +1)
+ {
+ s->avl.bal = -1, r->avl.bal = 0;
+ }
+ else
+ {
+ if (p->avl.bal == 0)
+ {
+ s->avl.bal = r->avl.bal = 0;
+ }
+ else
+ {
+ assert(p->avl.bal == -1);
+ s->avl.bal = 0, r->avl.bal = 1;
+ }
+ }
+ p->avl.bal = 0;
+ }
+ }
+
+ /* A10. */
+ if (t_modified)
+ {
+ if (s == t->avl.link[1])
+ t->avl.link[1] = p;
+ else
+ t->avl.link[0] = p;
+ }
+ else
+ {
+ root_link = p;
+ }
+
+ *root_addr = root_link;
+ return 0;
+}
+
+
+/* avl_remove --
+ * removes the node from the tree.
+ *
+ * PARAMETERS:
+ * root_addr - Pointer to pointer to the root node
+ * node - Pointer to the node to remove
+ *
+ * RETURNS:
+ * 0 - Item removed
+ * -1 - No such item found
+ */
+static int
+avl_remove(bdbuf_buffer **root_addr, const bdbuf_buffer *node)
+{
+ /* Uses my Algorithm D, which can be found at
+ http://www.msu.edu/user/pfaffben/avl. Algorithm D is based on
+ Knuth's Algorithm 6.2.2D (Tree deletion) and 6.2.3A (Balanced
+ tree search and insertion), as well as the notes on pages 465-466
+ of Vol. 3. */
+
+ /* D1. */
+ bdbuf_buffer *pa[AVL_MAX_HEIGHT]; /* Stack P: Nodes. */
+ char a[AVL_MAX_HEIGHT]; /* Stack P: Bits. */
+ int k = 1; /* Stack P: Pointer. */
+
+ bdbuf_buffer **q;
+ bdbuf_buffer *p;
+
+
+ /*
+ * To avoid using unnessary instance of the 'bdbuf_buffer' (as pa[0])
+ * we will catch all access to pa[0] and use &root_avl instead
+ */
+ struct bdbuf_avl_node root_avl;
+
+ root_avl.link[0] = *root_addr;
+ root_avl.link[1] = NULL;
+ root_avl.bal = 0;
+ root_avl.cache = 0;
+
+ a[0] = 0;
+
+ p = root_avl.link[0];
+
+
+ k = 1;
+
+ for (;;)
+ {
+ /* D2. */
+ int diff;
+
+ if (p == NULL)
+ return -1;
+
+ diff = avl_cmp_node_node(node, p);
+
+ if (diff == 0)
+ break;
+
+ /* D3, D4. */
+ pa[k] = p;
+ if (diff < 0)
+ {
+ p = p->avl.link[0];
+ a[k] = 0;
+ }
+ else if (diff > 0)
+ {
+ p = p->avl.link[1];
+ a[k] = 1;
+ }
+ k++;
+ }
+
+ /* D5. */
+ if (k == 1)
+ {
+ /* Andron */
+ q = &root_avl.link[(int) a[k - 1]];
+ }
+ else
+ {
+ q = &pa[k - 1]->avl.link[(int) a[k - 1]];
+ }
+ if (p->avl.link[1] == NULL)
+ {
+ *q = p->avl.link[0];
+ if (*q)
+ (*q)->avl.bal = 0;
+ }
+ else
+ {
+ /* D6. */
+ bdbuf_buffer *r = p->avl.link[1];
+ if (r->avl.link[0] == NULL)
+ {
+ r->avl.link[0] = p->avl.link[0];
+ *q = r;
+ r->avl.bal = p->avl.bal;
+ a[k] = 1;
+ pa[k++] = r;
+ }
+ else
+ {
+ /* D7. */
+ bdbuf_buffer *s = r->avl.link[0];
+ int l = k++;
+
+ a[k] = 0;
+ pa[k++] = r;
+
+ /* D8. */
+ while (s->avl.link[0] != NULL)
+ {
+ r = s;
+ s = r->avl.link[0];
+ a[k] = 0;
+ pa[k++] = r;
+ }
+
+ /* D9. */
+ a[l] = 1;
+ pa[l] = s;
+ s->avl.link[0] = p->avl.link[0];
+ r->avl.link[0] = s->avl.link[1];
+ s->avl.link[1] = p->avl.link[1];
+ s->avl.bal = p->avl.bal;
+ *q = s;
+ }
+ }
+
+ assert(k > 0);
+ /* D10. */
+ while (--k)
+ {
+ bdbuf_buffer *s = pa[k], *r;
+
+ if (a[k] == 0)
+ {
+ /* D10. */
+ if (s->avl.bal == -1)
+ {
+ s->avl.bal = 0;
+ continue;
+ }
+ else if (s->avl.bal == 0)
+ {
+ s->avl.bal = 1;
+ break;
+ }
+
+ assert(s->avl.bal == +1);
+ r = s->avl.link[1];
+
+ assert(r != NULL);
+ if (r->avl.bal == 0)
+ {
+ /* D11. */
+ s->avl.link[1] = r->avl.link[0];
+ r->avl.link[0] = s;
+ r->avl.bal = -1;
+ if (k == 1)
+ {
+ /* Andron */
+ root_avl.link[(int) a[k - 1]] = r;
+ }
+ else
+ {
+ pa[k - 1]->avl.link[(int) a[k - 1]] = r;
+ }
+ break;
+ }
+ else
+ if (r->avl.bal == +1)
+ {
+ /* D12. */
+ s->avl.link[1] = r->avl.link[0];
+ r->avl.link[0] = s;
+ s->avl.bal = r->avl.bal = 0;
+ if (k == 1)
+ {
+ /* Andron */
+ root_avl.link[(int) a[k - 1]] = r;
+ }
+ else
+ {
+ pa[k - 1]->avl.link[(int) a[k - 1]] = r;
+ }
+ }
+ else
+ {
+ /* D13. */
+ assert(r->avl.bal == -1);
+ p = r->avl.link[0];
+ r->avl.link[0] = p->avl.link[1];
+ p->avl.link[1] = r;
+ s->avl.link[1] = p->avl.link[0];
+ p->avl.link[0] = s;
+ if (p->avl.bal == +1)
+ s->avl.bal = -1, r->avl.bal = 0;
+ else if (p->avl.bal == 0)
+ {
+ s->avl.bal = r->avl.bal = 0;
+ }
+ else
+ {
+ assert(p->avl.bal == -1);
+ s->avl.bal = 0, r->avl.bal = +1;
+ }
+ p->avl.bal = 0;
+ if (k == 1)
+ {
+ /* Andron */
+ root_avl.link[(int) a[k - 1]] = p;
+ }
+ else
+ {
+ pa[k - 1]->avl.link[(int) a[k - 1]] = p;
+ }
+ }
+ }
+ else
+ {
+ assert(a[k] == 1);
+
+ /* D10. */
+ if (s->avl.bal == +1)
+ {
+ s->avl.bal = 0;
+ continue;
+ }
+ else
+ if (s->avl.bal == 0)
+ {
+ s->avl.bal = -1;
+ break;
+ }
+
+ assert(s->avl.bal == -1);
+ r = s->avl.link[0];
+
+ if (r == NULL || r->avl.bal == 0)
+ {
+ /* D11. */
+ s->avl.link[0] = r->avl.link[1];
+ r->avl.link[1] = s;
+ r->avl.bal = 1;
+ if (k == 1)
+ {
+ /* Andron */
+ root_avl.link[(int) a[k - 1]] = r;
+ }
+ else
+ {
+ pa[k - 1]->avl.link[(int) a[k - 1]] = r;
+ }
+
+ break;
+ }
+ else
+ if (r->avl.bal == -1)
+ {
+ /* D12. */
+ s->avl.link[0] = r->avl.link[1];
+ r->avl.link[1] = s;
+ s->avl.bal = r->avl.bal = 0;
+ if (k == 1)
+ {
+ root_avl.link[(int) a[k - 1]] = r;
+ }
+ else
+ {
+ pa[k - 1]->avl.link[(int) a[k - 1]] = r;
+ }
+
+ }
+ else
+ if (r->avl.bal == +1)
+ {
+ /* D13. */
+ p = r->avl.link[1];
+ r->avl.link[1] = p->avl.link[0];
+ p->avl.link[0] = r;
+ s->avl.link[0] = p->avl.link[1];
+ p->avl.link[1] = s;
+ if (p->avl.bal == -1)
+ s->avl.bal = 1, r->avl.bal = 0;
+ else
+ if (p->avl.bal == 0)
+ s->avl.bal = r->avl.bal = 0;
+ else
+ {
+ assert(p->avl.bal == 1);
+ s->avl.bal = 0, r->avl.bal = -1;
+ }
+ p->avl.bal = 0;
+ if (k == 1)
+ {
+ /* Andron */
+ root_avl.link[(int) a[k - 1]] = p;
+ }
+ else
+ {
+ pa[k - 1]->avl.link[(int) a[k - 1]] = p;
+ }
+ }
+ }
+ }
+
+ *root_addr = root_avl.link[0];
+ return 0;
+}
+
+#endif
+
+/* bdbuf_initialize_pool --
+ * Initialize single buffer pool.
+ *
+ * PARAMETERS:
+ * config - buffer pool configuration
+ * pool - pool number
+ *
+ * RETURNS:
+ * RTEMS_SUCCESSFUL, if buffer pool initialized successfully, or error
+ * code if error occured.
+ */
+static rtems_status_code
+bdbuf_initialize_pool(rtems_bdbuf_config *config, int pool)
+{
+ bdbuf_pool *p = bd_ctx.pool + pool;
+ unsigned char *bufs;
+ bdbuf_buffer *b;
+ rtems_status_code rc;
+ int i;
+
+ p->blksize = config->size;
+ p->nblks = config->num;
+ p->tree = NULL;
+
+ Chain_Initialize_empty(&p->free);
+ Chain_Initialize_empty(&p->lru);
+
+ /* Allocate memory for buffer descriptors */
+ p->bdbufs = calloc(config->num, sizeof(bdbuf_buffer));
+ if (p->bdbufs == NULL)
+ {
+ return RTEMS_NO_MEMORY;
+ }
+
+ /* Allocate memory for buffers if required */
+ if (config->mem_area == NULL)
+ {
+ bufs = p->mallocd_bufs = malloc(config->num * config->size);
+ if (bufs == NULL)
+ {
+ free(p->bdbufs);
+ return RTEMS_NO_MEMORY;
+ }
+ }
+ else
+ {
+ bufs = config->mem_area;
+ p->mallocd_bufs = NULL;
+ }
+
+ for (i = 0, b = p->bdbufs; i < p->nblks; i++, b++, bufs += p->blksize)
+ {
+ b->dev = -1; b->block = 0;
+ b->buffer = bufs;
+ b->actual = b->modified = b->in_progress = FALSE;
+ b->use_count = 0;
+ b->pool = pool;
+ _Chain_Append(&p->free, &b->link);
+ }
+
+ rc = rtems_semaphore_create(
+ rtems_build_name('B', 'U', 'F', 'G'),
+ p->nblks,
+ RTEMS_FIFO | RTEMS_COUNTING_SEMAPHORE | RTEMS_NO_INHERIT_PRIORITY |
+ RTEMS_NO_PRIORITY_CEILING | RTEMS_LOCAL,
+ 0,
+ &p->bufget_sema);
+
+ if (rc != RTEMS_SUCCESSFUL)
+ {
+ free(p->bdbufs);
+ free(p->mallocd_bufs);
+ return rc;
+ }
+
+ return RTEMS_SUCCESSFUL;
+}
+
+/* bdbuf_release_pool --
+ * Free resources allocated for buffer pool with specified number.
+ *
+ * PARAMETERS:
+ * pool - buffer pool number
+ *
+ * RETURNS:
+ * RTEMS_SUCCESSFUL
+ */
+static rtems_status_code
+bdbuf_release_pool(rtems_bdpool_id pool)
+{
+ bdbuf_pool *p = bd_ctx.pool + pool;
+ rtems_semaphore_delete(p->bufget_sema);
+ free(p->bdbufs);
+ free(p->mallocd_bufs);
+ return RTEMS_SUCCESSFUL;
+}
+
+/* rtems_bdbuf_init --
+ * Prepare buffering layer to work - initialize buffer descritors
+ * and (if it is neccessary)buffers. Buffers will be allocated accoriding
+ * to the configuration table, each entry describes kind of block and
+ * amount requested. After initialization all blocks is placed into
+ * free elements lists.
+ *
+ * PARAMETERS:
+ * conf_table - pointer to the buffers configuration table
+ * size - number of entries in configuration table
+ *
+ * RETURNS:
+ * RTEMS status code (RTEMS_SUCCESSFUL if operation completed successfully
+ * or error code if error is occured)
+ */
+rtems_status_code
+rtems_bdbuf_init(rtems_bdbuf_config *conf_table, int size)
+{
+ rtems_bdpool_id i;
+ rtems_status_code rc;
+
+ if (size <= 0)
+ return RTEMS_INVALID_SIZE;
+
+ bd_ctx.npools = size;
+
+ /*
+ * Allocate memory for buffer pool descriptors
+ */
+ bd_ctx.pool = calloc(size, sizeof(bdbuf_pool));
+ if (bd_ctx.pool == NULL)
+ {
+ return RTEMS_NO_MEMORY;
+ }
+
+ Chain_Initialize_empty(&bd_ctx.mod);
+
+ /* Initialize buffer pools and roll out if something failed */
+ for (i = 0; i < size; i++)
+ {
+ rc = bdbuf_initialize_pool(conf_table + i, i);
+ if (rc != RTEMS_SUCCESSFUL)
+ {
+ rtems_bdpool_id j;
+ for (j = 0; j < i - 1; j++)
+ {
+ bdbuf_release_pool(j);
+ }
+ return rc;
+ }
+ }
+
+ /* Create buffer flush semaphore */
+ rc = rtems_semaphore_create(
+ rtems_build_name('B', 'F', 'L', 'U'), 0,
+ RTEMS_FIFO | RTEMS_COUNTING_SEMAPHORE | RTEMS_NO_INHERIT_PRIORITY |
+ RTEMS_NO_PRIORITY_CEILING | RTEMS_LOCAL, 0,
+ &bd_ctx.flush_sema);
+ if (rc != RTEMS_SUCCESSFUL)
+ {
+ for (i = 0; i < size; i++)
+ bdbuf_release_pool(i);
+ free(bd_ctx.pool);
+ return rc;
+ }
+
+ /* Create and start swapout task */
+ rc = rtems_task_create(
+ rtems_build_name('B', 'S', 'W', 'P'),
+ SWAPOUT_PRIORITY,
+ SWAPOUT_STACK_SIZE,
+ RTEMS_DEFAULT_MODES | RTEMS_NO_PREEMPT,
+ RTEMS_DEFAULT_ATTRIBUTES,
+ &bd_ctx.swapout_task);
+ if (rc != RTEMS_SUCCESSFUL)
+ {
+ rtems_semaphore_delete(bd_ctx.flush_sema);
+ for (i = 0; i < size; i++)
+ bdbuf_release_pool(i);
+ free(bd_ctx.pool);
+ return rc;
+ }
+
+ rc = rtems_task_start(bd_ctx.swapout_task, bdbuf_swapout_task, 0);
+ if (rc != RTEMS_SUCCESSFUL)
+ {
+ rtems_task_delete(bd_ctx.swapout_task);
+ rtems_semaphore_delete(bd_ctx.flush_sema);
+ for (i = 0; i < size; i++)
+ bdbuf_release_pool(i);
+ free(bd_ctx.pool);
+ return rc;
+ }
+
+ return RTEMS_SUCCESSFUL;
+}
+
+/* find_or_assign_buffer --
+ * Looks for buffer already assigned for this dev/block. If one is found
+ * obtain block buffer. If specified block already cached (i.e. there's
+ * block in the _modified_, or _recently_used_), return address
+ * of appropriate buffer descriptor and increment reference counter to 1.
+ * If block is not cached, allocate new buffer and return it. Data
+ * shouldn't be read to the buffer from media; buffer contains arbitrary
+ * data. This primitive may be blocked if there are no free buffer
+ * descriptors available and there are no unused non-modified (or
+ * synchronized with media) buffers available.
+ *
+ * PARAMETERS:
+ * device - device number (constructed of major and minor device number
+ * block - linear media block number
+ * ret_buf - address of the variable to store address of found descriptor
+ *
+ * RETURNS:
+ * RTEMS status code (RTEMS_SUCCESSFUL if operation completed successfully
+ * or error code if error is occured)
+ *
+ * SIDE EFFEECTS:
+ * bufget_sema may be obtained by this primitive
+ *
+ * NOTE:
+ * It is assumed that primitive invoked when thread preemption is disabled.
+ */
+static rtems_status_code
+find_or_assign_buffer(disk_device *dd,
+ blkdev_bnum block,
+ bdbuf_buffer **ret_buf)
+{
+ bdbuf_buffer *bd_buf;
+ bdbuf_pool *bd_pool;
+ rtems_status_code rc;
+ dev_t device;
+ ISR_Level level;
+
+ int blksize;
+
+ device = dd->dev;
+ bd_pool = bd_ctx.pool + dd->pool;
+ blksize = dd->block_size;
+
+again:
+ /* Looking for buffer descriptor used for this dev/block. */
+ bd_buf = avl_search(&bd_pool->tree, device, block);
+
+ if (bd_buf == NULL)
+ {
+ /* Try to obtain semaphore without waiting first. It is the most
+ frequent case when reasonable number of buffers configured. If
+ it is failed, obtain semaphore blocking on it. In this case
+ it should be checked that appropriate buffer hasn't been loaded
+ by another thread, because this thread is preempted */
+ rc = rtems_semaphore_obtain(bd_pool->bufget_sema, RTEMS_NO_WAIT, 0);
+ if (rc == RTEMS_UNSATISFIED)
+ {
+ rc = rtems_semaphore_obtain(bd_pool->bufget_sema,
+ RTEMS_WAIT, RTEMS_NO_TIMEOUT);
+ bd_buf = avl_search(&bd_pool->tree, device, block);
+ if (bd_buf != NULL)
+ rtems_semaphore_release(bd_pool->bufget_sema);
+ }
+ }
+
+ if (bd_buf == NULL)
+ {
+ /* Assign new buffer descriptor */
+ if (_Chain_Is_empty(&bd_pool->free))
+ {
+ bd_buf = (bdbuf_buffer *)Chain_Get(&bd_pool->lru);
+ if (bd_buf != NULL)
+ {
+ int avl_result;
+ avl_result = avl_remove(&bd_pool->tree, bd_buf);
+ if (avl_result != 0)
+ {
+ rtems_fatal_error_occurred(BLKDEV_FATAL_BDBUF_CONSISTENCY);
+ return RTEMS_INTERNAL_ERROR;
+ }
+ }
+ }
+ else
+ {
+ bd_buf = (bdbuf_buffer *)Chain_Get(&(bd_pool->free));
+ }
+
+ if (bd_buf == NULL)
+ {
+ goto again;
+ }
+ else
+ {
+ bd_buf->dev = device;
+ bd_buf->block = block;
+#ifdef BINARY_TREE
+ bd_buf->avl.left = NULL;
+ bd_buf->avl.right = NULL;
+#else
+ bd_buf->avl.link[0] = NULL;
+ bd_buf->avl.link[1] = NULL;
+#endif
+ bd_buf->use_count = 1;
+ bd_buf->modified = bd_buf->actual = bd_buf->in_progress = FALSE;
+ bd_buf->status = RTEMS_SUCCESSFUL;
+
+ if (avl_insert(&bd_pool->tree, bd_buf) != 0)
+ {
+ rtems_fatal_error_occurred(BLKDEV_FATAL_BDBUF_CONSISTENCY);
+ return RTEMS_INTERNAL_ERROR;
+ }
+
+ *ret_buf = bd_buf;
+
+ return RTEMS_SUCCESSFUL;
+ }
+ }
+ else
+ {
+ /* Buffer descriptor already assigned for this dev/block */
+ if (bd_buf->use_count == 0)
+ {
+ /* If we are removing from lru list, obtain the bufget_sema
+ * first. If we are removing from mod list, obtain flush sema.
+ * It should be obtained without blocking because we know
+ * that our buffer descriptor is in the list. */
+ if (bd_buf->modified)
+ {
+ rc = rtems_semaphore_obtain(bd_ctx.flush_sema,
+ RTEMS_NO_WAIT, 0);
+ }
+ else
+ {
+ rc = rtems_semaphore_obtain(bd_pool->bufget_sema,
+ RTEMS_NO_WAIT, 0);
+ }
+ /* It is possible that we couldn't obtain flush or bufget sema
+ * although buffer in the appropriate chain is available:
+ * semaphore may be released to swapout task, but this task
+ * actually did not start to process it. */
+ if (rc == RTEMS_UNSATISFIED)
+ rc = RTEMS_SUCCESSFUL;
+ if (rc != RTEMS_SUCCESSFUL)
+ {
+ rtems_fatal_error_occurred(BLKDEV_FATAL_BDBUF_CONSISTENCY);
+ return RTEMS_INTERNAL_ERROR;
+ }
+
+ /* Buffer descriptor is linked to the lru or mod chain. Remove
+ it from there. */
+ Chain_Extract(&bd_buf->link);
+ }
+ bd_buf->use_count++;
+ while (bd_buf->in_progress != 0)
+ {
+ _CORE_mutex_Seize(&bd_buf->transfer_sema, 0, TRUE,
+ WATCHDOG_NO_TIMEOUT, level);
+ }
+
+ *ret_buf = bd_buf;
+ return RTEMS_SUCCESSFUL;
+ }
+}
+
+/* rtems_bdbuf_get --
+ * Obtain block buffer. If specified block already cached (i.e. there's
+ * block in the _modified_, or _recently_used_), return address
+ * of appropriate buffer descriptor and increment reference counter to 1.
+ * If block is not cached, allocate new buffer and return it. Data
+ * shouldn't be read to the buffer from media; buffer may contains
+ * arbitrary data. This primitive may be blocked if there are no free
+ * buffer descriptors available and there are no unused non-modified
+ * (or synchronized with media) buffers available.
+ *
+ * PARAMETERS:
+ * device - device number (constructed of major and minor device number)
+ * block - linear media block number
+ * bd - address of variable to store pointer to the buffer descriptor
+ *
+ * RETURNS:
+ * RTEMS status code (RTEMS_SUCCESSFUL if operation completed successfully
+ * or error code if error is occured)
+ *
+ * SIDE EFFECTS:
+ * bufget_sema semaphore obtained by this primitive.
+ */
+rtems_status_code
+rtems_bdbuf_get(dev_t device, blkdev_bnum block, bdbuf_buffer **bd)
+{
+ rtems_status_code rc;
+ disk_device *dd;
+ disk_device *pdd;
+ preemption_key key;
+
+ /*
+ * Convert logical dev/block to physical one
+ */
+ dd = rtems_disk_lookup(device);
+ if (dd == NULL)
+ return RTEMS_INVALID_ID;
+
+ if (block >= dd->size)
+ {
+ rtems_disk_release(dd);
+ return RTEMS_INVALID_NUMBER;
+ }
+
+ pdd = dd->phys_dev;
+ block += dd->start;
+ rtems_disk_release(dd);
+
+ DISABLE_PREEMPTION(key);
+ rc = find_or_assign_buffer(pdd, block, bd);
+ ENABLE_PREEMPTION(key);
+
+ if (rc != RTEMS_SUCCESSFUL)
+ return rc;
+
+ return RTEMS_SUCCESSFUL;
+}
+
+/* bdbuf_initialize_transfer_sema --
+ * Initialize transfer_sema mutex semaphore associated with buffer
+ * descriptor.
+ */
+static inline void
+bdbuf_initialize_transfer_sema(bdbuf_buffer *bd_buf)
+{
+ CORE_mutex_Attributes mutex_attr;
+ mutex_attr.lock_nesting_behavior = CORE_MUTEX_NESTING_BLOCKS;
+ mutex_attr.only_owner_release = FALSE;
+ mutex_attr.discipline = CORE_MUTEX_DISCIPLINES_FIFO;
+ mutex_attr.priority_ceiling = 0;
+
+ _CORE_mutex_Initialize(&bd_buf->transfer_sema, OBJECTS_NO_CLASS,
+ &mutex_attr, CORE_MUTEX_LOCKED, NULL);
+}
+
+/* bdbuf_write_transfer_done --
+ * Callout function. Invoked by block device driver when data transfer
+ * to device (write) is completed. This function may be invoked from
+ * interrupt handler.
+ *
+ * PARAMETERS:
+ * arg - arbitrary argument specified in block device request
+ * structure (in this case - pointer to the appropriate
+ * bdbuf_buffer buffer descriptor structure).
+ * status - I/O completion status
+ * error - errno error code if status != RTEMS_SUCCESSFUL
+ *
+ * RETURNS:
+ * none
+ */
+static void
+bdbuf_write_transfer_done(void *arg, rtems_status_code status, int error)
+{
+ bdbuf_buffer *bd_buf = arg;
+ bd_buf->status = status;
+ bd_buf->error = error;
+ bd_buf->in_progress = bd_buf->modified = FALSE;
+ _CORE_mutex_Surrender(&bd_buf->transfer_sema, 0, NULL);
+ _CORE_mutex_Flush(&bd_buf->transfer_sema, NULL,
+ CORE_MUTEX_STATUS_SUCCESSFUL);
+}
+
+/* bdbuf_read_transfer_done --
+ * Callout function. Invoked by block device driver when data transfer
+ * from device (read) is completed. This function may be invoked from
+ * interrupt handler.
+ *
+ * PARAMETERS:
+ * arg - arbitrary argument specified in block device request
+ * structure (in this case - pointer to the appropriate
+ * bdbuf_buffer buffer descriptor structure).
+ * status - I/O completion status
+ * error - errno error code if status != RTEMS_SUCCESSFUL
+ *
+ * RETURNS:
+ * none
+ */
+static void
+bdbuf_read_transfer_done(void *arg, rtems_status_code status, int error)
+{
+ bdbuf_buffer *bd_buf = arg;
+ bd_buf->status = status;
+ bd_buf->error = error;
+ _CORE_mutex_Surrender(&bd_buf->transfer_sema, 0, NULL);
+ _CORE_mutex_Flush(&bd_buf->transfer_sema, NULL,
+ CORE_MUTEX_STATUS_SUCCESSFUL);
+}
+
+/* rtems_bdbuf_read --
+ * (Similar to the rtems_bdbuf_get, except reading data from media)
+ * Obtain block buffer. If specified block already cached, return address
+ * of appropriate buffer and increment reference counter to 1. If block is
+ * not cached, allocate new buffer and read data to it from the media.
+ * This primitive may be blocked on waiting until data to be read from
+ * media, if there are no free buffer descriptors available and there are
+ * no unused non-modified (or synchronized with media) buffers available.
+ *
+ * PARAMETERS:
+ * device - device number (consists of major and minor device number)
+ * block - linear media block number
+ * bd - address of variable to store pointer to the buffer descriptor
+ *
+ * RETURNS:
+ * RTEMS status code (RTEMS_SUCCESSFUL if operation completed successfully
+ * or error code if error is occured)
+ *
+ * SIDE EFFECTS:
+ * bufget_sema and transfer_sema semaphores obtained by this primitive.
+ */
+rtems_status_code
+rtems_bdbuf_read(dev_t device,
+ blkdev_bnum block,
+ bdbuf_buffer **bd)
+{
+ preemption_key key;
+ ISR_Level level;
+
+ bdbuf_buffer *bd_buf;
+ rtems_status_code rc;
+ int result;
+ disk_device *dd;
+ disk_device *pdd;
+ blkdev_request1 req;
+
+ dd = rtems_disk_lookup(device);
+ if (dd == NULL)
+ return RTEMS_INVALID_ID;
+
+ if (block >= dd->size)
+ {
+ rtems_disk_release(dd);
+ return RTEMS_INVALID_NUMBER;
+ }
+
+ pdd = dd->phys_dev;
+ block += dd->start;
+
+ DISABLE_PREEMPTION(key);
+ rc = find_or_assign_buffer(pdd, block, &bd_buf);
+
+ if (rc != RTEMS_SUCCESSFUL)
+ {
+ ENABLE_PREEMPTION(key);
+ rtems_disk_release(dd);
+ return rc;
+ }
+
+ if (!bd_buf->actual)
+ {
+ bd_buf->in_progress = 1;
+
+ req.req.req = BLKDEV_REQ_READ;
+ req.req.req_done = bdbuf_read_transfer_done;
+ req.req.done_arg = bd_buf;
+ req.req.start = block;
+ req.req.count = 1;
+ req.req.bufnum = 1;
+ req.req.bufs[0].length = dd->block_size;
+ req.req.bufs[0].buffer = bd_buf->buffer;
+
+ bdbuf_initialize_transfer_sema(bd_buf);
+ result = dd->ioctl(device, BLKIO_REQUEST, &req);
+ if (result == -1)
+ {
+ bd_buf->status = RTEMS_IO_ERROR;
+ bd_buf->error = errno;
+ bd_buf->actual = FALSE;
+ }
+ else
+ {
+ _CORE_mutex_Seize(&bd_buf->transfer_sema, 0, TRUE,
+ WATCHDOG_NO_TIMEOUT, level);
+ bd_buf->actual = TRUE;
+ }
+ bd_buf->in_progress = FALSE;
+ }
+ rtems_disk_release(dd);
+
+ ENABLE_PREEMPTION(key);
+
+ *bd = bd_buf;
+
+ return RTEMS_SUCCESSFUL;
+}
+
+
+/* bdbuf_release --
+ * Release buffer. Decrease buffer usage counter. If it is zero, further
+ * processing depends on modified attribute. If buffer was modified, it
+ * is inserted into mod chain and swapout task waken up. If buffer was
+ * not modified, it is returned to the end of lru chain making it available
+ * for further use.
+ *
+ * PARAMETERS:
+ * bd_buf - pointer to the released buffer descriptor.
+ *
+ * RETURNS:
+ * RTEMS_SUCCESSFUL if buffer released successfully, or error code if
+ * error occured.
+ *
+ * NOTE:
+ * This is internal function. It is assumed that task made non-preemptive
+ * before its invocation.
+ */
+static rtems_status_code
+bdbuf_release(bdbuf_buffer *bd_buf)
+{
+ bdbuf_pool *bd_pool;
+ rtems_status_code rc = RTEMS_SUCCESSFUL;
+
+ if (bd_buf->use_count <= 0)
+ return RTEMS_INTERNAL_ERROR;
+
+ bd_pool = bd_ctx.pool + bd_buf->pool;
+
+ bd_buf->use_count--;
+
+ if (bd_buf->use_count == 0)
+ {
+ if (bd_buf->modified)
+ {
+
+ /* Buffer was modified. Insert buffer to the modified buffers
+ * list and initiate flushing. */
+ Chain_Append(&bd_ctx.mod, &bd_buf->link);
+
+ /* Release the flush_sema */
+ rc = rtems_semaphore_release(bd_ctx.flush_sema);
+ }
+ else
+ {
+ /* Buffer was not modified. Add this descriptor to the
+ * end of lru chain and make it available for reuse. */
+ Chain_Append(&bd_pool->lru, &bd_buf->link);
+ rc = rtems_semaphore_release(bd_pool->bufget_sema);
+ }
+ }
+ return rc;
+}
+
+
+/* rtems_bdbuf_release --
+ * Release buffer allocated before. This primitive decrease the
+ * usage counter. If it is zero, further destiny of buffer depends on
+ * 'modified' status. If buffer was modified, it is placed to the end of
+ * mod list and flush task waken up. If buffer was not modified,
+ * it is placed to the end of lru list, and bufget_sema released, allowing
+ * to reuse this buffer.
+ *
+ * PARAMETERS:
+ * bd_buf - pointer to the bdbuf_buffer structure previously obtained using
+ * get/read primitive.
+ *
+ * RETURNS:
+ * RTEMS status code (RTEMS_SUCCESSFUL if operation completed successfully
+ * or error code if error is occured)
+ *
+ * SIDE EFFECTS:
+ * flush_sema and bufget_sema semaphores may be released by this primitive.
+ */
+rtems_status_code
+rtems_bdbuf_release(bdbuf_buffer *bd_buf)
+{
+ preemption_key key;
+ rtems_status_code rc = RTEMS_SUCCESSFUL;
+
+ if (bd_buf == NULL)
+ return RTEMS_INVALID_ADDRESS;
+
+ DISABLE_PREEMPTION(key);
+
+ rc = bdbuf_release(bd_buf);
+
+ ENABLE_PREEMPTION(key);
+
+ return rc;
+}
+
+/* rtems_bdbuf_release_modified --
+ * Release buffer allocated before, assuming that it is _modified_ by
+ * it's owner. This primitive decrease usage counter for buffer, mark
+ * buffer descriptor as modified. If usage counter is 0, insert it at
+ * end of mod chain and release flush_sema semaphore to activate the
+ * flush task.
+ *
+ * PARAMETERS:
+ * bd_buf - pointer to the bdbuf_buffer structure previously obtained using
+ * get/read primitive.
+ *
+ * RETURNS:
+ * RTEMS status code (RTEMS_SUCCESSFUL if operation completed successfully
+ * or error code if error is occured)
+ *
+ * SIDE EFFECTS:
+ * flush_sema semaphore may be released by this primitive.
+ */
+rtems_status_code
+rtems_bdbuf_release_modified(bdbuf_buffer *bd_buf)
+{
+ preemption_key key;
+ rtems_status_code rc = RTEMS_SUCCESSFUL;
+
+ if (bd_buf == NULL)
+ return RTEMS_INVALID_ADDRESS;
+
+ DISABLE_PREEMPTION(key);
+
+ if (!bd_buf->modified)
+ {
+ bdbuf_initialize_transfer_sema(bd_buf);
+ }
+ bd_buf->modified = TRUE;
+ bd_buf->actual = TRUE;
+ rc = bdbuf_release(bd_buf);
+
+ ENABLE_PREEMPTION(key);
+
+ return rc;
+}
+
+/* rtems_bdbuf_sync --
+ * Wait until specified buffer synchronized with disk. Invoked on exchanges
+ * critical for data consistency on the media. This primitive mark owned
+ * block as modified, decrease usage counter. If usage counter is 0,
+ * block inserted to the mod chain and flush_sema semaphore released.
+ * Finally, primitives blocked on transfer_sema semaphore.
+ *
+ * PARAMETERS:
+ * bd_buf - pointer to the bdbuf_buffer structure previously obtained using
+ * get/read primitive.
+ *
+ * RETURNS:
+ * RTEMS status code (RTEMS_SUCCESSFUL if operation completed successfully
+ * or error code if error is occured)
+ *
+ * SIDE EFFECTS:
+ * Primitive may be blocked on transfer_sema semaphore.
+ */
+rtems_status_code
+rtems_bdbuf_sync(bdbuf_buffer *bd_buf)
+{
+ preemption_key key;
+ ISR_Level level;
+ rtems_status_code rc = RTEMS_SUCCESSFUL;
+
+ if (bd_buf == NULL)
+ return RTEMS_INVALID_ADDRESS;
+
+ DISABLE_PREEMPTION(key);
+
+ if (!bd_buf->modified)
+ {
+ bdbuf_initialize_transfer_sema(bd_buf);
+ }
+ bd_buf->modified = TRUE;
+ bd_buf->actual = TRUE;
+
+ rc = bdbuf_release(bd_buf);
+
+ if (rc == RTEMS_SUCCESSFUL)
+ {
+ _CORE_mutex_Seize(&bd_buf->transfer_sema, 0, TRUE,
+ WATCHDOG_NO_TIMEOUT, level);
+ }
+
+ ENABLE_PREEMPTION(key);
+
+ return rc;
+}
+
+/* rtems_bdbuf_syncdev --
+ * Synchronize with disk all buffers containing the blocks belonging to
+ * specified device.
+ *
+ * PARAMETERS:
+ * dev - block device number
+ *
+ * RETURNS:
+ * 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)
+{
+ preemption_key key;
+ ISR_Level level;
+
+ bdbuf_buffer *bd_buf;
+ disk_device *dd;
+ bdbuf_pool *pool;
+
+ dd = rtems_disk_lookup(dev);
+ if (dd == NULL)
+ return RTEMS_INVALID_ID;
+
+ pool = bd_ctx.pool + dd->pool;
+
+ DISABLE_PREEMPTION(key);
+ do {
+ bd_buf = avl_search_for_sync(&pool->tree, dd);
+ if (bd_buf != NULL /* && bd_buf->modified */)
+ {
+ _CORE_mutex_Seize(&bd_buf->transfer_sema, 0, TRUE,
+ WATCHDOG_NO_TIMEOUT, level);
+ }
+ } while (bd_buf != NULL);
+ ENABLE_PREEMPTION(key);
+ return rtems_disk_release(dd);
+}
+
+/* bdbuf_swapout_task --
+ * Body of task which take care on flushing modified buffers to the
+ * disk.
+ */
+static rtems_task
+bdbuf_swapout_task(rtems_task_argument unused)
+{
+ rtems_status_code rc;
+ int result;
+ ISR_Level level;
+ bdbuf_buffer *bd_buf;
+ bdbuf_pool *bd_pool;
+ disk_device *dd;
+ blkdev_request1 req;
+
+ while (1)
+ {
+ rc = rtems_semaphore_obtain(bd_ctx.flush_sema, RTEMS_WAIT, 0);
+ if (rc != RTEMS_SUCCESSFUL)
+ {
+ rtems_fatal_error_occurred(BLKDEV_FATAL_BDBUF_SWAPOUT);
+ }
+
+ bd_buf = (bdbuf_buffer *)Chain_Get(&bd_ctx.mod);
+ if (bd_buf == NULL)
+ {
+ /* It is possible that flush_sema semaphore will be released, but
+ * buffer to be removed from mod chain before swapout task start
+ * its processing. */
+ continue;
+ }
+
+ bd_buf->in_progress = TRUE;
+ bd_buf->use_count++;
+ bd_pool = bd_ctx.pool + bd_buf->pool;
+ dd = rtems_disk_lookup(bd_buf->dev);
+
+ req.req.req = BLKDEV_REQ_WRITE;
+ req.req.req_done = bdbuf_write_transfer_done;
+ req.req.done_arg = bd_buf;
+ req.req.start = bd_buf->block + dd->start;
+ req.req.count = 1;
+ req.req.bufnum = 1;
+ req.req.bufs[0].length = dd->block_size;
+ req.req.bufs[0].buffer = bd_buf->buffer;
+
+ /* transfer_sema initialized when bd_buf inserted in the mod chain
+ first time */
+ result = dd->ioctl(dd->phys_dev->dev, BLKIO_REQUEST, &req);
+
+ rtems_disk_release(dd);
+
+ if (result == -1)
+ {
+ bd_buf->status = RTEMS_IO_ERROR;
+ bd_buf->error = errno;
+ /* Release tasks waiting on syncing this buffer */
+ _CORE_mutex_Flush(&bd_buf->transfer_sema, NULL,
+ CORE_MUTEX_STATUS_SUCCESSFUL);
+ }
+ else
+ {
+ if (bd_buf->in_progress)
+ {
+ _CORE_mutex_Seize(&bd_buf->transfer_sema, 0, TRUE, 0, level);
+ }
+ }
+ bd_buf->use_count--;
+
+ /* Another task have chance to use this buffer, or even
+ * modify it. If buffer is not in use, insert it in appropriate chain
+ * and release semaphore */
+ if (bd_buf->use_count == 0)
+ {
+ if (bd_buf->modified)
+ {
+ Chain_Append(&bd_ctx.mod, &bd_buf->link);
+ rc = rtems_semaphore_release(bd_ctx.flush_sema);
+ }
+ else
+ {
+ Chain_Append(&bd_pool->lru, &bd_buf->link);
+ rc = rtems_semaphore_release(bd_pool->bufget_sema);
+ }
+ }
+ }
+}
+
+/* rtems_bdbuf_find_pool --
+ * Find first appropriate buffer pool. This primitive returns the index
+ * of first buffer pool which block size is greater than or equal to
+ * specified size.
+ *
+ * PARAMETERS:
+ * block_size - requested block size
+ * pool - placeholder for result
+ *
+ * RETURNS:
+ * RTEMS status code: RTEMS_SUCCESSFUL if operation completed successfully,
+ * RTEMS_INVALID_SIZE if specified block size is invalid (not a power
+ * of 2), RTEMS_NOT_DEFINED if buffer pool for this or greater block size
+ * is not configured.
+ */
+rtems_status_code
+rtems_bdbuf_find_pool(int block_size, rtems_bdpool_id *pool)
+{
+ rtems_bdpool_id i;
+ bdbuf_pool *p;
+ int cursize = INT_MAX;
+ rtems_bdpool_id curid = -1;
+ rtems_boolean found = FALSE;
+ int j;
+
+ for (j = block_size; (j != 0) && ((j & 1) == 0); j >>= 1);
+ if (j != 1)
+ return RTEMS_INVALID_SIZE;
+
+ for (i = 0, p = bd_ctx.pool; i < bd_ctx.npools; i++, p++)
+ {
+ if ((p->blksize >= block_size) &&
+ (p->blksize < cursize))
+ {
+ curid = i;
+ cursize = p->blksize;
+ found = TRUE;
+ }
+ }
+
+ if (found)
+ {
+ if (pool != NULL)
+ *pool = curid;
+ return RTEMS_SUCCESSFUL;
+ }
+ else
+ {
+ return RTEMS_NOT_DEFINED;
+ }
+}
+
+/* rtems_bdbuf_get_pool_info --
+ * Obtain characteristics of buffer pool with specified number.
+ *
+ * PARAMETERS:
+ * pool - buffer pool number
+ * block_size - block size for which buffer pool is configured returned
+ * there
+ * blocks - number of buffers in buffer pool returned there
+ *
+ * RETURNS:
+ * RTEMS status code: RTEMS_SUCCESSFUL if operation completed successfully,
+ * RTEMS_INVALID_NUMBER if appropriate buffer pool is not configured.
+ *
+ * NOTE:
+ * Buffer pools enumerated contiguously starting from 0.
+ */
+rtems_status_code
+rtems_bdbuf_get_pool_info(rtems_bdpool_id pool, int *block_size,
+ int *blocks)
+{
+ if (pool >= bd_ctx.npools)
+ return RTEMS_INVALID_NUMBER;
+
+ if (block_size != NULL)
+ {
+ *block_size = bd_ctx.pool[pool].blksize;
+ }
+
+ if (blocks != NULL)
+ {
+ *blocks = bd_ctx.pool[pool].nblks;
+ }
+
+ return RTEMS_SUCCESSFUL;
+}
diff --git a/c/src/exec/libblock/src/blkdev.c b/c/src/exec/libblock/src/blkdev.c
new file mode 100644
index 0000000000..1004ec4de3
--- /dev/null
+++ b/c/src/exec/libblock/src/blkdev.c
@@ -0,0 +1,243 @@
+/*
+ * blkdev.h - block device driver generic support
+ *
+ * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia
+ * Author: Victor V. Vengerov <vvv@oktet.ru>
+ *
+ * @(#) $Id$
+ */
+
+#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,
+ rtems_device_minor_number minor,
+ void * arg)
+{
+ rtems_libio_rw_args_t *args = arg;
+ int block_size_log2;
+ int block_size;
+ char *buf;
+ unsigned int count;
+ unsigned int block;
+ unsigned int blkofs;
+ dev_t dev;
+ disk_device *dd;
+
+ dev = rtems_filesystem_make_dev_t(major, minor);
+ dd = rtems_disk_lookup(dev);
+ if (dd == NULL)
+ return RTEMS_INVALID_NUMBER;
+
+ block_size_log2 = dd->block_size_log2;
+ block_size = dd->block_size;
+
+ buf = args->buffer;
+ count = args->count;
+ args->bytes_moved = 0;
+
+ block = args->offset >> block_size_log2;
+ blkofs = args->offset & (block_size - 1);
+
+ while (count > 0)
+ {
+ bdbuf_buffer *diskbuf;
+ int copy;
+ rtems_status_code rc;
+
+ rc = rtems_bdbuf_read(dev, block, &diskbuf);
+ if (rc != RTEMS_SUCCESSFUL)
+ return rc;
+ 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)
+ return rc;
+ count -= copy;
+ buf += copy;
+ blkofs = 0;
+ block++;
+ }
+ return RTEMS_SUCCESSFUL;
+}
+
+/* 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,
+ rtems_device_minor_number minor,
+ void * arg)
+{
+ rtems_libio_rw_args_t *args = arg;
+ int block_size_log2;
+ int block_size;
+ char *buf;
+ unsigned int count;
+ unsigned int block;
+ unsigned int blkofs;
+ dev_t dev;
+ rtems_status_code rc;
+ disk_device *dd;
+
+ dev = rtems_filesystem_make_dev_t(major, minor);
+ dd = rtems_disk_lookup(dev);
+ if (dd == NULL)
+ return RTEMS_INVALID_NUMBER;
+
+ block_size_log2 = dd->block_size_log2;
+ block_size = dd->block_size;
+
+ buf = args->buffer;
+ count = args->count;
+ args->bytes_moved = 0;
+
+ block = args->offset >> block_size_log2;
+ blkofs = args->offset & (block_size - 1);
+
+ while (count > 0)
+ {
+ bdbuf_buffer *diskbuf;
+ int 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)
+ return rc;
+
+ 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)
+ return rc;
+
+ count -= copy;
+ buf += copy;
+ blkofs = 0;
+ block++;
+ }
+ return RTEMS_SUCCESSFUL;
+}
+
+/* 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)
+{
+ dev_t dev;
+ disk_device *dd;
+
+ dev = rtems_filesystem_make_dev_t(major, minor);
+ dd = rtems_disk_lookup(dev);
+ if (dd == NULL)
+ return RTEMS_INVALID_NUMBER;
+
+ dd->uses++;
+
+ rtems_disk_release(dd);
+
+ return RTEMS_SUCCESSFUL;
+}
+
+
+/* blkdev_generic_close --
+ * Generic block device close primitive.
+ */
+rtems_device_driver
+rtems_blkdev_generic_close(
+ rtems_device_major_number major,
+ rtems_device_minor_number minor,
+ void * arg)
+{
+ dev_t dev;
+ disk_device *dd;
+
+ dev = rtems_filesystem_make_dev_t(major, minor);
+ dd = rtems_disk_lookup(dev);
+ if (dd == NULL)
+ return RTEMS_INVALID_NUMBER;
+
+ dd->uses--;
+
+ 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,
+ rtems_device_minor_number minor,
+ void * arg)
+{
+ rtems_libio_ioctl_args_t *args = arg;
+ dev_t dev;
+ disk_device *dd;
+ int rc;
+
+ dev = rtems_filesystem_make_dev_t(major, minor);
+ dd = rtems_disk_lookup(dev);
+ if (dd == NULL)
+ return RTEMS_INVALID_NUMBER;
+
+ switch (args->command)
+ {
+ case BLKIO_GETBLKSIZE:
+ args->ioctl_return = dd->block_size;
+ break;
+
+ case BLKIO_GETSIZE:
+ args->ioctl_return = dd->size;
+ break;
+
+ case BLKIO_SYNCDEV:
+ rc = rtems_bdbuf_syncdev(dd->dev);
+ args->ioctl_return = (rc == RTEMS_SUCCESSFUL ? 0 : -1);
+ break;
+
+ case BLKIO_REQUEST:
+ {
+ blkdev_request *req = args->buffer;
+ req->start += dd->start;
+ args->ioctl_return = dd->ioctl(dd->phys_dev->dev, args->command,
+ req);
+ break;
+ }
+
+ default:
+ args->ioctl_return = dd->ioctl(dd->phys_dev->dev, args->command,
+ args->buffer);
+ break;
+ }
+ rtems_disk_release(dd);
+
+ return RTEMS_SUCCESSFUL;
+}
diff --git a/c/src/exec/libblock/src/diskdevs.c b/c/src/exec/libblock/src/diskdevs.c
new file mode 100644
index 0000000000..a9d6035e8b
--- /dev/null
+++ b/c/src/exec/libblock/src/diskdevs.c
@@ -0,0 +1,631 @@
+/*
+ * diskdevs.c - Physical and logical block devices (disks) support
+ *
+ * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia
+ * Author: Victor V. Vengerov <vvv@oktet.ru>
+ *
+ * @(#) $Id$
+ */
+
+
+#include <rtems.h>
+#include <rtems/libio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "rtems/diskdevs.h"
+#include "rtems/bdbuf.h"
+
+#define DISKTAB_INITIAL_SIZE 32
+
+/* Table of disk devices having the same major number */
+struct disk_device_table {
+ disk_device **minor; /* minor-indexed disk device table */
+ int size; /* Number of entries in the table */
+};
+
+/* Pointer to [major].minor[minor] indexed array of disk devices */
+static struct disk_device_table *disktab;
+
+/* Number of allocated entries in disktab table */
+static int disktab_size;
+
+/* Mutual exclusion semaphore for disk devices table */
+static rtems_id diskdevs_mutex;
+
+/* Flag meaning that disk I/O, buffering etc. already has been initialized. */
+static boolean disk_io_initialized = FALSE;
+
+/* 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 rtems_boolean diskdevs_protected;
+
+/* create_disk_entry --
+ * Return pointer to the disk_entry structure for the specified device, or
+ * create one if it is not exists.
+ *
+ * PARAMETERS:
+ * dev - device id (major, minor)
+ *
+ * RETURNS:
+ * pointer to the disk device descirptor entry, or NULL if no memory
+ * available for its creation.
+ */
+static disk_device *
+create_disk_entry(dev_t dev)
+{
+ rtems_device_major_number major;
+ rtems_device_minor_number minor;
+ struct disk_device **d;
+
+ rtems_filesystem_split_dev_t (dev, major, minor);
+
+ if (major >= disktab_size)
+ {
+ struct disk_device_table *p;
+ int newsize;
+ int i;
+ newsize = disktab_size * 2;
+ if (major >= newsize)
+ newsize = major + 1;
+ p = realloc(disktab, sizeof(struct disk_device_table) * newsize);
+ if (p == NULL)
+ return NULL;
+ p += disktab_size;
+ for (i = disktab_size; i < newsize; i++, p++)
+ {
+ p->minor = NULL;
+ p->size = 0;
+ }
+ disktab_size = newsize;
+ }
+
+ if ((disktab[major].minor == NULL) ||
+ (minor >= disktab[major].size))
+ {
+ int newsize;
+ disk_device **p;
+ int i;
+ int s = disktab[major].size;
+
+ if (s == 0)
+ newsize = DISKTAB_INITIAL_SIZE;
+ else
+ newsize = s * 2;
+ if (minor >= newsize)
+ newsize = minor + 1;
+
+ p = realloc(disktab[major].minor, sizeof(disk_device *) * newsize);
+ if (p == NULL)
+ return NULL;
+ disktab[major].minor = p;
+ p += s;
+ for (i = s; i < newsize; i++, p++)
+ *p = NULL;
+ disktab[major].size = newsize;
+ }
+
+ d = disktab[major].minor + minor;
+ if (*d == NULL)
+ {
+ *d = calloc(1, sizeof(disk_device));
+ }
+ return *d;
+}
+
+/* get_disk_entry --
+ * Get disk device descriptor by device number.
+ *
+ * PARAMETERS:
+ * dev - block device number
+ *
+ * RETURNS:
+ * Pointer to the disk device descriptor corresponding to the specified
+ * device number, or NULL if disk device with such number not exists.
+ */
+static inline disk_device *
+get_disk_entry(dev_t dev)
+{
+ rtems_device_major_number major;
+ rtems_device_minor_number minor;
+ struct disk_device_table *dtab;
+
+ rtems_filesystem_split_dev_t (dev, major, minor);
+
+ if ((major >= disktab_size) || (disktab == NULL))
+ return NULL;
+
+ dtab = disktab + major;
+
+ if ((minor >= dtab->size) || (dtab->minor == NULL))
+ return NULL;
+
+ return dtab->minor[minor];
+}
+
+/* create_disk --
+ * Check that disk entry for specified device number is not defined
+ * and create it.
+ *
+ * PARAMETERS:
+ * dev - device identifier (major, minor numbers)
+ * name - character name of device (e.g. /dev/hda)
+ * disdev - placeholder for pointer to created disk descriptor
+ *
+ * RETURNS:
+ * RTEMS_SUCCESSFUL if disk entry successfully created, or
+ * error code if error occured (device already registered,
+ * no memory available).
+ */
+static rtems_status_code
+create_disk(dev_t dev, char *name, disk_device **diskdev)
+{
+ disk_device *dd;
+ char *n;
+
+ dd = get_disk_entry(dev);
+ if (dd != NULL)
+ {
+ return RTEMS_RESOURCE_IN_USE;
+ }
+
+ if (name == NULL)
+ {
+ n = NULL;
+ }
+ else
+ {
+ int nlen = strlen(name) + 1;
+ n = malloc(nlen);
+ if (n == NULL)
+ return RTEMS_NO_MEMORY;
+ strncpy(n, name, nlen);
+ }
+
+ dd = create_disk_entry(dev);
+ if (dd == NULL)
+ {
+ free(n);
+ return RTEMS_NO_MEMORY;
+ }
+
+ dd->dev = dev;
+ dd->name = n;
+
+ *diskdev = dd;
+
+ return RTEMS_SUCCESSFUL;
+}
+
+/* rtems_disk_create_phys --
+ * Create physical disk entry. This function usually invoked from
+ * block device driver initialization code when physical device
+ * detected in the system. Device driver should provide ioctl handler
+ * to allow block device access operations. This primitive will register
+ * device in rtems (invoke rtems_io_register_name).
+ *
+ * PARAMETERS:
+ * dev - device identifier (major, minor numbers)
+ * block_size - size of disk block (minimum data transfer unit); must be
+ * power of 2
+ * disk_size - number of blocks on device
+ * handler - IOCTL handler (function providing basic block input/output
+ * request handling BIOREQUEST and other device management
+ * operations)
+ * name - character name of device (e.g. /dev/hda)
+ *
+ * RETURNS:
+ * RTEMS_SUCCESSFUL if information about new physical disk added, or
+ * error code if error occured (device already registered, wrong block
+ * size value, no memory available).
+ */
+rtems_status_code
+rtems_disk_create_phys(dev_t dev, int block_size, int disk_size,
+ block_device_ioctl handler,
+ char *name)
+{
+ int bs_log2;
+ int i;
+ disk_device *dd;
+ rtems_status_code rc;
+ rtems_bdpool_id pool;
+ rtems_device_major_number major;
+ rtems_device_minor_number minor;
+
+ rtems_filesystem_split_dev_t (dev, major, minor);
+
+
+ for (bs_log2 = 0, i = block_size; (i & 1) == 0; i >>= 1, bs_log2++);
+ if ((bs_log2 < 9) || (i != 1)) /* block size < 512 or not power of 2 */
+ return RTEMS_INVALID_NUMBER;
+
+ rc = rtems_semaphore_obtain(diskdevs_mutex, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
+ if (rc != RTEMS_SUCCESSFUL)
+ return rc;
+ diskdevs_protected = TRUE;
+
+ rc = rtems_bdbuf_find_pool(block_size, &pool);
+ if (rc != RTEMS_SUCCESSFUL)
+ {
+ diskdevs_protected = FALSE;
+ rtems_semaphore_release(diskdevs_mutex);
+ return rc;
+ }
+
+ rc = create_disk(dev, name, &dd);
+ if (rc != RTEMS_SUCCESSFUL)
+ {
+ diskdevs_protected = FALSE;
+ rtems_semaphore_release(diskdevs_mutex);
+ return rc;
+ }
+
+ dd->phys_dev = dd;
+ dd->uses = 0;
+ dd->start = 0;
+ dd->size = disk_size;
+ dd->block_size = block_size;
+ dd->block_size_log2 = bs_log2;
+ dd->ioctl = handler;
+ dd->pool = pool;
+
+ rc = rtems_io_register_name(name, major, minor);
+
+ diskdevs_protected = FALSE;
+ rtems_semaphore_release(diskdevs_mutex);
+
+ return rc;
+}
+
+/* rtems_disk_create_log --
+ * Create logical disk entry. Logical disk is contiguous area on physical
+ * disk. Disk may be splitted to several logical disks in several ways:
+ * manually or using information stored in blocks on physical disk
+ * (DOS-like partition table, BSD disk label, etc). This function usually
+ * invoked from application when application-specific splitting are in use,
+ * or from generic code which handle different logical disk organizations.
+ * This primitive will register device in rtems (invoke
+ * rtems_io_register_name).
+ *
+ * PARAMETERS:
+ * dev - logical device identifier (major, minor numbers)
+ * phys - physical device (block device which holds this logical disk)
+ * identifier
+ * start - starting block number on the physical device
+ * size - logical disk size in blocks
+ * name - logical disk name
+ *
+ * RETURNS:
+ * RTEMS_SUCCESSFUL if logical device successfully added, or error code
+ * if error occured (device already registered, no physical device
+ * exists, logical disk is out of physical disk boundaries, no memory
+ * available).
+ */
+rtems_status_code
+rtems_disk_create_log(dev_t dev, dev_t phys, int start, int size, char *name)
+{
+ disk_device *dd;
+ disk_device *pdd;
+ rtems_status_code rc;
+ rtems_device_major_number major;
+ rtems_device_minor_number minor;
+
+ rtems_filesystem_split_dev_t (dev, major, minor);
+
+ rc = rtems_semaphore_obtain(diskdevs_mutex, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
+ if (rc != RTEMS_SUCCESSFUL)
+ return rc;
+ diskdevs_protected = TRUE;
+
+ pdd = get_disk_entry(phys);
+ if (pdd == NULL)
+ {
+ diskdevs_protected = FALSE;
+ rtems_semaphore_release(diskdevs_mutex);
+ return RTEMS_INVALID_NUMBER;
+ }
+
+ rc = create_disk(dev, name, &dd);
+ if (rc != RTEMS_SUCCESSFUL)
+ {
+ diskdevs_protected = FALSE;
+ rtems_semaphore_release(diskdevs_mutex);
+ return rc;
+ }
+
+ dd->phys_dev = pdd;
+ dd->uses = 0;
+ dd->start = start;
+ dd->size = size;
+ dd->block_size = pdd->block_size;
+ dd->block_size_log2 = pdd->block_size_log2;
+ dd->ioctl = pdd->ioctl;
+
+ rc = rtems_io_register_name(name, major, minor);
+
+ diskdevs_protected = FALSE;
+ rc = rtems_semaphore_release(diskdevs_mutex);
+
+ return rc;
+}
+
+/* rtems_disk_delete --
+ * Delete physical or logical disk device. Device may be deleted if its
+ * use counter (and use counters of all logical devices - if it is
+ * physical device) equal to 0. When physical device deleted,
+ * all logical devices deleted inherently. Appropriate devices removed
+ * from "/dev" filesystem.
+ *
+ * PARAMETERS:
+ * dev - device identifier (major, minor numbers)
+ *
+ * RETURNS:
+ * RTEMS_SUCCESSFUL if block device successfully deleted, or error code
+ * if error occured (device is not defined, device is in use).
+ */
+rtems_status_code
+rtems_disk_delete(dev_t dev)
+{
+ rtems_status_code rc;
+ int used;
+ rtems_device_major_number maj;
+ rtems_device_minor_number min;
+
+ rc = rtems_semaphore_obtain(diskdevs_mutex, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
+ if (rc != RTEMS_SUCCESSFUL)
+ return rc;
+ diskdevs_protected = TRUE;
+
+ /* Check if this device is in use -- calculate usage counter */
+ used = 0;
+ for (maj = 0; maj < disktab_size; maj++)
+ {
+ struct disk_device_table *dtab = disktab + maj;
+ if (dtab != NULL)
+ {
+ for (min = 0; min < dtab->size; min++)
+ {
+ disk_device *dd = dtab->minor[min];
+ if ((dd != NULL) && (dd->phys_dev->dev == dev))
+ used += dd->uses;
+ }
+ }
+ }
+
+ if (used != 0)
+ {
+ diskdevs_protected = FALSE;
+ rtems_semaphore_release(diskdevs_mutex);
+ return RTEMS_RESOURCE_IN_USE;
+ }
+
+ /* Delete this device and all of its logical devices */
+ for (maj = 0; maj < disktab_size; maj++)
+ {
+ struct disk_device_table *dtab = disktab +maj;
+ if (dtab != NULL)
+ {
+ for (min = 0; min < dtab->size; min++)
+ {
+ disk_device *dd = dtab->minor[min];
+ if ((dd != NULL) && (dd->phys_dev->dev == dev))
+ {
+ unlink(dd->name);
+ free(dd->name);
+ free(dd);
+ dtab->minor[min] = NULL;
+ }
+ }
+ }
+ }
+
+ diskdevs_protected = FALSE;
+ rc = rtems_semaphore_release(diskdevs_mutex);
+ return rc;
+}
+
+/* rtems_disk_lookup --
+ * Find block device descriptor by its device identifier.
+ *
+ * PARAMETERS:
+ * dev - device identifier (major, minor numbers)
+ *
+ * RETURNS:
+ * pointer to the block device descriptor, or NULL if no such device
+ * exists.
+ */
+disk_device *
+rtems_disk_lookup(dev_t dev)
+{
+ rtems_interrupt_level level;
+ disk_device *dd;
+ rtems_status_code rc;
+
+ rtems_interrupt_disable(level);
+ if (diskdevs_protected)
+ {
+ rtems_interrupt_enable(level);
+ rc = rtems_semaphore_obtain(diskdevs_mutex, RTEMS_WAIT,
+ RTEMS_NO_TIMEOUT);
+ if (rc != RTEMS_SUCCESSFUL)
+ return NULL;
+ diskdevs_protected = TRUE;
+ dd = get_disk_entry(dev);
+ dd->uses++;
+ diskdevs_protected = FALSE;
+ rtems_semaphore_release(diskdevs_mutex);
+ return dd;
+ }
+ else
+ {
+ /* Frequent and quickest case */
+ dd = get_disk_entry(dev);
+ dd->uses++;
+ rtems_interrupt_enable(level);
+ return dd;
+ }
+}
+
+/* rtems_disk_release --
+ * Release disk_device structure (decrement usage counter to 1).
+ *
+ * PARAMETERS:
+ * dd - pointer to disk device structure
+ *
+ * RETURNS:
+ * RTEMS_SUCCESSFUL
+ */
+rtems_status_code
+rtems_disk_release(disk_device *dd)
+{
+ rtems_interrupt_level level;
+ rtems_interrupt_disable(level);
+ dd->uses--;
+ rtems_interrupt_enable(level);
+ return RTEMS_SUCCESSFUL;
+}
+
+/* rtems_disk_next --
+ * Disk device enumerator. Looking for device having device number larger
+ * than dev and return disk device descriptor for it. If there are no
+ * such device, NULL value returned.
+ *
+ * PARAMETERS:
+ * dev - device number (use -1 to start search)
+ *
+ * RETURNS:
+ * Pointer to the disk descriptor for next disk device, or NULL if all
+ * devices enumerated.
+ */
+disk_device *
+rtems_disk_next(dev_t dev)
+{
+ rtems_device_major_number major;
+ rtems_device_minor_number minor;
+ struct disk_device_table *dtab;
+
+ dev++;
+ rtems_filesystem_split_dev_t (dev, major, minor);
+
+ if (major >= disktab_size)
+ return NULL;
+
+ dtab = disktab + major;
+ while (TRUE)
+ {
+ if ((dtab == NULL) || (minor > dtab->size))
+ {
+ major++; minor = 0;
+ if (major >= disktab_size)
+ return NULL;
+ dtab = disktab + major;
+ }
+ else if (dtab->minor[minor] == NULL)
+ {
+ minor++;
+ }
+ else
+ return dtab->minor[minor];
+ }
+}
+
+/* rtems_disk_initialize --
+ * Initialization of disk device library (initialize all data structures,
+ * etc.)
+ *
+ * PARAMETERS:
+ * none
+ *
+ * RETURNS:
+ * RTEMS_SUCCESSFUL if library initialized, or error code if error
+ * occured.
+ */
+rtems_status_code
+rtems_disk_io_initialize(void)
+{
+ rtems_status_code rc;
+
+ if (disk_io_initialized)
+ return RTEMS_SUCCESSFUL;
+
+ disktab_size = DISKTAB_INITIAL_SIZE;
+ disktab = calloc(disktab_size, sizeof(struct disk_device_table));
+ if (disktab == NULL)
+ return RTEMS_NO_MEMORY;
+
+ diskdevs_protected = FALSE;
+ rc = 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 (rc != RTEMS_SUCCESSFUL)
+ {
+ free(disktab);
+ return rc;
+ }
+
+ rc = rtems_bdbuf_init(rtems_bdbuf_configuration,
+ rtems_bdbuf_configuration_size);
+
+ if (rc != RTEMS_SUCCESSFUL)
+ {
+ rtems_semaphore_delete(diskdevs_mutex);
+ free(disktab);
+ return rc;
+ }
+
+ disk_io_initialized = 1;
+ return RTEMS_SUCCESSFUL;
+}
+
+/* rtems_disk_io_done --
+ * Release all resources allocated for disk device interface.
+ *
+ * PARAMETERS:
+ * none
+ *
+ * RETURNS:
+ * RTEMS_SUCCESSFUL if all resources released, or error code if error
+ * occured.
+ */
+rtems_status_code
+rtems_disk_io_done(void)
+{
+ rtems_device_major_number maj;
+ rtems_device_minor_number min;
+ rtems_status_code rc;
+
+ /* Free data structures */
+ for (maj = 0; maj < disktab_size; maj++)
+ {
+ struct disk_device_table *dtab = disktab + maj;
+ if (dtab != NULL)
+ {
+ for (min = 0; min < dtab->size; min++)
+ {
+ disk_device *dd = dtab->minor[min];
+ unlink(dd->name);
+ free(dd->name);
+ free(dd);
+ }
+ free(dtab);
+ }
+ }
+ free(disktab);
+
+ rc = rtems_semaphore_release(diskdevs_mutex);
+
+ /* XXX bdbuf should be released too! */
+ disk_io_initialized = 0;
+ return rc;
+}
diff --git a/c/src/exec/libblock/src/ramdisk.c b/c/src/exec/libblock/src/ramdisk.c
new file mode 100644
index 0000000000..08f42846e0
--- /dev/null
+++ b/c/src/exec/libblock/src/ramdisk.c
@@ -0,0 +1,224 @@
+/* ramdisk.c -- RAM disk block device implementation
+ *
+ * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia
+ * Author: Victor V. Vengerov <vvv@oktet.ru>
+ *
+ * @(#) $Id$
+ */
+
+#include <rtems.h>
+#include <rtems/libio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "rtems/blkdev.h"
+#include "rtems/diskdevs.h"
+#include "rtems/ramdisk.h"
+
+#define RAMDISK_DEVICE_BASE_NAME "/dev/ramdisk"
+
+/* Internal RAM disk descriptor */
+struct ramdisk {
+ int block_size; /* RAM disk block size */
+ int block_num; /* Number of blocks on this RAM disk */
+ void *area; /* RAM disk memory area */
+ rtems_boolean initialized;/* RAM disk is initialized */
+ rtems_boolean malloced; /* != 0, if memory allocated by malloc for this
+ RAM disk */
+};
+
+static struct ramdisk *ramdisk;
+static int nramdisks;
+
+/* ramdisk_read --
+ * RAM disk READ request handler. This primitive copies data from RAM
+ * disk to supplied buffer and invoke the callout function to inform
+ * upper layer that reading is completed.
+ *
+ * PARAMETERS:
+ * req - pointer to the READ block device request info
+ *
+ * RETURNS:
+ * ioctl return value
+ */
+static int
+ramdisk_read(struct ramdisk *rd, blkdev_request *req)
+{
+ char *from;
+ rtems_unsigned32 i;
+ blkdev_sg_buffer *sg;
+ rtems_unsigned32 remains;
+
+ from = (char *)rd->area + (req->start * rd->block_size);
+ remains = rd->block_size * req->count;
+ sg = req->bufs;
+ for (i = 0; (remains > 0) && (i < req->bufnum); i++, sg++)
+ {
+ int count = sg->length;
+ if (count > remains)
+ count = remains;
+ memcpy(sg->buffer, from, count);
+ remains -= count;
+ }
+ req->req_done(req->done_arg, RTEMS_SUCCESSFUL, 0);
+ return 0;
+}
+
+/* ramdisk_write --
+ * RAM disk WRITE request handler. This primitive copies data from
+ * supplied buffer to RAM disk and invoke the callout function to inform
+ * upper layer that writing is completed.
+ *
+ * PARAMETERS:
+ * req - pointer to the WRITE block device request info
+ *
+ * RETURNS:
+ * ioctl return value
+ */
+static int
+ramdisk_write(struct ramdisk *rd, blkdev_request *req)
+{
+ char *to;
+ rtems_unsigned32 i;
+ blkdev_sg_buffer *sg;
+ rtems_unsigned32 remains;
+
+ to = (char *)rd->area + (req->start * rd->block_size);
+ remains = rd->block_size * req->count;
+ sg = req->bufs;
+ for (i = 0; (remains > 0) && (i < req->bufnum); i++, sg++)
+ {
+ int count = sg->length;
+ if (count > remains)
+ count = remains;
+ memcpy(to, sg->buffer, count);
+ remains -= count;
+ }
+ req->req_done(req->done_arg, RTEMS_SUCCESSFUL, 0);
+ return 0;
+}
+
+/* ramdisk_ioctl --
+ * IOCTL handler for RAM disk device.
+ *
+ * PARAMETERS:
+ * dev - device number (major, minor number)
+ * req - IOCTL request code
+ * argp - IOCTL argument
+ *
+ * RETURNS:
+ * IOCTL return value
+ */
+static int
+ramdisk_ioctl(dev_t dev, int req, void *argp)
+{
+ switch (req)
+ {
+ case BLKIO_REQUEST:
+ {
+ rtems_device_minor_number minor;
+ blkdev_request *r = argp;
+ struct ramdisk *rd;
+
+ minor = rtems_filesystem_dev_minor_t(dev);
+ if ((minor >= nramdisks) || !ramdisk[minor].initialized)
+ {
+ errno = ENODEV;
+ return -1;
+ }
+
+ rd = ramdisk + minor;
+
+ switch (r->req)
+ {
+ case BLKDEV_REQ_READ:
+ return ramdisk_read(rd, r);
+
+ case BLKDEV_REQ_WRITE:
+ return ramdisk_write(rd, r);
+
+ default:
+ errno = EBADRQC;
+ return -1;
+ }
+ break;
+ }
+
+ default:
+ errno = EBADRQC;
+ return -1;
+ }
+}
+
+/* ramdisk_initialize --
+ * RAM disk device driver initialization. Run through RAM disk
+ * configuration information and configure appropriate RAM disks.
+ *
+ * PARAMETERS:
+ * major - RAM disk major device number
+ * minor - minor device number, not applicable
+ * arg - initialization argument, not applicable
+ *
+ * RETURNS:
+ * none
+ */
+rtems_device_driver
+ramdisk_initialize(
+ rtems_device_major_number major,
+ rtems_device_minor_number minor,
+ void *arg)
+{
+ 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;
+
+ r = ramdisk = calloc(rtems_ramdisk_configuration_size,
+ sizeof(struct ramdisk));
+
+ for (i = 0; i < rtems_ramdisk_configuration_size; i++, c++, r++)
+ {
+ dev_t dev = rtems_filesystem_make_dev_t(major, i);
+ char name[sizeof(RAMDISK_DEVICE_BASE_NAME "0123456789")];
+ snprintf(name, sizeof(name), RAMDISK_DEVICE_BASE_NAME "%d", 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, name);
+ if (rc != RTEMS_SUCCESSFUL)
+ {
+ if (r->malloced)
+ {
+ free(r->area);
+ }
+ r->initialized = FALSE;
+ }
+ }
+ nramdisks = rtems_ramdisk_configuration_size;
+ return RTEMS_SUCCESSFUL;
+}