ccwgroup.c 16.7 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3
/*
 *  bus driver for ccwgroup
 *
4
 *  Copyright IBM Corp. 2002, 2012
5 6 7
 *
 *  Author(s): Arnd Bergmann (arndb@de.ibm.com)
 *	       Cornelia Huck (cornelia.huck@de.ibm.com)
L
Linus Torvalds 已提交
8 9 10 11 12 13 14 15 16 17
 */
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/list.h>
#include <linux/device.h>
#include <linux/init.h>
#include <linux/ctype.h>
#include <linux/dcache.h>

18
#include <asm/cio.h>
L
Linus Torvalds 已提交
19 20 21
#include <asm/ccwdev.h>
#include <asm/ccwgroup.h>

22 23 24
#include "device.h"

#define CCW_BUS_ID_SIZE		10
25

L
Linus Torvalds 已提交
26 27 28 29 30 31 32 33 34
/* In Linux 2.4, we had a channel device layer called "chandev"
 * that did all sorts of obscure stuff for networking devices.
 * This is another driver that serves as a replacement for just
 * one of its functions, namely the translation of single subchannels
 * to devices that use multiple subchannels.
 */

/* a device matches a driver if all its slave devices match the same
 * entry of the driver */
S
Sebastian Ott 已提交
35
static int ccwgroup_bus_match(struct device *dev, struct device_driver * drv)
L
Linus Torvalds 已提交
36
{
S
Sebastian Ott 已提交
37 38
	struct ccwgroup_device *gdev = to_ccwgroupdev(dev);
	struct ccwgroup_driver *gdrv = to_ccwgroupdrv(drv);
L
Linus Torvalds 已提交
39 40 41 42 43 44 45

	if (gdev->creator_id == gdrv->driver_id)
		return 1;

	return 0;
}

46
static struct bus_type ccwgroup_bus_type;
L
Linus Torvalds 已提交
47

S
Sebastian Ott 已提交
48
static void __ccwgroup_remove_symlinks(struct ccwgroup_device *gdev)
L
Linus Torvalds 已提交
49 50 51 52 53 54 55 56 57 58 59
{
	int i;
	char str[8];

	for (i = 0; i < gdev->count; i++) {
		sprintf(str, "cdev%d", i);
		sysfs_remove_link(&gdev->dev.kobj, str);
		sysfs_remove_link(&gdev->cdev[i]->dev.kobj, "group_device");
	}
}

60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
/*
 * Remove references from ccw devices to ccw group device and from
 * ccw group device to ccw devices.
 */
static void __ccwgroup_remove_cdev_refs(struct ccwgroup_device *gdev)
{
	struct ccw_device *cdev;
	int i;

	for (i = 0; i < gdev->count; i++) {
		cdev = gdev->cdev[i];
		if (!cdev)
			continue;
		spin_lock_irq(cdev->ccwlock);
		dev_set_drvdata(&cdev->dev, NULL);
		spin_unlock_irq(cdev->ccwlock);
		gdev->cdev[i] = NULL;
		put_device(&cdev->dev);
	}
}

S
Sebastian Ott 已提交
81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120
static int ccwgroup_set_online(struct ccwgroup_device *gdev)
{
	struct ccwgroup_driver *gdrv = to_ccwgroupdrv(gdev->dev.driver);
	int ret = 0;

	if (atomic_cmpxchg(&gdev->onoff, 0, 1) != 0)
		return -EAGAIN;
	if (gdev->state == CCWGROUP_ONLINE)
		goto out;
	if (gdrv->set_online)
		ret = gdrv->set_online(gdev);
	if (ret)
		goto out;

	gdev->state = CCWGROUP_ONLINE;
out:
	atomic_set(&gdev->onoff, 0);
	return ret;
}

static int ccwgroup_set_offline(struct ccwgroup_device *gdev)
{
	struct ccwgroup_driver *gdrv = to_ccwgroupdrv(gdev->dev.driver);
	int ret = 0;

	if (atomic_cmpxchg(&gdev->onoff, 0, 1) != 0)
		return -EAGAIN;
	if (gdev->state == CCWGROUP_OFFLINE)
		goto out;
	if (gdrv->set_offline)
		ret = gdrv->set_offline(gdev);
	if (ret)
		goto out;

	gdev->state = CCWGROUP_OFFLINE;
out:
	atomic_set(&gdev->onoff, 0);
	return ret;
}

