ccwgroup.c 16.3 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3
/*
 *  bus driver for ccwgroup
 *
4 5 6 7
 *  Copyright IBM Corp. 2002, 2009
 *
 *  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 32 33 34 35 36 37
/* 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 */
static int
ccwgroup_bus_match (struct device * dev, struct device_driver * drv)
{
	struct ccwgroup_device *gdev;
	struct ccwgroup_driver *gdrv;

38 39
	gdev = to_ccwgroupdev(dev);
	gdrv = to_ccwgroupdrv(drv);
L
Linus Torvalds 已提交
40 41 42 43 44 45 46

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

	return 0;
}
static int
47
ccwgroup_uevent (struct device *dev, struct kobj_uevent_env *env)
L
Linus Torvalds 已提交
48 49 50 51 52
{
	/* TODO */
	return 0;
}

53
static struct bus_type ccwgroup_bus_type;
L
Linus Torvalds 已提交
54

55
static void
L
Linus Torvalds 已提交
56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
__ccwgroup_remove_symlinks(struct ccwgroup_device *gdev)
{
	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");
	}
	
}

/*
 * Provide an 'ungroup' attribute so the user can remove group devices no
 * longer needed or accidentially created. Saves memory :)
 */
73 74 75 76
static void ccwgroup_ungroup_callback(struct device *dev)
{
	struct ccwgroup_device *gdev = to_ccwgroupdev(dev);

77
	mutex_lock(&gdev->reg_mutex);
78 79 80 81
	if (device_is_registered(&gdev->dev)) {
		__ccwgroup_remove_symlinks(gdev);
		device_unregister(dev);
	}
82
	mutex_unlock(&gdev->reg_mutex);
83 84
}

L
Linus Torvalds 已提交
85
static ssize_t
86
ccwgroup_ungroup_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
L
Linus Torvalds 已提交
87 88
{
	struct ccwgroup_device *gdev;
89
	int rc;
L
Linus Torvalds 已提交
90 91 92

	gdev = to_ccwgroupdev(dev);

93 94 95 96 97 98 99
	/* 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;
	}
100 101 102 103
	/* 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);
104 105
out:
	if (rc) {
106 107 108
		if (rc != -EAGAIN)
			/* Release onoff "lock" when ungrouping failed. */
			atomic_set(&gdev->onoff, 0);
109 110
		return rc;
	}
L
Linus Torvalds 已提交
111 112 113 114 115 116 117 118 119 120 121 122 123 124
	return count;
}

static DEVICE_ATTR(ungroup, 0200, NULL, ccwgroup_ungroup_store);

static void
ccwgroup_release (struct device *dev)
{
	struct ccwgroup_device *gdev;
	int i;

	gdev = to_ccwgroupdev(dev);

	for (i = 0; i < gdev->count; i++) {
125
		if (gdev->cdev[i]) {
126
			spin_lock_irq(gdev->cdev[i]->ccwlock);
127 128
			if (dev_get_drvdata(&gdev->cdev[i]->dev) == gdev)
				dev_set_drvdata(&gdev->cdev[i]->dev, NULL);
129
			spin_unlock_irq(gdev->cdev[i]->ccwlock);
130 131
			put_device(&gdev->cdev[i]->dev);
		}
L
Linus Torvalds 已提交
132 133 134 135
	}
	kfree(gdev);
}

136
static int
L
Linus Torvalds 已提交
137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169
__ccwgroup_create_symlinks(struct ccwgroup_device *gdev)
{
	char str[8];
	int i, rc;

	for (i = 0; i < gdev->count; i++) {
		rc = sysfs_create_link(&gdev->cdev[i]->dev.kobj, &gdev->dev.kobj,
				       "group_device");
		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);
		rc = sysfs_create_link(&gdev->dev.kobj, &gdev->cdev[i]->dev.kobj,
				       str);
		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;
}

170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186
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++;
	}
187
	if (len < CCW_BUS_ID_SIZE) {
188 189 190 191 192 193 194 195
		strlcpy(bus_id, start, len);
		rc = 0;
	} else
		rc = -EINVAL;
	*buf = end;
	return rc;
}

