xref: /haiku/src/add-ons/kernel/drivers/disk/virtual/virtio_block/virtio_block.cpp (revision e1c4049fed1047bdb957b0529e1921e97ef94770)
1 /*
2  * Copyright 2013, Jérôme Duval, korli@users.berlios.de.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 
7 #include <condition_variable.h>
8 #include <lock.h>
9 #include <StackOrHeapArray.h>
10 #include <virtio.h>
11 
12 #include "virtio_blk.h"
13 
14 
15 class DMAResource;
16 class IOScheduler;
17 
18 
19 static const uint8 kDriveIcon[] = {
20 	0x6e, 0x63, 0x69, 0x66, 0x08, 0x03, 0x01, 0x00, 0x00, 0x02, 0x00, 0x16,
21 	0x02, 0x3c, 0xc7, 0xee, 0x38, 0x9b, 0xc0, 0xba, 0x16, 0x57, 0x3e, 0x39,
22 	0xb0, 0x49, 0x77, 0xc8, 0x42, 0xad, 0xc7, 0x00, 0xff, 0xff, 0xd3, 0x02,
23 	0x00, 0x06, 0x02, 0x3c, 0x96, 0x32, 0x3a, 0x4d, 0x3f, 0xba, 0xfc, 0x01,
24 	0x3d, 0x5a, 0x97, 0x4b, 0x57, 0xa5, 0x49, 0x84, 0x4d, 0x00, 0x47, 0x47,
25 	0x47, 0xff, 0xa5, 0xa0, 0xa0, 0x02, 0x00, 0x16, 0x02, 0xbc, 0x59, 0x2f,
26 	0xbb, 0x29, 0xa7, 0x3c, 0x0c, 0xe4, 0xbd, 0x0b, 0x7c, 0x48, 0x92, 0xc0,
27 	0x4b, 0x79, 0x66, 0x00, 0x7d, 0xff, 0xd4, 0x02, 0x00, 0x06, 0x02, 0x38,
28 	0xdb, 0xb4, 0x39, 0x97, 0x33, 0xbc, 0x4a, 0x33, 0x3b, 0xa5, 0x42, 0x48,
29 	0x6e, 0x66, 0x49, 0xee, 0x7b, 0x00, 0x59, 0x67, 0x56, 0xff, 0xeb, 0xb2,
30 	0xb2, 0x03, 0xa7, 0xff, 0x00, 0x03, 0xff, 0x00, 0x00, 0x04, 0x01, 0x80,
31 	0x07, 0x0a, 0x06, 0x22, 0x3c, 0x22, 0x49, 0x44, 0x5b, 0x5a, 0x3e, 0x5a,
32 	0x31, 0x39, 0x25, 0x0a, 0x04, 0x22, 0x3c, 0x44, 0x4b, 0x5a, 0x31, 0x39,
33 	0x25, 0x0a, 0x04, 0x44, 0x4b, 0x44, 0x5b, 0x5a, 0x3e, 0x5a, 0x31, 0x0a,
34 	0x04, 0x22, 0x3c, 0x22, 0x49, 0x44, 0x5b, 0x44, 0x4b, 0x08, 0x02, 0x27,
35 	0x43, 0xb8, 0x14, 0xc1, 0xf1, 0x08, 0x02, 0x26, 0x43, 0x29, 0x44, 0x0a,
36 	0x05, 0x44, 0x5d, 0x49, 0x5d, 0x60, 0x3e, 0x5a, 0x3b, 0x5b, 0x3f, 0x08,
37 	0x0a, 0x07, 0x01, 0x06, 0x00, 0x0a, 0x00, 0x01, 0x00, 0x10, 0x01, 0x17,
38 	0x84, 0x00, 0x04, 0x0a, 0x01, 0x01, 0x01, 0x00, 0x0a, 0x02, 0x01, 0x02,
39 	0x00, 0x0a, 0x03, 0x01, 0x03, 0x00, 0x0a, 0x04, 0x01, 0x04, 0x10, 0x01,
40 	0x17, 0x85, 0x20, 0x04, 0x0a, 0x06, 0x01, 0x05, 0x30, 0x24, 0xb3, 0x99,
41 	0x01, 0x17, 0x82, 0x00, 0x04, 0x0a, 0x05, 0x01, 0x05, 0x30, 0x20, 0xb2,
42 	0xe6, 0x01, 0x17, 0x82, 0x00, 0x04
43 };
44 
45 
46 #define VIRTIO_BLOCK_DRIVER_MODULE_NAME "drivers/disk/virtual/virtio_block/driver_v1"
47 #define VIRTIO_BLOCK_DEVICE_MODULE_NAME "drivers/disk/virtual/virtio_block/device_v1"
48 #define VIRTIO_BLOCK_DEVICE_ID_GENERATOR	"virtio_block/device_id"
49 
50 
51 typedef struct {
52 	device_node*			node;
53 	::virtio_device			virtio_device;
54 	virtio_device_interface*	virtio;
55 	::virtio_queue			virtio_queue;
56 	IOScheduler*			io_scheduler;
57 	DMAResource*			dma_resource;
58 
59 	struct virtio_blk_config	config;
60 
61 	area_id					bufferArea;
62 	addr_t					bufferAddr;
63 	phys_addr_t				bufferPhysAddr;
64 
65 	uint64 					features;
66 	uint64					capacity;
67 	uint32					block_size;
68 	uint32					physical_block_size;
69 	status_t				media_status;
70 
71 	mutex					lock;
72 	int32					currentRequest;
73 	ConditionVariable		interruptCondition;
74 	ConditionVariableEntry 	interruptConditionEntry;
75 } virtio_block_driver_info;
76 
77 
78 typedef struct {
79 	virtio_block_driver_info*		info;
80 } virtio_block_handle;
81 
82 
83 #include <stdio.h>
84 #include <string.h>
85 #include <stdlib.h>
86 
87 #include <fs/devfs.h>
88 
89 #include "dma_resources.h"
90 #include "IORequest.h"
91 #include "IOSchedulerSimple.h"
92 
93 
94 //#define TRACE_VIRTIO_BLOCK
95 #ifdef TRACE_VIRTIO_BLOCK
96 #	define TRACE(x...) dprintf("virtio_block: " x)
97 #else
98 #	define TRACE(x...) ;
99 #endif
100 #define ERROR(x...)			dprintf("\33[33mvirtio_block:\33[0m " x)
101 #define CALLED() 			TRACE("CALLED %s\n", __PRETTY_FUNCTION__)
102 
103 
104 static device_manager_info* sDeviceManager;
105 
106 
107 bool virtio_block_set_capacity(virtio_block_driver_info* info);
108 
109 
110 const char *
111 get_feature_name(uint64 feature)
112 {
113 	switch (feature) {
114 		case VIRTIO_BLK_F_BARRIER:
115 			return "host barrier";
116 		case VIRTIO_BLK_F_SIZE_MAX:
117 			return "maximum segment size";
118 		case VIRTIO_BLK_F_SEG_MAX:
119 			return "maximum segment count";
120 		case VIRTIO_BLK_F_GEOMETRY:
121 			return "disk geometry";
122 		case VIRTIO_BLK_F_RO:
123 			return "read only";
124 		case VIRTIO_BLK_F_BLK_SIZE:
125 			return "block size";
126 		case VIRTIO_BLK_F_SCSI:
127 			return "scsi commands";
128 		case VIRTIO_BLK_F_FLUSH:
129 			return "flush command";
130 		case VIRTIO_BLK_F_TOPOLOGY:
131 			return "topology";
132 		case VIRTIO_BLK_F_CONFIG_WCE:
133 			return "config wce";
134 	}
135 	return NULL;
136 }
137 
138 
139 static status_t
140 get_geometry(virtio_block_handle* handle, device_geometry* geometry)
141 {
142 	virtio_block_driver_info* info = handle->info;
143 
144 	devfs_compute_geometry_size(geometry, info->capacity, info->block_size);
145 	geometry->bytes_per_physical_sector = info->physical_block_size;
146 
147 	geometry->device_type = B_DISK;
148 	geometry->removable = false;
149 
150 	geometry->read_only = ((info->features & VIRTIO_BLK_F_RO) != 0);
151 	geometry->write_once = false;
152 
153 	TRACE("virtio_block: get_geometry(): %" B_PRIu32 ", %" B_PRIu32 ", %" B_PRIu32 ", %" B_PRIu32
154 		", %d, %d, %d, %d\n", geometry->bytes_per_sector, geometry->sectors_per_track,
155 		geometry->cylinder_count, geometry->head_count, geometry->device_type,
156 		geometry->removable, geometry->read_only, geometry->write_once);
157 
158 	return B_OK;
159 }
160 
161 
162 static void
163 virtio_block_config_callback(void* driverCookie)
164 {
165 	virtio_block_driver_info* info = (virtio_block_driver_info*)driverCookie;
166 
167 	status_t status = info->virtio->read_device_config(info->virtio_device, 0,
168 		&info->config, sizeof(struct virtio_blk_config));
169 	if (status != B_OK)
170 		return;
171 
172 	if (virtio_block_set_capacity(info))
173 		info->media_status = B_DEV_MEDIA_CHANGED;
174 }
175 
176 
177 static void
178 virtio_block_callback(void* driverCookie, void* _cookie)
179 {
180 	virtio_block_driver_info* info = (virtio_block_driver_info*)_cookie;
181 
182 	void* cookie = NULL;
183 	while (info->virtio->queue_dequeue(info->virtio_queue, &cookie, NULL)) {
184 		if ((int32)(addr_t)cookie == atomic_get(&info->currentRequest))
185 			info->interruptCondition.NotifyAll();
186 	}
187 }
188 
189 
190 static status_t
191 do_io(void* cookie, IOOperation* operation)
192 {
193 	virtio_block_driver_info* info = (virtio_block_driver_info*)cookie;
194 
195 	if (mutex_trylock(&info->lock) != B_OK)
196 		return B_BUSY;
197 
198 	BStackOrHeapArray<physical_entry, 16> entries(operation->VecCount() + 2);
199 
200 	struct virtio_blk_outhdr *header = (struct virtio_blk_outhdr*)info->bufferAddr;
201 	header->type = operation->IsWrite() ? VIRTIO_BLK_T_OUT : VIRTIO_BLK_T_IN;
202 	header->sector = operation->Offset() / 512;
203 	header->ioprio = 1;
204 
205 	uint8* ack = (uint8*)info->bufferAddr + sizeof(struct virtio_blk_outhdr);
206 	*ack = 0xff;
207 
208 	entries[0].address = info->bufferPhysAddr;
209 	entries[0].size = sizeof(struct virtio_blk_outhdr);
210 	entries[operation->VecCount() + 1].address = entries[0].address
211 		+ sizeof(struct virtio_blk_outhdr);
212 	entries[operation->VecCount() + 1].size = sizeof(uint8);
213 
214 	memcpy(entries + 1, operation->Vecs(), operation->VecCount()
215 		* sizeof(physical_entry));
216 
217 	atomic_add(&info->currentRequest, 1);
218 	info->interruptCondition.Add(&info->interruptConditionEntry);
219 
220 	info->virtio->queue_request_v(info->virtio_queue, entries,
221 		1 + (operation->IsWrite() ? operation->VecCount() : 0 ),
222 		1 + (operation->IsWrite() ? 0 : operation->VecCount()),
223 		(void *)(addr_t)info->currentRequest);
224 
225 	status_t result = info->interruptConditionEntry.Wait(B_RELATIVE_TIMEOUT,
226 		10 * 1000 * 1000);
227 
228 	size_t bytesTransferred = 0;
229 	status_t status = B_OK;
230 	if (result != B_OK) {
231 		status = EIO;
232 	} else {
233 		switch (*ack) {
234 			case VIRTIO_BLK_S_OK:
235 				status = B_OK;
236 				bytesTransferred = operation->Length();
237 				break;
238 			case VIRTIO_BLK_S_UNSUPP:
239 				status = ENOTSUP;
240 				break;
241 			default:
242 				status = EIO;
243 				break;
244 		}
245 	}
246 
247 	info->io_scheduler->OperationCompleted(operation, status,
248 		bytesTransferred);
249 	mutex_unlock(&info->lock);
250 	return status;
251 }
252 
253 
254 //	#pragma mark - device module API
255 
256 
257 static status_t
258 virtio_block_init_device(void* _info, void** _cookie)
259 {
260 	CALLED();
261 	virtio_block_driver_info* info = (virtio_block_driver_info*)_info;
262 
263 	device_node* parent = sDeviceManager->get_parent_node(info->node);
264 	sDeviceManager->get_driver(parent, (driver_module_info **)&info->virtio,
265 		(void **)&info->virtio_device);
266 	sDeviceManager->put_node(parent);
267 
268 	info->virtio->negotiate_features(info->virtio_device,
269 		VIRTIO_BLK_F_BARRIER | VIRTIO_BLK_F_SIZE_MAX
270 			| VIRTIO_BLK_F_SEG_MAX | VIRTIO_BLK_F_GEOMETRY
271 			| VIRTIO_BLK_F_RO | VIRTIO_BLK_F_BLK_SIZE
272 			| VIRTIO_BLK_F_FLUSH | VIRTIO_BLK_F_TOPOLOGY
273 			| VIRTIO_FEATURE_RING_INDIRECT_DESC,
274 		&info->features, &get_feature_name);
275 
276 	status_t status = info->virtio->read_device_config(
277 		info->virtio_device, 0, &info->config,
278 		sizeof(struct virtio_blk_config));
279 	if (status != B_OK)
280 		return status;
281 
282 	virtio_block_set_capacity(info);
283 
284 	TRACE("virtio_block: capacity: %" B_PRIu64 ", block_size %" B_PRIu32 "\n",
285 		info->capacity, info->block_size);
286 
287 	status = info->virtio->alloc_queues(info->virtio_device, 1,
288 		&info->virtio_queue);
289 	if (status != B_OK) {
290 		ERROR("queue allocation failed (%s)\n", strerror(status));
291 		return status;
292 	}
293 	status = info->virtio->setup_interrupt(info->virtio_device,
294 		virtio_block_config_callback, info);
295 
296 	if (status == B_OK) {
297 		status = info->virtio->queue_setup_interrupt(info->virtio_queue,
298 			virtio_block_callback, info);
299 	}
300 
301 	*_cookie = info;
302 	return status;
303 }
304 
305 
306 static void
307 virtio_block_uninit_device(void* _cookie)
308 {
309 	CALLED();
310 	virtio_block_driver_info* info = (virtio_block_driver_info*)_cookie;
311 
312 	delete info->io_scheduler;
313 	delete info->dma_resource;
314 }
315 
316 
317 static status_t
318 virtio_block_open(void* _info, const char* path, int openMode, void** _cookie)
319 {
320 	CALLED();
321 	virtio_block_driver_info* info = (virtio_block_driver_info*)_info;
322 
323 	virtio_block_handle* handle = (virtio_block_handle*)malloc(
324 		sizeof(virtio_block_handle));
325 	if (handle == NULL)
326 		return B_NO_MEMORY;
327 
328 	handle->info = info;
329 
330 	*_cookie = handle;
331 	return B_OK;
332 }
333 
334 
335 static status_t
336 virtio_block_close(void* cookie)
337 {
338 	//virtio_block_handle* handle = (virtio_block_handle*)cookie;
339 	CALLED();
340 
341 	return B_OK;
342 }
343 
344 
345 static status_t
346 virtio_block_free(void* cookie)
347 {
348 	CALLED();
349 	virtio_block_handle* handle = (virtio_block_handle*)cookie;
350 
351 	free(handle);
352 	return B_OK;
353 }
354 
355 
356 static status_t
357 virtio_block_io(void *cookie, io_request *request)
358 {
359 	CALLED();
360 	virtio_block_handle* handle = (virtio_block_handle*)cookie;
361 
362 	return handle->info->io_scheduler->ScheduleRequest(request);
363 }
364 
365 
366 static status_t
367 virtio_block_ioctl(void* cookie, uint32 op, void* buffer, size_t length)
368 {
369 	CALLED();
370 	virtio_block_handle* handle = (virtio_block_handle*)cookie;
371 	virtio_block_driver_info* info = handle->info;
372 
373 	TRACE("ioctl(op = %" B_PRIu32 ")\n", op);
374 
375 	switch (op) {
376 		case B_GET_MEDIA_STATUS:
377 		{
378 			user_memcpy(buffer, &info->media_status, sizeof(info->media_status));
379 			TRACE("B_GET_MEDIA_STATUS: 0x%08" B_PRIx32 "\n", info->media_status);
380 			info->media_status = B_OK;
381 			return B_OK;
382 		}
383 
384 		case B_GET_DEVICE_SIZE:
385 		{
386 			size_t size = info->capacity * info->block_size;
387 			return user_memcpy(buffer, &size, sizeof(size_t));
388 		}
389 
390 		case B_GET_GEOMETRY:
391 		{
392 			if (buffer == NULL || length > sizeof(device_geometry))
393 				return B_BAD_VALUE;
394 
395 		 	device_geometry geometry;
396 			status_t status = get_geometry(handle, &geometry);
397 			if (status != B_OK)
398 				return status;
399 
400 			return user_memcpy(buffer, &geometry, length);
401 		}
402 
403 		case B_GET_ICON_NAME:
404 			return user_strlcpy((char*)buffer, "devices/drive-harddisk",
405 				B_FILE_NAME_LENGTH);
406 
407 		case B_GET_VECTOR_ICON:
408 		{
409 			// TODO: take device type into account!
410 			device_icon iconData;
411 			if (length != sizeof(device_icon))
412 				return B_BAD_VALUE;
413 			if (user_memcpy(&iconData, buffer, sizeof(device_icon)) != B_OK)
414 				return B_BAD_ADDRESS;
415 
416 			if (iconData.icon_size >= (int32)sizeof(kDriveIcon)) {
417 				if (user_memcpy(iconData.icon_data, kDriveIcon,
418 						sizeof(kDriveIcon)) != B_OK)
419 					return B_BAD_ADDRESS;
420 			}
421 
422 			iconData.icon_size = sizeof(kDriveIcon);
423 			return user_memcpy(buffer, &iconData, sizeof(device_icon));
424 		}
425 
426 		/*case B_FLUSH_DRIVE_CACHE:
427 			return synchronize_cache(info);*/
428 	}
429 
430 	return B_DEV_INVALID_IOCTL;
431 }
432 
433 
434 bool
435 virtio_block_set_capacity(virtio_block_driver_info* info)
436 {
437 	// get capacity
438 	uint32 blockSize = 512;
439 	if ((info->features & VIRTIO_BLK_F_BLK_SIZE) != 0)
440 		blockSize = info->config.blk_size;
441 	uint64 capacity = info->config.capacity * 512 / blockSize;
442 	uint32 physicalBlockSize = blockSize;
443 
444 	if ((info->features & VIRTIO_BLK_F_TOPOLOGY) != 0
445 		&& info->config.topology.physical_block_exp > 0) {
446 		physicalBlockSize = blockSize * (1 << info->config.topology.physical_block_exp);
447 	}
448 
449 	TRACE("set_capacity(device = %p, capacity = %" B_PRIu64 ", blockSize = %" B_PRIu32 ")\n",
450 		info, capacity, blockSize);
451 
452 	if (info->block_size == blockSize && info->capacity == capacity)
453 		return false;
454 
455 	info->capacity = capacity;
456 
457 	if (info->block_size != 0) {
458 		ERROR("old %" B_PRId32 ", new %" B_PRId32 "\n", info->block_size,
459 			blockSize);
460 		panic("updating DMAResource not yet implemented...");
461 	}
462 
463 	dma_restrictions restrictions;
464 	memset(&restrictions, 0, sizeof(restrictions));
465 	if ((info->features & VIRTIO_BLK_F_SIZE_MAX) != 0)
466 		restrictions.max_segment_size = info->config.size_max;
467 	if ((info->features & VIRTIO_BLK_F_SEG_MAX) != 0)
468 		restrictions.max_segment_count = info->config.seg_max;
469 
470 	// TODO: we need to replace the DMAResource in our IOScheduler
471 	status_t status = info->dma_resource->Init(restrictions, blockSize,
472 		1024, 32);
473 	if (status != B_OK)
474 		panic("initializing DMAResource failed: %s", strerror(status));
475 
476 	info->io_scheduler = new(std::nothrow) IOSchedulerSimple(
477 		info->dma_resource);
478 	if (info->io_scheduler == NULL)
479 		panic("allocating IOScheduler failed.");
480 
481 	// TODO: use whole device name here
482 	status = info->io_scheduler->Init("virtio");
483 	if (status != B_OK)
484 		panic("initializing IOScheduler failed: %s", strerror(status));
485 
486 	info->io_scheduler->SetCallback(do_io, info);
487 
488 	info->block_size = blockSize;
489 	info->physical_block_size = physicalBlockSize;
490 	return true;
491 }
492 
493 
494 //	#pragma mark - driver module API
495 
496 
497 static float
498 virtio_block_supports_device(device_node *parent)
499 {
500 	CALLED();
501 	const char *bus;
502 	uint16 deviceType;
503 
504 	// make sure parent is really the Virtio bus manager
505 	if (sDeviceManager->get_attr_string(parent, B_DEVICE_BUS, &bus, false))
506 		return -1;
507 
508 	if (strcmp(bus, "virtio"))
509 		return 0.0;
510 
511 	// check whether it's really a Direct Access Device
512 	if (sDeviceManager->get_attr_uint16(parent, VIRTIO_DEVICE_TYPE_ITEM,
513 			&deviceType, true) != B_OK || deviceType != VIRTIO_DEVICE_ID_BLOCK)
514 		return 0.0;
515 
516 	TRACE("Virtio block device found!\n");
517 
518 	return 0.6;
519 }
520 
521 
522 static status_t
523 virtio_block_register_device(device_node *node)
524 {
525 	CALLED();
526 
527 	device_attr attrs[] = {
528 		{ B_DEVICE_PRETTY_NAME, B_STRING_TYPE, {.string = "Virtio Block"} },
529 		{ NULL }
530 	};
531 
532 	return sDeviceManager->register_node(node, VIRTIO_BLOCK_DRIVER_MODULE_NAME,
533 		attrs, NULL, NULL);
534 }
535 
536 
537 static status_t
538 virtio_block_init_driver(device_node *node, void **cookie)
539 {
540 	CALLED();
541 
542 	virtio_block_driver_info* info = (virtio_block_driver_info*)malloc(
543 		sizeof(virtio_block_driver_info));
544 	if (info == NULL)
545 		return B_NO_MEMORY;
546 
547 	memset(info, 0, sizeof(*info));
548 
549 	info->media_status = B_OK;
550 	info->dma_resource = new(std::nothrow) DMAResource;
551 	if (info->dma_resource == NULL) {
552 		free(info);
553 		return B_NO_MEMORY;
554 	}
555 
556 	// create command buffer area
557 	info->bufferArea = create_area("virtio_block command buffer", (void**)&info->bufferAddr,
558 		B_ANY_KERNEL_BLOCK_ADDRESS, B_PAGE_SIZE,
559 		B_FULL_LOCK, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA);
560 	if (info->bufferArea < B_OK) {
561 		delete info->dma_resource;
562 		free(info);
563 		return info->bufferArea;
564 	}
565 
566 	physical_entry entry;
567 	status_t status = get_memory_map((void*)info->bufferAddr, B_PAGE_SIZE, &entry, 1);
568 	if (status != B_OK) {
569 		delete_area(info->bufferArea);
570 		delete info->dma_resource;
571 		free(info);
572 		return status;
573 	}
574 
575 	info->bufferPhysAddr = entry.address;
576 	info->interruptCondition.Init(info, "virtio block transfer");
577 	info->currentRequest = 0;
578 	mutex_init(&info->lock, "virtio block request");
579 
580 	info->node = node;
581 
582 	*cookie = info;
583 	return B_OK;
584 }
585 
586 
587 static void
588 virtio_block_uninit_driver(void *_cookie)
589 {
590 	CALLED();
591 	virtio_block_driver_info* info = (virtio_block_driver_info*)_cookie;
592 	mutex_destroy(&info->lock);
593 	delete_area(info->bufferArea);
594 	free(info);
595 }
596 
597 
598 static status_t
599 virtio_block_register_child_devices(void* _cookie)
600 {
601 	CALLED();
602 	virtio_block_driver_info* info = (virtio_block_driver_info*)_cookie;
603 	status_t status;
604 
605 	int32 id = sDeviceManager->create_id(VIRTIO_BLOCK_DEVICE_ID_GENERATOR);
606 	if (id < 0)
607 		return id;
608 
609 	char name[64];
610 	snprintf(name, sizeof(name), "disk/virtual/virtio_block/%" B_PRId32 "/raw",
611 		id);
612 
613 	status = sDeviceManager->publish_device(info->node, name,
614 		VIRTIO_BLOCK_DEVICE_MODULE_NAME);
615 
616 	return status;
617 }
618 
619 
620 //	#pragma mark -
621 
622 
623 module_dependency module_dependencies[] = {
624 	{ B_DEVICE_MANAGER_MODULE_NAME, (module_info**)&sDeviceManager },
625 	{ NULL }
626 };
627 
628 struct device_module_info sVirtioBlockDevice = {
629 	{
630 		VIRTIO_BLOCK_DEVICE_MODULE_NAME,
631 		0,
632 		NULL
633 	},
634 
635 	virtio_block_init_device,
636 	virtio_block_uninit_device,
637 	NULL, // remove,
638 
639 	virtio_block_open,
640 	virtio_block_close,
641 	virtio_block_free,
642 	NULL,	// read
643 	NULL,	// write
644 	virtio_block_io,
645 	virtio_block_ioctl,
646 
647 	NULL,	// select
648 	NULL,	// deselect
649 };
650 
651 struct driver_module_info sVirtioBlockDriver = {
652 	{
653 		VIRTIO_BLOCK_DRIVER_MODULE_NAME,
654 		0,
655 		NULL
656 	},
657 
658 	virtio_block_supports_device,
659 	virtio_block_register_device,
660 	virtio_block_init_driver,
661 	virtio_block_uninit_driver,
662 	virtio_block_register_child_devices,
663 	NULL,	// rescan
664 	NULL,	// removed
665 };
666 
667 module_info* modules[] = {
668 	(module_info*)&sVirtioBlockDriver,
669 	(module_info*)&sVirtioBlockDevice,
670 	NULL
671 };
672