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 418545b8bdSAxel Dörfler //#define TRACE(x) dprintf x 428545b8bdSAxel 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 { 1568545b8bdSAxel 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 248*b4f58628SAxel Dörfler // This applies to BeOS only: 249*b4f58628SAxel Dörfler // Work around a bug in BFS: the file is not synced before the cache is 250*b4f58628SAxel Dörfler // turned off, and thus causing possible inconsistencies. 251*b4f58628SAxel Dörfler // Unfortunately, this only solves one half of the issue; there is 252*b4f58628SAxel Dörfler // no way to remove the blocks in the cache, so changes made to the 253*b4f58628SAxel Dörfler // image have the chance to get lost. 254*b4f58628SAxel Dörfler fsync(fd); 255*b4f58628SAxel Dörfler 256bc4f7808SAxel Dörfler // This is a special reserved ioctl() opcode not defined anywhere in 257bc4f7808SAxel Dörfler // the Be headers. 258bc4f7808SAxel Dörfler if (ioctl(fd, 10000) != 0) { 2598545b8bdSAxel Dörfler dprintf("virtualdrive: disable caching ioctl failed\n"); 260bc4f7808SAxel Dörfler return errno; 261bc4f7808SAxel Dörfler } 262bc4f7808SAxel Dörfler } 263bc4f7808SAxel Dörfler 2647a424c39SIngo Weinhold // fill in the rest of the device_info structure 2657a424c39SIngo Weinhold if (error == B_OK) { 266bc4f7808SAxel Dörfler // open_count doesn't have to be changed here (virtualdrive_open() will do that for us) 2677a424c39SIngo Weinhold info.fd = fd; 2687a424c39SIngo Weinhold info.size = size; 2697a424c39SIngo Weinhold info.unused = false; 2707a424c39SIngo Weinhold info.registered = true; 2717a424c39SIngo Weinhold strcpy(info.file, initInfo->file_name); 272bc4f7808SAxel Dörfler info.device_path = sVirtualDriveName[index]; 2737a424c39SIngo Weinhold } else { 2747a424c39SIngo Weinhold // cleanup on error 2757a424c39SIngo Weinhold close(fd); 276bc4f7808SAxel Dörfler if (info.open_count == 0) 2777a424c39SIngo Weinhold clear_device_info(index); 2787a424c39SIngo Weinhold } 2797a424c39SIngo Weinhold return error; 2807a424c39SIngo Weinhold } 2817a424c39SIngo Weinhold 282bc4f7808SAxel Dörfler 2837a424c39SIngo Weinhold // uninit_device_info 2847a424c39SIngo Weinhold static 2857a424c39SIngo Weinhold status_t 2867a424c39SIngo Weinhold uninit_device_info(int32 index) 2877a424c39SIngo Weinhold { 2887a424c39SIngo Weinhold if (!is_valid_data_device_index(index)) 2897a424c39SIngo Weinhold return B_BAD_VALUE; 290bc4f7808SAxel Dörfler 2917a424c39SIngo Weinhold device_info &info = gDeviceInfos[index]; 2927a424c39SIngo Weinhold if (info.unused) 2937a424c39SIngo Weinhold return B_BAD_VALUE; 294bc4f7808SAxel Dörfler 2957a424c39SIngo Weinhold close(info.fd); 2967a424c39SIngo Weinhold clear_device_info(index); 2977a424c39SIngo Weinhold return B_OK; 2987a424c39SIngo Weinhold } 2997a424c39SIngo Weinhold 300bc4f7808SAxel Dörfler 301bc4f7808SAxel Dörfler // #pragma mark - 302bc4f7808SAxel Dörfler // public driver API 303bc4f7808SAxel Dörfler 304bc4f7808SAxel Dörfler 3057a424c39SIngo Weinhold status_t 3067a424c39SIngo Weinhold init_hardware(void) 3077a424c39SIngo Weinhold { 3088545b8bdSAxel Dörfler TRACE(("virtualdrive: init_hardware\n")); 3097a424c39SIngo Weinhold return B_OK; 3107a424c39SIngo Weinhold } 3117a424c39SIngo Weinhold 3127a424c39SIngo Weinhold 3137a424c39SIngo Weinhold status_t 3147a424c39SIngo Weinhold init_driver(void) 3157a424c39SIngo Weinhold { 3168545b8bdSAxel Dörfler TRACE(("virtualdrive: init\n")); 3177a424c39SIngo Weinhold 3187a424c39SIngo Weinhold new_lock(&driverlock, "virtualdrive lock"); 3197a424c39SIngo Weinhold 3207a424c39SIngo Weinhold // init the device infos 3217a424c39SIngo Weinhold for (int32 i = 0; i < kDeviceCount; i++) 3227a424c39SIngo Weinhold clear_device_info(i); 3237a424c39SIngo Weinhold 3247a424c39SIngo Weinhold return B_OK; 3257a424c39SIngo Weinhold } 3267a424c39SIngo Weinhold 3277a424c39SIngo Weinhold 3287a424c39SIngo Weinhold void 3297a424c39SIngo Weinhold uninit_driver(void) 3307a424c39SIngo Weinhold { 3318545b8bdSAxel Dörfler TRACE(("virtualdrive: uninit\n")); 3327a424c39SIngo Weinhold free_lock(&driverlock); 3337a424c39SIngo Weinhold } 3347a424c39SIngo Weinhold 3357a424c39SIngo Weinhold 336bc4f7808SAxel Dörfler const char ** 337bc4f7808SAxel Dörfler publish_devices(void) 338bc4f7808SAxel Dörfler { 3398545b8bdSAxel Dörfler TRACE(("virtualdrive: publish_devices\n")); 340bc4f7808SAxel Dörfler return sVirtualDriveName; 341bc4f7808SAxel Dörfler } 342bc4f7808SAxel Dörfler 343bc4f7808SAxel Dörfler 344bc4f7808SAxel Dörfler device_hooks * 345bc4f7808SAxel Dörfler find_device(const char* name) 346bc4f7808SAxel Dörfler { 3478545b8bdSAxel Dörfler TRACE(("virtualdrive: find_device(%s)\n", name)); 348bc4f7808SAxel Dörfler return &sVirtualDriveHooks; 349bc4f7808SAxel Dörfler } 350bc4f7808SAxel Dörfler 351bc4f7808SAxel Dörfler 352bc4f7808SAxel Dörfler // #pragma mark - 353bc4f7808SAxel Dörfler // the device hooks 354bc4f7808SAxel Dörfler 3557a424c39SIngo Weinhold 3567a424c39SIngo Weinhold static status_t 3577a424c39SIngo Weinhold virtualdrive_open(const char *name, uint32 flags, void **cookie) 3587a424c39SIngo Weinhold { 3598545b8bdSAxel Dörfler TRACE(("virtualdrive: open %s\n",name)); 3607a424c39SIngo Weinhold 3617a424c39SIngo Weinhold *cookie = (void *)-1; 3627a424c39SIngo Weinhold 3637a424c39SIngo Weinhold lock_driver(); 3647a424c39SIngo Weinhold 3657a424c39SIngo Weinhold int32 devIndex = dev_index_for_path(name); 3667a424c39SIngo Weinhold 3678545b8bdSAxel Dörfler TRACE(("virtualdrive: devIndex %ld!\n", devIndex)); 3687a424c39SIngo Weinhold 3697a424c39SIngo Weinhold if (!is_valid_device_index(devIndex)) { 3708545b8bdSAxel Dörfler TRACE(("virtualdrive: wrong index!\n")); 3717a424c39SIngo Weinhold unlock_driver(); 3727a424c39SIngo Weinhold return B_ERROR; 3737a424c39SIngo Weinhold } 3747a424c39SIngo Weinhold 3757a424c39SIngo Weinhold if (gDeviceInfos[devIndex].unused) { 3768545b8bdSAxel Dörfler TRACE(("virtualdrive: device is unused!\n")); 3777a424c39SIngo Weinhold unlock_driver(); 3787a424c39SIngo Weinhold return B_ERROR; 3797a424c39SIngo Weinhold } 3807a424c39SIngo Weinhold 3817a424c39SIngo Weinhold if (!gDeviceInfos[devIndex].registered) { 3828545b8bdSAxel Dörfler TRACE(("virtualdrive: device has been unregistered!\n")); 3837a424c39SIngo Weinhold unlock_driver(); 3847a424c39SIngo Weinhold return B_ERROR; 3857a424c39SIngo Weinhold } 3867a424c39SIngo Weinhold 3877a424c39SIngo Weinhold // store index in cookie 3887a424c39SIngo Weinhold *cookie = (void *)devIndex; 3897a424c39SIngo Weinhold 390bc4f7808SAxel Dörfler gDeviceInfos[devIndex].open_count++; 3917a424c39SIngo Weinhold 3927a424c39SIngo Weinhold unlock_driver(); 3937a424c39SIngo Weinhold return B_OK; 3947a424c39SIngo Weinhold } 3957a424c39SIngo Weinhold 3967a424c39SIngo Weinhold 3977a424c39SIngo Weinhold static status_t 3987a424c39SIngo Weinhold virtualdrive_close(void *cookie) 3997a424c39SIngo Weinhold { 4007a424c39SIngo Weinhold int32 devIndex = (int)cookie; 4017a424c39SIngo Weinhold 4028545b8bdSAxel Dörfler TRACE(("virtualdrive: close() devIndex = %ld\n", devIndex)); 4037a424c39SIngo Weinhold if (!is_valid_data_device_index(devIndex)) 4047a424c39SIngo Weinhold return B_OK; 4057a424c39SIngo Weinhold 4067a424c39SIngo Weinhold lock_driver(); 4077a424c39SIngo Weinhold 408bc4f7808SAxel Dörfler gDeviceInfos[devIndex].open_count--; 409bc4f7808SAxel Dörfler if (gDeviceInfos[devIndex].open_count == 0 && !gDeviceInfos[devIndex].registered) { 4107a424c39SIngo Weinhold // The last FD is closed and the device has been unregistered. Free its info. 4117a424c39SIngo Weinhold uninit_device_info(devIndex); 4127a424c39SIngo Weinhold } 4137a424c39SIngo Weinhold 4147a424c39SIngo Weinhold unlock_driver(); 4157a424c39SIngo Weinhold 4167a424c39SIngo Weinhold return B_OK; 4177a424c39SIngo Weinhold } 4187a424c39SIngo Weinhold 4197a424c39SIngo Weinhold 4207a424c39SIngo Weinhold static status_t 4217a424c39SIngo Weinhold virtualdrive_read(void *cookie, off_t position, void *buffer, size_t *numBytes) 4227a424c39SIngo Weinhold { 4238545b8bdSAxel Dörfler TRACE(("virtualdrive: read pos = 0x%08Lx, bytes = 0x%08lx\n", position, *numBytes)); 4248545b8bdSAxel Dörfler 4257a424c39SIngo Weinhold // check parameters 4267a424c39SIngo Weinhold int devIndex = (int)cookie; 4277a424c39SIngo Weinhold if (devIndex == kControlDevice) { 4288545b8bdSAxel Dörfler TRACE(("virtualdrive: reading from control device not allowed\n")); 4297a424c39SIngo Weinhold return B_NOT_ALLOWED; 4307a424c39SIngo Weinhold } 4317a424c39SIngo Weinhold if (position < 0) 4327a424c39SIngo Weinhold return B_BAD_VALUE; 4338545b8bdSAxel Dörfler 4347a424c39SIngo Weinhold lock_driver(); 4357a424c39SIngo Weinhold device_info &info = gDeviceInfos[devIndex]; 4367a424c39SIngo Weinhold // adjust position and numBytes according to the file size 4377a424c39SIngo Weinhold if (position > info.size) 4387a424c39SIngo Weinhold position = info.size; 4397a424c39SIngo Weinhold if (position + *numBytes > info.size) 4407a424c39SIngo Weinhold *numBytes = info.size - position; 4417a424c39SIngo Weinhold // read 4427a424c39SIngo Weinhold status_t error = B_OK; 4437a424c39SIngo Weinhold ssize_t bytesRead = read_pos(info.fd, position, buffer, *numBytes); 4447a424c39SIngo Weinhold if (bytesRead < 0) 4457a424c39SIngo Weinhold error = errno; 4467a424c39SIngo Weinhold else 4477a424c39SIngo Weinhold *numBytes = bytesRead; 4487a424c39SIngo Weinhold unlock_driver(); 4497a424c39SIngo Weinhold return error; 4507a424c39SIngo Weinhold } 4517a424c39SIngo Weinhold 4527a424c39SIngo Weinhold 4537a424c39SIngo Weinhold static status_t 4547a424c39SIngo Weinhold virtualdrive_write(void *cookie, off_t position, const void *buffer, size_t *numBytes) 4557a424c39SIngo Weinhold { 4568545b8bdSAxel Dörfler TRACE(("virtualdrive: write pos = 0x%08Lx, bytes = 0x%08lx\n", position, *numBytes)); 4578545b8bdSAxel Dörfler 4587a424c39SIngo Weinhold // check parameters 4597a424c39SIngo Weinhold int devIndex = (int)cookie; 4607a424c39SIngo Weinhold if (devIndex == kControlDevice) { 4618545b8bdSAxel Dörfler TRACE(("virtualdrive: writing to control device not allowed\n")); 4627a424c39SIngo Weinhold return B_NOT_ALLOWED; 4637a424c39SIngo Weinhold } 4647a424c39SIngo Weinhold if (position < 0) 4657a424c39SIngo Weinhold return B_BAD_VALUE; 4668545b8bdSAxel Dörfler 4677a424c39SIngo Weinhold lock_driver(); 4687a424c39SIngo Weinhold device_info &info = gDeviceInfos[devIndex]; 4697a424c39SIngo Weinhold // adjust position and numBytes according to the file size 4707a424c39SIngo Weinhold if (position > info.size) 4717a424c39SIngo Weinhold position = info.size; 4727a424c39SIngo Weinhold if (position + *numBytes > info.size) 4737a424c39SIngo Weinhold *numBytes = info.size - position; 4747a424c39SIngo Weinhold // read 4757a424c39SIngo Weinhold status_t error = B_OK; 4767a424c39SIngo Weinhold ssize_t bytesRead = write_pos(info.fd, position, buffer, *numBytes); 4777a424c39SIngo Weinhold if (bytesRead < 0) 4787a424c39SIngo Weinhold error = errno; 4797a424c39SIngo Weinhold else 4807a424c39SIngo Weinhold *numBytes = bytesRead; 4817a424c39SIngo Weinhold unlock_driver(); 4827a424c39SIngo Weinhold return error; 4837a424c39SIngo Weinhold } 4847a424c39SIngo Weinhold 4857a424c39SIngo Weinhold 4867a424c39SIngo Weinhold static status_t 4877a424c39SIngo Weinhold virtualdrive_control(void *cookie, uint32 op, void *arg, size_t len) 4887a424c39SIngo Weinhold { 4898545b8bdSAxel Dörfler TRACE(("virtualdrive: ioctl\n")); 4907a424c39SIngo Weinhold 4917a424c39SIngo Weinhold int devIndex = (int)cookie; 4927a424c39SIngo Weinhold device_info &info = gDeviceInfos[devIndex]; 4937a424c39SIngo Weinhold 4947a424c39SIngo Weinhold if (devIndex == kControlDevice || info.unused) { 4957a424c39SIngo Weinhold // control device or unused data device 4967a424c39SIngo Weinhold switch (op) { 4977a424c39SIngo Weinhold case B_GET_DEVICE_SIZE: 4987a424c39SIngo Weinhold case B_SET_NONBLOCKING_IO: 4997a424c39SIngo Weinhold case B_SET_BLOCKING_IO: 5007a424c39SIngo Weinhold case B_GET_READ_STATUS: 5017a424c39SIngo Weinhold case B_GET_WRITE_STATUS: 5027a424c39SIngo Weinhold case B_GET_ICON: 5037a424c39SIngo Weinhold case B_GET_GEOMETRY: 5047a424c39SIngo Weinhold case B_GET_BIOS_GEOMETRY: 5057a424c39SIngo Weinhold case B_GET_MEDIA_STATUS: 5067a424c39SIngo Weinhold case B_SET_UNINTERRUPTABLE_IO: 5077a424c39SIngo Weinhold case B_SET_INTERRUPTABLE_IO: 5087a424c39SIngo Weinhold case B_FLUSH_DRIVE_CACHE: 5097a424c39SIngo Weinhold case B_GET_BIOS_DRIVE_ID: 5107a424c39SIngo Weinhold case B_GET_DRIVER_FOR_DEVICE: 5117a424c39SIngo Weinhold case B_SET_DEVICE_SIZE: 5127a424c39SIngo Weinhold case B_SET_PARTITION: 5137a424c39SIngo Weinhold case B_FORMAT_DEVICE: 5147a424c39SIngo Weinhold case B_EJECT_DEVICE: 5157a424c39SIngo Weinhold case B_LOAD_MEDIA: 5167a424c39SIngo Weinhold case B_GET_NEXT_OPEN_DEVICE: 5178545b8bdSAxel Dörfler TRACE(("virtualdrive: another ioctl: %lx (%lu)\n", op, op)); 5187a424c39SIngo Weinhold return B_BAD_VALUE; 519bc4f7808SAxel Dörfler 5207a424c39SIngo Weinhold case VIRTUAL_DRIVE_REGISTER_FILE: 5217a424c39SIngo Weinhold { 5228545b8bdSAxel Dörfler TRACE(("virtualdrive: VIRTUAL_DRIVE_REGISTER_FILE\n")); 523bc4f7808SAxel Dörfler 5247a424c39SIngo Weinhold virtual_drive_info *driveInfo = (virtual_drive_info *)arg; 525bc4f7808SAxel Dörfler if (devIndex != kControlDevice || driveInfo == NULL 526bc4f7808SAxel Dörfler || driveInfo->magic != VIRTUAL_DRIVE_MAGIC 527bc4f7808SAxel Dörfler || driveInfo->drive_info_size != sizeof(virtual_drive_info)) 528bc4f7808SAxel Dörfler return B_BAD_VALUE; 529bc4f7808SAxel Dörfler 5307a424c39SIngo Weinhold status_t error = B_ERROR; 531bc4f7808SAxel Dörfler int32 i; 532bc4f7808SAxel Dörfler 5337a424c39SIngo Weinhold lock_driver(); 534bc4f7808SAxel Dörfler 535bc4f7808SAxel Dörfler // first, look if we already have opened that file and see 536bc4f7808SAxel Dörfler // if it's available to us which happens when it has been 537bc4f7808SAxel Dörfler // halted but is still in use by other components 538bc4f7808SAxel Dörfler for (i = 0; i < kDataDeviceCount; i++) { 539bc4f7808SAxel Dörfler if (!gDeviceInfos[i].unused 540bc4f7808SAxel Dörfler && gDeviceInfos[i].fd == -1 541bc4f7808SAxel Dörfler && !gDeviceInfos[i].registered 542bc4f7808SAxel Dörfler && !strcmp(gDeviceInfos[i].file, driveInfo->file_name)) { 543bc4f7808SAxel Dörfler // mark device as unused, so that init_device_info() will succeed 544bc4f7808SAxel Dörfler gDeviceInfos[i].unused = true; 545bc4f7808SAxel Dörfler error = B_OK; 546bc4f7808SAxel Dörfler break; 547bc4f7808SAxel Dörfler } 548bc4f7808SAxel Dörfler } 549bc4f7808SAxel Dörfler 550bc4f7808SAxel Dörfler if (error != B_OK) { 551bc4f7808SAxel Dörfler // find an unused data device 552bc4f7808SAxel Dörfler for (i = 0; i < kDataDeviceCount; i++) { 5537a424c39SIngo Weinhold if (gDeviceInfos[i].unused) { 554bc4f7808SAxel Dörfler error = B_OK; 555bc4f7808SAxel Dörfler break; 556bc4f7808SAxel Dörfler } 557bc4f7808SAxel Dörfler } 558bc4f7808SAxel Dörfler } 559bc4f7808SAxel Dörfler 560bc4f7808SAxel Dörfler if (error == B_OK) { 561bc4f7808SAxel Dörfler // we found a device slot, let's initialize it 5627a424c39SIngo Weinhold error = init_device_info(i, driveInfo); 5637a424c39SIngo Weinhold if (error == B_OK) { 5647a424c39SIngo Weinhold // return the device path 5657a424c39SIngo Weinhold strcpy(driveInfo->device_name, "/dev/"); 566bc4f7808SAxel Dörfler strcat(driveInfo->device_name, gDeviceInfos[i].device_path); 567bc4f7808SAxel Dörfler 5687a424c39SIngo Weinhold // on the first registration we need to open the 5697a424c39SIngo Weinhold // control device to stay loaded 5707a424c39SIngo Weinhold if (gRegistrationCount++ == 0) { 5717a424c39SIngo Weinhold char path[B_PATH_NAME_LENGTH]; 5727a424c39SIngo Weinhold strcpy(path, "/dev/"); 5737a424c39SIngo Weinhold strcat(path, info.device_path); 5747a424c39SIngo Weinhold gControlDeviceFD = open(path, O_RDONLY); 5757a424c39SIngo Weinhold } 5767a424c39SIngo Weinhold } 5777a424c39SIngo Weinhold } 578bc4f7808SAxel Dörfler 5797a424c39SIngo Weinhold unlock_driver(); 5807a424c39SIngo Weinhold return error; 5817a424c39SIngo Weinhold } 5827a424c39SIngo Weinhold case VIRTUAL_DRIVE_UNREGISTER_FILE: 5837a424c39SIngo Weinhold case VIRTUAL_DRIVE_GET_INFO: 5848545b8bdSAxel Dörfler TRACE(("virtualdrive: VIRTUAL_DRIVE_UNREGISTER_FILE/" 5858545b8bdSAxel Dörfler "VIRTUAL_DRIVE_GET_INFO on control device\n")); 586bc4f7808SAxel Dörfler // these are called on used data files only! 5877a424c39SIngo Weinhold return B_BAD_VALUE; 588bc4f7808SAxel Dörfler 5897a424c39SIngo Weinhold default: 5908545b8bdSAxel Dörfler TRACE(("virtualdrive: unknown ioctl: %lx (%lu)\n", op, op)); 5917a424c39SIngo Weinhold return B_BAD_VALUE; 5927a424c39SIngo Weinhold } 5937a424c39SIngo Weinhold } else { 5947a424c39SIngo Weinhold // used data device 5957a424c39SIngo Weinhold switch (op) { 5967a424c39SIngo Weinhold case B_GET_DEVICE_SIZE: 5978545b8bdSAxel Dörfler TRACE(("virtualdrive: B_GET_DEVICE_SIZE\n")); 5987a424c39SIngo Weinhold *(size_t*)arg = info.size; 5997a424c39SIngo Weinhold return B_OK; 6007a424c39SIngo Weinhold 6017a424c39SIngo Weinhold case B_SET_NONBLOCKING_IO: 6028545b8bdSAxel Dörfler TRACE(("virtualdrive: B_SET_NONBLOCKING_IO\n")); 6037a424c39SIngo Weinhold return B_OK; 6047a424c39SIngo Weinhold 6057a424c39SIngo Weinhold case B_SET_BLOCKING_IO: 6068545b8bdSAxel Dörfler TRACE(("virtualdrive: B_SET_BLOCKING_IO\n")); 6077a424c39SIngo Weinhold return B_OK; 6087a424c39SIngo Weinhold 6097a424c39SIngo Weinhold case B_GET_READ_STATUS: 6108545b8bdSAxel Dörfler TRACE(("virtualdrive: B_GET_READ_STATUS\n")); 6117a424c39SIngo Weinhold *(bool*)arg = true; 6127a424c39SIngo Weinhold return B_OK; 6137a424c39SIngo Weinhold 6147a424c39SIngo Weinhold case B_GET_WRITE_STATUS: 6158545b8bdSAxel Dörfler TRACE(("virtualdrive: B_GET_WRITE_STATUS\n")); 6167a424c39SIngo Weinhold *(bool*)arg = true; 6177a424c39SIngo Weinhold return B_OK; 6187a424c39SIngo Weinhold 6197a424c39SIngo Weinhold case B_GET_ICON: 620bc4f7808SAxel Dörfler { 6218545b8bdSAxel Dörfler TRACE(("virtualdrive: B_GET_ICON\n")); 622bc4f7808SAxel Dörfler device_icon *icon = (device_icon *)arg; 623bc4f7808SAxel Dörfler 624bc4f7808SAxel Dörfler if (icon->icon_size == kPrimaryImageWidth) { 625bc4f7808SAxel Dörfler memcpy(icon->icon_data, kPrimaryImageBits, kPrimaryImageWidth * kPrimaryImageHeight); 626bc4f7808SAxel Dörfler } else if (icon->icon_size == kSecondaryImageWidth) { 627bc4f7808SAxel Dörfler memcpy(icon->icon_data, kSecondaryImageBits, kSecondaryImageWidth * kSecondaryImageHeight); 628bc4f7808SAxel Dörfler } else 629bc4f7808SAxel Dörfler return B_ERROR; 630bc4f7808SAxel Dörfler 6317a424c39SIngo Weinhold return B_OK; 632bc4f7808SAxel Dörfler } 6337a424c39SIngo Weinhold 6347a424c39SIngo Weinhold case B_GET_GEOMETRY: 6358545b8bdSAxel Dörfler TRACE(("virtualdrive: B_GET_GEOMETRY\n")); 6367a424c39SIngo Weinhold *(device_geometry *)arg = info.geometry; 6377a424c39SIngo Weinhold return B_OK; 6387a424c39SIngo Weinhold 6397a424c39SIngo Weinhold case B_GET_BIOS_GEOMETRY: 6407a424c39SIngo Weinhold { 6418545b8bdSAxel Dörfler TRACE(("virtualdrive: B_GET_BIOS_GEOMETRY\n")); 6427a424c39SIngo Weinhold device_geometry *dg = (device_geometry *)arg; 6437a424c39SIngo Weinhold dg->bytes_per_sector = 512; 6447a424c39SIngo Weinhold dg->sectors_per_track = info.size / (512 * 1024); 6457a424c39SIngo Weinhold dg->cylinder_count = 1024; 6467a424c39SIngo Weinhold dg->head_count = 1; 6477a424c39SIngo Weinhold dg->device_type = info.geometry.device_type; 6487a424c39SIngo Weinhold dg->removable = info.geometry.removable; 6497a424c39SIngo Weinhold dg->read_only = info.geometry.read_only; 6507a424c39SIngo Weinhold dg->write_once = info.geometry.write_once; 6517a424c39SIngo Weinhold return B_OK; 6527a424c39SIngo Weinhold } 6537a424c39SIngo Weinhold 6547a424c39SIngo Weinhold case B_GET_MEDIA_STATUS: 6558545b8bdSAxel Dörfler TRACE(("virtualdrive: B_GET_MEDIA_STATUS\n")); 6567a424c39SIngo Weinhold *(status_t*)arg = B_NO_ERROR; 6577a424c39SIngo Weinhold return B_OK; 6587a424c39SIngo Weinhold 6597a424c39SIngo Weinhold case B_SET_UNINTERRUPTABLE_IO: 6608545b8bdSAxel Dörfler TRACE(("virtualdrive: B_SET_UNINTERRUPTABLE_IO\n")); 6617a424c39SIngo Weinhold return B_OK; 6627a424c39SIngo Weinhold 6637a424c39SIngo Weinhold case B_SET_INTERRUPTABLE_IO: 6648545b8bdSAxel Dörfler TRACE(("virtualdrive: B_SET_INTERRUPTABLE_IO\n")); 6657a424c39SIngo Weinhold return B_OK; 6667a424c39SIngo Weinhold 6677a424c39SIngo Weinhold case B_FLUSH_DRIVE_CACHE: 6688545b8bdSAxel Dörfler TRACE(("virtualdrive: B_FLUSH_DRIVE_CACHE\n")); 6697a424c39SIngo Weinhold return B_OK; 6707a424c39SIngo Weinhold 6717a424c39SIngo Weinhold case B_GET_BIOS_DRIVE_ID: 6728545b8bdSAxel Dörfler TRACE(("virtualdrive: B_GET_BIOS_DRIVE_ID\n")); 6737a424c39SIngo Weinhold *(uint8*)arg = 0xF8; 6747a424c39SIngo Weinhold return B_OK; 6757a424c39SIngo Weinhold 6767a424c39SIngo Weinhold case B_GET_DRIVER_FOR_DEVICE: 6777a424c39SIngo Weinhold case B_SET_DEVICE_SIZE: 6787a424c39SIngo Weinhold case B_SET_PARTITION: 6797a424c39SIngo Weinhold case B_FORMAT_DEVICE: 6807a424c39SIngo Weinhold case B_EJECT_DEVICE: 6817a424c39SIngo Weinhold case B_LOAD_MEDIA: 6827a424c39SIngo Weinhold case B_GET_NEXT_OPEN_DEVICE: 6838545b8bdSAxel Dörfler TRACE(("virtualdrive: another ioctl: %lx (%lu)\n", op, op)); 6847a424c39SIngo Weinhold return B_BAD_VALUE; 685bc4f7808SAxel Dörfler 6867a424c39SIngo Weinhold case VIRTUAL_DRIVE_REGISTER_FILE: 6878545b8bdSAxel Dörfler TRACE(("virtualdrive: VIRTUAL_DRIVE_REGISTER_FILE (data)\n")); 6887a424c39SIngo Weinhold return B_BAD_VALUE; 6897a424c39SIngo Weinhold case VIRTUAL_DRIVE_UNREGISTER_FILE: 6907a424c39SIngo Weinhold { 6918545b8bdSAxel Dörfler TRACE(("virtualdrive: VIRTUAL_DRIVE_UNREGISTER_FILE\n")); 6927a424c39SIngo Weinhold lock_driver(); 693bc4f7808SAxel Dörfler 694bc4f7808SAxel Dörfler bool immediately = (bool)arg; 695bc4f7808SAxel Dörfler bool wasRegistered = info.registered; 696bc4f7808SAxel Dörfler 697bc4f7808SAxel Dörfler info.registered = false; 698bc4f7808SAxel Dörfler 6997a424c39SIngo Weinhold // on the last unregistration we need to close the 7007a424c39SIngo Weinhold // control device 7017a424c39SIngo Weinhold if (wasRegistered && --gRegistrationCount == 0) { 7027a424c39SIngo Weinhold close(gControlDeviceFD); 7037a424c39SIngo Weinhold gControlDeviceFD = -1; 7047a424c39SIngo Weinhold } 705bc4f7808SAxel Dörfler 706bc4f7808SAxel Dörfler // if we "immediately" is true, we will stop our service immediately 707bc4f7808SAxel Dörfler // and close the underlying file, open it for other uses 708bc4f7808SAxel Dörfler if (immediately) { 7098545b8bdSAxel Dörfler TRACE(("virtualdrive: close file descriptor\n")); 710bc4f7808SAxel Dörfler // we cannot use uninit_device_info() here, since that does 711bc4f7808SAxel Dörfler // a little too much and would open the device for other 712bc4f7808SAxel Dörfler // uses. 713bc4f7808SAxel Dörfler close(info.fd); 714bc4f7808SAxel Dörfler info.fd = -1; 715bc4f7808SAxel Dörfler } 716bc4f7808SAxel Dörfler 7177a424c39SIngo Weinhold unlock_driver(); 7187a424c39SIngo Weinhold return B_OK; 7197a424c39SIngo Weinhold } 7207a424c39SIngo Weinhold case VIRTUAL_DRIVE_GET_INFO: 7217a424c39SIngo Weinhold { 7228545b8bdSAxel Dörfler TRACE(("virtualdrive: VIRTUAL_DRIVE_GET_INFO\n")); 723bc4f7808SAxel Dörfler 7247a424c39SIngo Weinhold virtual_drive_info *driveInfo = (virtual_drive_info *)arg; 725bc4f7808SAxel Dörfler if (driveInfo == NULL 726bc4f7808SAxel Dörfler || driveInfo->magic != VIRTUAL_DRIVE_MAGIC 727bc4f7808SAxel Dörfler || driveInfo->drive_info_size != sizeof(virtual_drive_info)) 728bc4f7808SAxel Dörfler return B_BAD_VALUE; 729bc4f7808SAxel Dörfler 7307a424c39SIngo Weinhold strcpy(driveInfo->file_name, info.file); 7317a424c39SIngo Weinhold strcpy(driveInfo->device_name, "/dev/"); 7327a424c39SIngo Weinhold strcat(driveInfo->device_name, info.device_path); 7337a424c39SIngo Weinhold driveInfo->geometry = info.geometry; 7347a424c39SIngo Weinhold driveInfo->use_geometry = true; 735bc4f7808SAxel Dörfler driveInfo->halted = info.fd == -1; 7367a424c39SIngo Weinhold return B_OK; 7377a424c39SIngo Weinhold } 738bc4f7808SAxel Dörfler 7397a424c39SIngo Weinhold default: 7408545b8bdSAxel Dörfler TRACE(("virtualdrive: unknown ioctl: %lx (%lu)\n", op, op)); 7417a424c39SIngo Weinhold return B_BAD_VALUE; 7427a424c39SIngo Weinhold } 7437a424c39SIngo Weinhold } 7447a424c39SIngo Weinhold 7457a424c39SIngo Weinhold } 7467a424c39SIngo Weinhold 7477a424c39SIngo Weinhold 7487a424c39SIngo Weinhold static status_t 7497a424c39SIngo Weinhold virtualdrive_free(void *cookie) 7507a424c39SIngo Weinhold { 7518545b8bdSAxel Dörfler TRACE(("virtualdrive: free cookie()\n")); 7527a424c39SIngo Weinhold return B_OK; 7537a424c39SIngo Weinhold } 7547a424c39SIngo Weinhold 7557a424c39SIngo Weinhold 7567a424c39SIngo Weinhold /* ----- 7577a424c39SIngo Weinhold function pointers for the device hooks entry points 7587a424c39SIngo Weinhold ----- */ 7597a424c39SIngo Weinhold 760bc4f7808SAxel Dörfler device_hooks sVirtualDriveHooks = { 7617a424c39SIngo Weinhold virtualdrive_open, /* -> open entry point */ 7627a424c39SIngo Weinhold virtualdrive_close, /* -> close entry point */ 7637a424c39SIngo Weinhold virtualdrive_free, /* -> free cookie */ 7647a424c39SIngo Weinhold virtualdrive_control, /* -> control entry point */ 7657a424c39SIngo Weinhold virtualdrive_read, /* -> read entry point */ 7667a424c39SIngo Weinhold virtualdrive_write /* -> write entry point */ 7677a424c39SIngo Weinhold }; 7687a424c39SIngo Weinhold 769