196
static int __is_valid_bus_id(char bus_id[CCW_BUS_ID_SIZE])
197 198 199 200 201 202 203 204 205
{
	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 已提交
206
/**
207
 * ccwgroup_create_from_string() - create and register a ccw group device
C
Cornelia Huck 已提交
208 209 210
 * @root: parent device for the new device
 * @creator_id: identifier of creating driver
 * @cdrv: ccw driver of slave devices
211 212
 * @num_devices: number of slave devices
 * @buf: buffer containing comma separated bus ids of slave devices
C
Cornelia Huck 已提交
213 214
 *
 * Create and register a new ccw group device as a child of @root. Slave
215
 * devices are obtained from the list of bus ids given in @buf and must all
C
Cornelia Huck 已提交
216 217 218 219 220
 * belong to @cdrv.
 * Returns:
 *  %0 on success and an error code on failure.
 * Context:
 *  non-atomic
L
Linus Torvalds 已提交
221
 */
222 223 224
int ccwgroup_create_from_string(struct device *root, unsigned int creator_id,
				struct ccw_driver *cdrv, int num_devices,
				const char *buf)
L
Linus Torvalds 已提交
225 226
{
	struct ccwgroup_device *gdev;
227
	int rc, i;
228
	char tmp_bus_id[CCW_BUS_ID_SIZE];
229
	const char *curr_buf;
L
Linus Torvalds 已提交
230

231 232
	gdev = kzalloc(sizeof(*gdev) + num_devices * sizeof(gdev->cdev[0]),
		       GFP_KERNEL);
L
Linus Torvalds 已提交
233 234 235 236
	if (!gdev)
		return -ENOMEM;

	atomic_set(&gdev->onoff, 0);
237 238
	mutex_init(&gdev->reg_mutex);
	mutex_lock(&gdev->reg_mutex);
239 240 241 242 243 244 245
	gdev->creator_id = creator_id;
	gdev->count = num_devices;
	gdev->dev.bus = &ccwgroup_bus_type;
	gdev->dev.parent = root;
	gdev->dev.release = ccwgroup_release;
	device_initialize(&gdev->dev);

246 247 248 249 250 251 252 253 254 255 256 257 258 259
	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 已提交
260 261 262 263
		if (!gdev->cdev[i]
		    || gdev->cdev[i]->id.driver_info !=
		    gdev->cdev[0]->id.driver_info) {
			rc = -EINVAL;
264
			goto error;
L
Linus Torvalds 已提交
265 266
		}
		/* Don't allow a device to belong to more than one group. */
267
		spin_lock_irq(gdev->cdev[i]->ccwlock);
268
		if (dev_get_drvdata(&gdev->cdev[i]->dev)) {
269
			spin_unlock_irq(gdev->cdev[i]->ccwlock);
L
Linus Torvalds 已提交
270
			rc = -EINVAL;
271
			goto error;
L
Linus Torvalds 已提交
272
		}
273
		dev_set_drvdata(&gdev->cdev[i]->dev, gdev);
274
		spin_unlock_irq(gdev->cdev[i]->ccwlock);
275
	}
276 277 278 279 280 281 282 283 284 285
	/* 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 已提交
286

287
	dev_set_name(&gdev->dev, "%s", dev_name(&gdev->cdev[0]->dev));
L
Linus Torvalds 已提交
288

289
	rc = device_add(&gdev->dev);
L
Linus Torvalds 已提交
290
	if (rc)
291
		goto error;
L
Linus Torvalds 已提交
292 293 294 295 296 297 298 299 300 301
	get_device(&gdev->dev);
	rc = device_create_file(&gdev->dev, &dev_attr_ungroup);

	if (rc) {
		device_unregister(&gdev->dev);
		goto error;
	}

	rc = __ccwgroup_create_symlinks(gdev);
	if (!rc) {
302
		mutex_unlock(&gdev->reg_mutex);
L
Linus Torvalds 已提交
303 304 305 306 307 308
		put_device(&gdev->dev);
		return 0;
	}
	device_remove_file(&gdev->dev, &dev_attr_ungroup);
	device_unregister(&gdev->dev);
error:
309
	for (i = 0; i < num_devices; i++)
L
Linus Torvalds 已提交
310
		if (gdev->cdev[i]) {
311
			spin_lock_irq(gdev->cdev[i]->ccwlock);
312 313
			if (dev_get_drvdata(&gdev->cdev[i]->dev) == gdev)
				dev_set_drvdata(&gdev->cdev[i]->dev, NULL);
314
			spin_unlock_irq(gdev->cdev[i]->ccwlock);
315
			put_device(&gdev->cdev[i]->dev);
316
			gdev->cdev[i] = NULL;
L
Linus Torvalds 已提交
317
		}
318 319
	mutex_unlock(&gdev->reg_mutex);
	put_device(&gdev->dev);
L
Linus Torvalds 已提交
320 321
	return rc;
}
322
EXPORT_SYMBOL(ccwgroup_create_from_string);
L
Linus Torvalds 已提交
323

324 325 326 327 328 329 330 331
static int ccwgroup_notifier(struct notifier_block *nb, unsigned long action,
			     void *data);

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

static int __init init_ccwgroup(void)
L
Linus Torvalds 已提交
332
{
333 334 335 336 337 338 339 340 341 342 343
	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 已提交
344 345
}

346
static void __exit cleanup_ccwgroup(void)
L
Linus Torvalds 已提交
347
{
348 349
	bus_unregister_notifier(&ccwgroup_bus_type, &ccwgroup_nb);
	bus_unregister(&ccwgroup_bus_type);
L
Linus Torvalds 已提交
350 351 352 353 354 355 356 357 358 359 360 361 362
}

module_init(init_ccwgroup);
module_exit(cleanup_ccwgroup);

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

static int
ccwgroup_set_online(struct ccwgroup_device *gdev)
{
	struct ccwgroup_driver *gdrv;
	int ret;

363
	if (atomic_cmpxchg(&gdev->onoff, 0, 1) != 0)
L
Linus Torvalds 已提交
364 365 366 367 368 369 370 371 372 373
		return -EAGAIN;
	if (gdev->state == CCWGROUP_ONLINE) {
		ret = 0;
		goto out;
	}
	if (!gdev->dev.driver) {
		ret = -EINVAL;
		goto out;
	}
	gdrv = to_ccwgroupdrv (gdev->dev.driver);
374
	if ((ret = gdrv->set_online ? gdrv->set_online(gdev) : 0))
L
Linus Torvalds 已提交
375 376 377 378 379 380 381 382 383 384 385 386 387 388
		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;
	int ret;

389
	if (atomic_cmpxchg(&gdev->onoff, 0, 1) != 0)
L
Linus Torvalds 已提交
390 391 392 393 394 395 396 397 398 399
		return -EAGAIN;
	if (gdev->state == CCWGROUP_OFFLINE) {
		ret = 0;
		goto out;
	}
	if (!gdev->dev.driver) {
		ret = -EINVAL;
		goto out;
	}
	gdrv = to_ccwgroupdrv (gdev->dev.driver);
400
	if ((ret = gdrv->set_offline ? gdrv->set_offline(gdev) : 0))
L
Linus Torvalds 已提交
401 402 403 404 405 406 407 408 409
		goto out;

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

static ssize_t
410
ccwgroup_online_store (struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
L
Linus Torvalds 已提交
411 412 413
{
	struct ccwgroup_device *gdev;
	struct ccwgroup_driver *gdrv;
414
	unsigned long value;
L
Linus Torvalds 已提交
415 416 417
	int ret;

	if (!dev->driver)
418 419 420 421
		return -ENODEV;

	gdev = to_ccwgroupdev(dev);
	gdrv = to_ccwgroupdrv(dev->driver);
L
Linus Torvalds 已提交
422 423 424 425

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

426 427 428
	ret = strict_strtoul(buf, 0, &value);
	if (ret)
		goto out;
429

L
Linus Torvalds 已提交
430
	if (value == 1)
431
		ret = ccwgroup_set_online(gdev);
L
Linus Torvalds 已提交
432
	else if (value == 0)
433
		ret = ccwgroup_set_offline(gdev);
L
Linus Torvalds 已提交
434 435
	else
		ret = -EINVAL;
436
out:
L
Linus Torvalds 已提交
437
	module_put(gdrv->owner);
438
	return (ret == 0) ? count : ret;
L
Linus Torvalds 已提交
439 440 441
}

static ssize_t
442
ccwgroup_online_show (struct device *dev, struct device_attribute *attr, char *buf)
L
Linus Torvalds 已提交
443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479
{
	int online;

	online = (to_ccwgroupdev(dev)->state == CCWGROUP_ONLINE);

	return sprintf(buf, online ? "1\n" : "0\n");
}

static DEVICE_ATTR(online, 0644, ccwgroup_online_show, ccwgroup_online_store);

static int
ccwgroup_probe (struct device *dev)
{
	struct ccwgroup_device *gdev;
	struct ccwgroup_driver *gdrv;

	int ret;

	gdev = to_ccwgroupdev(dev);
	gdrv = to_ccwgroupdrv(dev->driver);

	if ((ret = device_create_file(dev, &dev_attr_online)))
		return ret;

	ret = gdrv->probe ? gdrv->probe(gdev) : -ENODEV;
	if (ret)
		device_remove_file(dev, &dev_attr_online);

	return ret;
}

static int
ccwgroup_remove (struct device *dev)
{
	struct ccwgroup_device *gdev;
	struct ccwgroup_driver *gdrv;

480
	device_remove_file(dev, &dev_attr_online);
481
	device_remove_file(dev, &dev_attr_ungroup);
482 483 484 485

	if (!dev->driver)
		return 0;

L
Linus Torvalds 已提交
486 487 488
	gdev = to_ccwgroupdev(dev);
	gdrv = to_ccwgroupdrv(dev->driver);

489
	if (gdrv->remove)
L
Linus Torvalds 已提交
490
		gdrv->remove(gdev);
491

L
Linus Torvalds 已提交
492 493 494
	return 0;
}

495 496 497 498 499
static void ccwgroup_shutdown(struct device *dev)
{
	struct ccwgroup_device *gdev;
	struct ccwgroup_driver *gdrv;

500 501 502
	if (!dev->driver)
		return;

503 504
	gdev = to_ccwgroupdev(dev);
	gdrv = to_ccwgroupdrv(dev->driver);
505 506

	if (gdrv->shutdown)
507 508 509
		gdrv->shutdown(gdev);
}

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

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

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

588 589 590 591 592 593 594 595 596 597 598 599 600

static int ccwgroup_notifier(struct notifier_block *nb, unsigned long action,
			     void *data)
{
	struct device *dev = data;

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

	return NOTIFY_OK;
}


C
Cornelia Huck 已提交
601 602 603 604 605 606 607
/**
 * 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 已提交
608 609
{
	/* register our new driver with the core */
610 611
	cdriver->driver.bus = &ccwgroup_bus_type;
	cdriver->driver.name = cdriver->name;
612
	cdriver->driver.owner = cdriver->owner;
L
Linus Torvalds 已提交
613 614 615 616

	return driver_register(&cdriver->driver);
}