121 122
static ssize_t ccwgroup_online_store(struct device *dev,
				     struct device_attribute *attr,
S
Sebastian Ott 已提交
123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149
				     const char *buf, size_t count)
{
	struct ccwgroup_device *gdev = to_ccwgroupdev(dev);
	struct ccwgroup_driver *gdrv = to_ccwgroupdrv(dev->driver);
	unsigned long value;
	int ret;

	if (!dev->driver)
		return -EINVAL;
	if (!try_module_get(gdrv->driver.owner))
		return -EINVAL;

	ret = strict_strtoul(buf, 0, &value);
	if (ret)
		goto out;

	if (value == 1)
		ret = ccwgroup_set_online(gdev);
	else if (value == 0)
		ret = ccwgroup_set_offline(gdev);
	else
		ret = -EINVAL;
out:
	module_put(gdrv->driver.owner);
	return (ret == 0) ? count : ret;
}

150 151
static ssize_t ccwgroup_online_show(struct device *dev,
				    struct device_attribute *attr,
S
Sebastian Ott 已提交
152 153 154 155 156 157 158 159 160 161
				    char *buf)
{
	struct ccwgroup_device *gdev = to_ccwgroupdev(dev);
	int online;

	online = (gdev->state == CCWGROUP_ONLINE) ? 1 : 0;

	return scnprintf(buf, PAGE_SIZE, "%d\n", online);
}

L
Linus Torvalds 已提交
162 163 164 165
/*
 * Provide an 'ungroup' attribute so the user can remove group devices no
 * longer needed or accidentially created. Saves memory :)
 */
166 167 168 169
static void ccwgroup_ungroup_callback(struct device *dev)
{
	struct ccwgroup_device *gdev = to_ccwgroupdev(dev);

170
	mutex_lock(&gdev->reg_mutex);
171 172 173
	if (device_is_registered(&gdev->dev)) {
		__ccwgroup_remove_symlinks(gdev);
		device_unregister(dev);
174
		__ccwgroup_remove_cdev_refs(gdev);
175
	}
176
	mutex_unlock(&gdev->reg_mutex);
177 178
}

S
Sebastian Ott 已提交
179 180 181
static ssize_t ccwgroup_ungroup_store(struct device *dev,
				      struct device_attribute *attr,
				      const char *buf, size_t count)
L
Linus Torvalds 已提交
182
{
S
Sebastian Ott 已提交
183
	struct ccwgroup_device *gdev = to_ccwgroupdev(dev);
184
	int rc;
L
Linus Torvalds 已提交
185

186 187 188 189 190 191 192
	/* Prevent concurrent online/offline processing and ungrouping. */
	if (atomic_cmpxchg(&gdev->onoff, 0, 1) != 0)
		return -EAGAIN;
	if (gdev->state != CCWGROUP_OFFLINE) {
		rc = -EINVAL;
		goto out;
	}
193 194 195 196
	/* Note that we cannot unregister the device from one of its
	 * attribute methods, so we have to use this roundabout approach.
	 */
	rc = device_schedule_callback(dev, ccwgroup_ungroup_callback);
197 198
out:
	if (rc) {
199 200 201
		if (rc != -EAGAIN)
			/* Release onoff "lock" when ungrouping failed. */
			atomic_set(&gdev->onoff, 0);
202 203
		return rc;
	}
L
Linus Torvalds 已提交
204 205 206
	return count;
}
static DEVICE_ATTR(ungroup, 0200, NULL, ccwgroup_ungroup_store);
207 208 209 210 211 212 213 214 215 216 217 218 219 220
static DEVICE_ATTR(online, 0644, ccwgroup_online_show, ccwgroup_online_store);

