driver.c 9.5 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
/*
 *	Functions to handle I2O drivers (OSMs) and I2O bus type for sysfs
 *
 *	Copyright (C) 2004	Markus Lidel <Markus.Lidel@shadowconnect.com>
 *
 *	This program is free software; you can redistribute it and/or modify it
 *	under the terms of the GNU General Public License as published by the
 *	Free Software Foundation; either version 2 of the License, or (at your
 *	option) any later version.
 *
 *	Fixes/additions:
 *		Markus Lidel <Markus.Lidel@shadowconnect.com>
 *			initial version.
 */

#include <linux/device.h>
#include <linux/module.h>
#include <linux/rwsem.h>
#include <linux/i2o.h>
20
#include "core.h"
L
Linus Torvalds 已提交
21

22
#define OSM_NAME	"i2o"
23

L
Linus Torvalds 已提交
24
/* max_drivers - Maximum I2O drivers (OSMs) which could be registered */
25
static unsigned int i2o_max_drivers = I2O_MAX_DRIVERS;
L
Linus Torvalds 已提交
26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
module_param_named(max_drivers, i2o_max_drivers, uint, 0);
MODULE_PARM_DESC(max_drivers, "maximum number of OSM's to support");

/* I2O drivers lock and array */
static spinlock_t i2o_drivers_lock;
static struct i2o_driver **i2o_drivers;

/**
 *	i2o_bus_match - Tell if a I2O device class id match the class ids of
 *			the I2O driver (OSM)
 *
 *	@dev: device which should be verified
 *	@drv: the driver to match against
 *
 *	Used by the bus to check if the driver wants to handle the device.
 *
 *	Returns 1 if the class ids of the driver match the class id of the
 *	device, otherwise 0.
 */
static int i2o_bus_match(struct device *dev, struct device_driver *drv)
{
	struct i2o_device *i2o_dev = to_i2o_device(dev);
	struct i2o_driver *i2o_drv = to_i2o_driver(drv);
	struct i2o_class_id *ids = i2o_drv->classes;

	if (ids)
		while (ids->class_id != I2O_CLASS_END) {
			if (ids->class_id == i2o_dev->lct_data.class_id)
				return 1;
			ids++;
		}
	return 0;
};

/* I2O bus type */
61 62
extern struct device_attribute i2o_device_attrs[];

L
Linus Torvalds 已提交
63 64 65
struct bus_type i2o_bus_type = {
	.name = "i2o",
	.match = i2o_bus_match,
66
	.dev_attrs = i2o_device_attrs,
L
Linus Torvalds 已提交
67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
};

/**
 *	i2o_driver_register - Register a I2O driver (OSM) in the I2O core
 *	@drv: I2O driver which should be registered
 *
 *	Registers the OSM drv in the I2O core and creates an event queues if
 *	necessary.
 *
 *	Returns 0 on success or negative error code on failure.
 */
int i2o_driver_register(struct i2o_driver *drv)
{
	struct i2o_controller *c;
	int i;
	int rc = 0;
	unsigned long flags;

85
	osm_debug("Register driver %s\n", drv->name);
L
Linus Torvalds 已提交
86 87 88 89

	if (drv->event) {
		drv->event_queue = create_workqueue(drv->name);
		if (!drv->event_queue) {
90 91
			osm_err("Could not initialize event queue for driver "
				"%s\n", drv->name);
L
Linus Torvalds 已提交
92 93
			return -EFAULT;
		}
94
		osm_debug("Event queue initialized for driver %s\n", drv->name);
L
Linus Torvalds 已提交
95 96 97 98 99 100 101 102 103 104
	} else
		drv->event_queue = NULL;

	drv->driver.name = drv->name;
	drv->driver.bus = &i2o_bus_type;

	spin_lock_irqsave(&i2o_drivers_lock, flags);

	for (i = 0; i2o_drivers[i]; i++)
		if (i >= i2o_max_drivers) {
105 106
			osm_err("too many drivers registered, increase "
				"max_drivers\n");
L
Linus Torvalds 已提交
107 108 109 110 111 112 113 114 115
			spin_unlock_irqrestore(&i2o_drivers_lock, flags);
			return -EFAULT;
		}

	drv->context = i;
	i2o_drivers[i] = drv;

	spin_unlock_irqrestore(&i2o_drivers_lock, flags);

116
	osm_debug("driver %s gets context id %d\n", drv->name, drv->context);
L
Linus Torvalds 已提交
117 118 119 120 121 122

	list_for_each_entry(c, &i2o_controllers, list) {
		struct i2o_device *i2o_dev;

		i2o_driver_notify_controller_add(drv, c);
		list_for_each_entry(i2o_dev, &c->devices, list)
123
		    i2o_driver_notify_device_add(drv, i2o_dev);
L
Linus Torvalds 已提交
124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144
	}

	rc = driver_register(&drv->driver);
	if (rc)
		destroy_workqueue(drv->event_queue);

	return rc;
};

