* Copyright 2007, Ingo Weinhold <bonefish@cs.tu-berlin.de>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#include <string.h>
#include <KernelExport.h>
#include <Drivers.h>
#include <lock.h>
#include <util/AutoLock.h>
#include <util/kernel_cpp.h>
#include "RemoteDisk.h"
#ifdef TRACE_REMOTE_DISK
# define TRACE(x) dprintf x
#else
# define TRACE(x) do {} while (false)
#endif
const bigtime_t kInitRetryDelay = 10 * 1000000LL;
enum {
MAX_REMOTE_DISKS = 1
};
struct RemoteDiskDevice : recursive_lock {
RemoteDisk* remoteDisk;
bigtime_t lastInitRetryTime;
RemoteDiskDevice()
:
remoteDisk(NULL),
lastInitRetryTime(-1)
{
}
~RemoteDiskDevice()
{
delete remoteDisk;
Uninit();
}
status_t Init()
{
recursive_lock_init(this, "remote disk device");
return B_OK;
}
void Uninit()
{
recursive_lock_destroy(this);
}
status_t LazyInitDisk()
{
if (remoteDisk)
return B_OK;
if (lastInitRetryTime >= 0
&& system_time() < lastInitRetryTime + kInitRetryDelay) {
return B_ERROR;
}
remoteDisk = new(nothrow) RemoteDisk;
if (!remoteDisk) {
lastInitRetryTime = system_time();
return B_NO_MEMORY;
}
TRACE(("remote_disk: FindAnyRemoteDisk()\n"));
status_t error = remoteDisk->FindAnyRemoteDisk();
if (error != B_OK) {
delete remoteDisk;
remoteDisk = NULL;
lastInitRetryTime = system_time();
return B_NO_MEMORY;
}
return B_OK;
}
void GetGeometry(device_geometry* geometry, bool bios)
{
geometry->bytes_per_sector = REMOTE_DISK_BLOCK_SIZE;
geometry->sectors_per_track = 1;
geometry->cylinder_count = remoteDisk->Size() / REMOTE_DISK_BLOCK_SIZE;
geometry->head_count = 1;
geometry->device_type = B_DISK;
geometry->removable = true;
geometry->read_only = remoteDisk->IsReadOnly();
geometry->write_once = false;
geometry->bytes_per_physical_sector = REMOTE_DISK_BLOCK_SIZE;
}
};
typedef RecursiveLocker DeviceLocker;
static const char* kPublishedNames[] = {
"disk/virtual/remote_disk/0/raw",
NULL
};
static RemoteDiskDevice* sDevices;
static RemoteDiskDevice*
device_for_name(const char* name)
{
for (int i = 0; i < MAX_REMOTE_DISKS; i++) {
if (strcmp(name, kPublishedNames[i]) == 0)
return sDevices + i;
}
return NULL;
}
static status_t
remote_disk_open(const char* name, uint32 flags, void** cookie)
{
RemoteDiskDevice* device = device_for_name(name);
TRACE(("remote_disk_open(\"%s\") -> %p\n", name, device));
if (!device)
return B_BAD_VALUE;
DeviceLocker locker(device);
status_t error = device->LazyInitDisk();
if (error != B_OK)
return error;
*cookie = device;
return B_OK;
}
static status_t
remote_disk_close(void* cookie)
{
TRACE(("remote_disk_close(%p)\n", cookie));
return B_OK;
}
static status_t
remote_disk_read(void* cookie, off_t position, void* buffer, size_t* numBytes)
{
TRACE(("remote_disk_read(%p, %lld, %p, %lu)\n", cookie, position, buffer,
*numBytes));
RemoteDiskDevice* device = (RemoteDiskDevice*)cookie;
DeviceLocker locker(device);
ssize_t bytesRead = device->remoteDisk->ReadAt(position, buffer, *numBytes);
if (bytesRead < 0) {
*numBytes = 0;
TRACE(("remote_disk_read() failed: %s\n", strerror(bytesRead)));
return bytesRead;
}
*numBytes = bytesRead;
TRACE(("remote_disk_read() done: %ld\n", bytesRead));
return B_OK;
}
static status_t
remote_disk_write(void* cookie, off_t position, const void* buffer,
size_t* numBytes)
{
TRACE(("remote_disk_write(%p, %lld, %p, %lu)\n", cookie, position, buffer,
*numBytes));
RemoteDiskDevice* device = (RemoteDiskDevice*)cookie;
DeviceLocker locker(device);
ssize_t bytesWritten = device->remoteDisk->WriteAt(position, buffer,
*numBytes);
if (bytesWritten < 0) {
*numBytes = 0;
TRACE(("remote_disk_write() failed: %s\n", strerror(bytesRead)));
return bytesWritten;
}
*numBytes = bytesWritten;
TRACE(("remote_disk_written() done: %ld\n", bytesWritten));
return B_OK;
}
static status_t
remote_disk_control(void* cookie, uint32 op, void* arg, size_t len)
{
TRACE(("remote_disk_control(%p, %lu, %p, %lu)\n", cookie, op, arg, len));
RemoteDiskDevice* device = (RemoteDiskDevice*)cookie;
DeviceLocker locker(device);
switch (op) {
case B_GET_DEVICE_SIZE:
TRACE(("remote_disk: B_GET_DEVICE_SIZE\n"));
*(size_t*)arg = device->remoteDisk->Size();
return B_OK;
case B_SET_NONBLOCKING_IO:
TRACE(("remote_disk: B_SET_NONBLOCKING_IO\n"));
return B_OK;
case B_SET_BLOCKING_IO:
TRACE(("remote_disk: B_SET_BLOCKING_IO\n"));
return B_OK;
case B_GET_READ_STATUS:
TRACE(("remote_disk: B_GET_READ_STATUS\n"));
*(bool*)arg = true;
return B_OK;
case B_GET_WRITE_STATUS:
TRACE(("remote_disk: B_GET_WRITE_STATUS\n"));
*(bool*)arg = true;
return B_OK;
case B_GET_ICON:
{
TRACE(("remote_disk: B_GET_ICON\n"));
return B_BAD_VALUE;
}
case B_GET_BIOS_GEOMETRY:
case B_GET_GEOMETRY:
{
TRACE(("remote_disk: %s\n",
op == B_GET_BIOS_GEOMETRY ? "B_GET_BIOS_GEOMETRY" : "B_GET_GEOMETRY"));
if (arg == NULL || len > sizeof(device_geometry))
return B_BAD_VALUE;
device_geometry geometry;
device->GetGeometry(&geometry, op == B_GET_BIOS_GEOMETRY);
return user_memcpy(arg, &geometry, len);
}
case B_GET_MEDIA_STATUS:
TRACE(("remote_disk: B_GET_MEDIA_STATUS\n"));
*(status_t*)arg = B_NO_ERROR;
return B_OK;
case B_SET_UNINTERRUPTABLE_IO:
TRACE(("remote_disk: B_SET_UNINTERRUPTABLE_IO\n"));
return B_OK;
case B_SET_INTERRUPTABLE_IO:
TRACE(("remote_disk: B_SET_INTERRUPTABLE_IO\n"));
return B_OK;
case B_FLUSH_DRIVE_CACHE:
TRACE(("remote_disk: B_FLUSH_DRIVE_CACHE\n"));
return B_OK;
case B_GET_BIOS_DRIVE_ID:
TRACE(("remote_disk: B_GET_BIOS_DRIVE_ID\n"));
*(uint8*)arg = 0xF8;
return B_OK;
case B_GET_DRIVER_FOR_DEVICE:
case B_SET_DEVICE_SIZE:
case B_SET_PARTITION:
case B_FORMAT_DEVICE:
case B_EJECT_DEVICE:
case B_LOAD_MEDIA:
case B_GET_NEXT_OPEN_DEVICE:
TRACE(("remote_disk: another ioctl: %lx (%lu)\n", op, op));
return B_BAD_VALUE;
default:
TRACE(("remote_disk: unknown ioctl: %lx (%lu)\n", op, op));
return B_BAD_VALUE;
}
}
static status_t
remote_disk_free(void* cookie)
{
TRACE(("remote_disk_free(%p)\n", cookie));
return B_OK;
}
static device_hooks sDataDeviceHooks = {
remote_disk_open,
remote_disk_close,
remote_disk_free,
remote_disk_control,
remote_disk_read,
remote_disk_write
};
int32 api_version = B_CUR_DRIVER_API_VERSION;
status_t
init_hardware(void)
{
TRACE(("remote_disk: init_hardware()\n"));
return B_OK;
}
status_t
init_driver(void)
{
TRACE(("remote_disk: init_driver()\n"));
sDevices = new(nothrow) RemoteDiskDevice[MAX_REMOTE_DISKS];
if (!sDevices)
return B_NO_MEMORY;
status_t error = B_OK;
for (int i = 0; error == B_OK && i < MAX_REMOTE_DISKS; i++)
error = sDevices[i].Init();
if (error != B_OK) {
delete[] sDevices;
sDevices = NULL;
return error;
}
return B_OK;
}
void
uninit_driver(void)
{
TRACE(("remote_disk: uninit_driver()\n"));
delete[] sDevices;
}
const char**
publish_devices(void)
{
TRACE(("remote_disk: publish_devices()\n"));
return kPublishedNames;
}
device_hooks*
find_device(const char* name)
{
TRACE(("remote_disk: find_device(%s)\n", name));
return &sDataDeviceHooks;
}