static struct attribute *ccwgroup_attrs[] = {
	&dev_attr_online.attr,
	&dev_attr_ungroup.attr,
	NULL,
};
static struct attribute_group ccwgroup_attr_group = {
	.attrs = ccwgroup_attrs,
};
static const struct attribute_group *ccwgroup_attr_groups[] = {
	&ccwgroup_attr_group,
	NULL,
};
L
Linus Torvalds 已提交
221

S
Sebastian Ott 已提交
222
static void ccwgroup_release(struct device *dev)
L
Linus Torvalds 已提交
223
{
224
	kfree(to_ccwgroupdev(dev));
L
Linus Torvalds 已提交
225 226
}

S
Sebastian Ott 已提交
227
static int __ccwgroup_create_symlinks(struct ccwgroup_device *gdev)
L
Linus Torvalds 已提交
228 229 230 231 232
{
	char str[8];
	int i, rc;

	for (i = 0; i < gdev->count; i++) {
S
Sebastian Ott 已提交
233 234
		rc = sysfs_create_link(&gdev->cdev[i]->dev.kobj,
				       &gdev->dev.kobj, "group_device");
L
Linus Torvalds 已提交
235 236 237 238 239 240 241 242 243
		if (rc) {
			for (--i; i >= 0; i--)
				sysfs_remove_link(&gdev->cdev[i]->dev.kobj,
						  "group_device");
			return rc;
		}
	}
	for (i = 0; i < gdev->count; i++) {
		sprintf(str, "cdev%d", i);
S
Sebastian Ott 已提交
244 245
		rc = sysfs_create_link(&gdev->dev.kobj,
				       &gdev->cdev[i]->dev.kobj, str);
L
Linus Torvalds 已提交
246 247 248 249 250 251 252 253 254 255 256 257 258 259
		if (rc) {
			for (--i; i >= 0; i--) {
				sprintf(str, "cdev%d", i);
				sysfs_remove_link(&gdev->dev.kobj, str);
			}
			for (i = 0; i < gdev->count; i++)
				sysfs_remove_link(&gdev->cdev[i]->dev.kobj,
						  "group_device");
			return rc;
		}
	}
	return 0;
}

260
static int __get_next_id(const char **buf, struct ccw_dev_id *id)
261
{
262 263
	unsigned int cssid, ssid, devno;
	int ret = 0, len;
264 265 266 267 268 269 270 271 272 273 274 275 276 277
	char *start, *end;

	start = (char *)*buf;
	end = strchr(start, ',');
	if (!end) {
		/* Last entry. Strip trailing newline, if applicable. */
		end = strchr(start, '\n');
		if (end)
			*end = '\0';
		len = strlen(start) + 1;
	} else {
		len = end - start + 1;
		end++;
	}
278 279 280
	if (len <= CCW_BUS_ID_SIZE) {
		if (sscanf(start, "%2x.%1x.%04x", &cssid, &ssid, &devno) != 3)
			ret = -EINVAL;
281
	} else
282
		ret = -EINVAL;
283

284 285 286 287 288 289
	if (!ret) {
		id->ssid = ssid;
		id->devno = devno;
	}
	*buf = end;
	return ret;
290 291
}

C
Cornelia Huck 已提交
292
/**
293 294
 * ccwgroup_create_dev() - create and register a ccw group device
 * @parent: parent device for the new device
C
Cornelia Huck 已提交
295
 * @creator_id: identifier of creating driver
296
 * @gdrv: driver for the new group device
297 298
 * @num_devices: number of slave devices
 * @buf: buffer containing comma separated bus ids of slave devices
C
Cornelia Huck 已提交
299
 *
300
 * Create and register a new ccw group device as a child of @parent. Slave
301
 * devices are obtained from the list of bus ids given in @buf.
C
Cornelia Huck 已提交
302 303 304 305
 * Returns:
 *  %0 on success and an error code on failure.
 * Context:
 *  non-atomic
L
Linus Torvalds 已提交
306
 */
307
int ccwgroup_create_dev(struct device *parent, unsigned int creator_id,
308 309
			struct ccwgroup_driver *gdrv, int num_devices,
			const char *buf)