C
Cornelia Huck 已提交
617
static int
618
__ccwgroup_match_all(struct device *dev, void *data)
L
Linus Torvalds 已提交
619
{
620
	return 1;
L
Linus Torvalds 已提交
621 622
}

C
Cornelia Huck 已提交
623 624 625 626 627 628 629
/**
 * 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 已提交
630
{
631 632
	struct device *dev;

L
Linus Torvalds 已提交
633 634
	/* We don't want ccwgroup devices to live longer than their driver. */
	get_driver(&cdriver->driver);
635 636
	while ((dev = driver_find_device(&cdriver->driver, NULL, NULL,
					 __ccwgroup_match_all))) {
637 638 639 640
		struct ccwgroup_device *gdev = to_ccwgroupdev(dev);

		mutex_lock(&gdev->reg_mutex);
		__ccwgroup_remove_symlinks(gdev);
641
		device_unregister(dev);
642
		mutex_unlock(&gdev->reg_mutex);
643 644
		put_device(dev);
	}
L
Linus Torvalds 已提交
645 646 647 648
	put_driver(&cdriver->driver);
	driver_unregister(&cdriver->driver);
}

C
Cornelia Huck 已提交
649 650 651 652 653 654 655 656 657 658
/**
 * 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 已提交
659 660 661 662
{
	return 0;
}

663
static struct ccwgroup_device *
L
Linus Torvalds 已提交
664 665 666 667
__ccwgroup_get_gdev_by_cdev(struct ccw_device *cdev)
{
	struct ccwgroup_device *gdev;

668 669
	gdev = dev_get_drvdata(&cdev->dev);
	if (gdev) {
L
Linus Torvalds 已提交
670
		if (get_device(&gdev->dev)) {
671
			mutex_lock(&gdev->reg_mutex);
672
			if (device_is_registered(&gdev->dev))
L
Linus Torvalds 已提交
673
				return gdev;
674
			mutex_unlock(&gdev->reg_mutex);
L
Linus Torvalds 已提交
675 676 677 678 679 680 681
			put_device(&gdev->dev);
		}
		return NULL;
	}
	return NULL;
}

C
Cornelia Huck 已提交
682 683 684 685 686 687 688 689 690
/**
 * 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 已提交
691 692 693 694 695 696 697 698 699 700
{
	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. */
	gdev = __ccwgroup_get_gdev_by_cdev(cdev);
	if (gdev) {
		__ccwgroup_remove_symlinks(gdev);
		device_unregister(&gdev->dev);
701
		mutex_unlock(&gdev->reg_mutex);
L
Linus Torvalds 已提交
702 703 704 705 706 707 708 709 710
		put_device(&gdev->dev);
	}
}

MODULE_LICENSE("GPL");
EXPORT_SYMBOL(ccwgroup_driver_register);
EXPORT_SYMBOL(ccwgroup_driver_unregister);
EXPORT_SYMBOL(ccwgroup_probe_ccwdev);
EXPORT_SYMBOL(ccwgroup_remove_ccwdev);