/**
 *	i2o_driver_unregister - Unregister a I2O driver (OSM) from the I2O core
 *	@drv: I2O driver which should be unregistered
 *
 *	Unregisters the OSM drv from the I2O core and cleanup event queues if
 *	necessary.
 */
void i2o_driver_unregister(struct i2o_driver *drv)
{
	struct i2o_controller *c;
	unsigned long flags;

145
	osm_debug("unregister driver %s\n", drv->name);
L
Linus Torvalds 已提交
146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164

	driver_unregister(&drv->driver);

	list_for_each_entry(c, &i2o_controllers, list) {
		struct i2o_device *i2o_dev;

		list_for_each_entry(i2o_dev, &c->devices, list)
		    i2o_driver_notify_device_remove(drv, i2o_dev);

		i2o_driver_notify_controller_remove(drv, c);
	}

	spin_lock_irqsave(&i2o_drivers_lock, flags);
	i2o_drivers[drv->context] = NULL;
	spin_unlock_irqrestore(&i2o_drivers_lock, flags);

	if (drv->event_queue) {
		destroy_workqueue(drv->event_queue);
		drv->event_queue = NULL;
165
		osm_debug("event queue removed for %s\n", drv->name);
L
Linus Torvalds 已提交
166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181
	}
};

/**
 *	i2o_driver_dispatch - dispatch an I2O reply message
 *	@c: I2O controller of the message
 *	@m: I2O message number
 *	@msg: I2O message to be delivered
 *
 *	The reply is delivered to the driver from which the original message
 *	was. This function is only called from interrupt context.
 *
 *	Returns 0 on success and the message should not be flushed. Returns > 0
 *	on success and if the message should be flushed afterwords. Returns
 *	negative error code on failure (the message will be flushed too).
 */
182
int i2o_driver_dispatch(struct i2o_controller *c, u32 m)
L
Linus Torvalds 已提交
183 184
{
	struct i2o_driver *drv;
185 186
	struct i2o_message *msg = i2o_msg_out_to_virt(c, m);
	u32 context = le32_to_cpu(msg->u.s.icntxt);
187 188
	unsigned long flags;

189
	if (unlikely(context >= i2o_max_drivers)) {
190 191
		osm_warn("%s: Spurious reply to unknown driver %d\n", c->name,
			 context);
192 193
		return -EIO;
	}
L
Linus Torvalds 已提交
194

195
	spin_lock_irqsave(&i2o_drivers_lock, flags);
196
	drv = i2o_drivers[context];
197
	spin_unlock_irqrestore(&i2o_drivers_lock, flags);
L
Linus Torvalds 已提交
198

199
	if (unlikely(!drv)) {
200 201
		osm_warn("%s: Spurious reply to unknown driver %d\n", c->name,
			 context);
202 203
		return -EIO;
	}
L
Linus Torvalds 已提交
204

205
	if ((le32_to_cpu(msg->u.head[1]) >> 24) == I2O_CMD_UTIL_EVT_REGISTER) {
206 207 208
		struct i2o_device *dev, *tmp;
		struct i2o_event *evt;
		u16 size;
209
		u16 tid = le32_to_cpu(msg->u.head[1]) & 0xfff;
L
Linus Torvalds 已提交
210

211
		osm_debug("event received from device %d\n", tid);
L
Linus Torvalds 已提交
212

213 214 215
		if (!drv->event)
			return -EIO;

216
		/* cut of header from message size (in 32-bit words) */
217
		size = (le32_to_cpu(msg->u.head[0]) >> 16) - 5;
L
Linus Torvalds 已提交
218

219 220 221
		evt = kmalloc(size * 4 + sizeof(*evt), GFP_ATOMIC | __GFP_ZERO);
		if (!evt)
			return -ENOMEM;
L
Linus Torvalds 已提交
222

223
		evt->size = size;
224 225 226
		evt->tcntxt = le32_to_cpu(msg->u.s.tcntxt);
		evt->event_indicator = le32_to_cpu(msg->body[0]);
		memcpy(&evt->tcntxt, &msg->u.s.tcntxt, size * 4);
L
Linus Torvalds 已提交
227

228 229 230 231
		list_for_each_entry_safe(dev, tmp, &c->devices, list)
		    if (dev->lct_data.tid == tid) {
			evt->i2o_dev = dev;
			break;
L
Linus Torvalds 已提交
232 233
		}

234 235 236 237 238 239
		INIT_WORK(&evt->work, (void (*)(void *))drv->event, evt);
		queue_work(drv->event_queue, &evt->work);
		return 1;
	}

	if (unlikely(!drv->reply)) {
240 241
		osm_debug("%s: Reply to driver %s, but no reply function"
			  " defined!\n", c->name, drv->name);
L
Linus Torvalds 已提交
242
		return -EIO;
243 244 245
	}

	return drv->reply(c, m, msg);
L
Linus Torvalds 已提交
246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341
}