L
Linus Torvalds 已提交
310 311
{
	struct ccwgroup_device *gdev;
312
	struct ccw_dev_id dev_id;
313
	int rc, i;
L
Linus Torvalds 已提交
314

315 316
	gdev = kzalloc(sizeof(*gdev) + num_devices * sizeof(gdev->cdev[0]),
		       GFP_KERNEL);
L
Linus Torvalds 已提交
317 318 319 320
	if (!gdev)
		return -ENOMEM;

	atomic_set(&gdev->onoff, 0);
321 322
	mutex_init(&gdev->reg_mutex);
	mutex_lock(&gdev->reg_mutex);
323 324 325 326
	if (gdrv)
		gdev->creator_id = gdrv->driver_id;
	else
		gdev->creator_id = creator_id;
327 328
	gdev->count = num_devices;
	gdev->dev.bus = &ccwgroup_bus_type;
329
	gdev->dev.parent = parent;
330 331 332
	gdev->dev.release = ccwgroup_release;
	device_initialize(&gdev->dev);

333 334
	for (i = 0; i < num_devices && buf; i++) {
		rc = __get_next_id(&buf, &dev_id);
335 336
		if (rc != 0)
			goto error;
337
		gdev->cdev[i] = get_ccwdev_by_dev_id(&dev_id);
338 339 340 341
		/*
		 * All devices have to be of the same type in
		 * order to be grouped.
		 */
342 343 344
		if (!gdev->cdev[i] || !gdev->cdev[i]->drv ||
		    gdev->cdev[i]->drv != gdev->cdev[0]->drv ||
		    gdev->cdev[i]->id.driver_info !=
L
Linus Torvalds 已提交
345 346
		    gdev->cdev[0]->id.driver_info) {
			rc = -EINVAL;
347
			goto error;
L
Linus Torvalds 已提交
348 349
		}
		/* Don't allow a device to belong to more than one group. */
350
		spin_lock_irq(gdev->cdev[i]->ccwlock);
351
		if (dev_get_drvdata(&gdev->cdev[i]->dev)) {
352
			spin_unlock_irq(gdev->cdev[i]->ccwlock);
L
Linus Torvalds 已提交
353
			rc = -EINVAL;
354
			goto error;
L
Linus Torvalds 已提交
355
		}
356
		dev_set_drvdata(&gdev->cdev[i]->dev, gdev);
357
		spin_unlock_irq(gdev->cdev[i]->ccwlock);
358
	}
359
	/* Check for sufficient number of bus ids. */
360
	if (i < num_devices) {
361 362 363 364
		rc = -EINVAL;
		goto error;
	}
	/* Check for trailing stuff. */
365
	if (i == num_devices && strlen(buf) > 0) {
366 367 368
		rc = -EINVAL;
		goto error;
	}
L
Linus Torvalds 已提交
369

370
	dev_set_name(&gdev->dev, "%s", dev_name(&gdev->cdev[0]->dev));
371
	gdev->dev.groups = ccwgroup_attr_groups;
372 373 374 375 376 377 378

	if (gdrv) {
		gdev->dev.driver = &gdrv->driver;
		rc = gdrv->setup ? gdrv->setup(gdev) : 0;
		if (rc)
			goto error;
	}
379
	rc = device_add(&gdev->dev);
L
Linus Torvalds 已提交
380
	if (rc)
381
		goto error;
L
Linus Torvalds 已提交
382
	rc = __ccwgroup_create_symlinks(gdev);
S
Sebastian Ott 已提交
383 384 385
	if (rc) {
		device_del(&gdev->dev);
		goto error;
L
Linus Torvalds 已提交
386
	}
S
Sebastian Ott 已提交
387 388
	mutex_unlock(&gdev->reg_mutex);
	return 0;
L
Linus Torvalds 已提交
389
error:
390
	for (i = 0; i < num_devices; i++)
L
Linus Torvalds 已提交
391
		if (gdev->cdev[i]) {
392
			spin_lock_irq(gdev->cdev[i]->ccwlock);
393 394
			if (dev_get_drvdata(&gdev->cdev[i]->dev) == gdev)
				dev_set_drvdata(&gdev->cdev[i]->dev, NULL);
395
			spin_unlock_irq(gdev->cdev[i]->ccwlock);
396
			put_device(&gdev->cdev[i]->dev);
397
			gdev->cdev[i] = NULL;
L
Linus Torvalds 已提交
398
		}
399 400
	mutex_unlock(&gdev->reg_mutex);
	put_device(&gdev->dev);
L
Linus Torvalds 已提交
401 402
	return rc;
}
403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424
EXPORT_SYMBOL(ccwgroup_create_dev);

