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