blob: a7c666a795fc825ac47413b59bf660857b8f159d [file] [log] [blame]
#include "kvm/devices.h"
#include "kvm/kvm.h"
#include "kvm/pci.h"
#include "kvm/virtio-mmio.h"
#include <linux/err.h>
#include <linux/rbtree.h>
struct device_bus {
struct rb_root root;
int dev_num;
};
static struct device_bus device_trees[DEVICE_BUS_MAX] = {
[0 ... (DEVICE_BUS_MAX - 1)] = { RB_ROOT, 0 },
};
int device__register(struct device_header *dev)
{
struct device_bus *bus;
struct rb_node **node, *parent = NULL;
if (dev->bus_type >= DEVICE_BUS_MAX) {
pr_warning("Ignoring device registration on unknown bus %d\n",
dev->bus_type);
return -EINVAL;
}
bus = &device_trees[dev->bus_type];
dev->dev_num = bus->dev_num++;
switch (dev->bus_type) {
case DEVICE_BUS_PCI:
pci__assign_irq(dev);
break;
case DEVICE_BUS_MMIO:
virtio_mmio_assign_irq(dev);
break;
default:
break;
}
node = &bus->root.rb_node;
while (*node) {
int num = rb_entry(*node, struct device_header, node)->dev_num;
int result = dev->dev_num - num;
parent = *node;
if (result < 0)
node = &((*node)->rb_left);
else if (result > 0)
node = &((*node)->rb_right);
else
return -EEXIST;
}
rb_link_node(&dev->node, parent, node);
rb_insert_color(&dev->node, &bus->root);
return 0;
}
void device__unregister(struct device_header *dev)
{
struct device_bus *bus = &device_trees[dev->bus_type];
rb_erase(&dev->node, &bus->root);
}
struct device_header *device__find_dev(enum device_bus_type bus_type, u8 dev_num)
{
struct rb_node *node;
if (bus_type >= DEVICE_BUS_MAX)
return ERR_PTR(-EINVAL);
node = device_trees[bus_type].root.rb_node;
while (node) {
struct device_header *dev = rb_entry(node, struct device_header,
node);
if (dev_num < dev->dev_num) {
node = node->rb_left;
} else if (dev_num > dev->dev_num) {
node = node->rb_right;
} else {
return dev;
}
}
return NULL;
}
struct device_header *device__first_dev(enum device_bus_type bus_type)
{
struct rb_node *node;
if (bus_type >= DEVICE_BUS_MAX)
return NULL;
node = rb_first(&device_trees[bus_type].root);
return node ? rb_entry(node, struct device_header, node) : NULL;
}
struct device_header *device__next_dev(struct device_header *dev)
{
struct rb_node *node = rb_next(&dev->node);
return node ? rb_entry(node, struct device_header, node) : NULL;
}