ccwgroup.c 16.9 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 18 19 20
 */
#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>

#include <asm/ccwdev.h>
#include <asm/ccwgroup.h>

21 22
#define CCW_BUS_ID_SIZE		20

L
Linus Torvalds 已提交
23 24 25 26 27 28 29 30 31
/* 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 已提交
32
static int ccwgroup_bus_match(struct device *dev, struct device_driver * drv)
L
Linus Torvalds 已提交
33
{
S
Sebastian Ott 已提交
34 35
	struct ccwgroup_device *gdev = to_ccwgroupdev(dev);
	struct ccwgroup_driver *gdrv = to_ccwgroupdrv(drv);
L
Linus Torvalds 已提交
36 37 38 39 40 41 42

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

	return 0;
}

43
static struct bus_type ccwgroup_bus_type;
L
Linus Torvalds 已提交
44

S
Sebastian Ott 已提交
45
static void __ccwgroup_remove_symlinks(struct ccwgroup_device *gdev)
L
Linus Torvalds 已提交
46 47 48 49 50 51 52 53 54 55 56
{
	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");
	}
}

57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
/*
 * 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 已提交
78 79 80 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
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;
}

118 119
static ssize_t ccwgroup_online_store(struct device *dev,
				     struct device_attribute *attr,
S
Sebastian Ott 已提交
120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
				     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;
}

147 148
static ssize_t ccwgroup_online_show(struct device *dev,
				    struct device_attribute *attr,
S
Sebastian Ott 已提交
149 150 151 152 153 154 155 156 157 158
				    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 已提交
159 160 161 162
/*
 * Provide an 'ungroup' attribute so the user can remove group devices no
 * longer needed or accidentially created. Saves memory :)
 */
163 164 165 166
static void ccwgroup_ungroup_callback(struct device *dev)
{
	struct ccwgroup_device *gdev = to_ccwgroupdev(dev);

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

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

183 184 185 186 187 188 189
	/* 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;
	}
190 191 192 193
	/* 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);
194 195
out:
	if (rc) {
196 197 198
		if (rc != -EAGAIN)
			/* Release onoff "lock" when ungrouping failed. */
			atomic_set(&gdev->onoff, 0);
199 200
		return rc;
	}
L
Linus Torvalds 已提交
201 202 203
	return count;
}
static DEVICE_ATTR(ungroup, 0200, NULL, ccwgroup_ungroup_store);
204 205 206 207 208 209 210 211 212 213 214 215 216 217
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 已提交
218

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

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

	for (i = 0; i < gdev->count; i++) {
S
Sebastian Ott 已提交
230 231
		rc = sysfs_create_link(&gdev->cdev[i]->dev.kobj,
				       &gdev->dev.kobj, "group_device");
L
Linus Torvalds 已提交
232 233 234 235 236 237 238 239 240
		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 已提交
241 242
		rc = sysfs_create_link(&gdev->dev.kobj,
				       &gdev->cdev[i]->dev.kobj, str);
L
Linus Torvalds 已提交
243 244 245 246 247 248 249 250 251 252 253 254 255 256
		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;
}

257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273
static int __get_next_bus_id(const char **buf, char *bus_id)
{
	int rc, len;
	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++;
	}
274
	if (len < CCW_BUS_ID_SIZE) {
275 276 277 278 279 280 281 282
		strlcpy(bus_id, start, len);
		rc = 0;
	} else
		rc = -EINVAL;
	*buf = end;
	return rc;
}

283
static int __is_valid_bus_id(char bus_id[CCW_BUS_ID_SIZE])
284 285 286 287 288 289 290 291 292
{
	int cssid, ssid, devno;

	/* Must be of form %x.%x.%04x */
	if (sscanf(bus_id, "%x.%1x.%04x", &cssid, &ssid, &devno) != 3)
		return 0;
	return 1;
}

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

319 320
	gdev = kzalloc(sizeof(*gdev) + num_devices * sizeof(gdev->cdev[0]),
		       GFP_KERNEL);
