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