diff options
Diffstat (limited to 'drivers/message/i2o/driver.c')
-rw-r--r-- | drivers/message/i2o/driver.c | 374 |
1 files changed, 374 insertions, 0 deletions
diff --git a/drivers/message/i2o/driver.c b/drivers/message/i2o/driver.c new file mode 100644 index 000000000000..91f4edbb2a27 --- /dev/null +++ b/drivers/message/i2o/driver.c | |||
@@ -0,0 +1,374 @@ | |||
1 | /* | ||
2 | * Functions to handle I2O drivers (OSMs) and I2O bus type for sysfs | ||
3 | * | ||
4 | * Copyright (C) 2004 Markus Lidel <Markus.Lidel@shadowconnect.com> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify it | ||
7 | * under the terms of the GNU General Public License as published by the | ||
8 | * Free Software Foundation; either version 2 of the License, or (at your | ||
9 | * option) any later version. | ||
10 | * | ||
11 | * Fixes/additions: | ||
12 | * Markus Lidel <Markus.Lidel@shadowconnect.com> | ||
13 | * initial version. | ||
14 | */ | ||
15 | |||
16 | #include <linux/device.h> | ||
17 | #include <linux/module.h> | ||
18 | #include <linux/rwsem.h> | ||
19 | #include <linux/i2o.h> | ||
20 | |||
21 | /* max_drivers - Maximum I2O drivers (OSMs) which could be registered */ | ||
22 | unsigned int i2o_max_drivers = I2O_MAX_DRIVERS; | ||
23 | module_param_named(max_drivers, i2o_max_drivers, uint, 0); | ||
24 | MODULE_PARM_DESC(max_drivers, "maximum number of OSM's to support"); | ||
25 | |||
26 | /* I2O drivers lock and array */ | ||
27 | static spinlock_t i2o_drivers_lock; | ||
28 | static struct i2o_driver **i2o_drivers; | ||
29 | |||
30 | /** | ||
31 | * i2o_bus_match - Tell if a I2O device class id match the class ids of | ||
32 | * the I2O driver (OSM) | ||
33 | * | ||
34 | * @dev: device which should be verified | ||
35 | * @drv: the driver to match against | ||
36 | * | ||
37 | * Used by the bus to check if the driver wants to handle the device. | ||
38 | * | ||
39 | * Returns 1 if the class ids of the driver match the class id of the | ||
40 | * device, otherwise 0. | ||
41 | */ | ||
42 | static int i2o_bus_match(struct device *dev, struct device_driver *drv) | ||
43 | { | ||
44 | struct i2o_device *i2o_dev = to_i2o_device(dev); | ||
45 | struct i2o_driver *i2o_drv = to_i2o_driver(drv); | ||
46 | struct i2o_class_id *ids = i2o_drv->classes; | ||
47 | |||
48 | if (ids) | ||
49 | while (ids->class_id != I2O_CLASS_END) { | ||
50 | if (ids->class_id == i2o_dev->lct_data.class_id) | ||
51 | return 1; | ||
52 | ids++; | ||
53 | } | ||
54 | return 0; | ||
55 | }; | ||
56 | |||
57 | /* I2O bus type */ | ||
58 | struct bus_type i2o_bus_type = { | ||
59 | .name = "i2o", | ||
60 | .match = i2o_bus_match, | ||
61 | }; | ||
62 | |||
63 | /** | ||
64 | * i2o_driver_register - Register a I2O driver (OSM) in the I2O core | ||
65 | * @drv: I2O driver which should be registered | ||
66 | * | ||
67 | * Registers the OSM drv in the I2O core and creates an event queues if | ||
68 | * necessary. | ||
69 | * | ||
70 | * Returns 0 on success or negative error code on failure. | ||
71 | */ | ||
72 | int i2o_driver_register(struct i2o_driver *drv) | ||
73 | { | ||
74 | struct i2o_controller *c; | ||
75 | int i; | ||
76 | int rc = 0; | ||
77 | unsigned long flags; | ||
78 | |||
79 | pr_debug("i2o: Register driver %s\n", drv->name); | ||
80 | |||
81 | if (drv->event) { | ||
82 | drv->event_queue = create_workqueue(drv->name); | ||
83 | if (!drv->event_queue) { | ||
84 | printk(KERN_ERR "i2o: Could not initialize event queue " | ||
85 | "for driver %s\n", drv->name); | ||
86 | return -EFAULT; | ||
87 | } | ||
88 | pr_debug("i2o: Event queue initialized for driver %s\n", | ||
89 | drv->name); | ||
90 | } else | ||
91 | drv->event_queue = NULL; | ||
92 | |||
93 | drv->driver.name = drv->name; | ||
94 | drv->driver.bus = &i2o_bus_type; | ||
95 | |||
96 | spin_lock_irqsave(&i2o_drivers_lock, flags); | ||
97 | |||
98 | for (i = 0; i2o_drivers[i]; i++) | ||
99 | if (i >= i2o_max_drivers) { | ||
100 | printk(KERN_ERR "i2o: too many drivers registered, " | ||
101 | "increase max_drivers\n"); | ||
102 | spin_unlock_irqrestore(&i2o_drivers_lock, flags); | ||
103 | return -EFAULT; | ||
104 | } | ||
105 | |||
106 | drv->context = i; | ||
107 | i2o_drivers[i] = drv; | ||
108 | |||
109 | spin_unlock_irqrestore(&i2o_drivers_lock, flags); | ||
110 | |||
111 | pr_debug("i2o: driver %s gets context id %d\n", drv->name, | ||
112 | drv->context); | ||
113 | |||
114 | list_for_each_entry(c, &i2o_controllers, list) { | ||
115 | struct i2o_device *i2o_dev; | ||
116 | |||
117 | i2o_driver_notify_controller_add(drv, c); | ||
118 | list_for_each_entry(i2o_dev, &c->devices, list) | ||
119 | i2o_driver_notify_device_add(drv, i2o_dev); | ||
120 | } | ||
121 | |||
122 | |||
123 | rc = driver_register(&drv->driver); | ||
124 | if (rc) | ||
125 | destroy_workqueue(drv->event_queue); | ||
126 | |||
127 | return rc; | ||
128 | }; | ||
129 | |||
130 | /** | ||
131 | * i2o_driver_unregister - Unregister a I2O driver (OSM) from the I2O core | ||
132 | * @drv: I2O driver which should be unregistered | ||
133 | * | ||
134 | * Unregisters the OSM drv from the I2O core and cleanup event queues if | ||
135 | * necessary. | ||
136 | */ | ||
137 | void i2o_driver_unregister(struct i2o_driver *drv) | ||
138 | { | ||
139 | struct i2o_controller *c; | ||
140 | unsigned long flags; | ||
141 | |||
142 | pr_debug("i2o: unregister driver %s\n", drv->name); | ||
143 | |||
144 | driver_unregister(&drv->driver); | ||
145 | |||
146 | list_for_each_entry(c, &i2o_controllers, list) { | ||
147 | struct i2o_device *i2o_dev; | ||
148 | |||
149 | list_for_each_entry(i2o_dev, &c->devices, list) | ||
150 | i2o_driver_notify_device_remove(drv, i2o_dev); | ||
151 | |||
152 | i2o_driver_notify_controller_remove(drv, c); | ||
153 | } | ||
154 | |||
155 | spin_lock_irqsave(&i2o_drivers_lock, flags); | ||
156 | i2o_drivers[drv->context] = NULL; | ||
157 | spin_unlock_irqrestore(&i2o_drivers_lock, flags); | ||
158 | |||
159 | if (drv->event_queue) { | ||
160 | destroy_workqueue(drv->event_queue); | ||
161 | drv->event_queue = NULL; | ||
162 | pr_debug("i2o: event queue removed for %s\n", drv->name); | ||
163 | } | ||
164 | }; | ||
165 | |||
166 | /** | ||
167 | * i2o_driver_dispatch - dispatch an I2O reply message | ||
168 | * @c: I2O controller of the message | ||
169 | * @m: I2O message number | ||
170 | * @msg: I2O message to be delivered | ||
171 | * | ||
172 | * The reply is delivered to the driver from which the original message | ||
173 | * was. This function is only called from interrupt context. | ||
174 | * | ||
175 | * Returns 0 on success and the message should not be flushed. Returns > 0 | ||
176 | * on success and if the message should be flushed afterwords. Returns | ||
177 | * negative error code on failure (the message will be flushed too). | ||
178 | */ | ||
179 | int i2o_driver_dispatch(struct i2o_controller *c, u32 m, | ||
180 | struct i2o_message __iomem *msg) | ||
181 | { | ||
182 | struct i2o_driver *drv; | ||
183 | u32 context = readl(&msg->u.s.icntxt); | ||
184 | |||
185 | if (likely(context < i2o_max_drivers)) { | ||
186 | spin_lock(&i2o_drivers_lock); | ||
187 | drv = i2o_drivers[context]; | ||
188 | spin_unlock(&i2o_drivers_lock); | ||
189 | |||
190 | if (unlikely(!drv)) { | ||
191 | printk(KERN_WARNING "%s: Spurious reply to unknown " | ||
192 | "driver %d\n", c->name, context); | ||
193 | return -EIO; | ||
194 | } | ||
195 | |||
196 | if ((readl(&msg->u.head[1]) >> 24) == I2O_CMD_UTIL_EVT_REGISTER) { | ||
197 | struct i2o_device *dev, *tmp; | ||
198 | struct i2o_event *evt; | ||
199 | u16 size; | ||
200 | u16 tid; | ||
201 | |||
202 | tid = readl(&msg->u.head[1]) & 0x1fff; | ||
203 | |||
204 | pr_debug("%s: event received from device %d\n", c->name, | ||
205 | tid); | ||
206 | |||
207 | /* cut of header from message size (in 32-bit words) */ | ||
208 | size = (readl(&msg->u.head[0]) >> 16) - 5; | ||
209 | |||
210 | evt = kmalloc(size * 4 + sizeof(*evt), GFP_ATOMIC); | ||
211 | if (!evt) | ||
212 | return -ENOMEM; | ||
213 | memset(evt, 0, size * 4 + sizeof(*evt)); | ||
214 | |||
215 | evt->size = size; | ||
216 | memcpy_fromio(&evt->tcntxt, &msg->u.s.tcntxt, | ||
217 | (size + 2) * 4); | ||
218 | |||
219 | list_for_each_entry_safe(dev, tmp, &c->devices, list) | ||
220 | if (dev->lct_data.tid == tid) { | ||
221 | evt->i2o_dev = dev; | ||
222 | break; | ||
223 | } | ||
224 | |||
225 | INIT_WORK(&evt->work, (void (*)(void *))drv->event, | ||
226 | evt); | ||
227 | queue_work(drv->event_queue, &evt->work); | ||
228 | return 1; | ||
229 | } | ||
230 | |||
231 | if (likely(drv->reply)) | ||
232 | return drv->reply(c, m, msg); | ||
233 | else | ||
234 | pr_debug("%s: Reply to driver %s, but no reply function" | ||
235 | " defined!\n", c->name, drv->name); | ||
236 | return -EIO; | ||
237 | } else | ||
238 | printk(KERN_WARNING "%s: Spurious reply to unknown driver " | ||
239 | "%d\n", c->name, readl(&msg->u.s.icntxt)); | ||
240 | return -EIO; | ||
241 | } | ||
242 | |||
243 | /** | ||
244 | * i2o_driver_notify_controller_add_all - Send notify of added controller | ||
245 | * to all I2O drivers | ||
246 | * | ||
247 | * Send notifications to all registered drivers that a new controller was | ||
248 | * added. | ||
249 | */ | ||
250 | void i2o_driver_notify_controller_add_all(struct i2o_controller *c) | ||
251 | { | ||
252 | int i; | ||
253 | struct i2o_driver *drv; | ||
254 | |||
255 | for (i = 0; i < I2O_MAX_DRIVERS; i++) { | ||
256 | drv = i2o_drivers[i]; | ||
257 | |||
258 | if (drv) | ||
259 | i2o_driver_notify_controller_add(drv, c); | ||
260 | } | ||
261 | } | ||
262 | |||
263 | /** | ||
264 | * i2o_driver_notify_controller_remove_all - Send notify of removed | ||
265 | * controller to all I2O drivers | ||
266 | * | ||
267 | * Send notifications to all registered drivers that a controller was | ||
268 | * removed. | ||
269 | */ | ||
270 | void i2o_driver_notify_controller_remove_all(struct i2o_controller *c) | ||
271 | { | ||
272 | int i; | ||
273 | struct i2o_driver *drv; | ||
274 | |||
275 | for (i = 0; i < I2O_MAX_DRIVERS; i++) { | ||
276 | drv = i2o_drivers[i]; | ||
277 | |||
278 | if (drv) | ||
279 | i2o_driver_notify_controller_remove(drv, c); | ||
280 | } | ||
281 | } | ||
282 | |||
283 | /** | ||
284 | * i2o_driver_notify_device_add_all - Send notify of added device to all | ||
285 | * I2O drivers | ||
286 | * | ||
287 | * Send notifications to all registered drivers that a device was added. | ||
288 | */ | ||
289 | void i2o_driver_notify_device_add_all(struct i2o_device *i2o_dev) | ||
290 | { | ||
291 | int i; | ||
292 | struct i2o_driver *drv; | ||
293 | |||
294 | for (i = 0; i < I2O_MAX_DRIVERS; i++) { | ||
295 | drv = i2o_drivers[i]; | ||
296 | |||
297 | if (drv) | ||
298 | i2o_driver_notify_device_add(drv, i2o_dev); | ||
299 | } | ||
300 | } | ||
301 | |||
302 | /** | ||
303 | * i2o_driver_notify_device_remove_all - Send notify of removed device to | ||
304 | * all I2O drivers | ||
305 | * | ||
306 | * Send notifications to all registered drivers that a device was removed. | ||
307 | */ | ||
308 | void i2o_driver_notify_device_remove_all(struct i2o_device *i2o_dev) | ||
309 | { | ||
310 | int i; | ||
311 | struct i2o_driver *drv; | ||
312 | |||
313 | for (i = 0; i < I2O_MAX_DRIVERS; i++) { | ||
314 | drv = i2o_drivers[i]; | ||
315 | |||
316 | if (drv) | ||
317 | i2o_driver_notify_device_remove(drv, i2o_dev); | ||
318 | } | ||
319 | } | ||
320 | |||
321 | /** | ||
322 | * i2o_driver_init - initialize I2O drivers (OSMs) | ||
323 | * | ||
324 | * Registers the I2O bus and allocate memory for the array of OSMs. | ||
325 | * | ||
326 | * Returns 0 on success or negative error code on failure. | ||
327 | */ | ||
328 | int __init i2o_driver_init(void) | ||
329 | { | ||
330 | int rc = 0; | ||
331 | |||
332 | spin_lock_init(&i2o_drivers_lock); | ||
333 | |||
334 | if ((i2o_max_drivers < 2) || (i2o_max_drivers > 64) || | ||
335 | ((i2o_max_drivers ^ (i2o_max_drivers - 1)) != | ||
336 | (2 * i2o_max_drivers - 1))) { | ||
337 | printk(KERN_WARNING "i2o: max_drivers set to %d, but must be " | ||
338 | ">=2 and <= 64 and a power of 2\n", i2o_max_drivers); | ||
339 | i2o_max_drivers = I2O_MAX_DRIVERS; | ||
340 | } | ||
341 | printk(KERN_INFO "i2o: max drivers = %d\n", i2o_max_drivers); | ||
342 | |||
343 | i2o_drivers = | ||
344 | kmalloc(i2o_max_drivers * sizeof(*i2o_drivers), GFP_KERNEL); | ||
345 | if (!i2o_drivers) | ||
346 | return -ENOMEM; | ||
347 | |||
348 | memset(i2o_drivers, 0, i2o_max_drivers * sizeof(*i2o_drivers)); | ||
349 | |||
350 | rc = bus_register(&i2o_bus_type); | ||
351 | |||
352 | if (rc < 0) | ||
353 | kfree(i2o_drivers); | ||
354 | |||
355 | return rc; | ||
356 | }; | ||
357 | |||
358 | /** | ||
359 | * i2o_driver_exit - clean up I2O drivers (OSMs) | ||
360 | * | ||
361 | * Unregisters the I2O bus and free driver array. | ||
362 | */ | ||
363 | void __exit i2o_driver_exit(void) | ||
364 | { | ||
365 | bus_unregister(&i2o_bus_type); | ||
366 | kfree(i2o_drivers); | ||
367 | }; | ||
368 | |||
369 | EXPORT_SYMBOL(i2o_driver_register); | ||
370 | EXPORT_SYMBOL(i2o_driver_unregister); | ||
371 | EXPORT_SYMBOL(i2o_driver_notify_controller_add_all); | ||
372 | EXPORT_SYMBOL(i2o_driver_notify_controller_remove_all); | ||
373 | EXPORT_SYMBOL(i2o_driver_notify_device_add_all); | ||
374 | EXPORT_SYMBOL(i2o_driver_notify_device_remove_all); | ||