L
Linus Torvalds 已提交
321 322 323 324
	if (!gdev)
		return -ENOMEM;

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

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

378
	dev_set_name(&gdev->dev, "%s", dev_name(&gdev->cdev[0]->dev));
379
	gdev->dev.groups = ccwgroup_attr_groups;
380 381 382 383 384 385 386

	if (gdrv) {
		gdev->dev.driver = &gdrv->driver;
		rc = gdrv->setup ? gdrv->setup(gdev) : 0;
		if (rc)
			goto error;
	}
387
	rc = device_add(&gdev->dev);
L
Linus Torvalds 已提交
388
	if (rc)
389
		goto error;
L
Linus Torvalds 已提交
390
	rc = __ccwgroup_create_symlinks(gdev);
S
Sebastian Ott 已提交
391 392 393
	if (rc) {
		device_del(&gdev->dev);
		goto error;
L
Linus Torvalds 已提交
394
	}
S
Sebastian Ott 已提交
395 396
	mutex_unlock(&gdev->reg_mutex);
	return 0;
L
Linus Torvalds 已提交
397
error:
398
	for (i = 0; i < num_devices; i++)
L
Linus Torvalds 已提交
399
		if (gdev->cdev[i]) {
400
			spin_lock_irq(gdev->cdev[i]->ccwlock);
401 402
			if (dev_get_drvdata(&gdev->cdev[i]->dev) == gdev)
				dev_set_drvdata(&gdev->cdev[i]->dev, NULL);
403
			spin_unlock_irq(gdev->cdev[i]->ccwlock);
404
			put_device(&gdev->cdev[i]->dev);
405
			gdev->cdev[i] = NULL;
L
Linus Torvalds 已提交
406
		}
407 408
	mutex_unlock(&gdev->reg_mutex);
	put_device(&gdev->dev);
L
Linus Torvalds 已提交
409 410
	return rc;
}
411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435
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)
{
	return ccwgroup_create_dev(root, creator_id, cdrv, NULL,
				   num_devices, buf);
}
436
EXPORT_SYMBOL(ccwgroup_create_from_string);
L
Linus Torvalds 已提交
437

438
static int ccwgroup_notifier(struct notifier_block *nb, unsigned long action,
S
Sebastian Ott 已提交
439 440 441 442 443 444 445 446 447
			     void *data)
{
	struct device *dev = data;

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

	return NOTIFY_OK;
}
448 449 450 451 452 453

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

static int __init init_ccwgroup(void)
L
Linus Torvalds 已提交
454
{
455 456 457 458 459 460 461 462 463 464 465
	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 已提交
466 467
}

468
static void __exit cleanup_ccwgroup(void)
L
Linus Torvalds 已提交
469
{
470 471
	bus_unregister_notifier(&ccwgroup_bus_type, &ccwgroup_nb);
	bus_unregister(&ccwgroup_bus_type);
L
Linus Torvalds 已提交
472 473 474 475 476 477 478
}

module_init(init_ccwgroup);
module_exit(cleanup_ccwgroup);

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

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

S
Sebastian Ott 已提交
484
	return gdrv->probe ? gdrv->probe(gdev) : -ENODEV;
L
Linus Torvalds 已提交
485 486
}

S
Sebastian Ott 已提交
487
static int ccwgroup_remove(struct device *dev)
L
Linus Torvalds 已提交
488
{
S
Sebastian Ott 已提交
489 490
	struct ccwgroup_device *gdev = to_ccwgroupdev(dev);
	struct ccwgroup_driver *gdrv = to_ccwgroupdrv(dev->driver);
L
Linus Torvalds 已提交
491

492 493 494
	if (!dev->driver)
		return 0;
	if (gdrv->remove)
L
Linus Torvalds 已提交
495
		gdrv->remove(gdev);
496

L
Linus Torvalds 已提交
497 498 499
	return 0;
}

