xref: /haiku/src/add-ons/kernel/drivers/disk/scsi/scsi_disk/scsi_disk.cpp (revision 5d4fe207c7662e9bb6e40abb154ec474d25474da)
1fc128a4cSAxel Dörfler /*
28b1d35bdSDavid Sebek  * Copyright 2021 David Sebek, dasebek@gmail.com
38b1d35bdSDavid Sebek  * Copyright 2008-2013 Axel Dörfler, axeld@pinc-software.de
48b1d35bdSDavid Sebek  * Copyright 2002/03 Thomas Kurschel
58b1d35bdSDavid Sebek  * All rights reserved. Distributed under the terms of the MIT License.
6fc128a4cSAxel Dörfler  */
7fc128a4cSAxel Dörfler 
8960c56aeSAxel Dörfler 
9fc128a4cSAxel Dörfler /*!	Peripheral driver to handle any kind of SCSI disks,
10fc128a4cSAxel Dörfler 	i.e. hard disk and floopy disks (ZIP etc.)
11fc128a4cSAxel Dörfler 
12fc128a4cSAxel Dörfler 	Much work is done by scsi_periph and block_io.
13fc128a4cSAxel Dörfler 
14fc128a4cSAxel Dörfler 	You'll find das_... all over the place. This stands for
15fc128a4cSAxel Dörfler 	"Direct Access Storage" which is the official SCSI name for
16fc128a4cSAxel Dörfler 	normal (floppy/hard/ZIP)-disk drives.
17fc128a4cSAxel Dörfler */
18fc128a4cSAxel Dörfler 
19fc128a4cSAxel Dörfler 
20fc128a4cSAxel Dörfler #include "scsi_disk.h"
21fc128a4cSAxel Dörfler 
22fc128a4cSAxel Dörfler #include <string.h>
23fc128a4cSAxel Dörfler #include <stdlib.h>
24fc128a4cSAxel Dörfler 
2599086aa3SAxel Dörfler #include <AutoDeleter.h>
2699086aa3SAxel Dörfler 
279b9cb227SAxel Dörfler #include <fs/devfs.h>
2899086aa3SAxel Dörfler #include <util/fs_trim_support.h>
299b9cb227SAxel Dörfler 
300715529bSIngo Weinhold #include "dma_resources.h"
310715529bSIngo Weinhold #include "IORequest.h"
320715529bSIngo Weinhold #include "IOSchedulerSimple.h"
330715529bSIngo Weinhold 
34fc128a4cSAxel Dörfler 
35fc128a4cSAxel Dörfler //#define TRACE_SCSI_DISK
36fc128a4cSAxel Dörfler #ifdef TRACE_SCSI_DISK
37fc128a4cSAxel Dörfler #	define TRACE(x...) dprintf("scsi_disk: " x)
38fc128a4cSAxel Dörfler #else
39fc128a4cSAxel Dörfler #	define TRACE(x...) ;
40fc128a4cSAxel Dörfler #endif
41fc128a4cSAxel Dörfler 
42fc128a4cSAxel Dörfler 
4306ba3f0aSAxel Dörfler static const uint8 kDriveIcon[] = {
4406ba3f0aSAxel Dörfler 	0x6e, 0x63, 0x69, 0x66, 0x08, 0x03, 0x01, 0x00, 0x00, 0x02, 0x00, 0x16,
4506ba3f0aSAxel Dörfler 	0x02, 0x3c, 0xc7, 0xee, 0x38, 0x9b, 0xc0, 0xba, 0x16, 0x57, 0x3e, 0x39,
4606ba3f0aSAxel Dörfler 	0xb0, 0x49, 0x77, 0xc8, 0x42, 0xad, 0xc7, 0x00, 0xff, 0xff, 0xd3, 0x02,
4706ba3f0aSAxel Dörfler 	0x00, 0x06, 0x02, 0x3c, 0x96, 0x32, 0x3a, 0x4d, 0x3f, 0xba, 0xfc, 0x01,
4806ba3f0aSAxel Dörfler 	0x3d, 0x5a, 0x97, 0x4b, 0x57, 0xa5, 0x49, 0x84, 0x4d, 0x00, 0x47, 0x47,
4906ba3f0aSAxel Dörfler 	0x47, 0xff, 0xa5, 0xa0, 0xa0, 0x02, 0x00, 0x16, 0x02, 0xbc, 0x59, 0x2f,
5006ba3f0aSAxel Dörfler 	0xbb, 0x29, 0xa7, 0x3c, 0x0c, 0xe4, 0xbd, 0x0b, 0x7c, 0x48, 0x92, 0xc0,
5106ba3f0aSAxel Dörfler 	0x4b, 0x79, 0x66, 0x00, 0x7d, 0xff, 0xd4, 0x02, 0x00, 0x06, 0x02, 0x38,
5206ba3f0aSAxel Dörfler 	0xdb, 0xb4, 0x39, 0x97, 0x33, 0xbc, 0x4a, 0x33, 0x3b, 0xa5, 0x42, 0x48,
5306ba3f0aSAxel Dörfler 	0x6e, 0x66, 0x49, 0xee, 0x7b, 0x00, 0x59, 0x67, 0x56, 0xff, 0xeb, 0xb2,
5406ba3f0aSAxel Dörfler 	0xb2, 0x03, 0xa7, 0xff, 0x00, 0x03, 0xff, 0x00, 0x00, 0x04, 0x01, 0x80,
5506ba3f0aSAxel Dörfler 	0x07, 0x0a, 0x06, 0x22, 0x3c, 0x22, 0x49, 0x44, 0x5b, 0x5a, 0x3e, 0x5a,
5606ba3f0aSAxel Dörfler 	0x31, 0x39, 0x25, 0x0a, 0x04, 0x22, 0x3c, 0x44, 0x4b, 0x5a, 0x31, 0x39,
5706ba3f0aSAxel Dörfler 	0x25, 0x0a, 0x04, 0x44, 0x4b, 0x44, 0x5b, 0x5a, 0x3e, 0x5a, 0x31, 0x0a,
5806ba3f0aSAxel Dörfler 	0x04, 0x22, 0x3c, 0x22, 0x49, 0x44, 0x5b, 0x44, 0x4b, 0x08, 0x02, 0x27,
5906ba3f0aSAxel Dörfler 	0x43, 0xb8, 0x14, 0xc1, 0xf1, 0x08, 0x02, 0x26, 0x43, 0x29, 0x44, 0x0a,
6006ba3f0aSAxel Dörfler 	0x05, 0x44, 0x5d, 0x49, 0x5d, 0x60, 0x3e, 0x5a, 0x3b, 0x5b, 0x3f, 0x08,
6106ba3f0aSAxel Dörfler 	0x0a, 0x07, 0x01, 0x06, 0x00, 0x0a, 0x00, 0x01, 0x00, 0x10, 0x01, 0x17,
6206ba3f0aSAxel Dörfler 	0x84, 0x00, 0x04, 0x0a, 0x01, 0x01, 0x01, 0x00, 0x0a, 0x02, 0x01, 0x02,
6306ba3f0aSAxel Dörfler 	0x00, 0x0a, 0x03, 0x01, 0x03, 0x00, 0x0a, 0x04, 0x01, 0x04, 0x10, 0x01,
6406ba3f0aSAxel Dörfler 	0x17, 0x85, 0x20, 0x04, 0x0a, 0x06, 0x01, 0x05, 0x30, 0x24, 0xb3, 0x99,
6506ba3f0aSAxel Dörfler 	0x01, 0x17, 0x82, 0x00, 0x04, 0x0a, 0x05, 0x01, 0x05, 0x30, 0x20, 0xb2,
6606ba3f0aSAxel Dörfler 	0xe6, 0x01, 0x17, 0x82, 0x00, 0x04
6706ba3f0aSAxel Dörfler };
6806ba3f0aSAxel Dörfler 
6906ba3f0aSAxel Dörfler 
70fc128a4cSAxel Dörfler static scsi_periph_interface* sSCSIPeripheral;
71fc128a4cSAxel Dörfler static device_manager_info* sDeviceManager;
72fc128a4cSAxel Dörfler 
73fc128a4cSAxel Dörfler 
74fc128a4cSAxel Dörfler static status_t
update_capacity(das_driver_info * device)7524593e2cSAxel Dörfler update_capacity(das_driver_info* device)
76fc128a4cSAxel Dörfler {
77fc128a4cSAxel Dörfler 	TRACE("update_capacity()\n");
78fc128a4cSAxel Dörfler 
79fc128a4cSAxel Dörfler 	scsi_ccb *ccb = device->scsi->alloc_ccb(device->scsi_device);
80fc128a4cSAxel Dörfler 	if (ccb == NULL)
81fc128a4cSAxel Dörfler 		return B_NO_MEMORY;
82fc128a4cSAxel Dörfler 
83fc128a4cSAxel Dörfler 	status_t status = sSCSIPeripheral->check_capacity(
84fc128a4cSAxel Dörfler 		device->scsi_periph_device, ccb);
85fc128a4cSAxel Dörfler 
86fc128a4cSAxel Dörfler 	device->scsi->free_ccb(ccb);
87fc128a4cSAxel Dörfler 
88fc128a4cSAxel Dörfler 	return status;
89fc128a4cSAxel Dörfler }
90fc128a4cSAxel Dörfler 
91fc128a4cSAxel Dörfler 
92fc128a4cSAxel Dörfler static status_t
get_geometry(das_handle * handle,device_geometry * geometry)9324593e2cSAxel Dörfler get_geometry(das_handle* handle, device_geometry* geometry)
94fc128a4cSAxel Dörfler {
9524593e2cSAxel Dörfler 	das_driver_info* info = handle->info;
96fc128a4cSAxel Dörfler 
9724593e2cSAxel Dörfler 	status_t status = update_capacity(info);
989b9cb227SAxel Dörfler 	if (status != B_OK)
99fc128a4cSAxel Dörfler 		return status;
100fc128a4cSAxel Dörfler 
1019b9cb227SAxel Dörfler 	devfs_compute_geometry_size(geometry, info->capacity, info->block_size);
102688acf41SJérôme Duval 	geometry->bytes_per_physical_sector = info->physical_block_size;
1039b9cb227SAxel Dörfler 
104fc128a4cSAxel Dörfler 	geometry->device_type = B_DISK;
10524593e2cSAxel Dörfler 	geometry->removable = info->removable;
106fc128a4cSAxel Dörfler 
107fc128a4cSAxel Dörfler 	// TBD: for all but CD-ROMs, read mode sense - medium type
108fc128a4cSAxel Dörfler 	// (bit 7 of block device specific parameter for Optical Memory Block Device)
109fc128a4cSAxel Dörfler 	// (same for Direct-Access Block Devices)
110fc128a4cSAxel Dörfler 	// (same for write-once block devices)
111fc128a4cSAxel Dörfler 	// (same for optical memory block devices)
112fc128a4cSAxel Dörfler 	geometry->read_only = false;
113fc128a4cSAxel Dörfler 	geometry->write_once = false;
114fc128a4cSAxel Dörfler 
115f87871e3SJérôme Duval 	TRACE("scsi_disk: get_geometry(): %" B_PRId32 ", %" B_PRId32 ", %" B_PRId32
116688acf41SJérôme Duval 		", %" B_PRId32 ", %d, %d, %d, %d, %" B_PRId32 "\n", geometry->bytes_per_sector,
117f87871e3SJérôme Duval 		geometry->sectors_per_track, geometry->cylinder_count,
118f87871e3SJérôme Duval 		geometry->head_count, geometry->device_type,
119688acf41SJérôme Duval 		geometry->removable, geometry->read_only, geometry->write_once,
120688acf41SJérôme Duval 		geometry->bytes_per_physical_sector);
121fc128a4cSAxel Dörfler 
122fc128a4cSAxel Dörfler 	return B_OK;
123fc128a4cSAxel Dörfler }
124fc128a4cSAxel Dörfler 
125fc128a4cSAxel Dörfler 
126fc128a4cSAxel Dörfler static status_t
load_eject(das_driver_info * device,bool load)12724593e2cSAxel Dörfler load_eject(das_driver_info *device, bool load)
128fc128a4cSAxel Dörfler {
129fc128a4cSAxel Dörfler 	TRACE("load_eject()\n");
130fc128a4cSAxel Dörfler 
131fc128a4cSAxel Dörfler 	scsi_ccb *ccb = device->scsi->alloc_ccb(device->scsi_device);
132fc128a4cSAxel Dörfler 	if (ccb == NULL)
133fc128a4cSAxel Dörfler 		return B_NO_MEMORY;
134fc128a4cSAxel Dörfler 
135fc128a4cSAxel Dörfler 	err_res result = sSCSIPeripheral->send_start_stop(
136fc128a4cSAxel Dörfler 		device->scsi_periph_device, ccb, load, true);
137fc128a4cSAxel Dörfler 
138fc128a4cSAxel Dörfler 	device->scsi->free_ccb(ccb);
139fc128a4cSAxel Dörfler 
140fc128a4cSAxel Dörfler 	return result.error_code;
141fc128a4cSAxel Dörfler }
142fc128a4cSAxel Dörfler 
143fc128a4cSAxel Dörfler 
144fc128a4cSAxel Dörfler static status_t
synchronize_cache(das_driver_info * device)14524593e2cSAxel Dörfler synchronize_cache(das_driver_info *device)
146fc128a4cSAxel Dörfler {
147fc128a4cSAxel Dörfler 	TRACE("synchronize_cache()\n");
148fc128a4cSAxel Dörfler 
149fc128a4cSAxel Dörfler 	scsi_ccb *ccb = device->scsi->alloc_ccb(device->scsi_device);
150fc128a4cSAxel Dörfler 	if (ccb == NULL)
151fc128a4cSAxel Dörfler 		return B_NO_MEMORY;
152fc128a4cSAxel Dörfler 
153fc128a4cSAxel Dörfler 	err_res result = sSCSIPeripheral->synchronize_cache(
154fc128a4cSAxel Dörfler 		device->scsi_periph_device, ccb);
155fc128a4cSAxel Dörfler 
156fc128a4cSAxel Dörfler 	device->scsi->free_ccb(ccb);
157fc128a4cSAxel Dörfler 
158fc128a4cSAxel Dörfler 	return result.error_code;
159fc128a4cSAxel Dörfler }
160fc128a4cSAxel Dörfler 
161fc128a4cSAxel Dörfler 
162960c56aeSAxel Dörfler static status_t
trim_device(das_driver_info * device,fs_trim_data * trimData)16399086aa3SAxel Dörfler trim_device(das_driver_info* device, fs_trim_data* trimData)
164960c56aeSAxel Dörfler {
165960c56aeSAxel Dörfler 	TRACE("trim_device()\n");
166960c56aeSAxel Dörfler 
1678b1d35bdSDavid Sebek 	trimData->trimmed_size = 0;
1688b1d35bdSDavid Sebek 
169960c56aeSAxel Dörfler 	scsi_ccb* request = device->scsi->alloc_ccb(device->scsi_device);
170960c56aeSAxel Dörfler 	if (request == NULL)
171960c56aeSAxel Dörfler 		return B_NO_MEMORY;
172960c56aeSAxel Dörfler 
1738b1d35bdSDavid Sebek 	scsi_block_range* blockRanges = (scsi_block_range*)
1748b1d35bdSDavid Sebek 		malloc(trimData->range_count * sizeof(*blockRanges));
1758b1d35bdSDavid Sebek 	if (blockRanges == NULL)
1768b1d35bdSDavid Sebek 		return B_NO_MEMORY;
1778b1d35bdSDavid Sebek 
1788b1d35bdSDavid Sebek 	MemoryDeleter deleter(blockRanges);
1798b1d35bdSDavid Sebek 
18099086aa3SAxel Dörfler 	for (uint32 i = 0; i < trimData->range_count; i++) {
1818b1d35bdSDavid Sebek 		uint64 startBytes = trimData->ranges[i].offset;
1828b1d35bdSDavid Sebek 		uint64 sizeBytes = trimData->ranges[i].size;
1838b1d35bdSDavid Sebek 		uint32 blockSize = device->block_size;
1848b1d35bdSDavid Sebek 
1858b1d35bdSDavid Sebek 		// Align to a block boundary so we don't discard blocks
1868b1d35bdSDavid Sebek 		// that could also contain some other data
1878b1d35bdSDavid Sebek 		uint64 blockOffset = startBytes % blockSize;
1888b1d35bdSDavid Sebek 		if (blockOffset == 0) {
1898b1d35bdSDavid Sebek 			blockRanges[i].lba = startBytes / blockSize;
1908b1d35bdSDavid Sebek 			blockRanges[i].size = sizeBytes / blockSize;
1918b1d35bdSDavid Sebek 		} else {
1928b1d35bdSDavid Sebek 			blockRanges[i].lba = startBytes / blockSize + 1;
1938b1d35bdSDavid Sebek 			blockRanges[i].size = (sizeBytes - (blockSize - blockOffset))
1948b1d35bdSDavid Sebek 				/ blockSize;
19599086aa3SAxel Dörfler 		}
1968b1d35bdSDavid Sebek 	}
1978b1d35bdSDavid Sebek 
1988b1d35bdSDavid Sebek 	// Check ranges against device capacity and make them fit
1998b1d35bdSDavid Sebek 	for (uint32 i = 0; i < trimData->range_count; i++) {
2008b1d35bdSDavid Sebek 		if (blockRanges[i].lba >= device->capacity) {
2018b1d35bdSDavid Sebek 			dprintf("trim_device(): range offset (LBA) %" B_PRIu64
2028b1d35bdSDavid Sebek 				" exceeds device capacity %" B_PRIu64 "\n",
2038b1d35bdSDavid Sebek 				blockRanges[i].lba, device->capacity);
2048b1d35bdSDavid Sebek 			return B_BAD_VALUE;
2058b1d35bdSDavid Sebek 		}
2068b1d35bdSDavid Sebek 		uint64 maxSize = device->capacity - blockRanges[i].lba;
2078b1d35bdSDavid Sebek 		blockRanges[i].size = min_c(blockRanges[i].size, maxSize);
2088b1d35bdSDavid Sebek 	}
2098b1d35bdSDavid Sebek 
2108b1d35bdSDavid Sebek 	uint64 trimmedBlocks;
211960c56aeSAxel Dörfler 	status_t status = sSCSIPeripheral->trim_device(device->scsi_periph_device,
2128b1d35bdSDavid Sebek 		request, blockRanges, trimData->range_count, &trimmedBlocks);
213960c56aeSAxel Dörfler 
214960c56aeSAxel Dörfler 	device->scsi->free_ccb(request);
2158b1d35bdSDavid Sebek 	// Some blocks may have been trimmed even if trim_device returns a failure
2168b1d35bdSDavid Sebek 	trimData->trimmed_size = trimmedBlocks * device->block_size;
217960c56aeSAxel Dörfler 
218960c56aeSAxel Dörfler 	return status;
219960c56aeSAxel Dörfler }
220960c56aeSAxel Dörfler 
221960c56aeSAxel Dörfler 
22224593e2cSAxel Dörfler static status_t
do_io(void * cookie,IOOperation * operation)22324593e2cSAxel Dörfler do_io(void* cookie, IOOperation* operation)
224fc128a4cSAxel Dörfler {
22524593e2cSAxel Dörfler 	das_driver_info* info = (das_driver_info*)cookie;
22624593e2cSAxel Dörfler 
22724593e2cSAxel Dörfler 	// TODO: this can go away as soon as we pushed the IOOperation to the upper
22824593e2cSAxel Dörfler 	// layers - we can then set scsi_periph::io() as callback for the scheduler
22924593e2cSAxel Dörfler 	size_t bytesTransferred;
23024593e2cSAxel Dörfler 	status_t status = sSCSIPeripheral->io(info->scsi_periph_device, operation,
23124593e2cSAxel Dörfler 		&bytesTransferred);
23224593e2cSAxel Dörfler 
23324593e2cSAxel Dörfler 	info->io_scheduler->OperationCompleted(operation, status, bytesTransferred);
23424593e2cSAxel Dörfler 	return status;
23524593e2cSAxel Dörfler }
23624593e2cSAxel Dörfler 
23724593e2cSAxel Dörfler 
23824593e2cSAxel Dörfler //	#pragma mark - device module API
23924593e2cSAxel Dörfler 
24024593e2cSAxel Dörfler 
24124593e2cSAxel Dörfler static status_t
das_init_device(void * _info,void ** _cookie)24224593e2cSAxel Dörfler das_init_device(void* _info, void** _cookie)
24324593e2cSAxel Dörfler {
24424593e2cSAxel Dörfler 	das_driver_info* info = (das_driver_info*)_info;
245fc128a4cSAxel Dörfler 
246fc128a4cSAxel Dörfler 	// and get (initial) capacity
247fc128a4cSAxel Dörfler 	scsi_ccb *request = info->scsi->alloc_ccb(info->scsi_device);
248fc128a4cSAxel Dörfler 	if (request == NULL)
24924593e2cSAxel Dörfler 		return B_NO_MEMORY;
250fc128a4cSAxel Dörfler 
251fc128a4cSAxel Dörfler 	sSCSIPeripheral->check_capacity(info->scsi_periph_device, request);
252fc128a4cSAxel Dörfler 	info->scsi->free_ccb(request);
25324593e2cSAxel Dörfler 
25424593e2cSAxel Dörfler 	*_cookie = info;
25524593e2cSAxel Dörfler 	return B_OK;
25624593e2cSAxel Dörfler }
25724593e2cSAxel Dörfler 
25824593e2cSAxel Dörfler 
25924593e2cSAxel Dörfler static void
das_uninit_device(void * _cookie)26024593e2cSAxel Dörfler das_uninit_device(void* _cookie)
26124593e2cSAxel Dörfler {
26224593e2cSAxel Dörfler 	das_driver_info* info = (das_driver_info*)_cookie;
26324593e2cSAxel Dörfler 
26424593e2cSAxel Dörfler 	delete info->io_scheduler;
265fc128a4cSAxel Dörfler }
266fc128a4cSAxel Dörfler 
267fc128a4cSAxel Dörfler 
268fc128a4cSAxel Dörfler static status_t
das_open(void * _info,const char * path,int openMode,void ** _cookie)26924593e2cSAxel Dörfler das_open(void* _info, const char* path, int openMode, void** _cookie)
270fc128a4cSAxel Dörfler {
27124593e2cSAxel Dörfler 	das_driver_info* info = (das_driver_info*)_info;
272fc128a4cSAxel Dörfler 
27324593e2cSAxel Dörfler 	das_handle* handle = (das_handle*)malloc(sizeof(das_handle));
274fc128a4cSAxel Dörfler 	if (handle == NULL)
275fc128a4cSAxel Dörfler 		return B_NO_MEMORY;
276fc128a4cSAxel Dörfler 
27724593e2cSAxel Dörfler 	handle->info = info;
278fc128a4cSAxel Dörfler 
27924593e2cSAxel Dörfler 	status_t status = sSCSIPeripheral->handle_open(info->scsi_periph_device,
280fc128a4cSAxel Dörfler 		(periph_handle_cookie)handle, &handle->scsi_periph_handle);
281fc128a4cSAxel Dörfler 	if (status < B_OK) {
282fc128a4cSAxel Dörfler 		free(handle);
283fc128a4cSAxel Dörfler 		return status;
284fc128a4cSAxel Dörfler 	}
285fc128a4cSAxel Dörfler 
286fc128a4cSAxel Dörfler 	*_cookie = handle;
287fc128a4cSAxel Dörfler 	return B_OK;
288fc128a4cSAxel Dörfler }
289fc128a4cSAxel Dörfler 
290fc128a4cSAxel Dörfler 
291fc128a4cSAxel Dörfler static status_t
das_close(void * cookie)29224593e2cSAxel Dörfler das_close(void* cookie)
293fc128a4cSAxel Dörfler {
29424593e2cSAxel Dörfler 	das_handle* handle = (das_handle*)cookie;
295fc128a4cSAxel Dörfler 	TRACE("close()\n");
296fc128a4cSAxel Dörfler 
297fc128a4cSAxel Dörfler 	sSCSIPeripheral->handle_close(handle->scsi_periph_handle);
298fc128a4cSAxel Dörfler 	return B_OK;
299fc128a4cSAxel Dörfler }
300fc128a4cSAxel Dörfler 
301fc128a4cSAxel Dörfler 
302fc128a4cSAxel Dörfler static status_t
das_free(void * cookie)30324593e2cSAxel Dörfler das_free(void* cookie)
304fc128a4cSAxel Dörfler {
30524593e2cSAxel Dörfler 	das_handle* handle = (das_handle*)cookie;
306fc128a4cSAxel Dörfler 	TRACE("free()\n");
307fc128a4cSAxel Dörfler 
308fc128a4cSAxel Dörfler 	sSCSIPeripheral->handle_free(handle->scsi_periph_handle);
309fc128a4cSAxel Dörfler 	free(handle);
310fc128a4cSAxel Dörfler 	return B_OK;
311fc128a4cSAxel Dörfler }
312fc128a4cSAxel Dörfler 
313fc128a4cSAxel Dörfler 
314fc128a4cSAxel Dörfler static status_t
das_io(void * cookie,io_request * request)31524593e2cSAxel Dörfler das_io(void *cookie, io_request *request)
316fc128a4cSAxel Dörfler {
31724593e2cSAxel Dörfler 	das_handle* handle = (das_handle*)cookie;
31824593e2cSAxel Dörfler 
31924593e2cSAxel Dörfler 	return handle->info->io_scheduler->ScheduleRequest(request);
32024593e2cSAxel Dörfler }
32124593e2cSAxel Dörfler 
32224593e2cSAxel Dörfler 
32324593e2cSAxel Dörfler static status_t
das_ioctl(void * cookie,uint32 op,void * buffer,size_t length)32424593e2cSAxel Dörfler das_ioctl(void* cookie, uint32 op, void* buffer, size_t length)
32524593e2cSAxel Dörfler {
32624593e2cSAxel Dörfler 	das_handle* handle = (das_handle*)cookie;
32724593e2cSAxel Dörfler 	das_driver_info* info = handle->info;
328fc128a4cSAxel Dörfler 
329f87871e3SJérôme Duval 	TRACE("ioctl(op = %" B_PRIu32 ")\n", op);
330fc128a4cSAxel Dörfler 
331fc128a4cSAxel Dörfler 	switch (op) {
332fc128a4cSAxel Dörfler 		case B_GET_DEVICE_SIZE:
333fc128a4cSAxel Dörfler 		{
33424593e2cSAxel Dörfler 			status_t status = update_capacity(info);
335fc128a4cSAxel Dörfler 			if (status != B_OK)
336fc128a4cSAxel Dörfler 				return status;
337fc128a4cSAxel Dörfler 
33824593e2cSAxel Dörfler 			size_t size = info->capacity * info->block_size;
339fc128a4cSAxel Dörfler 			return user_memcpy(buffer, &size, sizeof(size_t));
340fc128a4cSAxel Dörfler 		}
341fc128a4cSAxel Dörfler 
342fc128a4cSAxel Dörfler 		case B_GET_GEOMETRY:
343fc128a4cSAxel Dörfler 		{
344688acf41SJérôme Duval 			if (buffer == NULL || length > sizeof(device_geometry))
345fc128a4cSAxel Dörfler 				return B_BAD_VALUE;
346fc128a4cSAxel Dörfler 
347fc128a4cSAxel Dörfler 		 	device_geometry geometry;
348fc128a4cSAxel Dörfler 			status_t status = get_geometry(handle, &geometry);
349fc128a4cSAxel Dörfler 			if (status != B_OK)
350fc128a4cSAxel Dörfler 				return status;
351fc128a4cSAxel Dörfler 
352688acf41SJérôme Duval 			return user_memcpy(buffer, &geometry, length);
353fc128a4cSAxel Dörfler 		}
354fc128a4cSAxel Dörfler 
35506ba3f0aSAxel Dörfler 		case B_GET_ICON_NAME:
35606ba3f0aSAxel Dörfler 			// TODO: take device type into account!
35758f14b4cSAxel Dörfler 			return user_strlcpy((char*)buffer, info->removable
35858f14b4cSAxel Dörfler 				? "devices/drive-removable-media" : "devices/drive-harddisk",
35906ba3f0aSAxel Dörfler 				B_FILE_NAME_LENGTH);
36006ba3f0aSAxel Dörfler 
36106ba3f0aSAxel Dörfler 		case B_GET_VECTOR_ICON:
36206ba3f0aSAxel Dörfler 		{
36306ba3f0aSAxel Dörfler 			// TODO: take device type into account!
36406ba3f0aSAxel Dörfler 			device_icon iconData;
36506ba3f0aSAxel Dörfler 			if (length != sizeof(device_icon))
36606ba3f0aSAxel Dörfler 				return B_BAD_VALUE;
36706ba3f0aSAxel Dörfler 			if (user_memcpy(&iconData, buffer, sizeof(device_icon)) != B_OK)
36806ba3f0aSAxel Dörfler 				return B_BAD_ADDRESS;
36906ba3f0aSAxel Dörfler 
37006ba3f0aSAxel Dörfler 			if (iconData.icon_size >= (int32)sizeof(kDriveIcon)) {
37106ba3f0aSAxel Dörfler 				if (user_memcpy(iconData.icon_data, kDriveIcon,
37206ba3f0aSAxel Dörfler 						sizeof(kDriveIcon)) != B_OK)
37306ba3f0aSAxel Dörfler 					return B_BAD_ADDRESS;
37406ba3f0aSAxel Dörfler 			}
37506ba3f0aSAxel Dörfler 
37606ba3f0aSAxel Dörfler 			iconData.icon_size = sizeof(kDriveIcon);
37706ba3f0aSAxel Dörfler 			return user_memcpy(buffer, &iconData, sizeof(device_icon));
37806ba3f0aSAxel Dörfler 		}
37906ba3f0aSAxel Dörfler 
380fc128a4cSAxel Dörfler 		case B_EJECT_DEVICE:
381fc128a4cSAxel Dörfler 		case B_SCSI_EJECT:
38224593e2cSAxel Dörfler 			return load_eject(info, false);
383fc128a4cSAxel Dörfler 
384fc128a4cSAxel Dörfler 		case B_LOAD_MEDIA:
38524593e2cSAxel Dörfler 			return load_eject(info, true);
386fc128a4cSAxel Dörfler 
387fc128a4cSAxel Dörfler 		case B_FLUSH_DRIVE_CACHE:
38824593e2cSAxel Dörfler 			return synchronize_cache(info);
389fc128a4cSAxel Dörfler 
390960c56aeSAxel Dörfler 		case B_TRIM_DEVICE:
391960c56aeSAxel Dörfler 		{
392cef80a1fSAdrien Destugues 			// We know the buffer is kernel-side because it has been
393cef80a1fSAdrien Destugues 			// preprocessed in devfs
394cef80a1fSAdrien Destugues 			ASSERT(IS_KERNEL_ADDRESS(buffer));
395cef80a1fSAdrien Destugues 			return trim_device(info, (fs_trim_data*)buffer);
396960c56aeSAxel Dörfler 		}
397960c56aeSAxel Dörfler 
398fc128a4cSAxel Dörfler 		default:
399fc128a4cSAxel Dörfler 			return sSCSIPeripheral->ioctl(handle->scsi_periph_handle, op,
400fc128a4cSAxel Dörfler 				buffer, length);
401fc128a4cSAxel Dörfler 	}
402fc128a4cSAxel Dörfler }
403fc128a4cSAxel Dörfler 
404fc128a4cSAxel Dörfler 
405fc128a4cSAxel Dörfler //	#pragma mark - scsi_periph callbacks
406fc128a4cSAxel Dörfler 
407fc128a4cSAxel Dörfler 
408fc128a4cSAxel Dörfler static void
das_set_capacity(das_driver_info * info,uint64 capacity,uint32 blockSize,uint32 physicalBlockSize)409688acf41SJérôme Duval das_set_capacity(das_driver_info* info, uint64 capacity, uint32 blockSize, uint32 physicalBlockSize)
410fc128a4cSAxel Dörfler {
411f87871e3SJérôme Duval 	TRACE("das_set_capacity(device = %p, capacity = %" B_PRIu64
412f87871e3SJérôme Duval 		", blockSize = %" B_PRIu32 ")\n", info, capacity, blockSize);
413fc128a4cSAxel Dörfler 
41424593e2cSAxel Dörfler 	info->capacity = capacity;
415fc128a4cSAxel Dörfler 
41624593e2cSAxel Dörfler 	if (info->block_size != blockSize) {
41724593e2cSAxel Dörfler 		if (info->block_size != 0) {
4180063d2baSAlex Smith 			dprintf("old %" B_PRId32 ", new %" B_PRId32 "\n", info->block_size,
4190063d2baSAlex Smith 				blockSize);
42024593e2cSAxel Dörfler 			panic("updating DMAResource not yet implemented...");
42124593e2cSAxel Dörfler 		}
42224593e2cSAxel Dörfler 
42324593e2cSAxel Dörfler 		// TODO: we need to replace the DMAResource in our IOScheduler
424025f7c32SIngo Weinhold 		status_t status = info->dma_resource->Init(info->node, blockSize, 1024,
425025f7c32SIngo Weinhold 			32);
42624593e2cSAxel Dörfler 		if (status != B_OK)
42724593e2cSAxel Dörfler 			panic("initializing DMAResource failed: %s", strerror(status));
42824593e2cSAxel Dörfler 
4290715529bSIngo Weinhold 		info->io_scheduler = new(std::nothrow) IOSchedulerSimple(
4300715529bSIngo Weinhold 			info->dma_resource);
43124593e2cSAxel Dörfler 		if (info->io_scheduler == NULL)
43224593e2cSAxel Dörfler 			panic("allocating IOScheduler failed.");
43324593e2cSAxel Dörfler 
43424593e2cSAxel Dörfler 		// TODO: use whole device name here
43524593e2cSAxel Dörfler 		status = info->io_scheduler->Init("scsi");
43624593e2cSAxel Dörfler 		if (status != B_OK)
43724593e2cSAxel Dörfler 			panic("initializing IOScheduler failed: %s", strerror(status));
43824593e2cSAxel Dörfler 
43924593e2cSAxel Dörfler 		info->io_scheduler->SetCallback(do_io, info);
44024593e2cSAxel Dörfler 	}
44124593e2cSAxel Dörfler 
44224593e2cSAxel Dörfler 	info->block_size = blockSize;
443688acf41SJérôme Duval 	info->physical_block_size = physicalBlockSize;
444fc128a4cSAxel Dörfler }
445fc128a4cSAxel Dörfler 
446fc128a4cSAxel Dörfler 
447fc128a4cSAxel Dörfler static void
das_media_changed(das_driver_info * device,scsi_ccb * request)44824593e2cSAxel Dörfler das_media_changed(das_driver_info *device, scsi_ccb *request)
449fc128a4cSAxel Dörfler {
450fc128a4cSAxel Dörfler 	// do a capacity check
45124593e2cSAxel Dörfler 	// TODO: is this a good idea (e.g. if this is an empty CD)?
452fc128a4cSAxel Dörfler 	sSCSIPeripheral->check_capacity(device->scsi_periph_device, request);
453fc128a4cSAxel Dörfler }
454fc128a4cSAxel Dörfler 
455fc128a4cSAxel Dörfler 
456fc128a4cSAxel Dörfler scsi_periph_callbacks callbacks = {
457688acf41SJérôme Duval 	(void (*)(periph_device_cookie, uint64, uint32, uint32))das_set_capacity,
458fc128a4cSAxel Dörfler 	(void (*)(periph_device_cookie, scsi_ccb *))das_media_changed
459fc128a4cSAxel Dörfler };
460fc128a4cSAxel Dörfler 
461fc128a4cSAxel Dörfler 
462fc128a4cSAxel Dörfler //	#pragma mark - driver module API
463fc128a4cSAxel Dörfler 
464fc128a4cSAxel Dörfler 
465fc128a4cSAxel Dörfler static float
das_supports_device(device_node * parent)466fc128a4cSAxel Dörfler das_supports_device(device_node *parent)
467fc128a4cSAxel Dörfler {
468fc128a4cSAxel Dörfler 	const char *bus;
469fc128a4cSAxel Dörfler 	uint8 deviceType;
470fc128a4cSAxel Dörfler 
471fc128a4cSAxel Dörfler 	// make sure parent is really the SCSI bus manager
472fc128a4cSAxel Dörfler 	if (sDeviceManager->get_attr_string(parent, B_DEVICE_BUS, &bus, false))
473fc128a4cSAxel Dörfler 		return -1;
474fc128a4cSAxel Dörfler 
475fc128a4cSAxel Dörfler 	if (strcmp(bus, "scsi"))
476fc128a4cSAxel Dörfler 		return 0.0;
477fc128a4cSAxel Dörfler 
478fc128a4cSAxel Dörfler 	// check whether it's really a Direct Access Device
479fc128a4cSAxel Dörfler 	if (sDeviceManager->get_attr_uint8(parent, SCSI_DEVICE_TYPE_ITEM,
480fc128a4cSAxel Dörfler 			&deviceType, true) != B_OK || deviceType != scsi_dev_direct_access)
481fc128a4cSAxel Dörfler 		return 0.0;
482fc128a4cSAxel Dörfler 
483fc128a4cSAxel Dörfler 	return 0.6;
484fc128a4cSAxel Dörfler }
485fc128a4cSAxel Dörfler 
486fc128a4cSAxel Dörfler 
487fc128a4cSAxel Dörfler /*!	Called whenever a new device was added to system;
488fc128a4cSAxel Dörfler 	if we really support it, we create a new node that gets
489fc128a4cSAxel Dörfler 	server by the block_io module
490fc128a4cSAxel Dörfler */
491fc128a4cSAxel Dörfler static status_t
das_register_device(device_node * node)492fc128a4cSAxel Dörfler das_register_device(device_node *node)
493fc128a4cSAxel Dörfler {
494fc128a4cSAxel Dörfler 	const scsi_res_inquiry *deviceInquiry = NULL;
495fc128a4cSAxel Dörfler 	size_t inquiryLength;
496fc128a4cSAxel Dörfler 	uint32 maxBlocks;
497fc128a4cSAxel Dörfler 
498fc128a4cSAxel Dörfler 	// get inquiry data
499fc128a4cSAxel Dörfler 	if (sDeviceManager->get_attr_raw(node, SCSI_DEVICE_INQUIRY_ITEM,
500fc128a4cSAxel Dörfler 			(const void **)&deviceInquiry, &inquiryLength, true) != B_OK
501ff38df48SAdrien Destugues 		|| inquiryLength < sizeof(scsi_res_inquiry))
502fc128a4cSAxel Dörfler 		return B_ERROR;
503fc128a4cSAxel Dörfler 
504fc128a4cSAxel Dörfler 	// get block limit of underlying hardware to lower it (if necessary)
50570e2d4acSAxel Dörfler 	if (sDeviceManager->get_attr_uint32(node, B_DMA_MAX_TRANSFER_BLOCKS,
506fc128a4cSAxel Dörfler 			&maxBlocks, true) != B_OK)
507fc128a4cSAxel Dörfler 		maxBlocks = INT_MAX;
508fc128a4cSAxel Dörfler 
509fc128a4cSAxel Dörfler 	// using 10 byte commands, at most 0xffff blocks can be transmitted at once
510fc128a4cSAxel Dörfler 	// (sadly, we cannot update this value later on if only 6 byte commands
511fc128a4cSAxel Dörfler 	//  are supported, but the block_io module can live with that)
512fc128a4cSAxel Dörfler 	maxBlocks = min_c(maxBlocks, 0xffff);
513fc128a4cSAxel Dörfler 
514fc128a4cSAxel Dörfler 	// ready to register
515fc128a4cSAxel Dörfler 	device_attr attrs[] = {
516215b685fSX512 		{ B_DEVICE_PRETTY_NAME, B_STRING_TYPE, { .string = "SCSI Disk" }},
517fc128a4cSAxel Dörfler 		// tell block_io whether the device is removable
518215b685fSX512 		{"removable", B_UINT8_TYPE, {.ui8 = deviceInquiry->removable_medium}},
519fc128a4cSAxel Dörfler 		// impose own max block restriction
520215b685fSX512 		{B_DMA_MAX_TRANSFER_BLOCKS, B_UINT32_TYPE, {.ui32 = maxBlocks}},
521fc128a4cSAxel Dörfler 		{ NULL }
522fc128a4cSAxel Dörfler 	};
523fc128a4cSAxel Dörfler 
52424593e2cSAxel Dörfler 	return sDeviceManager->register_node(node, SCSI_DISK_DRIVER_MODULE_NAME,
52524593e2cSAxel Dörfler 		attrs, NULL, NULL);
526fc128a4cSAxel Dörfler }
527fc128a4cSAxel Dörfler 
528fc128a4cSAxel Dörfler 
529fc128a4cSAxel Dörfler static status_t
das_init_driver(device_node * node,void ** cookie)530fc128a4cSAxel Dörfler das_init_driver(device_node *node, void **cookie)
531fc128a4cSAxel Dörfler {
532fc128a4cSAxel Dörfler 	TRACE("das_init_driver");
533fc128a4cSAxel Dörfler 
53424593e2cSAxel Dörfler 	uint8 removable;
53524593e2cSAxel Dörfler 	status_t status = sDeviceManager->get_attr_uint8(node, "removable",
536fc128a4cSAxel Dörfler 		&removable, false);
537fc128a4cSAxel Dörfler 	if (status != B_OK)
538fc128a4cSAxel Dörfler 		return status;
539fc128a4cSAxel Dörfler 
54024593e2cSAxel Dörfler 	das_driver_info* info = (das_driver_info*)malloc(sizeof(das_driver_info));
54124593e2cSAxel Dörfler 	if (info == NULL)
542fc128a4cSAxel Dörfler 		return B_NO_MEMORY;
543fc128a4cSAxel Dörfler 
54424593e2cSAxel Dörfler 	memset(info, 0, sizeof(*info));
545fc128a4cSAxel Dörfler 
54624593e2cSAxel Dörfler 	info->dma_resource = new(std::nothrow) DMAResource;
54724593e2cSAxel Dörfler 	if (info->dma_resource == NULL) {
54824593e2cSAxel Dörfler 		free(info);
54924593e2cSAxel Dörfler 		return B_NO_MEMORY;
55024593e2cSAxel Dörfler 	}
55124593e2cSAxel Dörfler 
55224593e2cSAxel Dörfler 	info->node = node;
55324593e2cSAxel Dörfler 	info->removable = removable;
554fc128a4cSAxel Dörfler 
555fc128a4cSAxel Dörfler 	device_node* parent = sDeviceManager->get_parent_node(node);
55624593e2cSAxel Dörfler 	sDeviceManager->get_driver(parent, (driver_module_info **)&info->scsi,
55724593e2cSAxel Dörfler 		(void **)&info->scsi_device);
558fc128a4cSAxel Dörfler 	sDeviceManager->put_node(parent);
559fc128a4cSAxel Dörfler 
56024593e2cSAxel Dörfler 	status = sSCSIPeripheral->register_device((periph_device_cookie)info,
56124593e2cSAxel Dörfler 		&callbacks, info->scsi_device, info->scsi, info->node,
56224593e2cSAxel Dörfler 		info->removable, 10, &info->scsi_periph_device);
563fc128a4cSAxel Dörfler 	if (status != B_OK) {
564f87871e3SJérôme Duval 		delete info->dma_resource;
56524593e2cSAxel Dörfler 		free(info);
566fc128a4cSAxel Dörfler 		return status;
567fc128a4cSAxel Dörfler 	}
568fc128a4cSAxel Dörfler 
56924593e2cSAxel Dörfler 	*cookie = info;
570fc128a4cSAxel Dörfler 	return B_OK;
571fc128a4cSAxel Dörfler }
572fc128a4cSAxel Dörfler 
573fc128a4cSAxel Dörfler 
574fc128a4cSAxel Dörfler static void
das_uninit_driver(void * _cookie)575fc128a4cSAxel Dörfler das_uninit_driver(void *_cookie)
576fc128a4cSAxel Dörfler {
57724593e2cSAxel Dörfler 	das_driver_info* info = (das_driver_info*)_cookie;
578fc128a4cSAxel Dörfler 
57924593e2cSAxel Dörfler 	sSCSIPeripheral->unregister_device(info->scsi_periph_device);
580f87871e3SJérôme Duval 	delete info->dma_resource;
58124593e2cSAxel Dörfler 	free(info);
582fc128a4cSAxel Dörfler }
583fc128a4cSAxel Dörfler 
584fc128a4cSAxel Dörfler 
585fc128a4cSAxel Dörfler static status_t
das_register_child_devices(void * _cookie)586fc128a4cSAxel Dörfler das_register_child_devices(void* _cookie)
587fc128a4cSAxel Dörfler {
58824593e2cSAxel Dörfler 	das_driver_info* info = (das_driver_info*)_cookie;
589fc128a4cSAxel Dörfler 	status_t status;
590fc128a4cSAxel Dörfler 
59124593e2cSAxel Dörfler 	char* name = sSCSIPeripheral->compose_device_name(info->node,
59224593e2cSAxel Dörfler 		"disk/scsi");
593fc128a4cSAxel Dörfler 	if (name == NULL)
594fc128a4cSAxel Dörfler 		return B_ERROR;
595fc128a4cSAxel Dörfler 
59624593e2cSAxel Dörfler 	status = sDeviceManager->publish_device(info->node, name,
59724593e2cSAxel Dörfler 		SCSI_DISK_DEVICE_MODULE_NAME);
598fc128a4cSAxel Dörfler 
599fc128a4cSAxel Dörfler 	free(name);
600fc128a4cSAxel Dörfler 	return status;
601fc128a4cSAxel Dörfler }
602fc128a4cSAxel Dörfler 
603fc128a4cSAxel Dörfler 
604a6ffcfb3SJérôme Duval static status_t
das_rescan_child_devices(void * _cookie)605a6ffcfb3SJérôme Duval das_rescan_child_devices(void* _cookie)
606a6ffcfb3SJérôme Duval {
607a6ffcfb3SJérôme Duval 	das_driver_info* info = (das_driver_info*)_cookie;
608a6ffcfb3SJérôme Duval 	uint64 capacity = info->capacity;
609a6ffcfb3SJérôme Duval 	update_capacity(info);
610a6ffcfb3SJérôme Duval 	if (info->capacity != capacity)
611a6ffcfb3SJérôme Duval 		sSCSIPeripheral->media_changed(info->scsi_periph_device);
612a6ffcfb3SJérôme Duval 	return B_OK;
613a6ffcfb3SJérôme Duval }
614a6ffcfb3SJérôme Duval 
615a6ffcfb3SJérôme Duval 
616a6ffcfb3SJérôme Duval 
617fc128a4cSAxel Dörfler module_dependency module_dependencies[] = {
618fc128a4cSAxel Dörfler 	{SCSI_PERIPH_MODULE_NAME, (module_info**)&sSCSIPeripheral},
619fc128a4cSAxel Dörfler 	{B_DEVICE_MANAGER_MODULE_NAME, (module_info**)&sDeviceManager},
620fc128a4cSAxel Dörfler 	{}
621fc128a4cSAxel Dörfler };
622fc128a4cSAxel Dörfler 
62324593e2cSAxel Dörfler struct device_module_info sSCSIDiskDevice = {
624fc128a4cSAxel Dörfler 	{
62524593e2cSAxel Dörfler 		SCSI_DISK_DEVICE_MODULE_NAME,
62624593e2cSAxel Dörfler 		0,
62724593e2cSAxel Dörfler 		NULL
62824593e2cSAxel Dörfler 	},
62924593e2cSAxel Dörfler 
63024593e2cSAxel Dörfler 	das_init_device,
63124593e2cSAxel Dörfler 	das_uninit_device,
63224593e2cSAxel Dörfler 	NULL, //das_remove,
63324593e2cSAxel Dörfler 
63424593e2cSAxel Dörfler 	das_open,
63524593e2cSAxel Dörfler 	das_close,
63624593e2cSAxel Dörfler 	das_free,
637*103c671eSAugustin Cavalier 	NULL,	// read
638*103c671eSAugustin Cavalier 	NULL,	// write
63924593e2cSAxel Dörfler 	das_io,
64024593e2cSAxel Dörfler 	das_ioctl,
64124593e2cSAxel Dörfler 
64224593e2cSAxel Dörfler 	NULL,	// select
64324593e2cSAxel Dörfler 	NULL,	// deselect
64424593e2cSAxel Dörfler };
64524593e2cSAxel Dörfler 
64624593e2cSAxel Dörfler struct driver_module_info sSCSIDiskDriver = {
647fc128a4cSAxel Dörfler 	{
64824593e2cSAxel Dörfler 		SCSI_DISK_DRIVER_MODULE_NAME,
649fc128a4cSAxel Dörfler 		0,
650fc128a4cSAxel Dörfler 		NULL
651fc128a4cSAxel Dörfler 	},
652fc128a4cSAxel Dörfler 
653fc128a4cSAxel Dörfler 	das_supports_device,
654fc128a4cSAxel Dörfler 	das_register_device,
655fc128a4cSAxel Dörfler 	das_init_driver,
656fc128a4cSAxel Dörfler 	das_uninit_driver,
657fc128a4cSAxel Dörfler 	das_register_child_devices,
658a6ffcfb3SJérôme Duval 	das_rescan_child_devices,
659fc128a4cSAxel Dörfler 	NULL,	// removed
660fc128a4cSAxel Dörfler };
661fc128a4cSAxel Dörfler 
662fc128a4cSAxel Dörfler module_info* modules[] = {
66324593e2cSAxel Dörfler 	(module_info*)&sSCSIDiskDriver,
66424593e2cSAxel Dörfler 	(module_info*)&sSCSIDiskDevice,
665fc128a4cSAxel Dörfler 	NULL
666fc128a4cSAxel Dörfler };
667