/**
 * ccwgroup_create_from_string() - create and register a ccw group device
 * @root: parent device for the new device
 * @creator_id: identifier of creating driver
 * @cdrv: ccw driver of slave devices
 * @num_devices: number of slave devices
 * @buf: buffer containing comma separated bus ids of slave devices
 *
 * Create and register a new ccw group device as a child of @root. Slave
 * devices are obtained from the list of bus ids given in @buf and must all
 * belong to @cdrv.
 * Returns:
 *  %0 on success and an error code on failure.
 * Context:
 *  non-atomic
 */
int ccwgroup_create_from_string(struct device *root, unsigned int creator_id,
				struct ccw_driver *cdrv, int num_devices,
				const char *buf)
{
425
	return ccwgroup_create_dev(root, creator_id, NULL, num_devices, buf);
426
}
427
EXPORT_SYMBOL(ccwgroup_create_from_string);
L
Linus Torvalds 已提交
428

429
static int ccwgroup_notifier(struct notifier_block *nb, unsigned long action,
S
Sebastian Ott 已提交
430 431 432 433 434 435 436 437 438
			     void *data)
{
	struct device *dev = data;

	if (action == BUS_NOTIFY_UNBIND_DRIVER)
		device_schedule_callback(dev, ccwgroup_ungroup_callback);

	return NOTIFY_OK;
}
439 440 441 442 443 444

static struct notifier_block ccwgroup_nb = {
	.notifier_call = ccwgroup_notifier
};

static int __init init_ccwgroup(void)
L
Linus Torvalds 已提交
445
{
446 447 448 449 450 451 452 453 454 455 456
	int ret;

	ret = bus_register(&ccwgroup_bus_type);
	if (ret)
		return ret;

	ret = bus_register_notifier(&ccwgroup_bus_type, &ccwgroup_nb);
	if (ret)
		bus_unregister(&ccwgroup_bus_type);

	return ret;
L
Linus Torvalds 已提交
457 458
}

459
static void __exit cleanup_ccwgroup(void)
L
Linus Torvalds 已提交
460
{
461 462
	bus_unregister_notifier(&ccwgroup_bus_type, &ccwgroup_nb);
	bus_unregister(&ccwgroup_bus_type);
L
Linus Torvalds 已提交
463 464 465 466 467 468 469
}

module_init(init_ccwgroup);
module_exit(cleanup_ccwgroup);

/************************** driver stuff ******************************/

S
Sebastian Ott 已提交
470
static int ccwgroup_probe(struct device *dev)
L
Linus Torvalds 已提交
471
{
S
Sebastian Ott 已提交
472 473
	struct ccwgroup_device *gdev = to_ccwgroupdev(dev);
	struct ccwgroup_driver *gdrv = to_ccwgroupdrv(dev->driver);
L
Linus Torvalds 已提交
474

S
Sebastian Ott 已提交
475
	return gdrv->probe ? gdrv->probe(gdev) : -ENODEV;
L
Linus Torvalds 已提交
476 477
}

S
Sebastian Ott 已提交
478
static int ccwgroup_remove(struct device *dev)
L
Linus Torvalds 已提交
479
{
S
Sebastian Ott 已提交
480 481
	struct ccwgroup_device *gdev = to_ccwgroupdev(dev);
	struct ccwgroup_driver *gdrv = to_ccwgroupdrv(dev->driver);
L
Linus Torvalds 已提交
482

483 484 485
	if (!dev->driver)
		return 0;
	if (gdrv->remove)
L
Linus Torvalds 已提交
486
		gdrv->remove(gdev);
487

L
Linus Torvalds 已提交
488 489 490
	return 0;
}

