diff options
Diffstat (limited to 'drivers/virtio/virtio.c')
-rw-r--r-- | drivers/virtio/virtio.c | 189 |
1 files changed, 189 insertions, 0 deletions
diff --git a/drivers/virtio/virtio.c b/drivers/virtio/virtio.c new file mode 100644 index 000000000000..15d7787dea87 --- /dev/null +++ b/drivers/virtio/virtio.c | |||
@@ -0,0 +1,189 @@ | |||
1 | #include <linux/virtio.h> | ||
2 | #include <linux/spinlock.h> | ||
3 | #include <linux/virtio_config.h> | ||
4 | |||
5 | static ssize_t device_show(struct device *_d, | ||
6 | struct device_attribute *attr, char *buf) | ||
7 | { | ||
8 | struct virtio_device *dev = container_of(_d,struct virtio_device,dev); | ||
9 | return sprintf(buf, "%hu", dev->id.device); | ||
10 | } | ||
11 | static ssize_t vendor_show(struct device *_d, | ||
12 | struct device_attribute *attr, char *buf) | ||
13 | { | ||
14 | struct virtio_device *dev = container_of(_d,struct virtio_device,dev); | ||
15 | return sprintf(buf, "%hu", dev->id.vendor); | ||
16 | } | ||
17 | static ssize_t status_show(struct device *_d, | ||
18 | struct device_attribute *attr, char *buf) | ||
19 | { | ||
20 | struct virtio_device *dev = container_of(_d,struct virtio_device,dev); | ||
21 | return sprintf(buf, "0x%08x", dev->config->get_status(dev)); | ||
22 | } | ||
23 | static ssize_t modalias_show(struct device *_d, | ||
24 | struct device_attribute *attr, char *buf) | ||
25 | { | ||
26 | struct virtio_device *dev = container_of(_d,struct virtio_device,dev); | ||
27 | |||
28 | return sprintf(buf, "virtio:d%08Xv%08X\n", | ||
29 | dev->id.device, dev->id.vendor); | ||
30 | } | ||
31 | static struct device_attribute virtio_dev_attrs[] = { | ||
32 | __ATTR_RO(device), | ||
33 | __ATTR_RO(vendor), | ||
34 | __ATTR_RO(status), | ||
35 | __ATTR_RO(modalias), | ||
36 | __ATTR_NULL | ||
37 | }; | ||
38 | |||
39 | static inline int virtio_id_match(const struct virtio_device *dev, | ||
40 | const struct virtio_device_id *id) | ||
41 | { | ||
42 | if (id->device != dev->id.device) | ||
43 | return 0; | ||
44 | |||
45 | return id->vendor == VIRTIO_DEV_ANY_ID || id->vendor != dev->id.vendor; | ||
46 | } | ||
47 | |||
48 | /* This looks through all the IDs a driver claims to support. If any of them | ||
49 | * match, we return 1 and the kernel will call virtio_dev_probe(). */ | ||
50 | static int virtio_dev_match(struct device *_dv, struct device_driver *_dr) | ||
51 | { | ||
52 | unsigned int i; | ||
53 | struct virtio_device *dev = container_of(_dv,struct virtio_device,dev); | ||
54 | const struct virtio_device_id *ids; | ||
55 | |||
56 | ids = container_of(_dr, struct virtio_driver, driver)->id_table; | ||
57 | for (i = 0; ids[i].device; i++) | ||
58 | if (virtio_id_match(dev, &ids[i])) | ||
59 | return 1; | ||
60 | return 0; | ||
61 | } | ||
62 | |||
63 | static int virtio_uevent(struct device *_dv, struct kobj_uevent_env *env) | ||
64 | { | ||
65 | struct virtio_device *dev = container_of(_dv,struct virtio_device,dev); | ||
66 | |||
67 | return add_uevent_var(env, "MODALIAS=virtio:d%08Xv%08X", | ||
68 | dev->id.device, dev->id.vendor); | ||
69 | } | ||
70 | |||
71 | static struct bus_type virtio_bus = { | ||
72 | .name = "virtio", | ||
73 | .match = virtio_dev_match, | ||
74 | .dev_attrs = virtio_dev_attrs, | ||
75 | .uevent = virtio_uevent, | ||
76 | }; | ||
77 | |||
78 | static void add_status(struct virtio_device *dev, unsigned status) | ||
79 | { | ||
80 | dev->config->set_status(dev, dev->config->get_status(dev) | status); | ||
81 | } | ||
82 | |||
83 | static int virtio_dev_probe(struct device *_d) | ||
84 | { | ||
85 | int err; | ||
86 | struct virtio_device *dev = container_of(_d,struct virtio_device,dev); | ||
87 | struct virtio_driver *drv = container_of(dev->dev.driver, | ||
88 | struct virtio_driver, driver); | ||
89 | |||
90 | add_status(dev, VIRTIO_CONFIG_S_DRIVER); | ||
91 | err = drv->probe(dev); | ||
92 | if (err) | ||
93 | add_status(dev, VIRTIO_CONFIG_S_FAILED); | ||
94 | else | ||
95 | add_status(dev, VIRTIO_CONFIG_S_DRIVER_OK); | ||
96 | return err; | ||
97 | } | ||
98 | |||
99 | int register_virtio_driver(struct virtio_driver *driver) | ||
100 | { | ||
101 | driver->driver.bus = &virtio_bus; | ||
102 | driver->driver.probe = virtio_dev_probe; | ||
103 | return driver_register(&driver->driver); | ||
104 | } | ||
105 | EXPORT_SYMBOL_GPL(register_virtio_driver); | ||
106 | |||
107 | void unregister_virtio_driver(struct virtio_driver *driver) | ||
108 | { | ||
109 | driver_unregister(&driver->driver); | ||
110 | } | ||
111 | EXPORT_SYMBOL_GPL(unregister_virtio_driver); | ||
112 | |||
113 | int register_virtio_device(struct virtio_device *dev) | ||
114 | { | ||
115 | int err; | ||
116 | |||
117 | dev->dev.bus = &virtio_bus; | ||
118 | sprintf(dev->dev.bus_id, "%u", dev->index); | ||
119 | |||
120 | /* Acknowledge that we've seen the device. */ | ||
121 | add_status(dev, VIRTIO_CONFIG_S_ACKNOWLEDGE); | ||
122 | |||
123 | /* device_register() causes the bus infrastructure to look for a | ||
124 | * matching driver. */ | ||
125 | err = device_register(&dev->dev); | ||
126 | if (err) | ||
127 | add_status(dev, VIRTIO_CONFIG_S_FAILED); | ||
128 | return err; | ||
129 | } | ||
130 | EXPORT_SYMBOL_GPL(register_virtio_device); | ||
131 | |||
132 | void unregister_virtio_device(struct virtio_device *dev) | ||
133 | { | ||
134 | device_unregister(&dev->dev); | ||
135 | } | ||
136 | EXPORT_SYMBOL_GPL(unregister_virtio_device); | ||
137 | |||
138 | int __virtio_config_val(struct virtio_device *vdev, | ||
139 | u8 type, void *val, size_t size) | ||
140 | { | ||
141 | void *token; | ||
142 | unsigned int len; | ||
143 | |||
144 | token = vdev->config->find(vdev, type, &len); | ||
145 | if (!token) | ||
146 | return -ENOENT; | ||
147 | |||
148 | if (len != size) | ||
149 | return -EIO; | ||
150 | |||
151 | vdev->config->get(vdev, token, val, size); | ||
152 | return 0; | ||
153 | } | ||
154 | EXPORT_SYMBOL_GPL(__virtio_config_val); | ||
155 | |||
156 | int virtio_use_bit(struct virtio_device *vdev, | ||
157 | void *token, unsigned int len, unsigned int bitnum) | ||
158 | { | ||
159 | unsigned long bits[16]; | ||
160 | |||
161 | /* This makes it convenient to pass-through find() results. */ | ||
162 | if (!token) | ||
163 | return 0; | ||
164 | |||
165 | /* bit not in range of this bitfield? */ | ||
166 | if (bitnum * 8 >= len / 2) | ||
167 | return 0; | ||
168 | |||
169 | /* Giant feature bitfields are silly. */ | ||
170 | BUG_ON(len > sizeof(bits)); | ||
171 | vdev->config->get(vdev, token, bits, len); | ||
172 | |||
173 | if (!test_bit(bitnum, bits)) | ||
174 | return 0; | ||
175 | |||
176 | /* Set acknowledge bit, and write it back. */ | ||
177 | set_bit(bitnum + len * 8 / 2, bits); | ||
178 | vdev->config->set(vdev, token, bits, len); | ||
179 | return 1; | ||
180 | } | ||
181 | EXPORT_SYMBOL_GPL(virtio_use_bit); | ||
182 | |||
183 | static int virtio_init(void) | ||
184 | { | ||
185 | if (bus_register(&virtio_bus) != 0) | ||
186 | panic("virtio bus registration failed"); | ||
187 | return 0; | ||
188 | } | ||
189 | core_initcall(virtio_init); | ||