500 501
static void ccwgroup_shutdown(struct device *dev)
{
S
Sebastian Ott 已提交
502 503
	struct ccwgroup_device *gdev = to_ccwgroupdev(dev);
	struct ccwgroup_driver *gdrv = to_ccwgroupdrv(dev->driver);
504

505 506 507
	if (!dev->driver)
		return;
	if (gdrv->shutdown)
508 509 510
		gdrv->shutdown(gdev);
}

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 562 563 564 565 566 567 568 569 570
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;
}

571
static const struct dev_pm_ops ccwgroup_pm_ops = {
572 573 574 575 576 577 578
	.prepare = ccwgroup_pm_prepare,
	.complete = ccwgroup_pm_complete,
	.freeze = ccwgroup_pm_freeze,
	.thaw = ccwgroup_pm_thaw,
	.restore = ccwgroup_pm_restore,
};

579 580 581 582 583
static struct bus_type ccwgroup_bus_type = {
	.name   = "ccwgroup",
	.match  = ccwgroup_bus_match,
	.probe  = ccwgroup_probe,
	.remove = ccwgroup_remove,
584
	.shutdown = ccwgroup_shutdown,
585
	.pm = &ccwgroup_pm_ops,
586 587
};

C
Cornelia Huck 已提交
588 589 590 591 592 593 594
/**
 * 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 已提交
595 596
{
	/* register our new driver with the core */
597
	cdriver->driver.bus = &ccwgroup_bus_type;
L
Linus Torvalds 已提交
598 599 600

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

S
Sebastian Ott 已提交
603
static int __ccwgroup_match_all(struct device *dev, void *data)
L
Linus Torvalds 已提交
604
{
605
	return 1;
L
Linus Torvalds 已提交
606 607
}

C
Cornelia Huck 已提交
608 609 610 611 612 613 614
/**
 * 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 已提交
615
{
616 617
	struct device *dev;

L
Linus Torvalds 已提交
618
	/* We don't want ccwgroup devices to live longer than their driver. */
619 620
	while ((dev = driver_find_device(&cdriver->driver, NULL, NULL,
					 __ccwgroup_match_all))) {
621 622 623 624
		struct ccwgroup_device *gdev = to_ccwgroupdev(dev);

		mutex_lock(&gdev->reg_mutex);
		__ccwgroup_remove_symlinks(gdev);
625
		device_unregister(dev);
626
		__ccwgroup_remove_cdev_refs(gdev);
627
		mutex_unlock(&gdev->reg_mutex);
628 629
		put_device(dev);
	}
L
Linus Torvalds 已提交
630 631
	driver_unregister(&cdriver->driver);
}
S
Sebastian Ott 已提交
632
EXPORT_SYMBOL(ccwgroup_driver_unregister);
L
Linus Torvalds 已提交
633

C
Cornelia Huck 已提交
634 635 636 637 638 639 640 641 642 643
/**
 * 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 已提交
644 645 646
{
	return 0;
}
S
Sebastian Ott 已提交
647
EXPORT_SYMBOL(ccwgroup_probe_ccwdev);
L
Linus Torvalds 已提交
648

C
Cornelia Huck 已提交
649 650 651 652 653 654 655 656 657
/**
 * 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 已提交
658 659 660 661 662 663
{
	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. */
664 665 666 667 668 669 670 671 672 673 674 675
	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 已提交
676 677
		__ccwgroup_remove_symlinks(gdev);
		device_unregister(&gdev->dev);
678
		__ccwgroup_remove_cdev_refs(gdev);
L
Linus Torvalds 已提交
679
	}
680 681 682
	mutex_unlock(&gdev->reg_mutex);
	/* Release ccwgroup device reference for local processing. */
	put_device(&gdev->dev);
L
Linus Torvalds 已提交
683 684
}
EXPORT_SYMBOL(ccwgroup_remove_ccwdev);
S
Sebastian Ott 已提交
685
MODULE_LICENSE("GPL");