491 492
static void ccwgroup_shutdown(struct device *dev)
{
S
Sebastian Ott 已提交
493 494
	struct ccwgroup_device *gdev = to_ccwgroupdev(dev);
	struct ccwgroup_driver *gdrv = to_ccwgroupdrv(dev->driver);
495

496 497 498
	if (!dev->driver)
		return;
	if (gdrv->shutdown)
499 500 501
		gdrv->shutdown(gdev);
}

502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561
static int ccwgroup_pm_prepare(struct device *dev)
{
	struct ccwgroup_device *gdev = to_ccwgroupdev(dev);
	struct ccwgroup_driver *gdrv = to_ccwgroupdrv(gdev->dev.driver);

	/* Fail while device is being set online/offline. */
	if (atomic_read(&gdev->onoff))
		return -EAGAIN;

	if (!gdev->dev.driver || gdev->state != CCWGROUP_ONLINE)
		return 0;

	return gdrv->prepare ? gdrv->prepare(gdev) : 0;
}

static void ccwgroup_pm_complete(struct device *dev)
{
	struct ccwgroup_device *gdev = to_ccwgroupdev(dev);
	struct ccwgroup_driver *gdrv = to_ccwgroupdrv(dev->driver);

	if (!gdev->dev.driver || gdev->state != CCWGROUP_ONLINE)
		return;

	if (gdrv->complete)
		gdrv->complete(gdev);
}

static int ccwgroup_pm_freeze(struct device *dev)
{
	struct ccwgroup_device *gdev = to_ccwgroupdev(dev);
	struct ccwgroup_driver *gdrv = to_ccwgroupdrv(gdev->dev.driver);

	if (!gdev->dev.driver || gdev->state != CCWGROUP_ONLINE)
		return 0;

	return gdrv->freeze ? gdrv->freeze(gdev) : 0;
}

static int ccwgroup_pm_thaw(struct device *dev)
{
	struct ccwgroup_device *gdev = to_ccwgroupdev(dev);
	struct ccwgroup_driver *gdrv = to_ccwgroupdrv(gdev->dev.driver);

	if (!gdev->dev.driver || gdev->state != CCWGROUP_ONLINE)
		return 0;

	return gdrv->thaw ? gdrv->thaw(gdev) : 0;
}

static int ccwgroup_pm_restore(struct device *dev)
{
	struct ccwgroup_device *gdev = to_ccwgroupdev(dev);
	struct ccwgroup_driver *gdrv = to_ccwgroupdrv(gdev->dev.driver);

	if (!gdev->dev.driver || gdev->state != CCWGROUP_ONLINE)
		return 0;

	return gdrv->restore ? gdrv->restore(gdev) : 0;
}

562
static const struct dev_pm_ops ccwgroup_pm_ops = {
563 564 565 566 567 568 569
	.prepare = ccwgroup_pm_prepare,
	.complete = ccwgroup_pm_complete,
	.freeze = ccwgroup_pm_freeze,
	.thaw = ccwgroup_pm_thaw,
	.restore = ccwgroup_pm_restore,
};

570 571 572 573 574
static struct bus_type ccwgroup_bus_type = {
	.name   = "ccwgroup",
	.match  = ccwgroup_bus_match,
	.probe  = ccwgroup_probe,
	.remove = ccwgroup_remove,
575
	.shutdown = ccwgroup_shutdown,
576
	.pm = &ccwgroup_pm_ops,
577 578
};

C
Cornelia Huck 已提交
579 580 581 582 583 584 585
/**
 * ccwgroup_driver_register() - register a ccw group driver
 * @cdriver: driver to be registered
 *
 * This function is mainly a wrapper around driver_register().
 */
int ccwgroup_driver_register(struct ccwgroup_driver *cdriver)
L
Linus Torvalds 已提交
586 587
{
	/* register our new driver with the core */
588
	cdriver->driver.bus = &ccwgroup_bus_type;
L
Linus Torvalds 已提交
589 590 591

	return driver_register(&cdriver->driver);
}
S
Sebastian Ott 已提交
592
EXPORT_SYMBOL(ccwgroup_driver_register);
L
Linus Torvalds 已提交
593

