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
= kmalloc(sizeof(*gdev
) + argc
*sizeof(gdev
->cdev
[0]), GFP_KERNEL
);
164 memset(gdev
, 0, sizeof(*gdev
) + argc
*sizeof(gdev
->cdev
[0]));
165 atomic_set(&gdev
->onoff
, 0);
168 for (i
= 0; i
< argc
; i
++) {
169 gdev
->cdev
[i
] = get_ccwdev_by_busid(cdrv
, argv
[i
]);
171 /* all devices have to be of the same type in
172 * order to be grouped */
174 || gdev
->cdev
[i
]->id
.driver_info
!=
175 gdev
->cdev
[0]->id
.driver_info
) {
179 /* Don't allow a device to belong to more than one group. */
180 if (gdev
->cdev
[i
]->dev
.driver_data
) {
185 for (i
= 0; i
< argc
; i
++)
186 gdev
->cdev
[i
]->dev
.driver_data
= gdev
;
189 gdev
->creator_id
= creator_id
;
191 gdev
->dev
= (struct device
) {
192 .bus
= &ccwgroup_bus_type
,
194 .release
= ccwgroup_release
,
197 snprintf (gdev
->dev
.bus_id
, BUS_ID_SIZE
, "%s",
198 gdev
->cdev
[0]->dev
.bus_id
);
200 rc
= device_register(&gdev
->dev
);
204 get_device(&gdev
->dev
);
205 rc
= device_create_file(&gdev
->dev
, &dev_attr_ungroup
);
208 device_unregister(&gdev
->dev
);
212 rc
= __ccwgroup_create_symlinks(gdev
);
214 put_device(&gdev
->dev
);
217 device_remove_file(&gdev
->dev
, &dev_attr_ungroup
);
218 device_unregister(&gdev
->dev
);
220 for (i
= 0; i
< argc
; i
++)
222 put_device(&gdev
->cdev
[i
]->dev
);
223 gdev
->cdev
[i
]->dev
.driver_data
= NULL
;
225 put_device(&gdev
->dev
);
228 for (i
= 0; i
< argc
; i
++)
230 put_device(&gdev
->cdev
[i
]->dev
);
232 gdev
->cdev
[i
]->dev
.driver_data
= NULL
;
241 return bus_register (&ccwgroup_bus_type
);
245 cleanup_ccwgroup (void)
247 bus_unregister (&ccwgroup_bus_type
);
250 module_init(init_ccwgroup
);
251 module_exit(cleanup_ccwgroup
);
253 /************************** driver stuff ******************************/
256 ccwgroup_set_online(struct ccwgroup_device
*gdev
)
258 struct ccwgroup_driver
*gdrv
;
261 if (atomic_cmpxchg(&gdev
->onoff
, 0, 1) != 0)
263 if (gdev
->state
== CCWGROUP_ONLINE
) {
267 if (!gdev
->dev
.driver
) {
271 gdrv
= to_ccwgroupdrv (gdev
->dev
.driver
);
272 if ((ret
= gdrv
->set_online
? gdrv
->set_online(gdev
) : 0))
275 gdev
->state
= CCWGROUP_ONLINE
;
277 atomic_set(&gdev
->onoff
, 0);
282 ccwgroup_set_offline(struct ccwgroup_device
*gdev
)
284 struct ccwgroup_driver
*gdrv
;
287 if (atomic_cmpxchg(&gdev
->onoff
, 0, 1) != 0)
289 if (gdev
->state
== CCWGROUP_OFFLINE
) {
293 if (!gdev
->dev
.driver
) {
297 gdrv
= to_ccwgroupdrv (gdev
->dev
.driver
);
298 if ((ret
= gdrv
->set_offline
? gdrv
->set_offline(gdev
) : 0))
301 gdev
->state
= CCWGROUP_OFFLINE
;
303 atomic_set(&gdev
->onoff
, 0);
308 ccwgroup_online_store (struct device
*dev
, struct device_attribute
*attr
, const char *buf
, size_t count
)
310 struct ccwgroup_device
*gdev
;
311 struct ccwgroup_driver
*gdrv
;
315 gdev
= to_ccwgroupdev(dev
);
319 gdrv
= to_ccwgroupdrv (gdev
->dev
.driver
);
320 if (!try_module_get(gdrv
->owner
))
323 value
= simple_strtoul(buf
, 0, 0);
326 ccwgroup_set_online(gdev
);
328 ccwgroup_set_offline(gdev
);
331 module_put(gdrv
->owner
);
336 ccwgroup_online_show (struct device
*dev
, struct device_attribute
*attr
, char *buf
)
340 online
= (to_ccwgroupdev(dev
)->state
== CCWGROUP_ONLINE
);
342 return sprintf(buf
, online
? "1\n" : "0\n");
345 static DEVICE_ATTR(online
, 0644, ccwgroup_online_show
, ccwgroup_online_store
);
348 ccwgroup_probe (struct device
*dev
)
350 struct ccwgroup_device
*gdev
;
351 struct ccwgroup_driver
*gdrv
;
355 gdev
= to_ccwgroupdev(dev
);
356 gdrv
= to_ccwgroupdrv(dev
->driver
);
358 if ((ret
= device_create_file(dev
, &dev_attr_online
)))
361 pr_debug("%s: device %s\n", __func__
, gdev
->dev
.bus_id
);
362 ret
= gdrv
->probe
? gdrv
->probe(gdev
) : -ENODEV
;
364 device_remove_file(dev
, &dev_attr_online
);
370 ccwgroup_remove (struct device
*dev
)
372 struct ccwgroup_device
*gdev
;
373 struct ccwgroup_driver
*gdrv
;
375 gdev
= to_ccwgroupdev(dev
);
376 gdrv
= to_ccwgroupdrv(dev
->driver
);
378 pr_debug("%s: device %s\n", __func__
, gdev
->dev
.bus_id
);
380 device_remove_file(dev
, &dev_attr_online
);
382 if (gdrv
&& gdrv
->remove
)
387 static struct bus_type ccwgroup_bus_type
= {
389 .match
= ccwgroup_bus_match
,
390 .uevent
= ccwgroup_uevent
,
391 .probe
= ccwgroup_probe
,
392 .remove
= ccwgroup_remove
,
396 ccwgroup_driver_register (struct ccwgroup_driver
*cdriver
)
398 /* register our new driver with the core */
399 cdriver
->driver
= (struct device_driver
) {
400 .bus
= &ccwgroup_bus_type
,
401 .name
= cdriver
->name
,
404 return driver_register(&cdriver
->driver
);
408 __ccwgroup_driver_unregister_device(struct device
*dev
, void *data
)
410 __ccwgroup_remove_symlinks(to_ccwgroupdev(dev
));
411 device_unregister(dev
);
417 ccwgroup_driver_unregister (struct ccwgroup_driver
*cdriver
)
419 /* We don't want ccwgroup devices to live longer than their driver. */
420 get_driver(&cdriver
->driver
);
421 driver_for_each_device(&cdriver
->driver
, NULL
, NULL
,
422 __ccwgroup_driver_unregister_device
);
423 put_driver(&cdriver
->driver
);
424 driver_unregister(&cdriver
->driver
);
428 ccwgroup_probe_ccwdev(struct ccw_device
*cdev
)
433 static inline struct ccwgroup_device
*
434 __ccwgroup_get_gdev_by_cdev(struct ccw_device
*cdev
)
436 struct ccwgroup_device
*gdev
;
438 if (cdev
->dev
.driver_data
) {
439 gdev
= (struct ccwgroup_device
*)cdev
->dev
.driver_data
;
440 if (get_device(&gdev
->dev
)) {
441 if (device_is_registered(&gdev
->dev
))
443 put_device(&gdev
->dev
);
451 ccwgroup_remove_ccwdev(struct ccw_device
*cdev
)
453 struct ccwgroup_device
*gdev
;
455 /* Ignore offlining errors, device is gone anyway. */
456 ccw_device_set_offline(cdev
);
457 /* If one of its devices is gone, the whole group is done for. */
458 gdev
= __ccwgroup_get_gdev_by_cdev(cdev
);
460 __ccwgroup_remove_symlinks(gdev
);
461 device_unregister(&gdev
->dev
);
462 put_device(&gdev
->dev
);
466 MODULE_LICENSE("GPL");
467 EXPORT_SYMBOL(ccwgroup_driver_register
);
468 EXPORT_SYMBOL(ccwgroup_driver_unregister
);
469 EXPORT_SYMBOL(ccwgroup_create
);
470 EXPORT_SYMBOL(ccwgroup_probe_ccwdev
);
471 EXPORT_SYMBOL(ccwgroup_remove_ccwdev
);