diff options
Diffstat (limited to 'drivers/lguest/lguest_bus.c')
-rw-r--r-- | drivers/lguest/lguest_bus.c | 148 |
1 files changed, 148 insertions, 0 deletions
diff --git a/drivers/lguest/lguest_bus.c b/drivers/lguest/lguest_bus.c new file mode 100644 index 000000000000..18d6ab21a43b --- /dev/null +++ b/drivers/lguest/lguest_bus.c | |||
@@ -0,0 +1,148 @@ | |||
1 | #include <linux/init.h> | ||
2 | #include <linux/bootmem.h> | ||
3 | #include <linux/lguest_bus.h> | ||
4 | #include <asm/io.h> | ||
5 | |||
6 | static ssize_t type_show(struct device *_dev, | ||
7 | struct device_attribute *attr, char *buf) | ||
8 | { | ||
9 | struct lguest_device *dev = container_of(_dev,struct lguest_device,dev); | ||
10 | return sprintf(buf, "%hu", lguest_devices[dev->index].type); | ||
11 | } | ||
12 | static ssize_t features_show(struct device *_dev, | ||
13 | struct device_attribute *attr, char *buf) | ||
14 | { | ||
15 | struct lguest_device *dev = container_of(_dev,struct lguest_device,dev); | ||
16 | return sprintf(buf, "%hx", lguest_devices[dev->index].features); | ||
17 | } | ||
18 | static ssize_t pfn_show(struct device *_dev, | ||
19 | struct device_attribute *attr, char *buf) | ||
20 | { | ||
21 | struct lguest_device *dev = container_of(_dev,struct lguest_device,dev); | ||
22 | return sprintf(buf, "%u", lguest_devices[dev->index].pfn); | ||
23 | } | ||
24 | static ssize_t status_show(struct device *_dev, | ||
25 | struct device_attribute *attr, char *buf) | ||
26 | { | ||
27 | struct lguest_device *dev = container_of(_dev,struct lguest_device,dev); | ||
28 | return sprintf(buf, "%hx", lguest_devices[dev->index].status); | ||
29 | } | ||
30 | static ssize_t status_store(struct device *_dev, struct device_attribute *attr, | ||
31 | const char *buf, size_t count) | ||
32 | { | ||
33 | struct lguest_device *dev = container_of(_dev,struct lguest_device,dev); | ||
34 | if (sscanf(buf, "%hi", &lguest_devices[dev->index].status) != 1) | ||
35 | return -EINVAL; | ||
36 | return count; | ||
37 | } | ||
38 | static struct device_attribute lguest_dev_attrs[] = { | ||
39 | __ATTR_RO(type), | ||
40 | __ATTR_RO(features), | ||
41 | __ATTR_RO(pfn), | ||
42 | __ATTR(status, 0644, status_show, status_store), | ||
43 | __ATTR_NULL | ||
44 | }; | ||
45 | |||
46 | static int lguest_dev_match(struct device *_dev, struct device_driver *_drv) | ||
47 | { | ||
48 | struct lguest_device *dev = container_of(_dev,struct lguest_device,dev); | ||
49 | struct lguest_driver *drv = container_of(_drv,struct lguest_driver,drv); | ||
50 | |||
51 | return (drv->device_type == lguest_devices[dev->index].type); | ||
52 | } | ||
53 | |||
54 | struct lguest_bus { | ||
55 | struct bus_type bus; | ||
56 | struct device dev; | ||
57 | }; | ||
58 | |||
59 | static struct lguest_bus lguest_bus = { | ||
60 | .bus = { | ||
61 | .name = "lguest", | ||
62 | .match = lguest_dev_match, | ||
63 | .dev_attrs = lguest_dev_attrs, | ||
64 | }, | ||
65 | .dev = { | ||
66 | .parent = NULL, | ||
67 | .bus_id = "lguest", | ||
68 | } | ||
69 | }; | ||
70 | |||
71 | static int lguest_dev_probe(struct device *_dev) | ||
72 | { | ||
73 | int ret; | ||
74 | struct lguest_device *dev = container_of(_dev,struct lguest_device,dev); | ||
75 | struct lguest_driver *drv = container_of(dev->dev.driver, | ||
76 | struct lguest_driver, drv); | ||
77 | |||
78 | lguest_devices[dev->index].status |= LGUEST_DEVICE_S_DRIVER; | ||
79 | ret = drv->probe(dev); | ||
80 | if (ret == 0) | ||
81 | lguest_devices[dev->index].status |= LGUEST_DEVICE_S_DRIVER_OK; | ||
82 | return ret; | ||
83 | } | ||
84 | |||
85 | int register_lguest_driver(struct lguest_driver *drv) | ||
86 | { | ||
87 | if (!lguest_devices) | ||
88 | return 0; | ||
89 | |||
90 | drv->drv.bus = &lguest_bus.bus; | ||
91 | drv->drv.name = drv->name; | ||
92 | drv->drv.owner = drv->owner; | ||
93 | drv->drv.probe = lguest_dev_probe; | ||
94 | |||
95 | return driver_register(&drv->drv); | ||
96 | } | ||
97 | EXPORT_SYMBOL_GPL(register_lguest_driver); | ||
98 | |||
99 | static void add_lguest_device(unsigned int index) | ||
100 | { | ||
101 | struct lguest_device *new; | ||
102 | |||
103 | lguest_devices[index].status |= LGUEST_DEVICE_S_ACKNOWLEDGE; | ||
104 | new = kmalloc(sizeof(struct lguest_device), GFP_KERNEL); | ||
105 | if (!new) { | ||
106 | printk(KERN_EMERG "Cannot allocate lguest device %u\n", index); | ||
107 | lguest_devices[index].status |= LGUEST_DEVICE_S_FAILED; | ||
108 | return; | ||
109 | } | ||
110 | |||
111 | new->index = index; | ||
112 | new->private = NULL; | ||
113 | memset(&new->dev, 0, sizeof(new->dev)); | ||
114 | new->dev.parent = &lguest_bus.dev; | ||
115 | new->dev.bus = &lguest_bus.bus; | ||
116 | sprintf(new->dev.bus_id, "%u", index); | ||
117 | if (device_register(&new->dev) != 0) { | ||
118 | printk(KERN_EMERG "Cannot register lguest device %u\n", index); | ||
119 | lguest_devices[index].status |= LGUEST_DEVICE_S_FAILED; | ||
120 | kfree(new); | ||
121 | } | ||
122 | } | ||
123 | |||
124 | static void scan_devices(void) | ||
125 | { | ||
126 | unsigned int i; | ||
127 | |||
128 | for (i = 0; i < LGUEST_MAX_DEVICES; i++) | ||
129 | if (lguest_devices[i].type) | ||
130 | add_lguest_device(i); | ||
131 | } | ||
132 | |||
133 | static int __init lguest_bus_init(void) | ||
134 | { | ||
135 | if (strcmp(paravirt_ops.name, "lguest") != 0) | ||
136 | return 0; | ||
137 | |||
138 | /* Devices are in page above top of "normal" mem. */ | ||
139 | lguest_devices = lguest_map(max_pfn<<PAGE_SHIFT, 1); | ||
140 | |||
141 | if (bus_register(&lguest_bus.bus) != 0 | ||
142 | || device_register(&lguest_bus.dev) != 0) | ||
143 | panic("lguest bus registration failed"); | ||
144 | |||
145 | scan_devices(); | ||
146 | return 0; | ||
147 | } | ||
148 | postcore_initcall(lguest_bus_init); | ||