/* * Copyright 2007, Ingo Weinhold . * All rights reserved. Distributed under the terms of the MIT License. */ #include #include #include #include #include #include #include "RemoteDisk.h" //#define TRACE_REMOTE_DISK #ifdef TRACE_REMOTE_DISK # define TRACE(x) dprintf x #else # define TRACE(x) do {} while (false) #endif const bigtime_t kInitRetryDelay = 10 * 1000000LL; // 10 s 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; // don't try to init, if the last attempt wasn't long enough ago if (lastInitRetryTime >= 0 && system_time() < lastInitRetryTime + kInitRetryDelay) { return B_ERROR; } // create the object remoteDisk = new(nothrow) RemoteDisk; if (!remoteDisk) { lastInitRetryTime = system_time(); return B_NO_MEMORY; } // find a server 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) { // TODO: Respect "bios" argument! 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", // "misc/remote_disk_control", NULL }; static RemoteDiskDevice* sDevices; // #pragma mark - internal functions // device_for_name 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; } // #pragma mark - data device hooks 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)); // nothing to do 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); // used data 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)); // nothing to do 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 }; // #pragma mark - public API 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; }