2 * drivers/s390/cio/ccwgroup.c
3 * bus driver for ccwgroup
5 * Copyright (C) 2002 IBM Deutschland Entwicklung GmbH,
7 * Author(s): Arnd Bergmann (arndb@de.ibm.com)
8 * Cornelia Huck (cornelia.huck@de.ibm.com)
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>
19 #include <asm/semaphore.h>
20 #include <asm/ccwdev.h>
21 #include <asm/ccwgroup.h>
23 /* In Linux 2.4, we had a channel device layer called "chandev"
24 * that did all sorts of obscure stuff for networking devices.
25 * This is another driver that serves as a replacement for just
26 * one of its functions, namely the translation of single subchannels
27 * to devices that use multiple subchannels.
30 /* a device matches a driver if all its slave devices match the same
31 * entry of the driver */
33 ccwgroup_bus_match (struct device
* dev
, struct device_driver
* drv
)
35 struct ccwgroup_device
*gdev
;
36 struct ccwgroup_driver
*gdrv
;
38 gdev
= container_of(dev
, struct ccwgroup_device
, dev
);
39 gdrv
= container_of(drv
, struct ccwgroup_driver
, driver
);
41 if (gdev
->creator_id
== gdrv
->driver_id
)
47 ccwgroup_uevent (struct device
*dev
, char **envp
, int num_envp
, char *buffer
,
54 static struct bus_type ccwgroup_bus_type
;
57 __ccwgroup_remove_symlinks(struct ccwgroup_device
*gdev
)
62 for (i
= 0; i
< gdev
->count
; i
++) {
63 sprintf(str
, "cdev%d", i
);
64 sysfs_remove_link(&gdev
->dev
.kobj
, str
);
65 sysfs_remove_link(&gdev
->cdev
[i
]->dev
.kobj
, "group_device");
71 * Provide an 'ungroup' attribute so the user can remove group devices no
72 * longer needed or accidentially created. Saves memory :)
75 ccwgroup_ungroup_store(struct device
*dev
, struct device_attribute
*attr
, const char *buf
, size_t count
)
77 struct ccwgroup_device
*gdev
;
79 gdev
= to_ccwgroupdev(dev
);
81 if (gdev
->state
!= CCWGROUP_OFFLINE
)
84 __ccwgroup_remove_symlinks(gdev
);
85 device_unregister(dev
);
90 static DEVICE_ATTR(ungroup
, 0200, NULL
, ccwgroup_ungroup_store
);
93 ccwgroup_release (struct device
*dev
)
95 struct ccwgroup_device
*gdev
;
98 gdev
= to_ccwgroupdev(dev
);
100 for (i
= 0; i
< gdev
->count
; i
++) {
101 gdev
->cdev
[i
]->dev
.driver_data
= NULL
;
102 put_device(&gdev
->cdev
[i
]->dev
);
108 __ccwgroup_create_symlinks(struct ccwgroup_device
*gdev
)
113 for (i
= 0; i
< gdev
->count
; i
++) {
114 rc
= sysfs_create_link(&gdev
->cdev
[i
]->dev
.kobj
, &gdev
->dev
.kobj
,
117 for (--i
; i
>= 0; i
--)
118 sysfs_remove_link(&gdev
->cdev
[i
]->dev
.kobj
,
123 for (i
= 0; i
< gdev
->count
; i
++) {
124 sprintf(str
, "cdev%d", i
);
125 rc
= sysfs_create_link(&gdev
->dev
.kobj
, &gdev
->cdev
[i
]->dev
.kobj
,
128 for (--i
; i
>= 0; i
--) {
129 sprintf(str
, "cdev%d", i
);
130 sysfs_remove_link(&gdev
->dev
.kobj
, str
);
132 for (i
= 0; i
< gdev
->count
; i
++)
133 sysfs_remove_link(&gdev
->cdev
[i
]->dev
.kobj
,
142 * try to add a new ccwgroup device for one driver
143 * argc and argv[] are a list of bus_id's of devices
144 * belonging to the driver.
147 ccwgroup_create(struct device
*root
,
148 unsigned int creator_id
,
149 struct ccw_driver
*cdrv
,
150 int argc
, char *argv
[])
152 struct ccwgroup_device
*gdev
;
157 if (argc
> 256) /* disallow dumb users */
160 gdev
= kzalloc(sizeof(*gdev
) + argc
*sizeof(gdev
->cdev
[0]), GFP_KERNEL
);
164 atomic_set(&gdev
->onoff
, 0);
167 for (i
= 0; i
< argc
; i
++) {
168 gdev
->cdev
[i
] = get_ccwdev_by_busid(cdrv
, argv
[i
]);
170 /* all devices have to be of the same type in
171 * order to be grouped */
173 || gdev
->cdev
[i
]->id
.driver_info
!=
174 gdev
->cdev
[0]->id
.driver_info
) {
178 /* Don't allow a device to belong to more than one group. */
179 if (gdev
->cdev
[i
]->dev
.driver_data
) {
184 for (i
= 0; i
< argc
; i
++)
185 gdev
->cdev
[i
]->dev
.driver_data
= gdev
;
188 gdev
->creator_id
= creator_id
;
190 gdev
->dev
= (struct device
) {
191 .bus
= &ccwgroup_bus_type
,
193 .release
= ccwgroup_release
,
196 snprintf (gdev
->dev
.bus_id
, BUS_ID_SIZE
, "%s",
197 gdev
->cdev
[0]->dev
.bus_id
);
199 rc
= device_register(&gdev
->dev
);
203 get_device(&gdev
->dev
);
204 rc
= device_create_file(&gdev
->dev
, &dev_attr_ungroup
);
207 device_unregister(&gdev
->dev
);
211 rc
= __ccwgroup_create_symlinks(gdev
);
213 put_device(&gdev
->dev
);
216 device_remove_file(&gdev
->dev
, &dev_attr_ungroup
);
217 device_unregister(&gdev
->dev
);
219 for (i
= 0; i
< argc
; i
++)
221 put_device(&gdev
->cdev
[i
]->dev
);
222 gdev
->cdev
[i
]->dev
.driver_data
= NULL
;
224 put_device(&gdev
->dev
);
227 for (i
= 0; i
< argc
; i
++)
229 put_device(&gdev
->cdev
[i
]->dev
);
231 gdev
->cdev
[i
]->dev
.driver_data
= NULL
;
240 return bus_register (&ccwgroup_bus_type
);
244 cleanup_ccwgroup (void)
246 bus_unregister (&ccwgroup_bus_type
);
249 module_init(init_ccwgroup
);
250 module_exit(cleanup_ccwgroup
);
252 /************************** driver stuff ******************************/
255 ccwgroup_set_online(struct ccwgroup_device
*gdev
)
257 struct ccwgroup_driver
*gdrv
;
260 if (atomic_cmpxchg(&gdev
->onoff
, 0, 1) != 0)
262 if (gdev
->state
== CCWGROUP_ONLINE
) {
266 if (!gdev
->dev
.driver
) {
270 gdrv
= to_ccwgroupdrv (gdev
->dev
.driver
);
271 if ((ret
= gdrv
->set_online
? gdrv
->set_online(gdev
) : 0))
274 gdev
->state
= CCWGROUP_ONLINE
;
276 atomic_set(&gdev
->onoff
, 0);
281 ccwgroup_set_offline(struct ccwgroup_device
*gdev
)
283 struct ccwgroup_driver
*gdrv
;
286 if (atomic_cmpxchg(&gdev
->onoff
, 0, 1) != 0)
288 if (gdev
->state
== CCWGROUP_OFFLINE
) {
292 if (!gdev
->dev
.driver
) {
296 gdrv
= to_ccwgroupdrv (gdev
->dev
.driver
);
297 if ((ret
= gdrv
->set_offline
? gdrv
->set_offline(gdev
) : 0))
300 gdev
->state
= CCWGROUP_OFFLINE
;
302 atomic_set(&gdev
->onoff
, 0);
307 ccwgroup_online_store (struct device
*dev
, struct device_attribute
*attr
, const char *buf
, size_t count
)
309 struct ccwgroup_device
*gdev
;
310 struct ccwgroup_driver
*gdrv
;
314 gdev
= to_ccwgroupdev(dev
);
318 gdrv
= to_ccwgroupdrv (gdev
->dev
.driver
);
319 if (!try_module_get(gdrv
->owner
))
322 value
= simple_strtoul(buf
, 0, 0);
325 ccwgroup_set_online(gdev
);
327 ccwgroup_set_offline(gdev
);
330 module_put(gdrv
->owner
);
335 ccwgroup_online_show (struct device
*dev
, struct device_attribute
*attr
, char *buf
)
339 online
= (to_ccwgroupdev(dev
)->state
== CCWGROUP_ONLINE
);
341 return sprintf(buf
, online
? "1\n" : "0\n");
344 static DEVICE_ATTR(online
, 0644, ccwgroup_online_show
, ccwgroup_online_store
);
347 ccwgroup_probe (struct device
*dev
)
349 struct ccwgroup_device
*gdev
;
350 struct ccwgroup_driver
*gdrv
;
354 gdev
= to_ccwgroupdev(dev
);
355 gdrv
= to_ccwgroupdrv(dev
->driver
);
357 if ((ret
= device_create_file(dev
, &dev_attr_online
)))
360 pr_debug("%s: device %s\n", __func__
, gdev
->dev
.bus_id
);
361 ret
= gdrv
->probe
? gdrv
->probe(gdev
) : -ENODEV
;
363 device_remove_file(dev
, &dev_attr_online
);
369 ccwgroup_remove (struct device
*dev
)
371 struct ccwgroup_device
*gdev
;
372 struct ccwgroup_driver
*gdrv
;
374 gdev
= to_ccwgroupdev(dev
);
375 gdrv
= to_ccwgroupdrv(dev
->driver
);
377 pr_debug("%s: device %s\n", __func__
, gdev
->dev
.bus_id
);
379 device_remove_file(dev
, &dev_attr_online
);
381 if (gdrv
&& gdrv
->remove
)
386 static struct bus_type ccwgroup_bus_type
= {
388 .match
= ccwgroup_bus_match
,
389 .uevent
= ccwgroup_uevent
,
390 .probe
= ccwgroup_probe
,
391 .remove
= ccwgroup_remove
,
395 ccwgroup_driver_register (struct ccwgroup_driver
*cdriver
)
397 /* register our new driver with the core */
398 cdriver
->driver
= (struct device_driver
) {
399 .bus
= &ccwgroup_bus_type
,
400 .name
= cdriver
->name
,
403 return driver_register(&cdriver
->driver
);
407 __ccwgroup_driver_unregister_device(struct device
*dev
, void *data
)
409 __ccwgroup_remove_symlinks(to_ccwgroupdev(dev
));
410 device_unregister(dev
);
416 ccwgroup_driver_unregister (struct ccwgroup_driver
*cdriver
)
418 /* We don't want ccwgroup devices to live longer than their driver. */
419 get_driver(&cdriver
->driver
);
420 driver_for_each_device(&cdriver
->driver
, NULL
, NULL
,
421 __ccwgroup_driver_unregister_device
);
422 put_driver(&cdriver
->driver
);
423 driver_unregister(&cdriver
->driver
);
427 ccwgroup_probe_ccwdev(struct ccw_device
*cdev
)
432 static inline struct ccwgroup_device
*
433 __ccwgroup_get_gdev_by_cdev(struct ccw_device
*cdev
)
435 struct ccwgroup_device
*gdev
;
437 if (cdev
->dev
.driver_data
) {
438 gdev
= (struct ccwgroup_device
*)cdev
->dev
.driver_data
;
439 if (get_device(&gdev
->dev
)) {
440 if (device_is_registered(&gdev
->dev
))
442 put_device(&gdev
->dev
);
450 ccwgroup_remove_ccwdev(struct ccw_device
*cdev
)
452 struct ccwgroup_device
*gdev
;
454 /* Ignore offlining errors, device is gone anyway. */
455 ccw_device_set_offline(cdev
);
456 /* If one of its devices is gone, the whole group is done for. */
457 gdev
= __ccwgroup_get_gdev_by_cdev(cdev
);
459 __ccwgroup_remove_symlinks(gdev
);
460 device_unregister(&gdev
->dev
);
461 put_device(&gdev
->dev
);
465 MODULE_LICENSE("GPL");
466 EXPORT_SYMBOL(ccwgroup_driver_register
);
467 EXPORT_SYMBOL(ccwgroup_driver_unregister
);
468 EXPORT_SYMBOL(ccwgroup_create
);
469 EXPORT_SYMBOL(ccwgroup_probe_ccwdev
);
470 EXPORT_SYMBOL(ccwgroup_remove_ccwdev
);