2 * drivers/s390/cio/ccwgroup.c
3 * bus driver for ccwgroup
6 * Copyright (C) 2002 IBM Deutschland Entwicklung GmbH,
8 * Author(s): Arnd Bergmann (arndb@de.ibm.com)
9 * Cornelia Huck (cohuck@de.ibm.com)
11 #include <linux/module.h>
12 #include <linux/errno.h>
13 #include <linux/slab.h>
14 #include <linux/list.h>
15 #include <linux/device.h>
16 #include <linux/init.h>
17 #include <linux/ctype.h>
18 #include <linux/dcache.h>
20 #include <asm/semaphore.h>
21 #include <asm/ccwdev.h>
22 #include <asm/ccwgroup.h>
24 /* In Linux 2.4, we had a channel device layer called "chandev"
25 * that did all sorts of obscure stuff for networking devices.
26 * This is another driver that serves as a replacement for just
27 * one of its functions, namely the translation of single subchannels
28 * to devices that use multiple subchannels.
31 /* a device matches a driver if all its slave devices match the same
32 * entry of the driver */
34 ccwgroup_bus_match (struct device
* dev
, struct device_driver
* drv
)
36 struct ccwgroup_device
*gdev
;
37 struct ccwgroup_driver
*gdrv
;
39 gdev
= container_of(dev
, struct ccwgroup_device
, dev
);
40 gdrv
= container_of(drv
, struct ccwgroup_driver
, driver
);
42 if (gdev
->creator_id
== gdrv
->driver_id
)
48 ccwgroup_hotplug (struct device
*dev
, char **envp
, int num_envp
, char *buffer
,
55 static struct bus_type ccwgroup_bus_type
= {
57 .match
= ccwgroup_bus_match
,
58 .hotplug
= ccwgroup_hotplug
,
62 __ccwgroup_remove_symlinks(struct ccwgroup_device
*gdev
)
67 for (i
= 0; i
< gdev
->count
; i
++) {
68 sprintf(str
, "cdev%d", i
);
69 sysfs_remove_link(&gdev
->dev
.kobj
, str
);
70 sysfs_remove_link(&gdev
->cdev
[i
]->dev
.kobj
, "group_device");
76 * Provide an 'ungroup' attribute so the user can remove group devices no
77 * longer needed or accidentially created. Saves memory :)
80 ccwgroup_ungroup_store(struct device
*dev
, const char *buf
, size_t count
)
82 struct ccwgroup_device
*gdev
;
84 gdev
= to_ccwgroupdev(dev
);
86 if (gdev
->state
!= CCWGROUP_OFFLINE
)
89 __ccwgroup_remove_symlinks(gdev
);
90 device_unregister(dev
);
95 static DEVICE_ATTR(ungroup
, 0200, NULL
, ccwgroup_ungroup_store
);
98 ccwgroup_release (struct device
*dev
)
100 struct ccwgroup_device
*gdev
;
103 gdev
= to_ccwgroupdev(dev
);
105 for (i
= 0; i
< gdev
->count
; i
++) {
106 gdev
->cdev
[i
]->dev
.driver_data
= NULL
;
107 put_device(&gdev
->cdev
[i
]->dev
);
113 __ccwgroup_create_symlinks(struct ccwgroup_device
*gdev
)
118 for (i
= 0; i
< gdev
->count
; i
++) {
119 rc
= sysfs_create_link(&gdev
->cdev
[i
]->dev
.kobj
, &gdev
->dev
.kobj
,
122 for (--i
; i
>= 0; i
--)
123 sysfs_remove_link(&gdev
->cdev
[i
]->dev
.kobj
,
128 for (i
= 0; i
< gdev
->count
; i
++) {
129 sprintf(str
, "cdev%d", i
);
130 rc
= sysfs_create_link(&gdev
->dev
.kobj
, &gdev
->cdev
[i
]->dev
.kobj
,
133 for (--i
; i
>= 0; i
--) {
134 sprintf(str
, "cdev%d", i
);
135 sysfs_remove_link(&gdev
->dev
.kobj
, str
);
137 for (i
= 0; i
< gdev
->count
; i
++)
138 sysfs_remove_link(&gdev
->cdev
[i
]->dev
.kobj
,
147 * try to add a new ccwgroup device for one driver
148 * argc and argv[] are a list of bus_id's of devices
149 * belonging to the driver.
152 ccwgroup_create(struct device
*root
,
153 unsigned int creator_id
,
154 struct ccw_driver
*cdrv
,
155 int argc
, char *argv
[])
157 struct ccwgroup_device
*gdev
;
162 if (argc
> 256) /* disallow dumb users */
165 gdev
= kmalloc(sizeof(*gdev
) + argc
*sizeof(gdev
->cdev
[0]), GFP_KERNEL
);
169 memset(gdev
, 0, sizeof(*gdev
) + argc
*sizeof(gdev
->cdev
[0]));
170 atomic_set(&gdev
->onoff
, 0);
173 for (i
= 0; i
< argc
; i
++) {
174 gdev
->cdev
[i
] = get_ccwdev_by_busid(cdrv
, argv
[i
]);
176 /* all devices have to be of the same type in
177 * order to be grouped */
179 || gdev
->cdev
[i
]->id
.driver_info
!=
180 gdev
->cdev
[0]->id
.driver_info
) {
184 /* Don't allow a device to belong to more than one group. */
185 if (gdev
->cdev
[i
]->dev
.driver_data
) {
190 for (i
= 0; i
< argc
; i
++)
191 gdev
->cdev
[i
]->dev
.driver_data
= gdev
;
194 gdev
->creator_id
= creator_id
;
196 gdev
->dev
= (struct device
) {
197 .bus
= &ccwgroup_bus_type
,
199 .release
= ccwgroup_release
,
202 snprintf (gdev
->dev
.bus_id
, BUS_ID_SIZE
, "%s",
203 gdev
->cdev
[0]->dev
.bus_id
);
205 rc
= device_register(&gdev
->dev
);
209 get_device(&gdev
->dev
);
210 rc
= device_create_file(&gdev
->dev
, &dev_attr_ungroup
);
213 device_unregister(&gdev
->dev
);
217 rc
= __ccwgroup_create_symlinks(gdev
);
219 put_device(&gdev
->dev
);
222 device_remove_file(&gdev
->dev
, &dev_attr_ungroup
);
223 device_unregister(&gdev
->dev
);
225 for (i
= 0; i
< argc
; i
++)
227 put_device(&gdev
->cdev
[i
]->dev
);
228 gdev
->cdev
[i
]->dev
.driver_data
= NULL
;
230 put_device(&gdev
->dev
);
233 for (i
= 0; i
< argc
; i
++)
235 put_device(&gdev
->cdev
[i
]->dev
);
237 gdev
->cdev
[i
]->dev
.driver_data
= NULL
;
246 return bus_register (&ccwgroup_bus_type
);
250 cleanup_ccwgroup (void)
252 bus_unregister (&ccwgroup_bus_type
);
255 module_init(init_ccwgroup
);
256 module_exit(cleanup_ccwgroup
);
258 /************************** driver stuff ******************************/
261 ccwgroup_set_online(struct ccwgroup_device
*gdev
)
263 struct ccwgroup_driver
*gdrv
;
266 if (atomic_compare_and_swap(0, 1, &gdev
->onoff
))
268 if (gdev
->state
== CCWGROUP_ONLINE
) {
272 if (!gdev
->dev
.driver
) {
276 gdrv
= to_ccwgroupdrv (gdev
->dev
.driver
);
277 if ((ret
= gdrv
->set_online(gdev
)))
280 gdev
->state
= CCWGROUP_ONLINE
;
282 atomic_set(&gdev
->onoff
, 0);
287 ccwgroup_set_offline(struct ccwgroup_device
*gdev
)
289 struct ccwgroup_driver
*gdrv
;
292 if (atomic_compare_and_swap(0, 1, &gdev
->onoff
))
294 if (gdev
->state
== CCWGROUP_OFFLINE
) {
298 if (!gdev
->dev
.driver
) {
302 gdrv
= to_ccwgroupdrv (gdev
->dev
.driver
);
303 if ((ret
= gdrv
->set_offline(gdev
)))
306 gdev
->state
= CCWGROUP_OFFLINE
;
308 atomic_set(&gdev
->onoff
, 0);
313 ccwgroup_online_store (struct device
*dev
, const char *buf
, size_t count
)
315 struct ccwgroup_device
*gdev
;
316 struct ccwgroup_driver
*gdrv
;
320 gdev
= to_ccwgroupdev(dev
);
324 gdrv
= to_ccwgroupdrv (gdev
->dev
.driver
);
325 if (!try_module_get(gdrv
->owner
))
328 value
= simple_strtoul(buf
, 0, 0);
331 ccwgroup_set_online(gdev
);
333 ccwgroup_set_offline(gdev
);
336 module_put(gdrv
->owner
);
341 ccwgroup_online_show (struct device
*dev
, char *buf
)
345 online
= (to_ccwgroupdev(dev
)->state
== CCWGROUP_ONLINE
);
347 return sprintf(buf
, online
? "1\n" : "0\n");
350 static DEVICE_ATTR(online
, 0644, ccwgroup_online_show
, ccwgroup_online_store
);
353 ccwgroup_probe (struct device
*dev
)
355 struct ccwgroup_device
*gdev
;
356 struct ccwgroup_driver
*gdrv
;
360 gdev
= to_ccwgroupdev(dev
);
361 gdrv
= to_ccwgroupdrv(dev
->driver
);
363 if ((ret
= device_create_file(dev
, &dev_attr_online
)))
366 pr_debug("%s: device %s\n", __func__
, gdev
->dev
.bus_id
);
367 ret
= gdrv
->probe
? gdrv
->probe(gdev
) : -ENODEV
;
369 device_remove_file(dev
, &dev_attr_online
);
375 ccwgroup_remove (struct device
*dev
)
377 struct ccwgroup_device
*gdev
;
378 struct ccwgroup_driver
*gdrv
;
380 gdev
= to_ccwgroupdev(dev
);
381 gdrv
= to_ccwgroupdrv(dev
->driver
);
383 pr_debug("%s: device %s\n", __func__
, gdev
->dev
.bus_id
);
385 device_remove_file(dev
, &dev_attr_online
);
387 if (gdrv
&& gdrv
->remove
)
393 ccwgroup_driver_register (struct ccwgroup_driver
*cdriver
)
395 /* register our new driver with the core */
396 cdriver
->driver
= (struct device_driver
) {
397 .bus
= &ccwgroup_bus_type
,
398 .name
= cdriver
->name
,
399 .probe
= ccwgroup_probe
,
400 .remove
= ccwgroup_remove
,
403 return driver_register(&cdriver
->driver
);
406 static inline struct device
*
407 __get_next_ccwgroup_device(struct device_driver
*drv
)
409 struct device
*dev
, *d
;
411 down_read(&drv
->bus
->subsys
.rwsem
);
413 list_for_each_entry(d
, &drv
->devices
, driver_list
) {
418 up_read(&drv
->bus
->subsys
.rwsem
);
423 ccwgroup_driver_unregister (struct ccwgroup_driver
*cdriver
)
427 /* We don't want ccwgroup devices to live longer than their driver. */
428 get_driver(&cdriver
->driver
);
429 while ((dev
= __get_next_ccwgroup_device(&cdriver
->driver
))) {
430 __ccwgroup_remove_symlinks(to_ccwgroupdev(dev
));
431 device_unregister(dev
);
434 put_driver(&cdriver
->driver
);
435 driver_unregister(&cdriver
->driver
);
439 ccwgroup_probe_ccwdev(struct ccw_device
*cdev
)
444 static inline struct ccwgroup_device
*
445 __ccwgroup_get_gdev_by_cdev(struct ccw_device
*cdev
)
447 struct ccwgroup_device
*gdev
;
449 if (cdev
->dev
.driver_data
) {
450 gdev
= (struct ccwgroup_device
*)cdev
->dev
.driver_data
;
451 if (get_device(&gdev
->dev
)) {
452 if (!list_empty(&gdev
->dev
.node
))
454 put_device(&gdev
->dev
);
462 ccwgroup_remove_ccwdev(struct ccw_device
*cdev
)
464 struct ccwgroup_device
*gdev
;
466 /* Ignore offlining errors, device is gone anyway. */
467 ccw_device_set_offline(cdev
);
468 /* If one of its devices is gone, the whole group is done for. */
469 gdev
= __ccwgroup_get_gdev_by_cdev(cdev
);
471 __ccwgroup_remove_symlinks(gdev
);
472 device_unregister(&gdev
->dev
);
473 put_device(&gdev
->dev
);
477 MODULE_LICENSE("GPL");
478 EXPORT_SYMBOL(ccwgroup_driver_register
);
479 EXPORT_SYMBOL(ccwgroup_driver_unregister
);
480 EXPORT_SYMBOL(ccwgroup_create
);
481 EXPORT_SYMBOL(ccwgroup_probe_ccwdev
);
482 EXPORT_SYMBOL(ccwgroup_remove_ccwdev
);