1/*
2 * Copyright 2008-2013, Axel D��rfler, axeld@pinc-software.de.
3 * Copyright 2002/03, Thomas Kurschel. All rights reserved.
4 * Distributed under the terms of the MIT License.
5 */
6
7
8/*!	Peripheral driver to handle any kind of SCSI disks,
9	i.e. hard disk and floopy disks (ZIP etc.)
10
11	Much work is done by scsi_periph and block_io.
12
13	You'll find das_... all over the place. This stands for
14	"Direct Access Storage" which is the official SCSI name for
15	normal (floppy/hard/ZIP)-disk drives.
16*/
17
18
19#include "scsi_disk.h"
20
21#include <string.h>
22#include <stdlib.h>
23
24#include <AutoDeleter.h>
25
26#include <fs/devfs.h>
27#include <util/fs_trim_support.h>
28
29#include "dma_resources.h"
30#include "IORequest.h"
31#include "IOSchedulerSimple.h"
32
33
34//#define TRACE_SCSI_DISK
35#ifdef TRACE_SCSI_DISK
36#	define TRACE(x...) dprintf("scsi_disk: " x)
37#else
38#	define TRACE(x...) ;
39#endif
40
41
42static const uint8 kDriveIcon[] = {
43	0x6e, 0x63, 0x69, 0x66, 0x08, 0x03, 0x01, 0x00, 0x00, 0x02, 0x00, 0x16,
44	0x02, 0x3c, 0xc7, 0xee, 0x38, 0x9b, 0xc0, 0xba, 0x16, 0x57, 0x3e, 0x39,
45	0xb0, 0x49, 0x77, 0xc8, 0x42, 0xad, 0xc7, 0x00, 0xff, 0xff, 0xd3, 0x02,
46	0x00, 0x06, 0x02, 0x3c, 0x96, 0x32, 0x3a, 0x4d, 0x3f, 0xba, 0xfc, 0x01,
47	0x3d, 0x5a, 0x97, 0x4b, 0x57, 0xa5, 0x49, 0x84, 0x4d, 0x00, 0x47, 0x47,
48	0x47, 0xff, 0xa5, 0xa0, 0xa0, 0x02, 0x00, 0x16, 0x02, 0xbc, 0x59, 0x2f,
49	0xbb, 0x29, 0xa7, 0x3c, 0x0c, 0xe4, 0xbd, 0x0b, 0x7c, 0x48, 0x92, 0xc0,
50	0x4b, 0x79, 0x66, 0x00, 0x7d, 0xff, 0xd4, 0x02, 0x00, 0x06, 0x02, 0x38,
51	0xdb, 0xb4, 0x39, 0x97, 0x33, 0xbc, 0x4a, 0x33, 0x3b, 0xa5, 0x42, 0x48,
52	0x6e, 0x66, 0x49, 0xee, 0x7b, 0x00, 0x59, 0x67, 0x56, 0xff, 0xeb, 0xb2,
53	0xb2, 0x03, 0xa7, 0xff, 0x00, 0x03, 0xff, 0x00, 0x00, 0x04, 0x01, 0x80,
54	0x07, 0x0a, 0x06, 0x22, 0x3c, 0x22, 0x49, 0x44, 0x5b, 0x5a, 0x3e, 0x5a,
55	0x31, 0x39, 0x25, 0x0a, 0x04, 0x22, 0x3c, 0x44, 0x4b, 0x5a, 0x31, 0x39,
56	0x25, 0x0a, 0x04, 0x44, 0x4b, 0x44, 0x5b, 0x5a, 0x3e, 0x5a, 0x31, 0x0a,
57	0x04, 0x22, 0x3c, 0x22, 0x49, 0x44, 0x5b, 0x44, 0x4b, 0x08, 0x02, 0x27,
58	0x43, 0xb8, 0x14, 0xc1, 0xf1, 0x08, 0x02, 0x26, 0x43, 0x29, 0x44, 0x0a,
59	0x05, 0x44, 0x5d, 0x49, 0x5d, 0x60, 0x3e, 0x5a, 0x3b, 0x5b, 0x3f, 0x08,
60	0x0a, 0x07, 0x01, 0x06, 0x00, 0x0a, 0x00, 0x01, 0x00, 0x10, 0x01, 0x17,
61	0x84, 0x00, 0x04, 0x0a, 0x01, 0x01, 0x01, 0x00, 0x0a, 0x02, 0x01, 0x02,
62	0x00, 0x0a, 0x03, 0x01, 0x03, 0x00, 0x0a, 0x04, 0x01, 0x04, 0x10, 0x01,
63	0x17, 0x85, 0x20, 0x04, 0x0a, 0x06, 0x01, 0x05, 0x30, 0x24, 0xb3, 0x99,
64	0x01, 0x17, 0x82, 0x00, 0x04, 0x0a, 0x05, 0x01, 0x05, 0x30, 0x20, 0xb2,
65	0xe6, 0x01, 0x17, 0x82, 0x00, 0x04
66};
67
68
69static scsi_periph_interface* sSCSIPeripheral;
70static device_manager_info* sDeviceManager;
71
72
73static status_t
74update_capacity(das_driver_info* device)
75{
76	TRACE("update_capacity()\n");
77
78	scsi_ccb *ccb = device->scsi->alloc_ccb(device->scsi_device);
79	if (ccb == NULL)
80		return B_NO_MEMORY;
81
82	status_t status = sSCSIPeripheral->check_capacity(
83		device->scsi_periph_device, ccb);
84
85	device->scsi->free_ccb(ccb);
86
87	return status;
88}
89
90
91static status_t
92get_geometry(das_handle* handle, device_geometry* geometry)
93{
94	das_driver_info* info = handle->info;
95
96	status_t status = update_capacity(info);
97	if (status != B_OK)
98		return status;
99
100	devfs_compute_geometry_size(geometry, info->capacity, info->block_size);
101
102	geometry->device_type = B_DISK;
103	geometry->removable = info->removable;
104
105	// TBD: for all but CD-ROMs, read mode sense - medium type
106	// (bit 7 of block device specific parameter for Optical Memory Block Device)
107	// (same for Direct-Access Block Devices)
108	// (same for write-once block devices)
109	// (same for optical memory block devices)
110	geometry->read_only = false;
111	geometry->write_once = false;
112
113	TRACE("scsi_disk: get_geometry(): %" B_PRId32 ", %" B_PRId32 ", %" B_PRId32
114		", %" B_PRId32 ", %d, %d, %d, %d\n", geometry->bytes_per_sector,
115		geometry->sectors_per_track, geometry->cylinder_count,
116		geometry->head_count, geometry->device_type,
117		geometry->removable, geometry->read_only, geometry->write_once);
118
119	return B_OK;
120}
121
122
123static status_t
124load_eject(das_driver_info *device, bool load)
125{
126	TRACE("load_eject()\n");
127
128	scsi_ccb *ccb = device->scsi->alloc_ccb(device->scsi_device);
129	if (ccb == NULL)
130		return B_NO_MEMORY;
131
132	err_res result = sSCSIPeripheral->send_start_stop(
133		device->scsi_periph_device, ccb, load, true);
134
135	device->scsi->free_ccb(ccb);
136
137	return result.error_code;
138}
139
140
141static status_t
142synchronize_cache(das_driver_info *device)
143{
144	TRACE("synchronize_cache()\n");
145
146	scsi_ccb *ccb = device->scsi->alloc_ccb(device->scsi_device);
147	if (ccb == NULL)
148		return B_NO_MEMORY;
149
150	err_res result = sSCSIPeripheral->synchronize_cache(
151		device->scsi_periph_device, ccb);
152
153	device->scsi->free_ccb(ccb);
154
155	return result.error_code;
156}
157
158
159#if 0
160static status_t
161trim_device(das_driver_info* device, fs_trim_data* trimData)
162{
163	TRACE("trim_device()\n");
164
165	scsi_ccb* request = device->scsi->alloc_ccb(device->scsi_device);
166	if (request == NULL)
167		return B_NO_MEMORY;
168
169	uint64 trimmedSize = 0;
170	for (uint32 i = 0; i < trimData->range_count; i++) {
171		trimmedSize += trimData->ranges[i].size;
172	}
173	status_t status = sSCSIPeripheral->trim_device(device->scsi_periph_device,
174		request, (scsi_block_range*)&trimData->ranges[0],
175		trimData->range_count);
176
177	device->scsi->free_ccb(request);
178	if (status == B_OK)
179		trimData->trimmed_size = trimmedSize;
180
181	return status;
182}
183#endif
184
185
186static int
187log2(uint32 x)
188{
189	int y;
190
191	for (y = 31; y >= 0; --y) {
192		if (x == ((uint32)1 << y))
193			break;
194	}
195
196	return y;
197}
198
199
200static status_t
201do_io(void* cookie, IOOperation* operation)
202{
203	das_driver_info* info = (das_driver_info*)cookie;
204
205	// TODO: this can go away as soon as we pushed the IOOperation to the upper
206	// layers - we can then set scsi_periph::io() as callback for the scheduler
207	size_t bytesTransferred;
208	status_t status = sSCSIPeripheral->io(info->scsi_periph_device, operation,
209		&bytesTransferred);
210
211	info->io_scheduler->OperationCompleted(operation, status, bytesTransferred);
212	return status;
213}
214
215
216//	#pragma mark - device module API
217
218
219static status_t
220das_init_device(void* _info, void** _cookie)
221{
222	das_driver_info* info = (das_driver_info*)_info;
223
224	// and get (initial) capacity
225	scsi_ccb *request = info->scsi->alloc_ccb(info->scsi_device);
226	if (request == NULL)
227		return B_NO_MEMORY;
228
229	sSCSIPeripheral->check_capacity(info->scsi_periph_device, request);
230	info->scsi->free_ccb(request);
231
232	*_cookie = info;
233	return B_OK;
234}
235
236
237static void
238das_uninit_device(void* _cookie)
239{
240	das_driver_info* info = (das_driver_info*)_cookie;
241
242	delete info->io_scheduler;
243}
244
245
246static status_t
247das_open(void* _info, const char* path, int openMode, void** _cookie)
248{
249	das_driver_info* info = (das_driver_info*)_info;
250
251	das_handle* handle = (das_handle*)malloc(sizeof(das_handle));
252	if (handle == NULL)
253		return B_NO_MEMORY;
254
255	handle->info = info;
256
257	status_t status = sSCSIPeripheral->handle_open(info->scsi_periph_device,
258		(periph_handle_cookie)handle, &handle->scsi_periph_handle);
259	if (status < B_OK) {
260		free(handle);
261		return status;
262	}
263
264	*_cookie = handle;
265	return B_OK;
266}
267
268
269static status_t
270das_close(void* cookie)
271{
272	das_handle* handle = (das_handle*)cookie;
273	TRACE("close()\n");
274
275	sSCSIPeripheral->handle_close(handle->scsi_periph_handle);
276	return B_OK;
277}
278
279
280static status_t
281das_free(void* cookie)
282{
283	das_handle* handle = (das_handle*)cookie;
284	TRACE("free()\n");
285
286	sSCSIPeripheral->handle_free(handle->scsi_periph_handle);
287	free(handle);
288	return B_OK;
289}
290
291
292static status_t
293das_read(void* cookie, off_t pos, void* buffer, size_t* _length)
294{
295	das_handle* handle = (das_handle*)cookie;
296	size_t length = *_length;
297
298	IORequest request;
299	status_t status = request.Init(pos, (addr_t)buffer, length, false, 0);
300	if (status != B_OK)
301		return status;
302
303	status = handle->info->io_scheduler->ScheduleRequest(&request);
304	if (status != B_OK)
305		return status;
306
307	status = request.Wait(0, 0);
308	if (status == B_OK)
309		*_length = length;
310	else
311		dprintf("das_read(): request.Wait() returned: %s\n", strerror(status));
312
313	return status;
314}
315
316
317static status_t
318das_write(void* cookie, off_t pos, const void* buffer, size_t* _length)
319{
320	das_handle* handle = (das_handle*)cookie;
321	size_t length = *_length;
322
323	IORequest request;
324	status_t status = request.Init(pos, (addr_t)buffer, length, true, 0);
325	if (status != B_OK)
326		return status;
327
328	status = handle->info->io_scheduler->ScheduleRequest(&request);
329	if (status != B_OK)
330		return status;
331
332	status = request.Wait(0, 0);
333	if (status == B_OK)
334		*_length = length;
335	else
336		dprintf("das_write(): request.Wait() returned: %s\n", strerror(status));
337
338	return status;
339}
340
341
342static status_t
343das_io(void *cookie, io_request *request)
344{
345	das_handle* handle = (das_handle*)cookie;
346
347	return handle->info->io_scheduler->ScheduleRequest(request);
348}
349
350
351static status_t
352das_ioctl(void* cookie, uint32 op, void* buffer, size_t length)
353{
354	das_handle* handle = (das_handle*)cookie;
355	das_driver_info* info = handle->info;
356
357	TRACE("ioctl(op = %" B_PRIu32 ")\n", op);
358
359	switch (op) {
360		case B_GET_DEVICE_SIZE:
361		{
362			status_t status = update_capacity(info);
363			if (status != B_OK)
364				return status;
365
366			size_t size = info->capacity * info->block_size;
367			return user_memcpy(buffer, &size, sizeof(size_t));
368		}
369
370		case B_GET_GEOMETRY:
371		{
372			if (buffer == NULL /*|| length != sizeof(device_geometry)*/)
373				return B_BAD_VALUE;
374
375		 	device_geometry geometry;
376			status_t status = get_geometry(handle, &geometry);
377			if (status != B_OK)
378				return status;
379
380			return user_memcpy(buffer, &geometry, sizeof(device_geometry));
381		}
382
383		case B_GET_ICON_NAME:
384			// TODO: take device type into account!
385			return user_strlcpy((char*)buffer, info->removable
386				? "devices/drive-removable-media" : "devices/drive-harddisk",
387				B_FILE_NAME_LENGTH);
388
389		case B_GET_VECTOR_ICON:
390		{
391			// TODO: take device type into account!
392			device_icon iconData;
393			if (length != sizeof(device_icon))
394				return B_BAD_VALUE;
395			if (user_memcpy(&iconData, buffer, sizeof(device_icon)) != B_OK)
396				return B_BAD_ADDRESS;
397
398			if (iconData.icon_size >= (int32)sizeof(kDriveIcon)) {
399				if (user_memcpy(iconData.icon_data, kDriveIcon,
400						sizeof(kDriveIcon)) != B_OK)
401					return B_BAD_ADDRESS;
402			}
403
404			iconData.icon_size = sizeof(kDriveIcon);
405			return user_memcpy(buffer, &iconData, sizeof(device_icon));
406		}
407
408		case B_EJECT_DEVICE:
409		case B_SCSI_EJECT:
410			return load_eject(info, false);
411
412		case B_LOAD_MEDIA:
413			return load_eject(info, true);
414
415		case B_FLUSH_DRIVE_CACHE:
416			return synchronize_cache(info);
417
418#if 0
419		case B_TRIM_DEVICE:
420		{
421			fs_trim_data* trimData;
422			MemoryDeleter deleter;
423			status_t status = get_trim_data_from_user(buffer, length, deleter,
424				trimData);
425			if (status != B_OK)
426				return status;
427
428			status = trim_device(info, trimData);
429			if (status != B_OK)
430				return status;
431
432			return copy_trim_data_to_user(buffer, trimData);
433		}
434#endif
435
436		default:
437			return sSCSIPeripheral->ioctl(handle->scsi_periph_handle, op,
438				buffer, length);
439	}
440}
441
442
443//	#pragma mark - scsi_periph callbacks
444
445
446static void
447das_set_capacity(das_driver_info* info, uint64 capacity, uint32 blockSize)
448{
449	TRACE("das_set_capacity(device = %p, capacity = %" B_PRIu64
450		", blockSize = %" B_PRIu32 ")\n", info, capacity, blockSize);
451
452	// get log2, if possible
453	uint32 blockShift = log2(blockSize);
454
455	if ((1UL << blockShift) != blockSize)
456		blockShift = 0;
457
458	info->capacity = capacity;
459
460	if (info->block_size != blockSize) {
461		if (info->block_size != 0) {
462			dprintf("old %" B_PRId32 ", new %" B_PRId32 "\n", info->block_size,
463				blockSize);
464			panic("updating DMAResource not yet implemented...");
465		}
466
467		// TODO: we need to replace the DMAResource in our IOScheduler
468		status_t status = info->dma_resource->Init(info->node, blockSize, 1024,
469			32);
470		if (status != B_OK)
471			panic("initializing DMAResource failed: %s", strerror(status));
472
473		info->io_scheduler = new(std::nothrow) IOSchedulerSimple(
474			info->dma_resource);
475		if (info->io_scheduler == NULL)
476			panic("allocating IOScheduler failed.");
477
478		// TODO: use whole device name here
479		status = info->io_scheduler->Init("scsi");
480		if (status != B_OK)
481			panic("initializing IOScheduler failed: %s", strerror(status));
482
483		info->io_scheduler->SetCallback(do_io, info);
484	}
485
486	info->block_size = blockSize;
487}
488
489
490static void
491das_media_changed(das_driver_info *device, scsi_ccb *request)
492{
493	// do a capacity check
494	// TODO: is this a good idea (e.g. if this is an empty CD)?
495	sSCSIPeripheral->check_capacity(device->scsi_periph_device, request);
496}
497
498
499scsi_periph_callbacks callbacks = {
500	(void (*)(periph_device_cookie, uint64, uint32))das_set_capacity,
501	(void (*)(periph_device_cookie, scsi_ccb *))das_media_changed
502};
503
504
505//	#pragma mark - driver module API
506
507
508static float
509das_supports_device(device_node *parent)
510{
511	const char *bus;
512	uint8 deviceType;
513
514	// make sure parent is really the SCSI bus manager
515	if (sDeviceManager->get_attr_string(parent, B_DEVICE_BUS, &bus, false))
516		return -1;
517
518	if (strcmp(bus, "scsi"))
519		return 0.0;
520
521	// check whether it's really a Direct Access Device
522	if (sDeviceManager->get_attr_uint8(parent, SCSI_DEVICE_TYPE_ITEM,
523			&deviceType, true) != B_OK || deviceType != scsi_dev_direct_access)
524		return 0.0;
525
526	return 0.6;
527}
528
529
530/*!	Called whenever a new device was added to system;
531	if we really support it, we create a new node that gets
532	server by the block_io module
533*/
534static status_t
535das_register_device(device_node *node)
536{
537	const scsi_res_inquiry *deviceInquiry = NULL;
538	size_t inquiryLength;
539	uint32 maxBlocks;
540
541	// get inquiry data
542	if (sDeviceManager->get_attr_raw(node, SCSI_DEVICE_INQUIRY_ITEM,
543			(const void **)&deviceInquiry, &inquiryLength, true) != B_OK
544		|| inquiryLength < sizeof(deviceInquiry))
545		return B_ERROR;
546
547	// get block limit of underlying hardware to lower it (if necessary)
548	if (sDeviceManager->get_attr_uint32(node, B_DMA_MAX_TRANSFER_BLOCKS,
549			&maxBlocks, true) != B_OK)
550		maxBlocks = INT_MAX;
551
552	// using 10 byte commands, at most 0xffff blocks can be transmitted at once
553	// (sadly, we cannot update this value later on if only 6 byte commands
554	//  are supported, but the block_io module can live with that)
555	maxBlocks = min_c(maxBlocks, 0xffff);
556
557	// ready to register
558	device_attr attrs[] = {
559		// tell block_io whether the device is removable
560		{"removable", B_UINT8_TYPE, {ui8: deviceInquiry->removable_medium}},
561		// impose own max block restriction
562		{B_DMA_MAX_TRANSFER_BLOCKS, B_UINT32_TYPE, {ui32: maxBlocks}},
563		{ NULL }
564	};
565
566	return sDeviceManager->register_node(node, SCSI_DISK_DRIVER_MODULE_NAME,
567		attrs, NULL, NULL);
568}
569
570
571static status_t
572das_init_driver(device_node *node, void **cookie)
573{
574	TRACE("das_init_driver");
575
576	uint8 removable;
577	status_t status = sDeviceManager->get_attr_uint8(node, "removable",
578		&removable, false);
579	if (status != B_OK)
580		return status;
581
582	das_driver_info* info = (das_driver_info*)malloc(sizeof(das_driver_info));
583	if (info == NULL)
584		return B_NO_MEMORY;
585
586	memset(info, 0, sizeof(*info));
587
588	info->dma_resource = new(std::nothrow) DMAResource;
589	if (info->dma_resource == NULL) {
590		free(info);
591		return B_NO_MEMORY;
592	}
593
594	info->node = node;
595	info->removable = removable;
596
597	device_node* parent = sDeviceManager->get_parent_node(node);
598	sDeviceManager->get_driver(parent, (driver_module_info **)&info->scsi,
599		(void **)&info->scsi_device);
600	sDeviceManager->put_node(parent);
601
602	status = sSCSIPeripheral->register_device((periph_device_cookie)info,
603		&callbacks, info->scsi_device, info->scsi, info->node,
604		info->removable, 10, &info->scsi_periph_device);
605	if (status != B_OK) {
606		delete info->dma_resource;
607		free(info);
608		return status;
609	}
610
611	*cookie = info;
612	return B_OK;
613}
614
615
616static void
617das_uninit_driver(void *_cookie)
618{
619	das_driver_info* info = (das_driver_info*)_cookie;
620
621	sSCSIPeripheral->unregister_device(info->scsi_periph_device);
622	delete info->dma_resource;
623	free(info);
624}
625
626
627static status_t
628das_register_child_devices(void* _cookie)
629{
630	das_driver_info* info = (das_driver_info*)_cookie;
631	status_t status;
632
633	char* name = sSCSIPeripheral->compose_device_name(info->node,
634		"disk/scsi");
635	if (name == NULL)
636		return B_ERROR;
637
638	status = sDeviceManager->publish_device(info->node, name,
639		SCSI_DISK_DEVICE_MODULE_NAME);
640
641	free(name);
642	return status;
643}
644
645
646static status_t
647das_rescan_child_devices(void* _cookie)
648{
649	das_driver_info* info = (das_driver_info*)_cookie;
650	uint64 capacity = info->capacity;
651	update_capacity(info);
652	if (info->capacity != capacity)
653		sSCSIPeripheral->media_changed(info->scsi_periph_device);
654	return B_OK;
655}
656
657
658
659module_dependency module_dependencies[] = {
660	{SCSI_PERIPH_MODULE_NAME, (module_info**)&sSCSIPeripheral},
661	{B_DEVICE_MANAGER_MODULE_NAME, (module_info**)&sDeviceManager},
662	{}
663};
664
665struct device_module_info sSCSIDiskDevice = {
666	{
667		SCSI_DISK_DEVICE_MODULE_NAME,
668		0,
669		NULL
670	},
671
672	das_init_device,
673	das_uninit_device,
674	NULL, //das_remove,
675
676	das_open,
677	das_close,
678	das_free,
679	das_read,
680	das_write,
681	das_io,
682	das_ioctl,
683
684	NULL,	// select
685	NULL,	// deselect
686};
687
688struct driver_module_info sSCSIDiskDriver = {
689	{
690		SCSI_DISK_DRIVER_MODULE_NAME,
691		0,
692		NULL
693	},
694
695	das_supports_device,
696	das_register_device,
697	das_init_driver,
698	das_uninit_driver,
699	das_register_child_devices,
700	das_rescan_child_devices,
701	NULL,	// removed
702};
703
704module_info* modules[] = {
705	(module_info*)&sSCSIDiskDriver,
706	(module_info*)&sSCSIDiskDevice,
707	NULL
708};
709