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