Rearranged source code a bit (clear distinction between device and drive API).

Added support for halting and resuming a device; the driver will close the underlying
file as long as the device is halted, so that other applications can access the file
(files set to uncached cannot be opened anymore). A device is resumed when you install
it again - note that the device might have been closed in the mean time, so it's
not guaranteed to return the same device.
Now returns a valid icon.
Removed unnecessary comments (this is no driver writers tutorial).
VIRTUAL_DRIVE_GET_INFO now also indicates if the device is halted or not.
Made the ioctl() calls more save by adding some sanity checks (magic value and
structure size).
Minor style changes, renamed device_info.opencount to open_count, added the "s"
suffix to static global variables (virtualdrive_name -> sVirtualDriveName, ...).
Fixed a possible bug: the file is now set to uncached after it has been resized;
it might not work the other way around.


git-svn-id: file:///srv/svn/repos/haiku/trunk/current@5462 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Axel Dörfler
2003-11-23 22:12:38 +00:00
parent 6caa00df87
commit bc4f78089e
@@ -8,6 +8,7 @@
//
// Author: Marcus Overhagen <[email protected]>
// Ingo Weinhold <[email protected]>
// Axel Doerfler <[email protected]>
// ----------------------------------------------------------------------
#include <fcntl.h>
@@ -23,6 +24,7 @@
#include "lock.h"
#include "virtualdrive.h"
#include "virtualdrive_icon.h"
/*
[2:07] <geist> when you open the file in the driver, use stat() to see if it's a file. if it is, call ioctl 10000 on the underlying file
@@ -46,7 +48,7 @@ static int dev_index_for_path(const char *path);
null-terminated array of device names supported by this driver
----- */
static const char *virtualdrive_name[] = {
static const char *sVirtualDriveName[] = {
VIRTUAL_DRIVE_DIRECTORY_REL "/0",
VIRTUAL_DRIVE_DIRECTORY_REL "/1",
VIRTUAL_DRIVE_DIRECTORY_REL "/2",
@@ -62,11 +64,12 @@ static const char *virtualdrive_name[] = {
};
int32 api_version = B_CUR_DRIVER_API_VERSION;
extern device_hooks sVirtualDriveHooks;
lock driverlock;
typedef struct device_info {
int32 opencount;
int32 open_count;
int fd;
off_t size;
bool unused;
@@ -87,6 +90,7 @@ static int gControlDeviceFD = -1;
static thread_id gLockOwner = -1;
static int32 gLockOwnerNesting = 0;
// lock_driver
void
lock_driver()
@@ -99,6 +103,7 @@ lock_driver()
gLockOwnerNesting++;
}
// unlock_driver
void
unlock_driver()
@@ -119,6 +124,7 @@ is_valid_device_index(int32 index)
return (index >= 0 && index < kDeviceCount);
}
// is_valid_data_device_index
static inline
bool
@@ -127,23 +133,40 @@ is_valid_data_device_index(int32 index)
return (is_valid_device_index(index) && index != kControlDevice);
}
// dev_index_for_path
static
int
dev_index_for_path(const char *path)
{
int i;
for (i = 0; i < kDeviceCount; i++) {
if (!strcmp(path, gDeviceInfos[i].device_path))
return i;
}
return -1;
}
// clear_device_info
static
void
clear_device_info(int32 index)
{
TRACE("virtualdrive: clear_device_info(%ld)\n", index);
TRACE("virtualdrive: clear_device_info(%ld)\n", index);
device_info &info = gDeviceInfos[index];
info.opencount = 0;
info.open_count = 0;
info.fd = -1;
info.size = 0;
info.unused = (index != kDeviceCount - 1);
info.registered = !info.unused;
info.file[0] = '\0';
info.device_path = virtualdrive_name[index];
info.device_path = sVirtualDriveName[index];
info.geometry.read_only = true;
}
// init_device_info
static
status_t
@@ -151,29 +174,28 @@ init_device_info(int32 index, virtual_drive_info *initInfo)
{
if (!is_valid_data_device_index(index) || !initInfo)
return B_BAD_VALUE;
device_info &info = gDeviceInfos[index];
if (!info.unused)
return B_BAD_VALUE;
bool readOnly = (initInfo->use_geometry && initInfo->geometry.read_only);
// open the file
int fd = open(initInfo->file_name, (readOnly ? O_RDONLY : O_RDWR));
if (fd < 0)
return errno;
// disable caching for underlying file! (else this driver will deadlock)
status_t error = B_OK;
if (ioctl(fd, 10000) != 0) {
error = errno;
TRACE("virtualdrive: disable caching ioctl failed\n");
}
// get the file size
off_t fileSize = 0;
if (error == B_OK) {
struct stat st;
if (fstat(fd, &st) == 0)
fileSize = st.st_size;
else
error = errno;
}
struct stat st;
if (fstat(fd, &st) == 0)
fileSize = st.st_size;
else
error = errno;
// If we shall use the supplied geometry, we enlarge the file, if
// necessary. Otherwise we fill in the geometry according to the size of the file.
off_t size = 0;
@@ -218,23 +240,38 @@ init_device_info(int32 index, virtual_drive_info *initInfo)
* info.geometry.head_count;
}
}
if (error == B_OK) {
// Disable caching for underlying file! (else this driver will deadlock)
// We probably cannot resize the file once the cache has been disabled!
// This is a special reserved ioctl() opcode not defined anywhere in
// the Be headers.
if (ioctl(fd, 10000) != 0) {
TRACE("virtualdrive: disable caching ioctl failed\n");
return errno;
}
}
// fill in the rest of the device_info structure
if (error == B_OK) {
info.opencount = 0;
// open_count doesn't have to be changed here (virtualdrive_open() will do that for us)
info.fd = fd;
info.size = size;
info.unused = false;
info.registered = true;
strcpy(info.file, initInfo->file_name);
info.device_path = virtualdrive_name[index];
info.device_path = sVirtualDriveName[index];
} else {
// cleanup on error
close(fd);
clear_device_info(index);
if (info.open_count == 0)
clear_device_info(index);
}
return error;
}
// uninit_device_info
static
status_t
@@ -242,31 +279,31 @@ uninit_device_info(int32 index)
{
if (!is_valid_data_device_index(index))
return B_BAD_VALUE;
device_info &info = gDeviceInfos[index];
if (info.unused)
return B_BAD_VALUE;
close(info.fd);
clear_device_info(index);
return B_OK;
}
/* ----------
init_hardware - called once the first time the driver is loaded
----- */
// #pragma mark -
// public driver API
status_t
init_hardware (void)
init_hardware(void)
{
TRACE("virtualdrive: init_hardware\n");
return B_OK;
}
/* ----------
init_driver - optional function - called every time the driver
is loaded.
----- */
status_t
init_driver (void)
init_driver(void)
{
TRACE("virtualdrive: init\n");
@@ -280,35 +317,47 @@ init_driver (void)
}
/* ----------
uninit_driver - optional function - called every time the driver
is unloaded
----- */
void
uninit_driver (void)
uninit_driver(void)
{
TRACE("virtualdrive: uninit\n");
free_lock(&driverlock);
}
/* ----------
virtualdrive_open - handle open() calls
----- */
const char **
publish_devices(void)
{
TRACE("virtualdrive: publish_devices\n");
return sVirtualDriveName;
}
device_hooks *
find_device(const char* name)
{
TRACE("virtualdrive: find_device(%s)\n", name);
return &sVirtualDriveHooks;
}
// #pragma mark -
// the device hooks
static status_t
virtualdrive_open (const char *name, uint32 flags, void** cookie)
virtualdrive_open(const char *name, uint32 flags, void **cookie)
{
TRACE("virtualdrive: open %s\n",name);
*cookie = (void *) -1;
*cookie = (void *)-1;
lock_driver();
int32 devIndex = dev_index_for_path(name);
TRACE("virtualdrive: devIndex %ld!\n", devIndex);
if (!is_valid_device_index(devIndex)) {
TRACE("virtualdrive: wrong index!\n");
unlock_driver();
@@ -328,21 +377,17 @@ virtualdrive_open (const char *name, uint32 flags, void** cookie)
}
// store index in cookie
*cookie = (void *) devIndex;
*cookie = (void *)devIndex;
gDeviceInfos[devIndex].opencount++;
gDeviceInfos[devIndex].open_count++;
unlock_driver();
return B_OK;
}
/* ----------
virtualdrive_close - handle close() calls
----- */
static status_t
virtualdrive_close (void* cookie)
virtualdrive_close(void *cookie)
{
TRACE("virtualdrive: close\n");
@@ -353,25 +398,21 @@ virtualdrive_close (void* cookie)
return B_OK;
lock_driver();
gDeviceInfos[devIndex].opencount--;
if (gDeviceInfos[devIndex].opencount == 0 && !gDeviceInfos[devIndex].registered) {
gDeviceInfos[devIndex].open_count--;
if (gDeviceInfos[devIndex].open_count == 0 && !gDeviceInfos[devIndex].registered) {
// The last FD is closed and the device has been unregistered. Free its info.
uninit_device_info(devIndex);
}
unlock_driver();
return B_OK;
}
/* ----------
virtualdrive_read - handle read() calls
----- */
static status_t
virtualdrive_read (void* cookie, off_t position, void *buffer, size_t* numBytes)
virtualdrive_read(void *cookie, off_t position, void *buffer, size_t *numBytes)
{
TRACE("virtualdrive: read pos = 0x%08Lx, bytes = 0x%08lx\n",position,*numBytes);
// check parameters
@@ -401,12 +442,8 @@ virtualdrive_read (void* cookie, off_t position, void *buffer, size_t* numBytes)
}
/* ----------
virtualdrive_write - handle write() calls
----- */
static status_t
virtualdrive_write(void* cookie, off_t position, const void* buffer, size_t* numBytes)
virtualdrive_write(void *cookie, off_t position, const void *buffer, size_t *numBytes)
{
TRACE("virtualdrive: write pos = 0x%08Lx, bytes = 0x%08lx\n",position,*numBytes);
// check parameters
@@ -436,18 +473,14 @@ virtualdrive_write(void* cookie, off_t position, const void* buffer, size_t* num
}
/* ----------
virtualdrive_control - handle ioctl calls
----- */
static status_t
virtualdrive_control (void* cookie, uint32 op, void* arg, size_t len)
virtualdrive_control(void *cookie, uint32 op, void *arg, size_t len)
{
TRACE("virtualdrive: ioctl\n");
int devIndex = (int)cookie;
device_info &info = gDeviceInfos[devIndex];
if (devIndex == kControlDevice || info.unused) {
// control device or unused data device
switch (op) {
@@ -473,35 +506,66 @@ virtualdrive_control (void* cookie, uint32 op, void* arg, size_t len)
case B_GET_NEXT_OPEN_DEVICE:
TRACE("virtualdrive: another ioctl: %lx (%lu)\n", op, op);
return B_BAD_VALUE;
case VIRTUAL_DRIVE_REGISTER_FILE:
{
TRACE("virtualdrive: VIRTUAL_DRIVE_REGISTER_FILE\n");
if (devIndex != kControlDevice || !arg)
virtual_drive_info *driveInfo = (virtual_drive_info *)arg;
if (devIndex != kControlDevice || driveInfo == NULL
|| driveInfo->magic != VIRTUAL_DRIVE_MAGIC
|| driveInfo->drive_info_size != sizeof(virtual_drive_info))
return B_BAD_VALUE;
// find an unused data device and initialize it
virtual_drive_info *driveInfo = (virtual_drive_info*)arg;
status_t error = B_ERROR;
lock_driver();
for (int32 i = 0; i < kDataDeviceCount; i++) {
if (gDeviceInfos[i].unused) {
error = init_device_info(i, driveInfo);
if (error == B_OK) {
// return the device path
strcpy(driveInfo->device_name, "/dev/");
strcat(driveInfo->device_name,
gDeviceInfos[i].device_path);
// on the first registration we need to open the
// control device to stay loaded
if (gRegistrationCount++ == 0) {
char path[B_PATH_NAME_LENGTH];
strcpy(path, "/dev/");
strcat(path, info.device_path);
gControlDeviceFD = open(path, O_RDONLY);
}
}
int32 i;
lock_driver();
// first, look if we already have opened that file and see
// if it's available to us which happens when it has been
// halted but is still in use by other components
for (i = 0; i < kDataDeviceCount; i++) {
if (!gDeviceInfos[i].unused
&& gDeviceInfos[i].fd == -1
&& !gDeviceInfos[i].registered
&& !strcmp(gDeviceInfos[i].file, driveInfo->file_name)) {
// mark device as unused, so that init_device_info() will succeed
gDeviceInfos[i].unused = true;
error = B_OK;
break;
}
}
if (error != B_OK) {
// find an unused data device
for (i = 0; i < kDataDeviceCount; i++) {
if (gDeviceInfos[i].unused) {
error = B_OK;
break;
}
}
}
if (error == B_OK) {
// we found a device slot, let's initialize it
error = init_device_info(i, driveInfo);
if (error == B_OK) {
// return the device path
strcpy(driveInfo->device_name, "/dev/");
strcat(driveInfo->device_name, gDeviceInfos[i].device_path);
// on the first registration we need to open the
// control device to stay loaded
if (gRegistrationCount++ == 0) {
char path[B_PATH_NAME_LENGTH];
strcpy(path, "/dev/");
strcat(path, info.device_path);
gControlDeviceFD = open(path, O_RDONLY);
}
}
}
unlock_driver();
return error;
}
@@ -509,7 +573,9 @@ virtualdrive_control (void* cookie, uint32 op, void* arg, size_t len)
case VIRTUAL_DRIVE_GET_INFO:
TRACE("virtualdrive: VIRTUAL_DRIVE_UNREGISTER_FILE/"
"VIRTUAL_DRIVE_GET_INFO\n");
// these are called on used data files only!
return B_BAD_VALUE;
default:
TRACE("virtualdrive: unknown ioctl: %lx (%lu)\n", op, op);
return B_BAD_VALUE;
@@ -541,8 +607,19 @@ virtualdrive_control (void* cookie, uint32 op, void* arg, size_t len)
return B_OK;
case B_GET_ICON:
{
TRACE("virtualdrive: B_GET_ICON\n");
device_icon *icon = (device_icon *)arg;
if (icon->icon_size == kPrimaryImageWidth) {
memcpy(icon->icon_data, kPrimaryImageBits, kPrimaryImageWidth * kPrimaryImageHeight);
} else if (icon->icon_size == kSecondaryImageWidth) {
memcpy(icon->icon_data, kSecondaryImageBits, kSecondaryImageWidth * kSecondaryImageHeight);
} else
return B_ERROR;
return B_OK;
}
case B_GET_GEOMETRY:
TRACE("virtualdrive: B_GET_GEOMETRY\n");
@@ -595,6 +672,7 @@ virtualdrive_control (void* cookie, uint32 op, void* arg, size_t len)
case B_GET_NEXT_OPEN_DEVICE:
TRACE("virtualdrive: another ioctl: %lx (%lu)\n", op, op);
return B_BAD_VALUE;
case VIRTUAL_DRIVE_REGISTER_FILE:
TRACE("virtualdrive: VIRTUAL_DRIVE_REGISTER_FILE (data)\n");
return B_BAD_VALUE;
@@ -602,30 +680,52 @@ virtualdrive_control (void* cookie, uint32 op, void* arg, size_t len)
{
TRACE("virtualdrive: VIRTUAL_DRIVE_UNREGISTER_FILE\n");
lock_driver();
bool wasRegistered = gDeviceInfos[devIndex].registered;
gDeviceInfos[devIndex].registered = false;
bool immediately = (bool)arg;
bool wasRegistered = info.registered;
info.registered = false;
// on the last unregistration we need to close the
// control device
if (wasRegistered && --gRegistrationCount == 0) {
close(gControlDeviceFD);
gControlDeviceFD = -1;
}
// if we "immediately" is true, we will stop our service immediately
// and close the underlying file, open it for other uses
if (immediately) {
TRACE("virtualdrive: close file descriptor\n");
// we cannot use uninit_device_info() here, since that does
// a little too much and would open the device for other
// uses.
close(info.fd);
info.fd = -1;
}
unlock_driver();
return B_OK;
}
case VIRTUAL_DRIVE_GET_INFO:
{
TRACE("virtualdrive: VIRTUAL_DRIVE_GET_INFO\n");
if (!arg)
virtual_drive_info *driveInfo = (virtual_drive_info *)arg;
if (driveInfo == NULL
|| driveInfo->magic != VIRTUAL_DRIVE_MAGIC
|| driveInfo->drive_info_size != sizeof(virtual_drive_info))
return B_BAD_VALUE;
virtual_drive_info *driveInfo = (virtual_drive_info*)arg;
strcpy(driveInfo->file_name, info.file);
strcpy(driveInfo->device_name, "/dev/");
strcat(driveInfo->device_name, info.device_path);
driveInfo->geometry = info.geometry;
driveInfo->use_geometry = true;
driveInfo->halted = info.fd == -1;
return B_OK;
}
default:
TRACE("virtualdrive: unknown ioctl: %lx (%lu)\n", op, op);
return B_BAD_VALUE;
@@ -635,12 +735,8 @@ virtualdrive_control (void* cookie, uint32 op, void* arg, size_t len)
}
/* -----
virtualdrive_free - called after the last device is closed, and after
all i/o is complete.
----- */
static status_t
virtualdrive_free (void* cookie)
virtualdrive_free(void *cookie)
{
TRACE("virtualdrive: free\n");
return B_OK;
@@ -651,7 +747,7 @@ virtualdrive_free (void* cookie)
function pointers for the device hooks entry points
----- */
device_hooks virtualdrive_hooks = {
device_hooks sVirtualDriveHooks = {
virtualdrive_open, /* -> open entry point */
virtualdrive_close, /* -> close entry point */
virtualdrive_free, /* -> free cookie */
@@ -660,40 +756,3 @@ device_hooks virtualdrive_hooks = {
virtualdrive_write /* -> write entry point */
};
/* ----------
publish_devices - return a null-terminated array of devices
supported by this driver.
----- */
const char**
publish_devices()
{
TRACE("virtualdrive: publish_devices\n");
return virtualdrive_name;
}
/* ----------
find_device - return ptr to device hooks structure for a
given device name
----- */
device_hooks*
find_device(const char* name)
{
TRACE("virtualdrive: find_device(%s)\n", name);
return &virtualdrive_hooks;
}
// dev_index_for_path
static
int
dev_index_for_path(const char *path)
{
int i;
for (i = 0; i < kDeviceCount; i++) {
if (!strcmp(path, gDeviceInfos[i].device_path))
return i;
}
return -1;
}