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