xref: /haiku/src/add-ons/kernel/drivers/disk/virtual/virtio_block/virtio_block.cpp (revision 002f37b0cca92e4cf72857c72ac95db5a8b09615)
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 struct DMAResource;
13 struct 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 	release_sem_etc(info->sem_cb, 1, B_DO_NOT_RESCHEDULE);
192 }
193 
194 
195 static status_t
196 do_io(void* cookie, IOOperation* operation)
197 {
198 	virtio_block_driver_info* info = (virtio_block_driver_info*)cookie;
199 
200 	size_t bytesTransferred = 0;
201 	status_t status = B_OK;
202 
203 	physical_entry entries[operation->VecCount() + 2];
204 
205 	void *buffer = malloc(sizeof(struct virtio_blk_outhdr) + sizeof(uint8));
206 	struct virtio_blk_outhdr *header = (struct virtio_blk_outhdr*)buffer;
207 	header->type = operation->IsWrite() ? VIRTIO_BLK_T_OUT : VIRTIO_BLK_T_IN;
208 	header->sector = operation->Offset() / 512;
209 	header->ioprio = 1;
210 
211 	uint8* ack = (uint8*)buffer + sizeof(struct virtio_blk_outhdr);
212 	*ack = 0xff;
213 
214 	get_memory_map(buffer, sizeof(struct virtio_blk_outhdr) + sizeof(uint8),
215 		&entries[0], 1);
216 	entries[operation->VecCount() + 1].address = entries[0].address
217 		+ sizeof(struct virtio_blk_outhdr);
218 	entries[operation->VecCount() + 1].size = sizeof(uint8);
219 	entries[0].size = sizeof(struct virtio_blk_outhdr);
220 
221 	memcpy(entries + 1, operation->Vecs(), operation->VecCount()
222 		* sizeof(physical_entry));
223 
224 	info->virtio->queue_request_v(info->virtio_queue, entries,
225 		1 + (operation->IsWrite() ? operation->VecCount() : 0 ),
226 		1 + (operation->IsWrite() ? 0 : operation->VecCount()),
227 		virtio_block_callback, info);
228 
229 	acquire_sem(info->sem_cb);
230 
231 	switch (*ack) {
232 		case VIRTIO_BLK_S_OK:
233 			status = B_OK;
234 			bytesTransferred = operation->Length();
235 			break;
236 		case VIRTIO_BLK_S_UNSUPP:
237 			status = ENOTSUP;
238 			break;
239 		default:
240 			status = EIO;
241 			break;
242 	}
243 	free(buffer);
244 
245 	info->io_scheduler->OperationCompleted(operation, status,
246 		bytesTransferred);
247 	return status;
248 }
249 
250 
251 //	#pragma mark - device module API
252 
253 
254 static status_t
255 virtio_block_init_device(void* _info, void** _cookie)
256 {
257 	CALLED();
258 	virtio_block_driver_info* info = (virtio_block_driver_info*)_info;
259 
260 	device_node* parent = sDeviceManager->get_parent_node(info->node);
261 	sDeviceManager->get_driver(parent, (driver_module_info **)&info->virtio,
262 		(void **)&info->virtio_device);
263 	sDeviceManager->put_node(parent);
264 
265 	info->virtio->negociate_features(info->virtio_device,
266 		VIRTIO_BLK_F_BARRIER | VIRTIO_BLK_F_SIZE_MAX
267 			| VIRTIO_BLK_F_SEG_MAX | VIRTIO_BLK_F_GEOMETRY
268 			| VIRTIO_BLK_F_RO | VIRTIO_BLK_F_BLK_SIZE
269 			| VIRTIO_BLK_F_FLUSH | VIRTIO_FEATURE_RING_INDIRECT_DESC,
270 		&info->features, &get_feature_name);
271 
272 	status_t status = info->virtio->read_device_config(
273 		info->virtio_device, 0, &info->config,
274 		sizeof(struct virtio_blk_config));
275 	if (status != B_OK)
276 		return status;
277 
278 	// and get (initial) capacity
279 	uint32 block_size = 512;
280 	if ((info->features & VIRTIO_BLK_F_BLK_SIZE) != 0)
281 		block_size = info->config.blk_size;
282 	uint64 capacity = info->config.capacity * 512 / block_size;
283 
284 	virtio_block_set_capacity(info, capacity, block_size);
285 
286 	TRACE("virtio_block: capacity: %" B_PRIu64 ", block_size %" B_PRIu32 "\n",
287 		info->capacity, info->block_size);
288 
289 	status = info->virtio->alloc_queues(info->virtio_device, 1,
290 		&info->virtio_queue);
291 	if (status != B_OK) {
292 		ERROR("queue allocation failed (%s)\n", strerror(status));
293 		return status;
294 	}
295 	status = info->virtio->setup_interrupt(info->virtio_device,
296 		virtio_block_config_callback, info);
297 
298 	*_cookie = info;
299 	return status;
300 }
301 
302 
303 static void
304 virtio_block_uninit_device(void* _cookie)
305 {
306 	CALLED();
307 	virtio_block_driver_info* info = (virtio_block_driver_info*)_cookie;
308 
309 	delete info->io_scheduler;
310 	delete info->dma_resource;
311 }
312 
313 
314 static status_t
315 virtio_block_open(void* _info, const char* path, int openMode, void** _cookie)
316 {
317 	CALLED();
318 	virtio_block_driver_info* info = (virtio_block_driver_info*)_info;
319 
320 	virtio_block_handle* handle = (virtio_block_handle*)malloc(
321 		sizeof(virtio_block_handle));
322 	if (handle == NULL)
323 		return B_NO_MEMORY;
324 
325 	handle->info = info;
326 
327 	*_cookie = handle;
328 	return B_OK;
329 }
330 
331 
332 static status_t
333 virtio_block_close(void* cookie)
334 {
335 	//virtio_block_handle* handle = (virtio_block_handle*)cookie;
336 	CALLED();
337 
338 	return B_OK;
339 }
340 
341 
342 static status_t
343 virtio_block_free(void* cookie)
344 {
345 	CALLED();
346 	virtio_block_handle* handle = (virtio_block_handle*)cookie;
347 
348 	free(handle);
349 	return B_OK;
350 }
351 
352 
353 static status_t
354 virtio_block_read(void* cookie, off_t pos, void* buffer, size_t* _length)
355 {
356 	CALLED();
357 	virtio_block_handle* handle = (virtio_block_handle*)cookie;
358 	size_t length = *_length;
359 
360 	IORequest request;
361 	status_t status = request.Init(pos, (addr_t)buffer, length, false, 0);
362 	if (status != B_OK)
363 		return status;
364 
365 	status = handle->info->io_scheduler->ScheduleRequest(&request);
366 	if (status != B_OK)
367 		return status;
368 
369 	status = request.Wait(0, 0);
370 	if (status == B_OK)
371 		*_length = length;
372 	else
373 		dprintf("read(): request.Wait() returned: %s\n", strerror(status));
374 
375 	return status;
376 }
377 
378 
379 static status_t
380 virtio_block_write(void* cookie, off_t pos, const void* buffer,
381 	size_t* _length)
382 {
383 	CALLED();
384 	virtio_block_handle* handle = (virtio_block_handle*)cookie;
385 	size_t length = *_length;
386 
387 	IORequest request;
388 	status_t status = request.Init(pos, (addr_t)buffer, length, true, 0);
389 	if (status != B_OK)
390 		return status;
391 
392 	status = handle->info->io_scheduler->ScheduleRequest(&request);
393 	if (status != B_OK)
394 		return status;
395 
396 	status = request.Wait(0, 0);
397 	if (status == B_OK)
398 		*_length = length;
399 	else
400 		dprintf("write(): request.Wait() returned: %s\n", strerror(status));
401 
402 	return status;
403 }
404 
405 
406 static status_t
407 virtio_block_io(void *cookie, io_request *request)
408 {
409 	CALLED();
410 	virtio_block_handle* handle = (virtio_block_handle*)cookie;
411 
412 	return handle->info->io_scheduler->ScheduleRequest(request);
413 }
414 
415 
416 static status_t
417 virtio_block_ioctl(void* cookie, uint32 op, void* buffer, size_t length)
418 {
419 	CALLED();
420 	virtio_block_handle* handle = (virtio_block_handle*)cookie;
421 	virtio_block_driver_info* info = handle->info;
422 
423 	TRACE("ioctl(op = %ld)\n", op);
424 
425 	switch (op) {
426 		case B_GET_MEDIA_STATUS:
427 		{
428 			*(status_t *)buffer = info->media_status;
429 			info->media_status = B_OK;
430 			TRACE("B_GET_MEDIA_STATUS: 0x%08lx\n", *(status_t *)buffer);
431 			return B_OK;
432 			break;
433 		}
434 
435 		case B_GET_DEVICE_SIZE:
436 		{
437 			size_t size = info->capacity * info->block_size;
438 			return user_memcpy(buffer, &size, sizeof(size_t));
439 		}
440 
441 		case B_GET_GEOMETRY:
442 		{
443 			if (buffer == NULL /*|| length != sizeof(device_geometry)*/)
444 				return B_BAD_VALUE;
445 
446 		 	device_geometry geometry;
447 			status_t status = get_geometry(handle, &geometry);
448 			if (status != B_OK)
449 				return status;
450 
451 			return user_memcpy(buffer, &geometry, sizeof(device_geometry));
452 		}
453 
454 		case B_GET_ICON_NAME:
455 			return user_strlcpy((char*)buffer, "devices/drive-harddisk",
456 				B_FILE_NAME_LENGTH);
457 
458 		case B_GET_VECTOR_ICON:
459 		{
460 			// TODO: take device type into account!
461 			device_icon iconData;
462 			if (length != sizeof(device_icon))
463 				return B_BAD_VALUE;
464 			if (user_memcpy(&iconData, buffer, sizeof(device_icon)) != B_OK)
465 				return B_BAD_ADDRESS;
466 
467 			if (iconData.icon_size >= (int32)sizeof(kDriveIcon)) {
468 				if (user_memcpy(iconData.icon_data, kDriveIcon,
469 						sizeof(kDriveIcon)) != B_OK)
470 					return B_BAD_ADDRESS;
471 			}
472 
473 			iconData.icon_size = sizeof(kDriveIcon);
474 			return user_memcpy(buffer, &iconData, sizeof(device_icon));
475 		}
476 
477 		/*case B_FLUSH_DRIVE_CACHE:
478 			return synchronize_cache(info);*/
479 	}
480 
481 	return B_DEV_INVALID_IOCTL;
482 }
483 
484 
485 void
486 virtio_block_set_capacity(virtio_block_driver_info* info, uint64 capacity,
487 	uint32 blockSize)
488 {
489 	TRACE("set_capacity(device = %p, capacity = %Ld, blockSize = %ld)\n",
490 		info, capacity, blockSize);
491 
492 	// get log2, if possible
493 	uint32 blockShift = log2(blockSize);
494 
495 	if ((1UL << blockShift) != blockSize)
496 		blockShift = 0;
497 
498 	info->capacity = capacity;
499 
500 	if (info->block_size != blockSize) {
501 		if (info->block_size != 0) {
502 			ERROR("old %" B_PRId32 ", new %" B_PRId32 "\n", info->block_size,
503 				blockSize);
504 			panic("updating DMAResource not yet implemented...");
505 		}
506 
507 		dma_restrictions restrictions;
508 		memset(&restrictions, 0, sizeof(restrictions));
509 		if ((info->features & VIRTIO_BLK_F_SIZE_MAX) != 0)
510 			restrictions.max_segment_size = info->config.size_max;
511 		if ((info->features & VIRTIO_BLK_F_SEG_MAX) != 0)
512 			restrictions.max_segment_count = info->config.seg_max;
513 
514 		// TODO: we need to replace the DMAResource in our IOScheduler
515 		status_t status = info->dma_resource->Init(restrictions, blockSize,
516 			1024, 32);
517 		if (status != B_OK)
518 			panic("initializing DMAResource failed: %s", strerror(status));
519 
520 		info->io_scheduler = new(std::nothrow) IOSchedulerSimple(
521 			info->dma_resource);
522 		if (info->io_scheduler == NULL)
523 			panic("allocating IOScheduler failed.");
524 
525 		// TODO: use whole device name here
526 		status = info->io_scheduler->Init("virtio");
527 		if (status != B_OK)
528 			panic("initializing IOScheduler failed: %s", strerror(status));
529 
530 		info->io_scheduler->SetCallback(do_io, info);
531 	}
532 
533 	info->block_size = blockSize;
534 }
535 
536 
537 //	#pragma mark - driver module API
538 
539 
540 static float
541 virtio_block_supports_device(device_node *parent)
542 {
543 	CALLED();
544 	const char *bus;
545 	uint16 deviceType;
546 
547 	// make sure parent is really the Virtio bus manager
548 	if (sDeviceManager->get_attr_string(parent, B_DEVICE_BUS, &bus, false))
549 		return -1;
550 
551 	if (strcmp(bus, "virtio"))
552 		return 0.0;
553 
554 	// check whether it's really a Direct Access Device
555 	if (sDeviceManager->get_attr_uint16(parent, VIRTIO_DEVICE_TYPE_ITEM,
556 			&deviceType, true) != B_OK || deviceType != VIRTIO_DEVICE_ID_BLOCK)
557 		return 0.0;
558 
559 	TRACE("Virtio block device found!\n");
560 
561 	return 0.6;
562 }
563 
564 
565 static status_t
566 virtio_block_register_device(device_node *node)
567 {
568 	CALLED();
569 
570 	// ready to register
571 	device_attr attrs[] = {
572 		{ NULL }
573 	};
574 
575 	return sDeviceManager->register_node(node, VIRTIO_BLOCK_DRIVER_MODULE_NAME,
576 		attrs, NULL, NULL);
577 }
578 
579 
580 static status_t
581 virtio_block_init_driver(device_node *node, void **cookie)
582 {
583 	CALLED();
584 
585 	virtio_block_driver_info* info = (virtio_block_driver_info*)malloc(
586 		sizeof(virtio_block_driver_info));
587 	if (info == NULL)
588 		return B_NO_MEMORY;
589 
590 	memset(info, 0, sizeof(*info));
591 
592 	info->media_status = B_OK;
593 	info->dma_resource = new(std::nothrow) DMAResource;
594 	if (info->dma_resource == NULL) {
595 		free(info);
596 		return B_NO_MEMORY;
597 	}
598 
599 	info->sem_cb = create_sem(0, "virtio_block_cb");
600 	if (info->sem_cb < 0) {
601 		delete info->dma_resource;
602 		status_t status = info->sem_cb;
603 		free(info);
604 		return status;
605 	}
606 	info->node = node;
607 
608 	*cookie = info;
609 	return B_OK;
610 }
611 
612 
613 static void
614 virtio_block_uninit_driver(void *_cookie)
615 {
616 	CALLED();
617 	virtio_block_driver_info* info = (virtio_block_driver_info*)_cookie;
618 	delete_sem(info->sem_cb);
619 	free(info);
620 }
621 
622 
623 static status_t
624 virtio_block_register_child_devices(void* _cookie)
625 {
626 	CALLED();
627 	virtio_block_driver_info* info = (virtio_block_driver_info*)_cookie;
628 	status_t status;
629 
630 	int32 id = sDeviceManager->create_id(VIRTIO_BLOCK_DEVICE_ID_GENERATOR);
631 	if (id < 0)
632 		return id;
633 
634 	char name[64];
635 	snprintf(name, sizeof(name), "disk/virtual/virtio_block/%" B_PRId32 "/raw",
636 		id);
637 
638 	status = sDeviceManager->publish_device(info->node, name,
639 		VIRTIO_BLOCK_DEVICE_MODULE_NAME);
640 
641 	return status;
642 }
643 
644 
645 //	#pragma mark -
646 
647 
648 module_dependency module_dependencies[] = {
649 	{B_DEVICE_MANAGER_MODULE_NAME, (module_info**)&sDeviceManager},
650 	{}
651 };
652 
653 struct device_module_info sVirtioBlockDevice = {
654 	{
655 		VIRTIO_BLOCK_DEVICE_MODULE_NAME,
656 		0,
657 		NULL
658 	},
659 
660 	virtio_block_init_device,
661 	virtio_block_uninit_device,
662 	NULL, // remove,
663 
664 	virtio_block_open,
665 	virtio_block_close,
666 	virtio_block_free,
667 	virtio_block_read,
668 	virtio_block_write,
669 	virtio_block_io,
670 	virtio_block_ioctl,
671 
672 	NULL,	// select
673 	NULL,	// deselect
674 };
675 
676 struct driver_module_info sVirtioBlockDriver = {
677 	{
678 		VIRTIO_BLOCK_DRIVER_MODULE_NAME,
679 		0,
680 		NULL
681 	},
682 
683 	virtio_block_supports_device,
684 	virtio_block_register_device,
685 	virtio_block_init_driver,
686 	virtio_block_uninit_driver,
687 	virtio_block_register_child_devices,
688 	NULL,	// rescan
689 	NULL,	// removed
690 };
691 
692 module_info* modules[] = {
693 	(module_info*)&sVirtioBlockDriver,
694 	(module_info*)&sVirtioBlockDevice,
695 	NULL
696 };
697