S
Sebastian Ott 已提交
594
static int __ccwgroup_match_all(struct device *dev, void *data)
L
Linus Torvalds 已提交
595
{
596
	return 1;
L
Linus Torvalds 已提交
597 598
}

C
Cornelia Huck 已提交
599 600 601 602 603 604 605
/**
 * ccwgroup_driver_unregister() - deregister a ccw group driver
 * @cdriver: driver to be deregistered
 *
 * This function is mainly a wrapper around driver_unregister().
 */
void ccwgroup_driver_unregister(struct ccwgroup_driver *cdriver)
L
Linus Torvalds 已提交
606
{
607 608
	struct device *dev;

L
Linus Torvalds 已提交
609
	/* We don't want ccwgroup devices to live longer than their driver. */
610 611
	while ((dev = driver_find_device(&cdriver->driver, NULL, NULL,
					 __ccwgroup_match_all))) {
612 613 614 615
		struct ccwgroup_device *gdev = to_ccwgroupdev(dev);

		mutex_lock(&gdev->reg_mutex);
		__ccwgroup_remove_symlinks(gdev);
616
		device_unregister(dev);
617
		__ccwgroup_remove_cdev_refs(gdev);
618
		mutex_unlock(&gdev->reg_mutex);
619 620
		put_device(dev);
	}
L
Linus Torvalds 已提交
621 622
	driver_unregister(&cdriver->driver);
}
S
Sebastian Ott 已提交
623
EXPORT_SYMBOL(ccwgroup_driver_unregister);
L
Linus Torvalds 已提交
624

C
Cornelia Huck 已提交
625 626 627 628 629 630 631 632 633 634
/**
 * ccwgroup_probe_ccwdev() - probe function for slave devices
 * @cdev: ccw device to be probed
 *
 * This is a dummy probe function for ccw devices that are slave devices in
 * a ccw group device.
 * Returns:
 *  always %0
 */
int ccwgroup_probe_ccwdev(struct ccw_device *cdev)
L
Linus Torvalds 已提交
635 636 637
{
	return 0;
}
S
Sebastian Ott 已提交
638
EXPORT_SYMBOL(ccwgroup_probe_ccwdev);
L
Linus Torvalds 已提交
639

C
Cornelia Huck 已提交
640 641 642 643 644 645 646 647 648
/**
 * ccwgroup_remove_ccwdev() - remove function for slave devices
 * @cdev: ccw device to be removed
 *
 * This is a remove function for ccw devices that are slave devices in a ccw
 * group device. It sets the ccw device offline and also deregisters the
 * embedding ccw group device.
 */
void ccwgroup_remove_ccwdev(struct ccw_device *cdev)
L
Linus Torvalds 已提交
649 650 651 652 653 654
{
	struct ccwgroup_device *gdev;

	/* Ignore offlining errors, device is gone anyway. */
	ccw_device_set_offline(cdev);
	/* If one of its devices is gone, the whole group is done for. */
655 656 657 658 659 660 661 662 663 664 665 666
	spin_lock_irq(cdev->ccwlock);
	gdev = dev_get_drvdata(&cdev->dev);
	if (!gdev) {
		spin_unlock_irq(cdev->ccwlock);
		return;
	}
	/* Get ccwgroup device reference for local processing. */
	get_device(&gdev->dev);
	spin_unlock_irq(cdev->ccwlock);
	/* Unregister group device. */
	mutex_lock(&gdev->reg_mutex);
	if (device_is_registered(&gdev->dev)) {
L
Linus Torvalds 已提交
667 668
		__ccwgroup_remove_symlinks(gdev);
		device_unregister(&gdev->dev);
669
		__ccwgroup_remove_cdev_refs(gdev);
L
Linus Torvalds 已提交
670
	}
671 672 673
	mutex_unlock(&gdev->reg_mutex);
	/* Release ccwgroup device reference for local processing. */
	put_device(&gdev->dev);
L
Linus Torvalds 已提交
674 675
}
EXPORT_SYMBOL(ccwgroup_remove_ccwdev);
S
Sebastian Ott 已提交
676
MODULE_LICENSE("GPL");