/*
 * Copyright 2013, 2018, Jérôme Duval, jerome.duval@gmail.com.
 * Distributed under the terms of the MIT License.
 */
 
 
#include "VirtioPrivate.h"
 
 
device_manager_info *gDeviceManager = NULL;
 
 
//	#pragma mark -
 
 
static status_t
virtio_device_init(device_node *node, void **_device)
{
	CALLED();
	VirtioDevice *device = new(std::nothrow) VirtioDevice(node);
	if (device == NULL)
		return B_NO_MEMORY;
 
	status_t result = device->InitCheck();
	if (result != B_OK) {
		ERROR("failed to set up virtio device object\n");
		return result;
	}
 
	*_device = device;
	return B_OK;
}
 
 
static void
virtio_device_uninit(void *_device)
{
	CALLED();
	VirtioDevice *device = (VirtioDevice *)_device;
	delete device;
}
 
 
static void
virtio_device_removed(void *_device)
{
	CALLED();
	//VirtioDevice *device = (VirtioDevice *)_device;
}
 
 
//	#pragma mark -
 
 
status_t
virtio_negotiate_features(void* _device, uint32 supported,
		uint32* negotiated, const char* (*get_feature_name)(uint32))
{
	CALLED();
	VirtioDevice *device = (VirtioDevice *)_device;
 
	return device->NegotiateFeatures(supported, negotiated, get_feature_name);
}
 
 
status_t
virtio_read_device_config(void* _device, uint8 offset, void* buffer,
		size_t bufferSize)
{
	CALLED();
	VirtioDevice *device = (VirtioDevice *)_device;
 
	return device->ReadDeviceConfig(offset, buffer, bufferSize);
}
 
 
status_t
virtio_write_device_config(void* _device, uint8 offset,
		const void* buffer, size_t bufferSize)
{
	CALLED();
	VirtioDevice *device = (VirtioDevice *)_device;
 
	return device->WriteDeviceConfig(offset, buffer, bufferSize);
}
 
 
status_t
virtio_alloc_queues(virtio_device _device, size_t count, virtio_queue *queues)
{
	CALLED();
	VirtioDevice *device = (VirtioDevice *)_device;
	return device->AllocateQueues(count, queues);
}
 
 
void
virtio_free_queues(virtio_device _device)
{
	CALLED();
	VirtioDevice *device = (VirtioDevice *)_device;
	device->FreeQueues();
}
 
 
status_t
virtio_setup_interrupt(virtio_device _device, virtio_intr_func config_handler,
	void *driverCookie)
{
	CALLED();
	VirtioDevice *device = (VirtioDevice *)_device;
	return device->SetupInterrupt(config_handler, driverCookie);
}
 
 
status_t
virtio_free_interrupts(virtio_device _device)
{
	CALLED();
	VirtioDevice *device = (VirtioDevice *)_device;
	return device->FreeInterrupts();
}
 
 
status_t
virtio_queue_setup_interrupt(virtio_queue _queue, virtio_callback_func handler,
	void *cookie)
{
	CALLED();
	VirtioQueue *queue = (VirtioQueue *)_queue;
	return queue->SetupInterrupt(handler, cookie);
}
 
 
status_t
virtio_queue_request_v(virtio_queue _queue, const physical_entry* vector,
		size_t readVectorCount, size_t writtenVectorCount, void *cookie)
{
	CALLED();
	VirtioQueue *queue = (VirtioQueue *)_queue;
	return queue->QueueRequest(vector, readVectorCount, writtenVectorCount,
		cookie);
}
 
 
status_t
virtio_queue_request(virtio_queue _queue, const physical_entry *readEntry,
		const physical_entry *writtenEntry, void *cookie)
{
	physical_entry entries[2];
	if (readEntry != NULL) {
		entries[0] = *readEntry;
		if (writtenEntry != NULL)
			entries[1] = *writtenEntry;
	} else if (writtenEntry != NULL)
		entries[0] = *writtenEntry;
 
	return virtio_queue_request_v(_queue, entries, readEntry != NULL ? 1 : 0,
		writtenEntry != NULL? 1 : 0, cookie);
}
 
 
bool
virtio_queue_is_full(virtio_queue _queue)
{
	VirtioQueue *queue = (VirtioQueue *)_queue;
	return queue->IsFull();
}
 
 
bool
virtio_queue_is_empty(virtio_queue _queue)
{
	VirtioQueue *queue = (VirtioQueue *)_queue;
	return queue->IsEmpty();
}
 
 
uint16
virtio_queue_size(virtio_queue _queue)
{
	VirtioQueue *queue = (VirtioQueue *)_queue;
	return queue->Size();
}
 
 
void*
virtio_queue_dequeue(virtio_queue _queue, uint32* _usedLength)
{
	VirtioQueue *queue = (VirtioQueue *)_queue;
	return queue->Dequeue(_usedLength);
}
 
 
//	#pragma mark -
 
 
status_t
virtio_added_device(device_node *parent)
{
	CALLED();
 
	uint16 deviceType;
	if (gDeviceManager->get_attr_uint16(parent,
		VIRTIO_DEVICE_TYPE_ITEM, &deviceType, true) != B_OK) {
		ERROR("device type missing\n");
		return B_ERROR;
	}
 
	device_attr attributes[] = {
		// info about device
		{ B_DEVICE_BUS, B_STRING_TYPE, { string: "virtio" }},
		{ VIRTIO_DEVICE_TYPE_ITEM, B_UINT16_TYPE,
			{ ui16: deviceType }},
		{ NULL }
	};
 
	return gDeviceManager->register_node(parent, VIRTIO_DEVICE_MODULE_NAME,
		attributes, NULL, NULL);
}
 
 
status_t
virtio_queue_interrupt_handler(virtio_sim sim, uint16 queue)
{
	VirtioDevice* device = (VirtioDevice*)sim;
	return device->QueueInterrupt(queue);
}
 
 
status_t
virtio_config_interrupt_handler(virtio_sim sim)
{
	VirtioDevice* device = (VirtioDevice*)sim;
	return device->ConfigInterrupt();
}
 
 
static status_t
std_ops(int32 op, ...)
{
	switch (op) {
		case B_MODULE_INIT:
		case B_MODULE_UNINIT:
			return B_OK;
 
		default:
			break;
	}
 
	return B_ERROR;
}
 
 
//	#pragma mark -
 
 
virtio_device_interface virtio_device_module = {
	{
		{
			VIRTIO_DEVICE_MODULE_NAME,
			0,
			std_ops
		},
 
		NULL, // supported devices
		NULL, // register node
		virtio_device_init,
		virtio_device_uninit,
		NULL, // register child devices
		NULL, // rescan
		virtio_device_removed,
		NULL, // suspend
		NULL, // resume
	},
 
	virtio_negotiate_features,
	virtio_read_device_config,
	virtio_write_device_config,
	virtio_alloc_queues,
	virtio_free_queues,
	virtio_setup_interrupt,
	virtio_free_interrupts,
	virtio_queue_setup_interrupt,
	virtio_queue_request,
	virtio_queue_request_v,
	virtio_queue_is_full,
	virtio_queue_is_empty,
	virtio_queue_size,
	virtio_queue_dequeue
};
 
virtio_for_controller_interface virtio_for_controller_module = {
	{
		{
			VIRTIO_FOR_CONTROLLER_MODULE_NAME,
			0,
			&std_ops
		},
 
		NULL, // supported devices
		virtio_added_device,
		NULL,
		NULL,
		NULL
	},
 
	virtio_queue_interrupt_handler,
	virtio_config_interrupt_handler
};
 
 
module_dependency module_dependencies[] = {
	{ B_DEVICE_MANAGER_MODULE_NAME, (module_info **)&gDeviceManager },
	{}
};
 
 
extern struct driver_module_info sVirtioBalloonDriver;
extern struct driver_module_info sVirtioBalloonDeviceInterface;
 
 
module_info *modules[] = {
	(module_info *)&virtio_for_controller_module,
	(module_info *)&virtio_device_module,
	(module_info *)&sVirtioBalloonDriver,
	(module_info *)&sVirtioBalloonDeviceInterface,
	NULL
};
 

V773 The function was exited without releasing the 'device' pointer. A memory leak is possible.