summaryrefslogtreecommitdiffstats
path: root/cpukit/libblock/src/diskdevs.c
diff options
context:
space:
mode:
Diffstat (limited to 'cpukit/libblock/src/diskdevs.c')
-rw-r--r--cpukit/libblock/src/diskdevs.c631
1 files changed, 631 insertions, 0 deletions
diff --git a/cpukit/libblock/src/diskdevs.c b/cpukit/libblock/src/diskdevs.c
new file mode 100644
index 0000000000..a9d6035e8b
--- /dev/null
+++ b/cpukit/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;
+}