/** * 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) { struct ccwgroup_device *gdev; int rc, i; char tmp_bus_id[CCW_BUS_ID_SIZE]; const char *curr_buf; gdev = kzalloc(sizeof(*gdev) + num_devices * sizeof(gdev->cdev[0]), GFP_KERNEL); if (!gdev) return -ENOMEM; atomic_set(&gdev->onoff, 0); mutex_init(&gdev->reg_mutex); mutex_lock(&gdev->reg_mutex); 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); 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. */ if (!gdev->cdev[i] || gdev->cdev[i]->id.driver_info != gdev->cdev[0]->id.driver_info) { rc = -EINVAL; goto error; } /* Don't allow a device to belong to more than one group. */ spin_lock_irq(gdev->cdev[i]->ccwlock); if (dev_get_drvdata(&gdev->cdev[i]->dev)) { spin_unlock_irq(gdev->cdev[i]->ccwlock); rc = -EINVAL; goto error; } dev_set_drvdata(&gdev->cdev[i]->dev, gdev); spin_unlock_irq(gdev->cdev[i]->ccwlock); } /* 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; } dev_set_name(&gdev->dev, "%s", dev_name(&gdev->cdev[0]->dev)); rc = device_add(&gdev->dev); if (rc) goto error; 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) { mutex_unlock(&gdev->reg_mutex); put_device(&gdev->dev); return 0; } device_remove_file(&gdev->dev, &dev_attr_ungroup); device_unregister(&gdev->dev); error: for (i = 0; i < num_devices; i++) if (gdev->cdev[i]) { spin_lock_irq(gdev->cdev[i]->ccwlock); if (dev_get_drvdata(&gdev->cdev[i]->dev) == gdev) dev_set_drvdata(&gdev->cdev[i]->dev, NULL); spin_unlock_irq(gdev->cdev[i]->ccwlock); put_device(&gdev->cdev[i]->dev); gdev->cdev[i] = NULL; } mutex_unlock(&gdev->reg_mutex); put_device(&gdev->dev); return rc; }
/* * try to add a new ccwgroup device for one driver * argc and argv[] are a list of bus_id's of devices * belonging to the driver. */ int ccwgroup_create(struct device *root, unsigned int creator_id, struct ccw_driver *cdrv, int argc, char *argv[]) { struct ccwgroup_device *gdev; int i; int rc; int del_drvdata; if (argc > 256) /* disallow dumb users */ return -EINVAL; gdev = kmalloc(sizeof(*gdev) + argc*sizeof(gdev->cdev[0]), GFP_KERNEL); if (!gdev) return -ENOMEM; memset(gdev, 0, sizeof(*gdev) + argc*sizeof(gdev->cdev[0])); atomic_set(&gdev->onoff, 0); del_drvdata = 0; for (i = 0; i < argc; i++) { gdev->cdev[i] = get_ccwdev_by_busid(cdrv, argv[i]); /* all devices have to be of the same type in * order to be grouped */ if (!gdev->cdev[i] || gdev->cdev[i]->id.driver_info != gdev->cdev[0]->id.driver_info) { rc = -EINVAL; goto free_dev; } /* Don't allow a device to belong to more than one group. */ if (gdev->cdev[i]->dev.driver_data) { rc = -EINVAL; goto free_dev; } } for (i = 0; i < argc; i++) gdev->cdev[i]->dev.driver_data = gdev; del_drvdata = 1; gdev->creator_id = creator_id; gdev->count = argc; gdev->dev = (struct device ) { .bus = &ccwgroup_bus_type, .parent = root, .release = ccwgroup_release, }; snprintf (gdev->dev.bus_id, BUS_ID_SIZE, "%s", gdev->cdev[0]->dev.bus_id); rc = device_register(&gdev->dev); if (rc) goto free_dev; 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) { put_device(&gdev->dev); return 0; } device_remove_file(&gdev->dev, &dev_attr_ungroup); device_unregister(&gdev->dev); error: for (i = 0; i < argc; i++) if (gdev->cdev[i]) { put_device(&gdev->cdev[i]->dev); gdev->cdev[i]->dev.driver_data = NULL; } put_device(&gdev->dev); return rc; free_dev: for (i = 0; i < argc; i++) if (gdev->cdev[i]) { put_device(&gdev->cdev[i]->dev); if (del_drvdata) gdev->cdev[i]->dev.driver_data = NULL; } kfree(gdev); return rc; }
/** * ccwgroup_create_dev() - create and register a ccw group device * @parent: parent device for the new device * @gdrv: driver for the new group device * @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 @parent. Slave * devices are obtained from the list of bus ids given in @buf. * Returns: * %0 on success and an error code on failure. * Context: * non-atomic */ int ccwgroup_create_dev(struct device *parent, struct ccwgroup_driver *gdrv, int num_devices, const char *buf) { struct ccwgroup_device *gdev; struct ccw_dev_id dev_id; int rc, i; gdev = kzalloc(sizeof(*gdev) + num_devices * sizeof(gdev->cdev[0]), GFP_KERNEL); if (!gdev) return -ENOMEM; atomic_set(&gdev->onoff, 0); mutex_init(&gdev->reg_mutex); mutex_lock(&gdev->reg_mutex); INIT_WORK(&gdev->ungroup_work, ccwgroup_ungroup_workfn); gdev->count = num_devices; gdev->dev.bus = &ccwgroup_bus_type; gdev->dev.parent = parent; gdev->dev.release = ccwgroup_release; device_initialize(&gdev->dev); for (i = 0; i < num_devices && buf; i++) { rc = __get_next_id(&buf, &dev_id); if (rc != 0) goto error; gdev->cdev[i] = get_ccwdev_by_dev_id(&dev_id); /* * All devices have to be of the same type in * order to be grouped. */ if (!gdev->cdev[i] || !gdev->cdev[i]->drv || gdev->cdev[i]->drv != gdev->cdev[0]->drv || gdev->cdev[i]->id.driver_info != gdev->cdev[0]->id.driver_info) { rc = -EINVAL; goto error; } /* Don't allow a device to belong to more than one group. */ spin_lock_irq(gdev->cdev[i]->ccwlock); if (dev_get_drvdata(&gdev->cdev[i]->dev)) { spin_unlock_irq(gdev->cdev[i]->ccwlock); rc = -EINVAL; goto error; } dev_set_drvdata(&gdev->cdev[i]->dev, gdev); spin_unlock_irq(gdev->cdev[i]->ccwlock); } /* Check for sufficient number of bus ids. */ if (i < num_devices) { rc = -EINVAL; goto error; } /* Check for trailing stuff. */ if (i == num_devices && strlen(buf) > 0) { rc = -EINVAL; goto error; } dev_set_name(&gdev->dev, "%s", dev_name(&gdev->cdev[0]->dev)); gdev->dev.groups = ccwgroup_attr_groups; if (gdrv) { gdev->dev.driver = &gdrv->driver; rc = gdrv->setup ? gdrv->setup(gdev) : 0; if (rc) goto error; } rc = device_add(&gdev->dev); if (rc) goto error; rc = __ccwgroup_create_symlinks(gdev); if (rc) { device_del(&gdev->dev); goto error; } mutex_unlock(&gdev->reg_mutex); return 0; error: for (i = 0; i < num_devices; i++) if (gdev->cdev[i]) { spin_lock_irq(gdev->cdev[i]->ccwlock); if (dev_get_drvdata(&gdev->cdev[i]->dev) == gdev) dev_set_drvdata(&gdev->cdev[i]->dev, NULL); spin_unlock_irq(gdev->cdev[i]->ccwlock); put_device(&gdev->cdev[i]->dev); gdev->cdev[i] = NULL; } mutex_unlock(&gdev->reg_mutex); put_device(&gdev->dev); return rc; }