17a424c39SIngo Weinhold // ---------------------------------------------------------------------- 27a424c39SIngo Weinhold // This software is part of the OpenBeOS distribution and is covered 37a424c39SIngo Weinhold // by the OpenBeOS license. 47a424c39SIngo Weinhold // 57a424c39SIngo Weinhold // File Name: virtualdrive.c 67a424c39SIngo Weinhold // 77a424c39SIngo Weinhold // Description: Driver that emulates virtual drives. 87a424c39SIngo Weinhold // 97a424c39SIngo Weinhold // Author: Marcus Overhagen <Marcus@Overhagen.de> 107a424c39SIngo Weinhold // Ingo Weinhold <bonefish@users.sf.net> 11bc4f7808SAxel Dörfler // Axel Doerfler <axeld@pinc-software.de> 127a424c39SIngo Weinhold // ---------------------------------------------------------------------- 137a424c39SIngo Weinhold 147a424c39SIngo Weinhold #include <fcntl.h> 157a424c39SIngo Weinhold #include <errno.h> 167a424c39SIngo Weinhold #include <malloc.h> 177a424c39SIngo Weinhold #include <stdio.h> 187a424c39SIngo Weinhold #include <string.h> 197a424c39SIngo Weinhold #include <unistd.h> 207a424c39SIngo Weinhold 217a424c39SIngo Weinhold #include <KernelExport.h> 227a424c39SIngo Weinhold #include <Drivers.h> 237a424c39SIngo Weinhold #include <Errors.h> 247a424c39SIngo Weinhold 257a424c39SIngo Weinhold #include "lock.h" 267a424c39SIngo Weinhold #include "virtualdrive.h" 27bc4f7808SAxel Dörfler #include "virtualdrive_icon.h" 287a424c39SIngo Weinhold 297a424c39SIngo Weinhold /* 307a424c39SIngo Weinhold [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 317a424c39SIngo Weinhold [2:07] <geist> that's the disable-cache ioctl 327a424c39SIngo Weinhold [2:08] <geist> bfs is probably doing the same algorithm, and seeing that you are a device and not a file, and so it doesn't call 10000 on you 337a424c39SIngo Weinhold [2:08] <marcus_o> thanks, I will try calling it 347a424c39SIngo Weinhold [2:08] <geist> and dont bother using dosfs as a host fs, it wont work 357a424c39SIngo Weinhold [2:09] <geist> bfs is the only fs that's reasonably safe being reentered like that, but only if the underlying one is opened with the cache disabled on that file 367a424c39SIngo Weinhold [2:09] <geist> that ioctl is used on the swap file as well 377a424c39SIngo Weinhold [2:10] <marcus_o> I'm currently allocating memory in the driver's write() function hook 387a424c39SIngo Weinhold [2:10] <geist> cant do that 397a424c39SIngo Weinhold */ 407a424c39SIngo Weinhold 41*8545b8bdSAxel Dörfler //#define TRACE(x) dprintf x 42*8545b8bdSAxel Dörfler #define TRACE(x) ; 437a424c39SIngo Weinhold #define MB (1024LL * 1024LL) 447a424c39SIngo Weinhold 457a424c39SIngo Weinhold static int dev_index_for_path(const char *path); 467a424c39SIngo Weinhold 477a424c39SIngo Weinhold /* ----- 487a424c39SIngo Weinhold null-terminated array of device names supported by this driver 497a424c39SIngo Weinhold ----- */ 507a424c39SIngo Weinhold 51bc4f7808SAxel Dörfler static const char *sVirtualDriveName[] = { 527a424c39SIngo Weinhold VIRTUAL_DRIVE_DIRECTORY_REL "/0", 537a424c39SIngo Weinhold VIRTUAL_DRIVE_DIRECTORY_REL "/1", 547a424c39SIngo Weinhold VIRTUAL_DRIVE_DIRECTORY_REL "/2", 557a424c39SIngo Weinhold VIRTUAL_DRIVE_DIRECTORY_REL "/3", 567a424c39SIngo Weinhold VIRTUAL_DRIVE_DIRECTORY_REL "/4", 577a424c39SIngo Weinhold VIRTUAL_DRIVE_DIRECTORY_REL "/5", 587a424c39SIngo Weinhold VIRTUAL_DRIVE_DIRECTORY_REL "/6", 597a424c39SIngo Weinhold VIRTUAL_DRIVE_DIRECTORY_REL "/7", 607a424c39SIngo Weinhold VIRTUAL_DRIVE_DIRECTORY_REL "/8", 617a424c39SIngo Weinhold VIRTUAL_DRIVE_DIRECTORY_REL "/9", 627a424c39SIngo Weinhold VIRTUAL_DRIVE_CONTROL_DEVICE_REL, 637a424c39SIngo Weinhold NULL 647a424c39SIngo Weinhold }; 657a424c39SIngo Weinhold 667a424c39SIngo Weinhold int32 api_version = B_CUR_DRIVER_API_VERSION; 67bc4f7808SAxel Dörfler extern device_hooks sVirtualDriveHooks; 687a424c39SIngo Weinhold 697a424c39SIngo Weinhold lock driverlock; 707a424c39SIngo Weinhold 717a424c39SIngo Weinhold typedef struct device_info { 72bc4f7808SAxel Dörfler int32 open_count; 737a424c39SIngo Weinhold int fd; 747a424c39SIngo Weinhold off_t size; 757a424c39SIngo Weinhold bool unused; 767a424c39SIngo Weinhold bool registered; 777a424c39SIngo Weinhold char file[B_PATH_NAME_LENGTH]; 787a424c39SIngo Weinhold const char *device_path; 797a424c39SIngo Weinhold device_geometry geometry; 807a424c39SIngo Weinhold } device_info; 817a424c39SIngo Weinhold 827a424c39SIngo Weinhold #define kDeviceCount 11 837a424c39SIngo Weinhold #define kDataDeviceCount (kDeviceCount - 1) 847a424c39SIngo Weinhold #define kControlDevice (kDeviceCount - 1) 857a424c39SIngo Weinhold struct device_info gDeviceInfos[kDeviceCount]; 867a424c39SIngo Weinhold 877a424c39SIngo Weinhold static int32 gRegistrationCount = 0; 887a424c39SIngo Weinhold static int gControlDeviceFD = -1; 897a424c39SIngo Weinhold 907a424c39SIngo Weinhold static thread_id gLockOwner = -1; 917a424c39SIngo Weinhold static int32 gLockOwnerNesting = 0; 927a424c39SIngo Weinhold 93bc4f7808SAxel Dörfler 947a424c39SIngo Weinhold // lock_driver 957a424c39SIngo Weinhold void 967a424c39SIngo Weinhold lock_driver() 977a424c39SIngo Weinhold { 987a424c39SIngo Weinhold thread_id thread = find_thread(NULL); 997a424c39SIngo Weinhold if (gLockOwner != thread) { 1007a424c39SIngo Weinhold LOCK(driverlock); 1017a424c39SIngo Weinhold gLockOwner = thread; 1027a424c39SIngo Weinhold } 1037a424c39SIngo Weinhold gLockOwnerNesting++; 1047a424c39SIngo Weinhold } 1057a424c39SIngo Weinhold 106bc4f7808SAxel Dörfler 1077a424c39SIngo Weinhold // unlock_driver 1087a424c39SIngo Weinhold void 1097a424c39SIngo Weinhold unlock_driver() 1107a424c39SIngo Weinhold { 1117a424c39SIngo Weinhold thread_id thread = find_thread(NULL); 1127a424c39SIngo Weinhold if (gLockOwner == thread && --gLockOwnerNesting == 0) { 1137a424c39SIngo Weinhold gLockOwner = -1; 1147a424c39SIngo Weinhold UNLOCK(driverlock); 1157a424c39SIngo Weinhold } 1167a424c39SIngo Weinhold } 1177a424c39SIngo Weinhold 1187a424c39SIngo Weinhold 1197a424c39SIngo Weinhold // is_valid_device_index 1207a424c39SIngo Weinhold static inline 1217a424c39SIngo Weinhold bool 1227a424c39SIngo Weinhold is_valid_device_index(int32 index) 1237a424c39SIngo Weinhold { 1247a424c39SIngo Weinhold return (index >= 0 && index < kDeviceCount); 1257a424c39SIngo Weinhold } 1267a424c39SIngo Weinhold 127bc4f7808SAxel Dörfler 1287a424c39SIngo Weinhold // is_valid_data_device_index 1297a424c39SIngo Weinhold static inline 1307a424c39SIngo Weinhold bool 1317a424c39SIngo Weinhold is_valid_data_device_index(int32 index) 1327a424c39SIngo Weinhold { 1337a424c39SIngo Weinhold return (is_valid_device_index(index) && index != kControlDevice); 1347a424c39SIngo Weinhold } 1357a424c39SIngo Weinhold 136bc4f7808SAxel Dörfler 137bc4f7808SAxel Dörfler // dev_index_for_path 138bc4f7808SAxel Dörfler static 139bc4f7808SAxel Dörfler int 140bc4f7808SAxel Dörfler dev_index_for_path(const char *path) 141bc4f7808SAxel Dörfler { 142bc4f7808SAxel Dörfler int i; 143bc4f7808SAxel Dörfler for (i = 0; i < kDeviceCount; i++) { 144bc4f7808SAxel Dörfler if (!strcmp(path, gDeviceInfos[i].device_path)) 145bc4f7808SAxel Dörfler return i; 146bc4f7808SAxel Dörfler } 147bc4f7808SAxel Dörfler return -1; 148bc4f7808SAxel Dörfler } 149bc4f7808SAxel Dörfler 150bc4f7808SAxel Dörfler 1517a424c39SIngo Weinhold // clear_device_info 1527a424c39SIngo Weinhold static 1537a424c39SIngo Weinhold void 1547a424c39SIngo Weinhold clear_device_info(int32 index) 1557a424c39SIngo Weinhold { 156*8545b8bdSAxel Dörfler TRACE(("virtualdrive: clear_device_info(%ld)\n", index)); 157bc4f7808SAxel Dörfler 1587a424c39SIngo Weinhold device_info &info = gDeviceInfos[index]; 159bc4f7808SAxel Dörfler info.open_count = 0; 1607a424c39SIngo Weinhold info.fd = -1; 1617a424c39SIngo Weinhold info.size = 0; 1627a424c39SIngo Weinhold info.unused = (index != kDeviceCount - 1); 1637a424c39SIngo Weinhold info.registered = !info.unused; 1647a424c39SIngo Weinhold info.file[0] = '\0'; 165bc4f7808SAxel Dörfler info.device_path = sVirtualDriveName[index]; 1667a424c39SIngo Weinhold info.geometry.read_only = true; 1677a424c39SIngo Weinhold } 1687a424c39SIngo Weinhold 169bc4f7808SAxel Dörfler 1707a424c39SIngo Weinhold // init_device_info 1717a424c39SIngo Weinhold static 1727a424c39SIngo Weinhold status_t 1737a424c39SIngo Weinhold init_device_info(int32 index, virtual_drive_info *initInfo) 1747a424c39SIngo Weinhold { 1757a424c39SIngo Weinhold if (!is_valid_data_device_index(index) || !initInfo) 1767a424c39SIngo Weinhold return B_BAD_VALUE; 177bc4f7808SAxel Dörfler 1787a424c39SIngo Weinhold device_info &info = gDeviceInfos[index]; 1797a424c39SIngo Weinhold if (!info.unused) 1807a424c39SIngo Weinhold return B_BAD_VALUE; 181bc4f7808SAxel Dörfler 1827a424c39SIngo Weinhold bool readOnly = (initInfo->use_geometry && initInfo->geometry.read_only); 183bc4f7808SAxel Dörfler 1847a424c39SIngo Weinhold // open the file 1857a424c39SIngo Weinhold int fd = open(initInfo->file_name, (readOnly ? O_RDONLY : O_RDWR)); 1867a424c39SIngo Weinhold if (fd < 0) 1877a424c39SIngo Weinhold return errno; 188bc4f7808SAxel Dörfler 1897a424c39SIngo Weinhold status_t error = B_OK; 190bc4f7808SAxel Dörfler 1917a424c39SIngo Weinhold // get the file size 1927a424c39SIngo Weinhold off_t fileSize = 0; 1937a424c39SIngo Weinhold struct stat st; 1947a424c39SIngo Weinhold if (fstat(fd, &st) == 0) 1957a424c39SIngo Weinhold fileSize = st.st_size; 1967a424c39SIngo Weinhold else 1977a424c39SIngo Weinhold error = errno; 198bc4f7808SAxel Dörfler 1997a424c39SIngo Weinhold // If we shall use the supplied geometry, we enlarge the file, if 2007a424c39SIngo Weinhold // necessary. Otherwise we fill in the geometry according to the size of the file. 2017a424c39SIngo Weinhold off_t size = 0; 2027a424c39SIngo Weinhold if (error == B_OK) { 2037a424c39SIngo Weinhold if (initInfo->use_geometry) { 2047a424c39SIngo Weinhold // use the supplied geometry 2057a424c39SIngo Weinhold info.geometry = initInfo->geometry; 2067a424c39SIngo Weinhold size = (off_t)info.geometry.bytes_per_sector 2077a424c39SIngo Weinhold * info.geometry.sectors_per_track 2087a424c39SIngo Weinhold * info.geometry.cylinder_count 2097a424c39SIngo Weinhold * info.geometry.head_count; 2107a424c39SIngo Weinhold if (size > fileSize) { 2117a424c39SIngo Weinhold if (!readOnly) { 2127a424c39SIngo Weinhold if (ftruncate(fd, size) != 0) 2137a424c39SIngo Weinhold error = errno; 2147a424c39SIngo Weinhold } else 2157a424c39SIngo Weinhold error = B_NOT_ALLOWED; 2167a424c39SIngo Weinhold } 2177a424c39SIngo Weinhold } else { 2187a424c39SIngo Weinhold // fill in the geometry 2197a424c39SIngo Weinhold // default to 512 bytes block size 2207a424c39SIngo Weinhold uint32 blockSize = 512; 2217a424c39SIngo Weinhold // Optimally we have only 1 block per sector and only one head. 2227a424c39SIngo Weinhold // Since we have only a uint32 for the cylinder count, this won't work 2237a424c39SIngo Weinhold // for files > 2TB. So, we set the head count to the minimally possible 2247a424c39SIngo Weinhold // value. 2257a424c39SIngo Weinhold off_t blocks = fileSize / blockSize; 2267a424c39SIngo Weinhold uint32 heads = (blocks + ULONG_MAX - 1) / ULONG_MAX; 2277a424c39SIngo Weinhold if (heads == 0) 2287a424c39SIngo Weinhold heads = 1; 2297a424c39SIngo Weinhold info.geometry.bytes_per_sector = blockSize; 2307a424c39SIngo Weinhold info.geometry.sectors_per_track = 1; 2317a424c39SIngo Weinhold info.geometry.cylinder_count = blocks / heads; 2327a424c39SIngo Weinhold info.geometry.head_count = heads; 2337a424c39SIngo Weinhold info.geometry.device_type = B_DISK; // TODO: Add a new constant. 2347a424c39SIngo Weinhold info.geometry.removable = false; 2357a424c39SIngo Weinhold info.geometry.read_only = false; 2367a424c39SIngo Weinhold info.geometry.write_once = false; 2377a424c39SIngo Weinhold size = (off_t)info.geometry.bytes_per_sector 2387a424c39SIngo Weinhold * info.geometry.sectors_per_track 2397a424c39SIngo Weinhold * info.geometry.cylinder_count 2407a424c39SIngo Weinhold * info.geometry.head_count; 2417a424c39SIngo Weinhold } 2427a424c39SIngo Weinhold } 243bc4f7808SAxel Dörfler 244bc4f7808SAxel Dörfler if (error == B_OK) { 245bc4f7808SAxel Dörfler // Disable caching for underlying file! (else this driver will deadlock) 246bc4f7808SAxel Dörfler // We probably cannot resize the file once the cache has been disabled! 247bc4f7808SAxel Dörfler 248bc4f7808SAxel Dörfler // This is a special reserved ioctl() opcode not defined anywhere in 249bc4f7808SAxel Dörfler // the Be headers. 250bc4f7808SAxel Dörfler if (ioctl(fd, 10000) != 0) { 251*8545b8bdSAxel Dörfler dprintf("virtualdrive: disable caching ioctl failed\n"); 252bc4f7808SAxel Dörfler return errno; 253bc4f7808SAxel Dörfler } 254bc4f7808SAxel Dörfler } 255bc4f7808SAxel Dörfler 2567a424c39SIngo Weinhold // fill in the rest of the device_info structure 2577a424c39SIngo Weinhold if (error == B_OK) { 258bc4f7808SAxel Dörfler // open_count doesn't have to be changed here (virtualdrive_open() will do that for us) 2597a424c39SIngo Weinhold info.fd = fd; 2607a424c39SIngo Weinhold info.size = size; 2617a424c39SIngo Weinhold info.unused = false; 2627a424c39SIngo Weinhold info.registered = true; 2637a424c39SIngo Weinhold strcpy(info.file, initInfo->file_name); 264bc4f7808SAxel Dörfler info.device_path = sVirtualDriveName[index]; 2657a424c39SIngo Weinhold } else { 2667a424c39SIngo Weinhold // cleanup on error 2677a424c39SIngo Weinhold close(fd); 268bc4f7808SAxel Dörfler if (info.open_count == 0) 2697a424c39SIngo Weinhold clear_device_info(index); 2707a424c39SIngo Weinhold } 2717a424c39SIngo Weinhold return error; 2727a424c39SIngo Weinhold } 2737a424c39SIngo Weinhold 274bc4f7808SAxel Dörfler 2757a424c39SIngo Weinhold // uninit_device_info 2767a424c39SIngo Weinhold static 2777a424c39SIngo Weinhold status_t 2787a424c39SIngo Weinhold uninit_device_info(int32 index) 2797a424c39SIngo Weinhold { 2807a424c39SIngo Weinhold if (!is_valid_data_device_index(index)) 2817a424c39SIngo Weinhold return B_BAD_VALUE; 282bc4f7808SAxel Dörfler 2837a424c39SIngo Weinhold device_info &info = gDeviceInfos[index]; 2847a424c39SIngo Weinhold if (info.unused) 2857a424c39SIngo Weinhold return B_BAD_VALUE; 286bc4f7808SAxel Dörfler 2877a424c39SIngo Weinhold close(info.fd); 2887a424c39SIngo Weinhold clear_device_info(index); 2897a424c39SIngo Weinhold return B_OK; 2907a424c39SIngo Weinhold } 2917a424c39SIngo Weinhold 292bc4f7808SAxel Dörfler 293bc4f7808SAxel Dörfler // #pragma mark - 294bc4f7808SAxel Dörfler // public driver API 295bc4f7808SAxel Dörfler 296bc4f7808SAxel Dörfler 2977a424c39SIngo Weinhold status_t 2987a424c39SIngo Weinhold init_hardware(void) 2997a424c39SIngo Weinhold { 300*8545b8bdSAxel Dörfler TRACE(("virtualdrive: init_hardware\n")); 3017a424c39SIngo Weinhold return B_OK; 3027a424c39SIngo Weinhold } 3037a424c39SIngo Weinhold 3047a424c39SIngo Weinhold 3057a424c39SIngo Weinhold status_t 3067a424c39SIngo Weinhold init_driver(void) 3077a424c39SIngo Weinhold { 308*8545b8bdSAxel Dörfler TRACE(("virtualdrive: init\n")); 3097a424c39SIngo Weinhold 3107a424c39SIngo Weinhold new_lock(&driverlock, "virtualdrive lock"); 3117a424c39SIngo Weinhold 3127a424c39SIngo Weinhold // init the device infos 3137a424c39SIngo Weinhold for (int32 i = 0; i < kDeviceCount; i++) 3147a424c39SIngo Weinhold clear_device_info(i); 3157a424c39SIngo Weinhold 3167a424c39SIngo Weinhold return B_OK; 3177a424c39SIngo Weinhold } 3187a424c39SIngo Weinhold 3197a424c39SIngo Weinhold 3207a424c39SIngo Weinhold void 3217a424c39SIngo Weinhold uninit_driver(void) 3227a424c39SIngo Weinhold { 323*8545b8bdSAxel Dörfler TRACE(("virtualdrive: uninit\n")); 3247a424c39SIngo Weinhold free_lock(&driverlock); 3257a424c39SIngo Weinhold } 3267a424c39SIngo Weinhold 3277a424c39SIngo Weinhold 328bc4f7808SAxel Dörfler const char ** 329bc4f7808SAxel Dörfler publish_devices(void) 330bc4f7808SAxel Dörfler { 331*8545b8bdSAxel Dörfler TRACE(("virtualdrive: publish_devices\n")); 332bc4f7808SAxel Dörfler return sVirtualDriveName; 333bc4f7808SAxel Dörfler } 334bc4f7808SAxel Dörfler 335bc4f7808SAxel Dörfler 336bc4f7808SAxel Dörfler device_hooks * 337bc4f7808SAxel Dörfler find_device(const char* name) 338bc4f7808SAxel Dörfler { 339*8545b8bdSAxel Dörfler TRACE(("virtualdrive: find_device(%s)\n", name)); 340bc4f7808SAxel Dörfler return &sVirtualDriveHooks; 341bc4f7808SAxel Dörfler } 342bc4f7808SAxel Dörfler 343bc4f7808SAxel Dörfler 344bc4f7808SAxel Dörfler // #pragma mark - 345bc4f7808SAxel Dörfler // the device hooks 346bc4f7808SAxel Dörfler 3477a424c39SIngo Weinhold 3487a424c39SIngo Weinhold static status_t 3497a424c39SIngo Weinhold virtualdrive_open(const char *name, uint32 flags, void **cookie) 3507a424c39SIngo Weinhold { 351*8545b8bdSAxel Dörfler TRACE(("virtualdrive: open %s\n",name)); 3527a424c39SIngo Weinhold 3537a424c39SIngo Weinhold *cookie = (void *)-1; 3547a424c39SIngo Weinhold 3557a424c39SIngo Weinhold lock_driver(); 3567a424c39SIngo Weinhold 3577a424c39SIngo Weinhold int32 devIndex = dev_index_for_path(name); 3587a424c39SIngo Weinhold 359*8545b8bdSAxel Dörfler TRACE(("virtualdrive: devIndex %ld!\n", devIndex)); 3607a424c39SIngo Weinhold 3617a424c39SIngo Weinhold if (!is_valid_device_index(devIndex)) { 362*8545b8bdSAxel Dörfler TRACE(("virtualdrive: wrong index!\n")); 3637a424c39SIngo Weinhold unlock_driver(); 3647a424c39SIngo Weinhold return B_ERROR; 3657a424c39SIngo Weinhold } 3667a424c39SIngo Weinhold 3677a424c39SIngo Weinhold if (gDeviceInfos[devIndex].unused) { 368*8545b8bdSAxel Dörfler TRACE(("virtualdrive: device is unused!\n")); 3697a424c39SIngo Weinhold unlock_driver(); 3707a424c39SIngo Weinhold return B_ERROR; 3717a424c39SIngo Weinhold } 3727a424c39SIngo Weinhold 3737a424c39SIngo Weinhold if (!gDeviceInfos[devIndex].registered) { 374*8545b8bdSAxel Dörfler TRACE(("virtualdrive: device has been unregistered!\n")); 3757a424c39SIngo Weinhold unlock_driver(); 3767a424c39SIngo Weinhold return B_ERROR; 3777a424c39SIngo Weinhold } 3787a424c39SIngo Weinhold 3797a424c39SIngo Weinhold // store index in cookie 3807a424c39SIngo Weinhold *cookie = (void *)devIndex; 3817a424c39SIngo Weinhold 382bc4f7808SAxel Dörfler gDeviceInfos[devIndex].open_count++; 3837a424c39SIngo Weinhold 3847a424c39SIngo Weinhold unlock_driver(); 3857a424c39SIngo Weinhold return B_OK; 3867a424c39SIngo Weinhold } 3877a424c39SIngo Weinhold 3887a424c39SIngo Weinhold 3897a424c39SIngo Weinhold static status_t 3907a424c39SIngo Weinhold virtualdrive_close(void *cookie) 3917a424c39SIngo Weinhold { 3927a424c39SIngo Weinhold int32 devIndex = (int)cookie; 3937a424c39SIngo Weinhold 394*8545b8bdSAxel Dörfler TRACE(("virtualdrive: close() devIndex = %ld\n", devIndex)); 3957a424c39SIngo Weinhold if (!is_valid_data_device_index(devIndex)) 3967a424c39SIngo Weinhold return B_OK; 3977a424c39SIngo Weinhold 3987a424c39SIngo Weinhold lock_driver(); 3997a424c39SIngo Weinhold 400bc4f7808SAxel Dörfler gDeviceInfos[devIndex].open_count--; 401bc4f7808SAxel Dörfler if (gDeviceInfos[devIndex].open_count == 0 && !gDeviceInfos[devIndex].registered) { 4027a424c39SIngo Weinhold // The last FD is closed and the device has been unregistered. Free its info. 4037a424c39SIngo Weinhold uninit_device_info(devIndex); 4047a424c39SIngo Weinhold } 4057a424c39SIngo Weinhold 4067a424c39SIngo Weinhold unlock_driver(); 4077a424c39SIngo Weinhold 4087a424c39SIngo Weinhold return B_OK; 4097a424c39SIngo Weinhold } 4107a424c39SIngo Weinhold 4117a424c39SIngo Weinhold 4127a424c39SIngo Weinhold static status_t 4137a424c39SIngo Weinhold virtualdrive_read(void *cookie, off_t position, void *buffer, size_t *numBytes) 4147a424c39SIngo Weinhold { 415*8545b8bdSAxel Dörfler TRACE(("virtualdrive: read pos = 0x%08Lx, bytes = 0x%08lx\n", position, *numBytes)); 416*8545b8bdSAxel Dörfler 4177a424c39SIngo Weinhold // check parameters 4187a424c39SIngo Weinhold int devIndex = (int)cookie; 4197a424c39SIngo Weinhold if (devIndex == kControlDevice) { 420*8545b8bdSAxel Dörfler TRACE(("virtualdrive: reading from control device not allowed\n")); 4217a424c39SIngo Weinhold return B_NOT_ALLOWED; 4227a424c39SIngo Weinhold } 4237a424c39SIngo Weinhold if (position < 0) 4247a424c39SIngo Weinhold return B_BAD_VALUE; 425*8545b8bdSAxel Dörfler 4267a424c39SIngo Weinhold lock_driver(); 4277a424c39SIngo Weinhold device_info &info = gDeviceInfos[devIndex]; 4287a424c39SIngo Weinhold // adjust position and numBytes according to the file size 4297a424c39SIngo Weinhold if (position > info.size) 4307a424c39SIngo Weinhold position = info.size; 4317a424c39SIngo Weinhold if (position + *numBytes > info.size) 4327a424c39SIngo Weinhold *numBytes = info.size - position; 4337a424c39SIngo Weinhold // read 4347a424c39SIngo Weinhold status_t error = B_OK; 4357a424c39SIngo Weinhold ssize_t bytesRead = read_pos(info.fd, position, buffer, *numBytes); 4367a424c39SIngo Weinhold if (bytesRead < 0) 4377a424c39SIngo Weinhold error = errno; 4387a424c39SIngo Weinhold else 4397a424c39SIngo Weinhold *numBytes = bytesRead; 4407a424c39SIngo Weinhold unlock_driver(); 4417a424c39SIngo Weinhold return error; 4427a424c39SIngo Weinhold } 4437a424c39SIngo Weinhold 4447a424c39SIngo Weinhold 4457a424c39SIngo Weinhold static status_t 4467a424c39SIngo Weinhold virtualdrive_write(void *cookie, off_t position, const void *buffer, size_t *numBytes) 4477a424c39SIngo Weinhold { 448*8545b8bdSAxel Dörfler TRACE(("virtualdrive: write pos = 0x%08Lx, bytes = 0x%08lx\n", position, *numBytes)); 449*8545b8bdSAxel Dörfler 4507a424c39SIngo Weinhold // check parameters 4517a424c39SIngo Weinhold int devIndex = (int)cookie; 4527a424c39SIngo Weinhold if (devIndex == kControlDevice) { 453*8545b8bdSAxel Dörfler TRACE(("virtualdrive: writing to control device not allowed\n")); 4547a424c39SIngo Weinhold return B_NOT_ALLOWED; 4557a424c39SIngo Weinhold } 4567a424c39SIngo Weinhold if (position < 0) 4577a424c39SIngo Weinhold return B_BAD_VALUE; 458*8545b8bdSAxel Dörfler 4597a424c39SIngo Weinhold lock_driver(); 4607a424c39SIngo Weinhold device_info &info = gDeviceInfos[devIndex]; 4617a424c39SIngo Weinhold // adjust position and numBytes according to the file size 4627a424c39SIngo Weinhold if (position > info.size) 4637a424c39SIngo Weinhold position = info.size; 4647a424c39SIngo Weinhold if (position + *numBytes > info.size) 4657a424c39SIngo Weinhold *numBytes = info.size - position; 4667a424c39SIngo Weinhold // read 4677a424c39SIngo Weinhold status_t error = B_OK; 4687a424c39SIngo Weinhold ssize_t bytesRead = write_pos(info.fd, position, buffer, *numBytes); 4697a424c39SIngo Weinhold if (bytesRead < 0) 4707a424c39SIngo Weinhold error = errno; 4717a424c39SIngo Weinhold else 4727a424c39SIngo Weinhold *numBytes = bytesRead; 4737a424c39SIngo Weinhold unlock_driver(); 4747a424c39SIngo Weinhold return error; 4757a424c39SIngo Weinhold } 4767a424c39SIngo Weinhold 4777a424c39SIngo Weinhold 4787a424c39SIngo Weinhold static status_t 4797a424c39SIngo Weinhold virtualdrive_control(void *cookie, uint32 op, void *arg, size_t len) 4807a424c39SIngo Weinhold { 481*8545b8bdSAxel Dörfler TRACE(("virtualdrive: ioctl\n")); 4827a424c39SIngo Weinhold 4837a424c39SIngo Weinhold int devIndex = (int)cookie; 4847a424c39SIngo Weinhold device_info &info = gDeviceInfos[devIndex]; 4857a424c39SIngo Weinhold 4867a424c39SIngo Weinhold if (devIndex == kControlDevice || info.unused) { 4877a424c39SIngo Weinhold // control device or unused data device 4887a424c39SIngo Weinhold switch (op) { 4897a424c39SIngo Weinhold case B_GET_DEVICE_SIZE: 4907a424c39SIngo Weinhold case B_SET_NONBLOCKING_IO: 4917a424c39SIngo Weinhold case B_SET_BLOCKING_IO: 4927a424c39SIngo Weinhold case B_GET_READ_STATUS: 4937a424c39SIngo Weinhold case B_GET_WRITE_STATUS: 4947a424c39SIngo Weinhold case B_GET_ICON: 4957a424c39SIngo Weinhold case B_GET_GEOMETRY: 4967a424c39SIngo Weinhold case B_GET_BIOS_GEOMETRY: 4977a424c39SIngo Weinhold case B_GET_MEDIA_STATUS: 4987a424c39SIngo Weinhold case B_SET_UNINTERRUPTABLE_IO: 4997a424c39SIngo Weinhold case B_SET_INTERRUPTABLE_IO: 5007a424c39SIngo Weinhold case B_FLUSH_DRIVE_CACHE: 5017a424c39SIngo Weinhold case B_GET_BIOS_DRIVE_ID: 5027a424c39SIngo Weinhold case B_GET_DRIVER_FOR_DEVICE: 5037a424c39SIngo Weinhold case B_SET_DEVICE_SIZE: 5047a424c39SIngo Weinhold case B_SET_PARTITION: 5057a424c39SIngo Weinhold case B_FORMAT_DEVICE: 5067a424c39SIngo Weinhold case B_EJECT_DEVICE: 5077a424c39SIngo Weinhold case B_LOAD_MEDIA: 5087a424c39SIngo Weinhold case B_GET_NEXT_OPEN_DEVICE: 509*8545b8bdSAxel Dörfler TRACE(("virtualdrive: another ioctl: %lx (%lu)\n", op, op)); 5107a424c39SIngo Weinhold return B_BAD_VALUE; 511bc4f7808SAxel Dörfler 5127a424c39SIngo Weinhold case VIRTUAL_DRIVE_REGISTER_FILE: 5137a424c39SIngo Weinhold { 514*8545b8bdSAxel Dörfler TRACE(("virtualdrive: VIRTUAL_DRIVE_REGISTER_FILE\n")); 515bc4f7808SAxel Dörfler 5167a424c39SIngo Weinhold virtual_drive_info *driveInfo = (virtual_drive_info *)arg; 517bc4f7808SAxel Dörfler if (devIndex != kControlDevice || driveInfo == NULL 518bc4f7808SAxel Dörfler || driveInfo->magic != VIRTUAL_DRIVE_MAGIC 519bc4f7808SAxel Dörfler || driveInfo->drive_info_size != sizeof(virtual_drive_info)) 520bc4f7808SAxel Dörfler return B_BAD_VALUE; 521bc4f7808SAxel Dörfler 5227a424c39SIngo Weinhold status_t error = B_ERROR; 523bc4f7808SAxel Dörfler int32 i; 524bc4f7808SAxel Dörfler 5257a424c39SIngo Weinhold lock_driver(); 526bc4f7808SAxel Dörfler 527bc4f7808SAxel Dörfler // first, look if we already have opened that file and see 528bc4f7808SAxel Dörfler // if it's available to us which happens when it has been 529bc4f7808SAxel Dörfler // halted but is still in use by other components 530bc4f7808SAxel Dörfler for (i = 0; i < kDataDeviceCount; i++) { 531bc4f7808SAxel Dörfler if (!gDeviceInfos[i].unused 532bc4f7808SAxel Dörfler && gDeviceInfos[i].fd == -1 533bc4f7808SAxel Dörfler && !gDeviceInfos[i].registered 534bc4f7808SAxel Dörfler && !strcmp(gDeviceInfos[i].file, driveInfo->file_name)) { 535bc4f7808SAxel Dörfler // mark device as unused, so that init_device_info() will succeed 536bc4f7808SAxel Dörfler gDeviceInfos[i].unused = true; 537bc4f7808SAxel Dörfler error = B_OK; 538bc4f7808SAxel Dörfler break; 539bc4f7808SAxel Dörfler } 540bc4f7808SAxel Dörfler } 541bc4f7808SAxel Dörfler 542bc4f7808SAxel Dörfler if (error != B_OK) { 543bc4f7808SAxel Dörfler // find an unused data device 544bc4f7808SAxel Dörfler for (i = 0; i < kDataDeviceCount; i++) { 5457a424c39SIngo Weinhold if (gDeviceInfos[i].unused) { 546bc4f7808SAxel Dörfler error = B_OK; 547bc4f7808SAxel Dörfler break; 548bc4f7808SAxel Dörfler } 549bc4f7808SAxel Dörfler } 550bc4f7808SAxel Dörfler } 551bc4f7808SAxel Dörfler 552bc4f7808SAxel Dörfler if (error == B_OK) { 553bc4f7808SAxel Dörfler // we found a device slot, let's initialize it 5547a424c39SIngo Weinhold error = init_device_info(i, driveInfo); 5557a424c39SIngo Weinhold if (error == B_OK) { 5567a424c39SIngo Weinhold // return the device path 5577a424c39SIngo Weinhold strcpy(driveInfo->device_name, "/dev/"); 558bc4f7808SAxel Dörfler strcat(driveInfo->device_name, gDeviceInfos[i].device_path); 559bc4f7808SAxel Dörfler 5607a424c39SIngo Weinhold // on the first registration we need to open the 5617a424c39SIngo Weinhold // control device to stay loaded 5627a424c39SIngo Weinhold if (gRegistrationCount++ == 0) { 5637a424c39SIngo Weinhold char path[B_PATH_NAME_LENGTH]; 5647a424c39SIngo Weinhold strcpy(path, "/dev/"); 5657a424c39SIngo Weinhold strcat(path, info.device_path); 5667a424c39SIngo Weinhold gControlDeviceFD = open(path, O_RDONLY); 5677a424c39SIngo Weinhold } 5687a424c39SIngo Weinhold } 5697a424c39SIngo Weinhold } 570bc4f7808SAxel Dörfler 5717a424c39SIngo Weinhold unlock_driver(); 5727a424c39SIngo Weinhold return error; 5737a424c39SIngo Weinhold } 5747a424c39SIngo Weinhold case VIRTUAL_DRIVE_UNREGISTER_FILE: 5757a424c39SIngo Weinhold case VIRTUAL_DRIVE_GET_INFO: 576*8545b8bdSAxel Dörfler TRACE(("virtualdrive: VIRTUAL_DRIVE_UNREGISTER_FILE/" 577*8545b8bdSAxel Dörfler "VIRTUAL_DRIVE_GET_INFO on control device\n")); 578bc4f7808SAxel Dörfler // these are called on used data files only! 5797a424c39SIngo Weinhold return B_BAD_VALUE; 580bc4f7808SAxel Dörfler 5817a424c39SIngo Weinhold default: 582*8545b8bdSAxel Dörfler TRACE(("virtualdrive: unknown ioctl: %lx (%lu)\n", op, op)); 5837a424c39SIngo Weinhold return B_BAD_VALUE; 5847a424c39SIngo Weinhold } 5857a424c39SIngo Weinhold } else { 5867a424c39SIngo Weinhold // used data device 5877a424c39SIngo Weinhold switch (op) { 5887a424c39SIngo Weinhold case B_GET_DEVICE_SIZE: 589*8545b8bdSAxel Dörfler TRACE(("virtualdrive: B_GET_DEVICE_SIZE\n")); 5907a424c39SIngo Weinhold *(size_t*)arg = info.size; 5917a424c39SIngo Weinhold return B_OK; 5927a424c39SIngo Weinhold 5937a424c39SIngo Weinhold case B_SET_NONBLOCKING_IO: 594*8545b8bdSAxel Dörfler TRACE(("virtualdrive: B_SET_NONBLOCKING_IO\n")); 5957a424c39SIngo Weinhold return B_OK; 5967a424c39SIngo Weinhold 5977a424c39SIngo Weinhold case B_SET_BLOCKING_IO: 598*8545b8bdSAxel Dörfler TRACE(("virtualdrive: B_SET_BLOCKING_IO\n")); 5997a424c39SIngo Weinhold return B_OK; 6007a424c39SIngo Weinhold 6017a424c39SIngo Weinhold case B_GET_READ_STATUS: 602*8545b8bdSAxel Dörfler TRACE(("virtualdrive: B_GET_READ_STATUS\n")); 6037a424c39SIngo Weinhold *(bool*)arg = true; 6047a424c39SIngo Weinhold return B_OK; 6057a424c39SIngo Weinhold 6067a424c39SIngo Weinhold case B_GET_WRITE_STATUS: 607*8545b8bdSAxel Dörfler TRACE(("virtualdrive: B_GET_WRITE_STATUS\n")); 6087a424c39SIngo Weinhold *(bool*)arg = true; 6097a424c39SIngo Weinhold return B_OK; 6107a424c39SIngo Weinhold 6117a424c39SIngo Weinhold case B_GET_ICON: 612bc4f7808SAxel Dörfler { 613*8545b8bdSAxel Dörfler TRACE(("virtualdrive: B_GET_ICON\n")); 614bc4f7808SAxel Dörfler device_icon *icon = (device_icon *)arg; 615bc4f7808SAxel Dörfler 616bc4f7808SAxel Dörfler if (icon->icon_size == kPrimaryImageWidth) { 617bc4f7808SAxel Dörfler memcpy(icon->icon_data, kPrimaryImageBits, kPrimaryImageWidth * kPrimaryImageHeight); 618bc4f7808SAxel Dörfler } else if (icon->icon_size == kSecondaryImageWidth) { 619bc4f7808SAxel Dörfler memcpy(icon->icon_data, kSecondaryImageBits, kSecondaryImageWidth * kSecondaryImageHeight); 620bc4f7808SAxel Dörfler } else 621bc4f7808SAxel Dörfler return B_ERROR; 622bc4f7808SAxel Dörfler 6237a424c39SIngo Weinhold return B_OK; 624bc4f7808SAxel Dörfler } 6257a424c39SIngo Weinhold 6267a424c39SIngo Weinhold case B_GET_GEOMETRY: 627*8545b8bdSAxel Dörfler TRACE(("virtualdrive: B_GET_GEOMETRY\n")); 6287a424c39SIngo Weinhold *(device_geometry *)arg = info.geometry; 6297a424c39SIngo Weinhold return B_OK; 6307a424c39SIngo Weinhold 6317a424c39SIngo Weinhold case B_GET_BIOS_GEOMETRY: 6327a424c39SIngo Weinhold { 633*8545b8bdSAxel Dörfler TRACE(("virtualdrive: B_GET_BIOS_GEOMETRY\n")); 6347a424c39SIngo Weinhold device_geometry *dg = (device_geometry *)arg; 6357a424c39SIngo Weinhold dg->bytes_per_sector = 512; 6367a424c39SIngo Weinhold dg->sectors_per_track = info.size / (512 * 1024); 6377a424c39SIngo Weinhold dg->cylinder_count = 1024; 6387a424c39SIngo Weinhold dg->head_count = 1; 6397a424c39SIngo Weinhold dg->device_type = info.geometry.device_type; 6407a424c39SIngo Weinhold dg->removable = info.geometry.removable; 6417a424c39SIngo Weinhold dg->read_only = info.geometry.read_only; 6427a424c39SIngo Weinhold dg->write_once = info.geometry.write_once; 6437a424c39SIngo Weinhold return B_OK; 6447a424c39SIngo Weinhold } 6457a424c39SIngo Weinhold 6467a424c39SIngo Weinhold case B_GET_MEDIA_STATUS: 647*8545b8bdSAxel Dörfler TRACE(("virtualdrive: B_GET_MEDIA_STATUS\n")); 6487a424c39SIngo Weinhold *(status_t*)arg = B_NO_ERROR; 6497a424c39SIngo Weinhold return B_OK; 6507a424c39SIngo Weinhold 6517a424c39SIngo Weinhold case B_SET_UNINTERRUPTABLE_IO: 652*8545b8bdSAxel Dörfler TRACE(("virtualdrive: B_SET_UNINTERRUPTABLE_IO\n")); 6537a424c39SIngo Weinhold return B_OK; 6547a424c39SIngo Weinhold 6557a424c39SIngo Weinhold case B_SET_INTERRUPTABLE_IO: 656*8545b8bdSAxel Dörfler TRACE(("virtualdrive: B_SET_INTERRUPTABLE_IO\n")); 6577a424c39SIngo Weinhold return B_OK; 6587a424c39SIngo Weinhold 6597a424c39SIngo Weinhold case B_FLUSH_DRIVE_CACHE: 660*8545b8bdSAxel Dörfler TRACE(("virtualdrive: B_FLUSH_DRIVE_CACHE\n")); 6617a424c39SIngo Weinhold return B_OK; 6627a424c39SIngo Weinhold 6637a424c39SIngo Weinhold case B_GET_BIOS_DRIVE_ID: 664*8545b8bdSAxel Dörfler TRACE(("virtualdrive: B_GET_BIOS_DRIVE_ID\n")); 6657a424c39SIngo Weinhold *(uint8*)arg = 0xF8; 6667a424c39SIngo Weinhold return B_OK; 6677a424c39SIngo Weinhold 6687a424c39SIngo Weinhold case B_GET_DRIVER_FOR_DEVICE: 6697a424c39SIngo Weinhold case B_SET_DEVICE_SIZE: 6707a424c39SIngo Weinhold case B_SET_PARTITION: 6717a424c39SIngo Weinhold case B_FORMAT_DEVICE: 6727a424c39SIngo Weinhold case B_EJECT_DEVICE: 6737a424c39SIngo Weinhold case B_LOAD_MEDIA: 6747a424c39SIngo Weinhold case B_GET_NEXT_OPEN_DEVICE: 675*8545b8bdSAxel Dörfler TRACE(("virtualdrive: another ioctl: %lx (%lu)\n", op, op)); 6767a424c39SIngo Weinhold return B_BAD_VALUE; 677bc4f7808SAxel Dörfler 6787a424c39SIngo Weinhold case VIRTUAL_DRIVE_REGISTER_FILE: 679*8545b8bdSAxel Dörfler TRACE(("virtualdrive: VIRTUAL_DRIVE_REGISTER_FILE (data)\n")); 6807a424c39SIngo Weinhold return B_BAD_VALUE; 6817a424c39SIngo Weinhold case VIRTUAL_DRIVE_UNREGISTER_FILE: 6827a424c39SIngo Weinhold { 683*8545b8bdSAxel Dörfler TRACE(("virtualdrive: VIRTUAL_DRIVE_UNREGISTER_FILE\n")); 6847a424c39SIngo Weinhold lock_driver(); 685bc4f7808SAxel Dörfler 686bc4f7808SAxel Dörfler bool immediately = (bool)arg; 687bc4f7808SAxel Dörfler bool wasRegistered = info.registered; 688bc4f7808SAxel Dörfler 689bc4f7808SAxel Dörfler info.registered = false; 690bc4f7808SAxel Dörfler 6917a424c39SIngo Weinhold // on the last unregistration we need to close the 6927a424c39SIngo Weinhold // control device 6937a424c39SIngo Weinhold if (wasRegistered && --gRegistrationCount == 0) { 6947a424c39SIngo Weinhold close(gControlDeviceFD); 6957a424c39SIngo Weinhold gControlDeviceFD = -1; 6967a424c39SIngo Weinhold } 697bc4f7808SAxel Dörfler 698bc4f7808SAxel Dörfler // if we "immediately" is true, we will stop our service immediately 699bc4f7808SAxel Dörfler // and close the underlying file, open it for other uses 700bc4f7808SAxel Dörfler if (immediately) { 701*8545b8bdSAxel Dörfler TRACE(("virtualdrive: close file descriptor\n")); 702bc4f7808SAxel Dörfler // we cannot use uninit_device_info() here, since that does 703bc4f7808SAxel Dörfler // a little too much and would open the device for other 704bc4f7808SAxel Dörfler // uses. 705bc4f7808SAxel Dörfler close(info.fd); 706bc4f7808SAxel Dörfler info.fd = -1; 707bc4f7808SAxel Dörfler } 708bc4f7808SAxel Dörfler 7097a424c39SIngo Weinhold unlock_driver(); 7107a424c39SIngo Weinhold return B_OK; 7117a424c39SIngo Weinhold } 7127a424c39SIngo Weinhold case VIRTUAL_DRIVE_GET_INFO: 7137a424c39SIngo Weinhold { 714*8545b8bdSAxel Dörfler TRACE(("virtualdrive: VIRTUAL_DRIVE_GET_INFO\n")); 715bc4f7808SAxel Dörfler 7167a424c39SIngo Weinhold virtual_drive_info *driveInfo = (virtual_drive_info *)arg; 717bc4f7808SAxel Dörfler if (driveInfo == NULL 718bc4f7808SAxel Dörfler || driveInfo->magic != VIRTUAL_DRIVE_MAGIC 719bc4f7808SAxel Dörfler || driveInfo->drive_info_size != sizeof(virtual_drive_info)) 720bc4f7808SAxel Dörfler return B_BAD_VALUE; 721bc4f7808SAxel Dörfler 7227a424c39SIngo Weinhold strcpy(driveInfo->file_name, info.file); 7237a424c39SIngo Weinhold strcpy(driveInfo->device_name, "/dev/"); 7247a424c39SIngo Weinhold strcat(driveInfo->device_name, info.device_path); 7257a424c39SIngo Weinhold driveInfo->geometry = info.geometry; 7267a424c39SIngo Weinhold driveInfo->use_geometry = true; 727bc4f7808SAxel Dörfler driveInfo->halted = info.fd == -1; 7287a424c39SIngo Weinhold return B_OK; 7297a424c39SIngo Weinhold } 730bc4f7808SAxel Dörfler 7317a424c39SIngo Weinhold default: 732*8545b8bdSAxel Dörfler TRACE(("virtualdrive: unknown ioctl: %lx (%lu)\n", op, op)); 7337a424c39SIngo Weinhold return B_BAD_VALUE; 7347a424c39SIngo Weinhold } 7357a424c39SIngo Weinhold } 7367a424c39SIngo Weinhold 7377a424c39SIngo Weinhold } 7387a424c39SIngo Weinhold 7397a424c39SIngo Weinhold 7407a424c39SIngo Weinhold static status_t 7417a424c39SIngo Weinhold virtualdrive_free(void *cookie) 7427a424c39SIngo Weinhold { 743*8545b8bdSAxel Dörfler TRACE(("virtualdrive: free cookie()\n")); 7447a424c39SIngo Weinhold return B_OK; 7457a424c39SIngo Weinhold } 7467a424c39SIngo Weinhold 7477a424c39SIngo Weinhold 7487a424c39SIngo Weinhold /* ----- 7497a424c39SIngo Weinhold function pointers for the device hooks entry points 7507a424c39SIngo Weinhold ----- */ 7517a424c39SIngo Weinhold 752bc4f7808SAxel Dörfler device_hooks sVirtualDriveHooks = { 7537a424c39SIngo Weinhold virtualdrive_open, /* -> open entry point */ 7547a424c39SIngo Weinhold virtualdrive_close, /* -> close entry point */ 7557a424c39SIngo Weinhold virtualdrive_free, /* -> free cookie */ 7567a424c39SIngo Weinhold virtualdrive_control, /* -> control entry point */ 7577a424c39SIngo Weinhold virtualdrive_read, /* -> read entry point */ 7587a424c39SIngo Weinhold virtualdrive_write /* -> write entry point */ 7597a424c39SIngo Weinhold }; 7607a424c39SIngo Weinhold 761