/**
 *	i2o_driver_notify_controller_add_all - Send notify of added controller
 *					       to all I2O drivers
 *
 *	Send notifications to all registered drivers that a new controller was
 *	added.
 */
void i2o_driver_notify_controller_add_all(struct i2o_controller *c)
{
	int i;
	struct i2o_driver *drv;

	for (i = 0; i < I2O_MAX_DRIVERS; i++) {
		drv = i2o_drivers[i];

		if (drv)
			i2o_driver_notify_controller_add(drv, c);
	}
}

/**
 *	i2o_driver_notify_controller_remove_all - Send notify of removed
 *						  controller to all I2O drivers
 *
 *	Send notifications to all registered drivers that a controller was
 *	removed.
 */
void i2o_driver_notify_controller_remove_all(struct i2o_controller *c)
{
	int i;
	struct i2o_driver *drv;

	for (i = 0; i < I2O_MAX_DRIVERS; i++) {
		drv = i2o_drivers[i];

		if (drv)
			i2o_driver_notify_controller_remove(drv, c);
	}
}

/**
 *	i2o_driver_notify_device_add_all - Send notify of added device to all
 *					   I2O drivers
 *
 *	Send notifications to all registered drivers that a device was added.
 */
void i2o_driver_notify_device_add_all(struct i2o_device *i2o_dev)
{
	int i;
	struct i2o_driver *drv;

	for (i = 0; i < I2O_MAX_DRIVERS; i++) {
		drv = i2o_drivers[i];

		if (drv)
			i2o_driver_notify_device_add(drv, i2o_dev);
	}
}

/**
 *	i2o_driver_notify_device_remove_all - Send notify of removed device to
 *					      all I2O drivers
 *
 *	Send notifications to all registered drivers that a device was removed.
 */
void i2o_driver_notify_device_remove_all(struct i2o_device *i2o_dev)
{
	int i;
	struct i2o_driver *drv;

	for (i = 0; i < I2O_MAX_DRIVERS; i++) {
		drv = i2o_drivers[i];

		if (drv)
			i2o_driver_notify_device_remove(drv, i2o_dev);
	}
}

/**
 *	i2o_driver_init - initialize I2O drivers (OSMs)
 *
 *	Registers the I2O bus and allocate memory for the array of OSMs.
 *
 *	Returns 0 on success or negative error code on failure.
 */
int __init i2o_driver_init(void)
{
	int rc = 0;

	spin_lock_init(&i2o_drivers_lock);

	if ((i2o_max_drivers < 2) || (i2o_max_drivers > 64) ||
	    ((i2o_max_drivers ^ (i2o_max_drivers - 1)) !=
	     (2 * i2o_max_drivers - 1))) {
342 343
		osm_warn("max_drivers set to %d, but must be >=2 and <= 64 and "
			 "a power of 2\n", i2o_max_drivers);
L
Linus Torvalds 已提交
344 345
		i2o_max_drivers = I2O_MAX_DRIVERS;
	}
346
	osm_info("max drivers = %d\n", i2o_max_drivers);
L
Linus Torvalds 已提交
347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379

	i2o_drivers =
	    kmalloc(i2o_max_drivers * sizeof(*i2o_drivers), GFP_KERNEL);
	if (!i2o_drivers)
		return -ENOMEM;

	memset(i2o_drivers, 0, i2o_max_drivers * sizeof(*i2o_drivers));

	rc = bus_register(&i2o_bus_type);

	if (rc < 0)
		kfree(i2o_drivers);

	return rc;
};

/**
 *	i2o_driver_exit - clean up I2O drivers (OSMs)
 *
 *	Unregisters the I2O bus and free driver array.
 */
void __exit i2o_driver_exit(void)
{
	bus_unregister(&i2o_bus_type);
	kfree(i2o_drivers);
};

EXPORT_SYMBOL(i2o_driver_register);
EXPORT_SYMBOL(i2o_driver_unregister);
EXPORT_SYMBOL(i2o_driver_notify_controller_add_all);
EXPORT_SYMBOL(i2o_driver_notify_controller_remove_all);
EXPORT_SYMBOL(i2o_driver_notify_device_add_all);
EXPORT_SYMBOL(i2o_driver_notify_device_remove_all);