root/drivers/s390/cio/ccwgroup.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. __ccwgroup_remove_symlinks
  2. __ccwgroup_remove_cdev_refs
  3. ccwgroup_set_online
  4. ccwgroup_set_offline
  5. ccwgroup_online_store
  6. ccwgroup_online_show
  7. ccwgroup_ungroup
  8. ccwgroup_ungroup_store
  9. ccwgroup_ungroup_workfn
  10. ccwgroup_release
  11. __ccwgroup_create_symlinks
  12. __get_next_id
  13. ccwgroup_create_dev
  14. ccwgroup_notifier
  15. init_ccwgroup
  16. cleanup_ccwgroup
  17. ccwgroup_remove
  18. ccwgroup_shutdown
  19. ccwgroup_pm_prepare
  20. ccwgroup_pm_complete
  21. ccwgroup_pm_freeze
  22. ccwgroup_pm_thaw
  23. ccwgroup_pm_restore
  24. dev_is_ccwgroup
  25. ccwgroup_driver_register
  26. ccwgroup_driver_unregister
  27. get_ccwgroupdev_by_busid
  28. ccwgroup_probe_ccwdev
  29. ccwgroup_remove_ccwdev

   1 // SPDX-License-Identifier: GPL-2.0
   2 /*
   3  *  bus driver for ccwgroup
   4  *
   5  *  Copyright IBM Corp. 2002, 2012
   6  *
   7  *  Author(s): Arnd Bergmann (arndb@de.ibm.com)
   8  *             Cornelia Huck (cornelia.huck@de.ibm.com)
   9  */
  10 #include <linux/module.h>
  11 #include <linux/errno.h>
  12 #include <linux/slab.h>
  13 #include <linux/list.h>
  14 #include <linux/device.h>
  15 #include <linux/init.h>
  16 #include <linux/ctype.h>
  17 #include <linux/dcache.h>
  18 
  19 #include <asm/cio.h>
  20 #include <asm/ccwdev.h>
  21 #include <asm/ccwgroup.h>
  22 
  23 #include "device.h"
  24 
  25 #define CCW_BUS_ID_SIZE         10
  26 
  27 /* In Linux 2.4, we had a channel device layer called "chandev"
  28  * that did all sorts of obscure stuff for networking devices.
  29  * This is another driver that serves as a replacement for just
  30  * one of its functions, namely the translation of single subchannels
  31  * to devices that use multiple subchannels.
  32  */
  33 
  34 static struct bus_type ccwgroup_bus_type;
  35 
  36 static void __ccwgroup_remove_symlinks(struct ccwgroup_device *gdev)
  37 {
  38         int i;
  39         char str[16];
  40 
  41         for (i = 0; i < gdev->count; i++) {
  42                 sprintf(str, "cdev%d", i);
  43                 sysfs_remove_link(&gdev->dev.kobj, str);
  44                 sysfs_remove_link(&gdev->cdev[i]->dev.kobj, "group_device");
  45         }
  46 }
  47 
  48 /*
  49  * Remove references from ccw devices to ccw group device and from
  50  * ccw group device to ccw devices.
  51  */
  52 static void __ccwgroup_remove_cdev_refs(struct ccwgroup_device *gdev)
  53 {
  54         struct ccw_device *cdev;
  55         int i;
  56 
  57         for (i = 0; i < gdev->count; i++) {
  58                 cdev = gdev->cdev[i];
  59                 if (!cdev)
  60                         continue;
  61                 spin_lock_irq(cdev->ccwlock);
  62                 dev_set_drvdata(&cdev->dev, NULL);
  63                 spin_unlock_irq(cdev->ccwlock);
  64                 gdev->cdev[i] = NULL;
  65                 put_device(&cdev->dev);
  66         }
  67 }
  68 
  69 /**
  70  * ccwgroup_set_online() - enable a ccwgroup device
  71  * @gdev: target ccwgroup device
  72  *
  73  * This function attempts to put the ccwgroup device into the online state.
  74  * Returns:
  75  *  %0 on success and a negative error value on failure.
  76  */
  77 int ccwgroup_set_online(struct ccwgroup_device *gdev)
  78 {
  79         struct ccwgroup_driver *gdrv = to_ccwgroupdrv(gdev->dev.driver);
  80         int ret = -EINVAL;
  81 
  82         if (atomic_cmpxchg(&gdev->onoff, 0, 1) != 0)
  83                 return -EAGAIN;
  84         if (gdev->state == CCWGROUP_ONLINE)
  85                 goto out;
  86         if (gdrv->set_online)
  87                 ret = gdrv->set_online(gdev);
  88         if (ret)
  89                 goto out;
  90 
  91         gdev->state = CCWGROUP_ONLINE;
  92 out:
  93         atomic_set(&gdev->onoff, 0);
  94         return ret;
  95 }
  96 EXPORT_SYMBOL(ccwgroup_set_online);
  97 
  98 /**
  99  * ccwgroup_set_offline() - disable a ccwgroup device
 100  * @gdev: target ccwgroup device
 101  *
 102  * This function attempts to put the ccwgroup device into the offline state.
 103  * Returns:
 104  *  %0 on success and a negative error value on failure.
 105  */
 106 int ccwgroup_set_offline(struct ccwgroup_device *gdev)
 107 {
 108         struct ccwgroup_driver *gdrv = to_ccwgroupdrv(gdev->dev.driver);
 109         int ret = -EINVAL;
 110 
 111         if (atomic_cmpxchg(&gdev->onoff, 0, 1) != 0)
 112                 return -EAGAIN;
 113         if (gdev->state == CCWGROUP_OFFLINE)
 114                 goto out;
 115         if (gdrv->set_offline)
 116                 ret = gdrv->set_offline(gdev);
 117         if (ret)
 118                 goto out;
 119 
 120         gdev->state = CCWGROUP_OFFLINE;
 121 out:
 122         atomic_set(&gdev->onoff, 0);
 123         return ret;
 124 }
 125 EXPORT_SYMBOL(ccwgroup_set_offline);
 126 
 127 static ssize_t ccwgroup_online_store(struct device *dev,
 128                                      struct device_attribute *attr,
 129                                      const char *buf, size_t count)
 130 {
 131         struct ccwgroup_device *gdev = to_ccwgroupdev(dev);
 132         unsigned long value;
 133         int ret;
 134 
 135         device_lock(dev);
 136         if (!dev->driver) {
 137                 ret = -EINVAL;
 138                 goto out;
 139         }
 140 
 141         ret = kstrtoul(buf, 0, &value);
 142         if (ret)
 143                 goto out;
 144 
 145         if (value == 1)
 146                 ret = ccwgroup_set_online(gdev);
 147         else if (value == 0)
 148                 ret = ccwgroup_set_offline(gdev);
 149         else
 150                 ret = -EINVAL;
 151 out:
 152         device_unlock(dev);
 153         return (ret == 0) ? count : ret;
 154 }
 155 
 156 static ssize_t ccwgroup_online_show(struct device *dev,
 157                                     struct device_attribute *attr,
 158                                     char *buf)
 159 {
 160         struct ccwgroup_device *gdev = to_ccwgroupdev(dev);
 161         int online;
 162 
 163         online = (gdev->state == CCWGROUP_ONLINE) ? 1 : 0;
 164 
 165         return scnprintf(buf, PAGE_SIZE, "%d\n", online);
 166 }
 167 
 168 /*
 169  * Provide an 'ungroup' attribute so the user can remove group devices no
 170  * longer needed or accidentially created. Saves memory :)
 171  */
 172 static void ccwgroup_ungroup(struct ccwgroup_device *gdev)
 173 {
 174         mutex_lock(&gdev->reg_mutex);
 175         if (device_is_registered(&gdev->dev)) {
 176                 __ccwgroup_remove_symlinks(gdev);
 177                 device_unregister(&gdev->dev);
 178                 __ccwgroup_remove_cdev_refs(gdev);
 179         }
 180         mutex_unlock(&gdev->reg_mutex);
 181 }
 182 
 183 static ssize_t ccwgroup_ungroup_store(struct device *dev,
 184                                       struct device_attribute *attr,
 185                                       const char *buf, size_t count)
 186 {
 187         struct ccwgroup_device *gdev = to_ccwgroupdev(dev);
 188         int rc = 0;
 189 
 190         /* Prevent concurrent online/offline processing and ungrouping. */
 191         if (atomic_cmpxchg(&gdev->onoff, 0, 1) != 0)
 192                 return -EAGAIN;
 193         if (gdev->state != CCWGROUP_OFFLINE) {
 194                 rc = -EINVAL;
 195                 goto out;
 196         }
 197 
 198         if (device_remove_file_self(dev, attr))
 199                 ccwgroup_ungroup(gdev);
 200         else
 201                 rc = -ENODEV;
 202 out:
 203         if (rc) {
 204                 /* Release onoff "lock" when ungrouping failed. */
 205                 atomic_set(&gdev->onoff, 0);
 206                 return rc;
 207         }
 208         return count;
 209 }
 210 static DEVICE_ATTR(ungroup, 0200, NULL, ccwgroup_ungroup_store);
 211 static DEVICE_ATTR(online, 0644, ccwgroup_online_show, ccwgroup_online_store);
 212 
 213 static struct attribute *ccwgroup_attrs[] = {
 214         &dev_attr_online.attr,
 215         &dev_attr_ungroup.attr,
 216         NULL,
 217 };
 218 static struct attribute_group ccwgroup_attr_group = {
 219         .attrs = ccwgroup_attrs,
 220 };
 221 static const struct attribute_group *ccwgroup_attr_groups[] = {
 222         &ccwgroup_attr_group,
 223         NULL,
 224 };
 225 
 226 static void ccwgroup_ungroup_workfn(struct work_struct *work)
 227 {
 228         struct ccwgroup_device *gdev =
 229                 container_of(work, struct ccwgroup_device, ungroup_work);
 230 
 231         ccwgroup_ungroup(gdev);
 232         put_device(&gdev->dev);
 233 }
 234 
 235 static void ccwgroup_release(struct device *dev)
 236 {
 237         kfree(to_ccwgroupdev(dev));
 238 }
 239 
 240 static int __ccwgroup_create_symlinks(struct ccwgroup_device *gdev)
 241 {
 242         char str[16];
 243         int i, rc;
 244 
 245         for (i = 0; i < gdev->count; i++) {
 246                 rc = sysfs_create_link(&gdev->cdev[i]->dev.kobj,
 247                                        &gdev->dev.kobj, "group_device");
 248                 if (rc) {
 249                         for (--i; i >= 0; i--)
 250                                 sysfs_remove_link(&gdev->cdev[i]->dev.kobj,
 251                                                   "group_device");
 252                         return rc;
 253                 }
 254         }
 255         for (i = 0; i < gdev->count; i++) {
 256                 sprintf(str, "cdev%d", i);
 257                 rc = sysfs_create_link(&gdev->dev.kobj,
 258                                        &gdev->cdev[i]->dev.kobj, str);
 259                 if (rc) {
 260                         for (--i; i >= 0; i--) {
 261                                 sprintf(str, "cdev%d", i);
 262                                 sysfs_remove_link(&gdev->dev.kobj, str);
 263                         }
 264                         for (i = 0; i < gdev->count; i++)
 265                                 sysfs_remove_link(&gdev->cdev[i]->dev.kobj,
 266                                                   "group_device");
 267                         return rc;
 268                 }
 269         }
 270         return 0;
 271 }
 272 
 273 static int __get_next_id(const char **buf, struct ccw_dev_id *id)
 274 {
 275         unsigned int cssid, ssid, devno;
 276         int ret = 0, len;
 277         char *start, *end;
 278 
 279         start = (char *)*buf;
 280         end = strchr(start, ',');
 281         if (!end) {
 282                 /* Last entry. Strip trailing newline, if applicable. */
 283                 end = strchr(start, '\n');
 284                 if (end)
 285                         *end = '\0';
 286                 len = strlen(start) + 1;
 287         } else {
 288                 len = end - start + 1;
 289                 end++;
 290         }
 291         if (len <= CCW_BUS_ID_SIZE) {
 292                 if (sscanf(start, "%2x.%1x.%04x", &cssid, &ssid, &devno) != 3)
 293                         ret = -EINVAL;
 294         } else
 295                 ret = -EINVAL;
 296 
 297         if (!ret) {
 298                 id->ssid = ssid;
 299                 id->devno = devno;
 300         }
 301         *buf = end;
 302         return ret;
 303 }
 304 
 305 /**
 306  * ccwgroup_create_dev() - create and register a ccw group device
 307  * @parent: parent device for the new device
 308  * @gdrv: driver for the new group device
 309  * @num_devices: number of slave devices
 310  * @buf: buffer containing comma separated bus ids of slave devices
 311  *
 312  * Create and register a new ccw group device as a child of @parent. Slave
 313  * devices are obtained from the list of bus ids given in @buf.
 314  * Returns:
 315  *  %0 on success and an error code on failure.
 316  * Context:
 317  *  non-atomic
 318  */
 319 int ccwgroup_create_dev(struct device *parent, struct ccwgroup_driver *gdrv,
 320                         int num_devices, const char *buf)
 321 {
 322         struct ccwgroup_device *gdev;
 323         struct ccw_dev_id dev_id;
 324         int rc, i;
 325 
 326         if (num_devices < 1)
 327                 return -EINVAL;
 328 
 329         gdev = kzalloc(struct_size(gdev, cdev, num_devices), GFP_KERNEL);
 330         if (!gdev)
 331                 return -ENOMEM;
 332 
 333         atomic_set(&gdev->onoff, 0);
 334         mutex_init(&gdev->reg_mutex);
 335         mutex_lock(&gdev->reg_mutex);
 336         INIT_WORK(&gdev->ungroup_work, ccwgroup_ungroup_workfn);
 337         gdev->count = num_devices;
 338         gdev->dev.bus = &ccwgroup_bus_type;
 339         gdev->dev.parent = parent;
 340         gdev->dev.release = ccwgroup_release;
 341         device_initialize(&gdev->dev);
 342 
 343         for (i = 0; i < num_devices && buf; i++) {
 344                 rc = __get_next_id(&buf, &dev_id);
 345                 if (rc != 0)
 346                         goto error;
 347                 gdev->cdev[i] = get_ccwdev_by_dev_id(&dev_id);
 348                 /*
 349                  * All devices have to be of the same type in
 350                  * order to be grouped.
 351                  */
 352                 if (!gdev->cdev[i] || !gdev->cdev[i]->drv ||
 353                     gdev->cdev[i]->drv != gdev->cdev[0]->drv ||
 354                     gdev->cdev[i]->id.driver_info !=
 355                     gdev->cdev[0]->id.driver_info) {
 356                         rc = -EINVAL;
 357                         goto error;
 358                 }
 359                 /* Don't allow a device to belong to more than one group. */
 360                 spin_lock_irq(gdev->cdev[i]->ccwlock);
 361                 if (dev_get_drvdata(&gdev->cdev[i]->dev)) {
 362                         spin_unlock_irq(gdev->cdev[i]->ccwlock);
 363                         rc = -EINVAL;
 364                         goto error;
 365                 }
 366                 dev_set_drvdata(&gdev->cdev[i]->dev, gdev);
 367                 spin_unlock_irq(gdev->cdev[i]->ccwlock);
 368         }
 369         /* Check for sufficient number of bus ids. */
 370         if (i < num_devices) {
 371                 rc = -EINVAL;
 372                 goto error;
 373         }
 374         /* Check for trailing stuff. */
 375         if (i == num_devices && buf && strlen(buf) > 0) {
 376                 rc = -EINVAL;
 377                 goto error;
 378         }
 379         /* Check if the devices are bound to the required ccw driver. */
 380         if (gdrv && gdrv->ccw_driver &&
 381             gdev->cdev[0]->drv != gdrv->ccw_driver) {
 382                 rc = -EINVAL;
 383                 goto error;
 384         }
 385 
 386         dev_set_name(&gdev->dev, "%s", dev_name(&gdev->cdev[0]->dev));
 387         gdev->dev.groups = ccwgroup_attr_groups;
 388 
 389         if (gdrv) {
 390                 gdev->dev.driver = &gdrv->driver;
 391                 rc = gdrv->setup ? gdrv->setup(gdev) : 0;
 392                 if (rc)
 393                         goto error;
 394         }
 395         rc = device_add(&gdev->dev);
 396         if (rc)
 397                 goto error;
 398         rc = __ccwgroup_create_symlinks(gdev);
 399         if (rc) {
 400                 device_del(&gdev->dev);
 401                 goto error;
 402         }
 403         mutex_unlock(&gdev->reg_mutex);
 404         return 0;
 405 error:
 406         for (i = 0; i < num_devices; i++)
 407                 if (gdev->cdev[i]) {
 408                         spin_lock_irq(gdev->cdev[i]->ccwlock);
 409                         if (dev_get_drvdata(&gdev->cdev[i]->dev) == gdev)
 410                                 dev_set_drvdata(&gdev->cdev[i]->dev, NULL);
 411                         spin_unlock_irq(gdev->cdev[i]->ccwlock);
 412                         put_device(&gdev->cdev[i]->dev);
 413                         gdev->cdev[i] = NULL;
 414                 }
 415         mutex_unlock(&gdev->reg_mutex);
 416         put_device(&gdev->dev);
 417         return rc;
 418 }
 419 EXPORT_SYMBOL(ccwgroup_create_dev);
 420 
 421 static int ccwgroup_notifier(struct notifier_block *nb, unsigned long action,
 422                              void *data)
 423 {
 424         struct ccwgroup_device *gdev = to_ccwgroupdev(data);
 425 
 426         if (action == BUS_NOTIFY_UNBIND_DRIVER) {
 427                 get_device(&gdev->dev);
 428                 schedule_work(&gdev->ungroup_work);
 429         }
 430 
 431         return NOTIFY_OK;
 432 }
 433 
 434 static struct notifier_block ccwgroup_nb = {
 435         .notifier_call = ccwgroup_notifier
 436 };
 437 
 438 static int __init init_ccwgroup(void)
 439 {
 440         int ret;
 441 
 442         ret = bus_register(&ccwgroup_bus_type);
 443         if (ret)
 444                 return ret;
 445 
 446         ret = bus_register_notifier(&ccwgroup_bus_type, &ccwgroup_nb);
 447         if (ret)
 448                 bus_unregister(&ccwgroup_bus_type);
 449 
 450         return ret;
 451 }
 452 
 453 static void __exit cleanup_ccwgroup(void)
 454 {
 455         bus_unregister_notifier(&ccwgroup_bus_type, &ccwgroup_nb);
 456         bus_unregister(&ccwgroup_bus_type);
 457 }
 458 
 459 module_init(init_ccwgroup);
 460 module_exit(cleanup_ccwgroup);
 461 
 462 /************************** driver stuff ******************************/
 463 
 464 static int ccwgroup_remove(struct device *dev)
 465 {
 466         struct ccwgroup_device *gdev = to_ccwgroupdev(dev);
 467         struct ccwgroup_driver *gdrv = to_ccwgroupdrv(dev->driver);
 468 
 469         if (!dev->driver)
 470                 return 0;
 471         if (gdrv->remove)
 472                 gdrv->remove(gdev);
 473 
 474         return 0;
 475 }
 476 
 477 static void ccwgroup_shutdown(struct device *dev)
 478 {
 479         struct ccwgroup_device *gdev = to_ccwgroupdev(dev);
 480         struct ccwgroup_driver *gdrv = to_ccwgroupdrv(dev->driver);
 481 
 482         if (!dev->driver)
 483                 return;
 484         if (gdrv->shutdown)
 485                 gdrv->shutdown(gdev);
 486 }
 487 
 488 static int ccwgroup_pm_prepare(struct device *dev)
 489 {
 490         struct ccwgroup_device *gdev = to_ccwgroupdev(dev);
 491         struct ccwgroup_driver *gdrv = to_ccwgroupdrv(gdev->dev.driver);
 492 
 493         /* Fail while device is being set online/offline. */
 494         if (atomic_read(&gdev->onoff))
 495                 return -EAGAIN;
 496 
 497         if (!gdev->dev.driver || gdev->state != CCWGROUP_ONLINE)
 498                 return 0;
 499 
 500         return gdrv->prepare ? gdrv->prepare(gdev) : 0;
 501 }
 502 
 503 static void ccwgroup_pm_complete(struct device *dev)
 504 {
 505         struct ccwgroup_device *gdev = to_ccwgroupdev(dev);
 506         struct ccwgroup_driver *gdrv = to_ccwgroupdrv(dev->driver);
 507 
 508         if (!gdev->dev.driver || gdev->state != CCWGROUP_ONLINE)
 509                 return;
 510 
 511         if (gdrv->complete)
 512                 gdrv->complete(gdev);
 513 }
 514 
 515 static int ccwgroup_pm_freeze(struct device *dev)
 516 {
 517         struct ccwgroup_device *gdev = to_ccwgroupdev(dev);
 518         struct ccwgroup_driver *gdrv = to_ccwgroupdrv(gdev->dev.driver);
 519 
 520         if (!gdev->dev.driver || gdev->state != CCWGROUP_ONLINE)
 521                 return 0;
 522 
 523         return gdrv->freeze ? gdrv->freeze(gdev) : 0;
 524 }
 525 
 526 static int ccwgroup_pm_thaw(struct device *dev)
 527 {
 528         struct ccwgroup_device *gdev = to_ccwgroupdev(dev);
 529         struct ccwgroup_driver *gdrv = to_ccwgroupdrv(gdev->dev.driver);
 530 
 531         if (!gdev->dev.driver || gdev->state != CCWGROUP_ONLINE)
 532                 return 0;
 533 
 534         return gdrv->thaw ? gdrv->thaw(gdev) : 0;
 535 }
 536 
 537 static int ccwgroup_pm_restore(struct device *dev)
 538 {
 539         struct ccwgroup_device *gdev = to_ccwgroupdev(dev);
 540         struct ccwgroup_driver *gdrv = to_ccwgroupdrv(gdev->dev.driver);
 541 
 542         if (!gdev->dev.driver || gdev->state != CCWGROUP_ONLINE)
 543                 return 0;
 544 
 545         return gdrv->restore ? gdrv->restore(gdev) : 0;
 546 }
 547 
 548 static const struct dev_pm_ops ccwgroup_pm_ops = {
 549         .prepare = ccwgroup_pm_prepare,
 550         .complete = ccwgroup_pm_complete,
 551         .freeze = ccwgroup_pm_freeze,
 552         .thaw = ccwgroup_pm_thaw,
 553         .restore = ccwgroup_pm_restore,
 554 };
 555 
 556 static struct bus_type ccwgroup_bus_type = {
 557         .name   = "ccwgroup",
 558         .remove = ccwgroup_remove,
 559         .shutdown = ccwgroup_shutdown,
 560         .pm = &ccwgroup_pm_ops,
 561 };
 562 
 563 bool dev_is_ccwgroup(struct device *dev)
 564 {
 565         return dev->bus == &ccwgroup_bus_type;
 566 }
 567 EXPORT_SYMBOL(dev_is_ccwgroup);
 568 
 569 /**
 570  * ccwgroup_driver_register() - register a ccw group driver
 571  * @cdriver: driver to be registered
 572  *
 573  * This function is mainly a wrapper around driver_register().
 574  */
 575 int ccwgroup_driver_register(struct ccwgroup_driver *cdriver)
 576 {
 577         /* register our new driver with the core */
 578         cdriver->driver.bus = &ccwgroup_bus_type;
 579 
 580         return driver_register(&cdriver->driver);
 581 }
 582 EXPORT_SYMBOL(ccwgroup_driver_register);
 583 
 584 /**
 585  * ccwgroup_driver_unregister() - deregister a ccw group driver
 586  * @cdriver: driver to be deregistered
 587  *
 588  * This function is mainly a wrapper around driver_unregister().
 589  */
 590 void ccwgroup_driver_unregister(struct ccwgroup_driver *cdriver)
 591 {
 592         struct device *dev;
 593 
 594         /* We don't want ccwgroup devices to live longer than their driver. */
 595         while ((dev = driver_find_next_device(&cdriver->driver, NULL))) {
 596                 struct ccwgroup_device *gdev = to_ccwgroupdev(dev);
 597 
 598                 ccwgroup_ungroup(gdev);
 599                 put_device(dev);
 600         }
 601         driver_unregister(&cdriver->driver);
 602 }
 603 EXPORT_SYMBOL(ccwgroup_driver_unregister);
 604 
 605 /**
 606  * get_ccwgroupdev_by_busid() - obtain device from a bus id
 607  * @gdrv: driver the device is owned by
 608  * @bus_id: bus id of the device to be searched
 609  *
 610  * This function searches all devices owned by @gdrv for a device with a bus
 611  * id matching @bus_id.
 612  * Returns:
 613  *  If a match is found, its reference count of the found device is increased
 614  *  and it is returned; else %NULL is returned.
 615  */
 616 struct ccwgroup_device *get_ccwgroupdev_by_busid(struct ccwgroup_driver *gdrv,
 617                                                  char *bus_id)
 618 {
 619         struct device *dev;
 620 
 621         dev = driver_find_device_by_name(&gdrv->driver, bus_id);
 622 
 623         return dev ? to_ccwgroupdev(dev) : NULL;
 624 }
 625 EXPORT_SYMBOL_GPL(get_ccwgroupdev_by_busid);
 626 
 627 /**
 628  * ccwgroup_probe_ccwdev() - probe function for slave devices
 629  * @cdev: ccw device to be probed
 630  *
 631  * This is a dummy probe function for ccw devices that are slave devices in
 632  * a ccw group device.
 633  * Returns:
 634  *  always %0
 635  */
 636 int ccwgroup_probe_ccwdev(struct ccw_device *cdev)
 637 {
 638         return 0;
 639 }
 640 EXPORT_SYMBOL(ccwgroup_probe_ccwdev);
 641 
 642 /**
 643  * ccwgroup_remove_ccwdev() - remove function for slave devices
 644  * @cdev: ccw device to be removed
 645  *
 646  * This is a remove function for ccw devices that are slave devices in a ccw
 647  * group device. It sets the ccw device offline and also deregisters the
 648  * embedding ccw group device.
 649  */
 650 void ccwgroup_remove_ccwdev(struct ccw_device *cdev)
 651 {
 652         struct ccwgroup_device *gdev;
 653 
 654         /* Ignore offlining errors, device is gone anyway. */
 655         ccw_device_set_offline(cdev);
 656         /* If one of its devices is gone, the whole group is done for. */
 657         spin_lock_irq(cdev->ccwlock);
 658         gdev = dev_get_drvdata(&cdev->dev);
 659         if (!gdev) {
 660                 spin_unlock_irq(cdev->ccwlock);
 661                 return;
 662         }
 663         /* Get ccwgroup device reference for local processing. */
 664         get_device(&gdev->dev);
 665         spin_unlock_irq(cdev->ccwlock);
 666         /* Unregister group device. */
 667         ccwgroup_ungroup(gdev);
 668         /* Release ccwgroup device reference for local processing. */
 669         put_device(&gdev->dev);
 670 }
 671 EXPORT_SYMBOL(ccwgroup_remove_ccwdev);
 672 MODULE_LICENSE("GPL");

/* [<][>][^][v][top][bottom][index][help] */