forked from Imagelibrary/rtems
642 lines
17 KiB
C
642 lines
17 KiB
C
/*
|
|
* 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$
|
|
*/
|
|
|
|
#if HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include <rtems.h>
|
|
#include <rtems/libio.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h> /* unlink */
|
|
#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 */
|
|
typedef struct rtems_disk_device_table {
|
|
rtems_disk_device **minor; /* minor-indexed disk device table */
|
|
uint32_t size; /* Number of entries in the table */
|
|
} rtems_disk_device_table;
|
|
|
|
/* Pointer to [major].minor[minor] indexed array of disk devices */
|
|
static rtems_disk_device_table *disktab;
|
|
|
|
/* Number of allocated entries in disktab table */
|
|
static uint32_t 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 bool 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 bool 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 descriptor entry, or NULL if no memory
|
|
* available for its creation.
|
|
*/
|
|
static rtems_disk_device *
|
|
create_disk_entry(dev_t dev)
|
|
{
|
|
rtems_device_major_number major;
|
|
rtems_device_minor_number minor;
|
|
rtems_disk_device **d;
|
|
|
|
rtems_filesystem_split_dev_t (dev, major, minor);
|
|
|
|
if (major >= disktab_size)
|
|
{
|
|
rtems_disk_device_table *p;
|
|
uint32_t newsize;
|
|
uint32_t i;
|
|
newsize = disktab_size * 2;
|
|
if (major >= newsize)
|
|
newsize = major + 1;
|
|
p = realloc(disktab, sizeof(rtems_disk_device_table) * newsize);
|
|
if (p == NULL)
|
|
return NULL;
|
|
disktab = p;
|
|
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))
|
|
{
|
|
uint32_t newsize;
|
|
rtems_disk_device **p;
|
|
uint32_t i;
|
|
uint32_t 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(rtems_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(rtems_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 rtems_disk_device *
|
|
get_disk_entry(dev_t dev)
|
|
{
|
|
rtems_device_major_number major;
|
|
rtems_device_minor_number minor;
|
|
rtems_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, const char *name, rtems_disk_device **diskdev)
|
|
{
|
|
rtems_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,
|
|
rtems_block_device_ioctl handler,
|
|
const char *name)
|
|
{
|
|
int bs_log2;
|
|
int i;
|
|
rtems_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);
|
|
|
|
if (handler (dd->phys_dev->dev,
|
|
RTEMS_BLKDEV_CAPABILITIES,
|
|
&dd->capabilities) < 0)
|
|
dd->capabilities = 0;
|
|
|
|
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)
|
|
{
|
|
rtems_disk_device *dd;
|
|
rtems_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++)
|
|
{
|
|
rtems_disk_device_table *dtab = disktab + maj;
|
|
if (dtab != NULL)
|
|
{
|
|
for (min = 0; min < dtab->size; min++)
|
|
{
|
|
rtems_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++)
|
|
{
|
|
rtems_disk_device_table *dtab = disktab +maj;
|
|
if (dtab != NULL)
|
|
{
|
|
for (min = 0; min < dtab->size; min++)
|
|
{
|
|
rtems_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_obtain --
|
|
* 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.
|
|
*/
|
|
rtems_disk_device *
|
|
rtems_disk_obtain(dev_t dev)
|
|
{
|
|
rtems_interrupt_level level;
|
|
rtems_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 rtems_disk_device structure (decrement usage counter to 1).
|
|
*
|
|
* PARAMETERS:
|
|
* dd - pointer to disk device structure
|
|
*
|
|
* RETURNS:
|
|
* RTEMS_SUCCESSFUL
|
|
*/
|
|
rtems_status_code
|
|
rtems_disk_release(rtems_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.
|
|
*/
|
|
rtems_disk_device *
|
|
rtems_disk_next(dev_t dev)
|
|
{
|
|
rtems_device_major_number major;
|
|
rtems_device_minor_number minor;
|
|
rtems_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(rtems_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();
|
|
|
|
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++)
|
|
{
|
|
rtems_disk_device_table *dtab = disktab + maj;
|
|
if (dtab != NULL)
|
|
{
|
|
for (min = 0; min < dtab->size; min++)
|
|
{
|
|
rtems_disk_device *dd = dtab->minor[min];
|
|
unlink(dd->name);
|
|
free(dd->name);
|
|
free(dd);
|
|
}
|
|
free(dtab);
|
|
}
|
|
}
|
|
free(disktab);
|
|
|
|
rc = rtems_semaphore_delete(diskdevs_mutex);
|
|
|
|
/* XXX bdbuf should be released too! */
|
|
disk_io_initialized = 0;
|
|
return rc;
|
|
}
|