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
;
156 if (argc
> 256) /* disallow dumb users */
159 gdev
= kzalloc(sizeof(*gdev
) + argc
*sizeof(gdev
->cdev
[0]), GFP_KERNEL
);
163 atomic_set(&gdev
->onoff
, 0);
165 for (i
= 0; i
< argc
; i
++) {
166 gdev
->cdev
[i
] = get_ccwdev_by_busid(cdrv
, argv
[i
]);
168 /* all devices have to be of the same type in
169 * order to be grouped */
171 || gdev
->cdev
[i
]->id
.driver_info
!=
172 gdev
->cdev
[0]->id
.driver_info
) {
176 /* Don't allow a device to belong to more than one group. */
177 if (gdev
->cdev
[i
]->dev
.driver_data
) {
181 gdev
->cdev
[i
]->dev
.driver_data
= gdev
;
184 gdev
->creator_id
= creator_id
;
186 gdev
->dev
.bus
= &ccwgroup_bus_type
;
187 gdev
->dev
.parent
= root
;
188 gdev
->dev
.release
= ccwgroup_release
;
190 snprintf (gdev
->dev
.bus_id
, BUS_ID_SIZE
, "%s",
191 gdev
->cdev
[0]->dev
.bus_id
);
193 rc
= device_register(&gdev
->dev
);
197 get_device(&gdev
->dev
);
198 rc
= device_create_file(&gdev
->dev
, &dev_attr_ungroup
);
201 device_unregister(&gdev
->dev
);
205 rc
= __ccwgroup_create_symlinks(gdev
);
207 put_device(&gdev
->dev
);
210 device_remove_file(&gdev
->dev
, &dev_attr_ungroup
);
211 device_unregister(&gdev
->dev
);
213 for (i
= 0; i
< argc
; i
++)
215 put_device(&gdev
->cdev
[i
]->dev
);
216 gdev
->cdev
[i
]->dev
.driver_data
= NULL
;
218 put_device(&gdev
->dev
);
221 for (i
= 0; i
< argc
; i
++)
223 if (gdev
->cdev
[i
]->dev
.driver_data
== gdev
)
224 gdev
->cdev
[i
]->dev
.driver_data
= NULL
;
225 put_device(&gdev
->cdev
[i
]->dev
);
234 return bus_register (&ccwgroup_bus_type
);
238 cleanup_ccwgroup (void)
240 bus_unregister (&ccwgroup_bus_type
);
243 module_init(init_ccwgroup
);
244 module_exit(cleanup_ccwgroup
);
246 /************************** driver stuff ******************************/
249 ccwgroup_set_online(struct ccwgroup_device
*gdev
)
251 struct ccwgroup_driver
*gdrv
;
254 if (atomic_cmpxchg(&gdev
->onoff
, 0, 1) != 0)
256 if (gdev
->state
== CCWGROUP_ONLINE
) {
260 if (!gdev
->dev
.driver
) {
264 gdrv
= to_ccwgroupdrv (gdev
->dev
.driver
);
265 if ((ret
= gdrv
->set_online
? gdrv
->set_online(gdev
) : 0))
268 gdev
->state
= CCWGROUP_ONLINE
;
270 atomic_set(&gdev
->onoff
, 0);
275 ccwgroup_set_offline(struct ccwgroup_device
*gdev
)
277 struct ccwgroup_driver
*gdrv
;
280 if (atomic_cmpxchg(&gdev
->onoff
, 0, 1) != 0)
282 if (gdev
->state
== CCWGROUP_OFFLINE
) {
286 if (!gdev
->dev
.driver
) {
290 gdrv
= to_ccwgroupdrv (gdev
->dev
.driver
);
291 if ((ret
= gdrv
->set_offline
? gdrv
->set_offline(gdev
) : 0))
294 gdev
->state
= CCWGROUP_OFFLINE
;
296 atomic_set(&gdev
->onoff
, 0);
301 ccwgroup_online_store (struct device
*dev
, struct device_attribute
*attr
, const char *buf
, size_t count
)
303 struct ccwgroup_device
*gdev
;
304 struct ccwgroup_driver
*gdrv
;
308 gdev
= to_ccwgroupdev(dev
);
312 gdrv
= to_ccwgroupdrv (gdev
->dev
.driver
);
313 if (!try_module_get(gdrv
->owner
))
316 value
= simple_strtoul(buf
, NULL
, 0);
319 ccwgroup_set_online(gdev
);
321 ccwgroup_set_offline(gdev
);
324 module_put(gdrv
->owner
);
329 ccwgroup_online_show (struct device
*dev
, struct device_attribute
*attr
, char *buf
)
333 online
= (to_ccwgroupdev(dev
)->state
== CCWGROUP_ONLINE
);
335 return sprintf(buf
, online
? "1\n" : "0\n");
338 static DEVICE_ATTR(online
, 0644, ccwgroup_online_show
, ccwgroup_online_store
);
341 ccwgroup_probe (struct device
*dev
)
343 struct ccwgroup_device
*gdev
;
344 struct ccwgroup_driver
*gdrv
;
348 gdev
= to_ccwgroupdev(dev
);
349 gdrv
= to_ccwgroupdrv(dev
->driver
);
351 if ((ret
= device_create_file(dev
, &dev_attr_online
)))
354 pr_debug("%s: device %s\n", __func__
, gdev
->dev
.bus_id
);
355 ret
= gdrv
->probe
? gdrv
->probe(gdev
) : -ENODEV
;
357 device_remove_file(dev
, &dev_attr_online
);
363 ccwgroup_remove (struct device
*dev
)
365 struct ccwgroup_device
*gdev
;
366 struct ccwgroup_driver
*gdrv
;
368 gdev
= to_ccwgroupdev(dev
);
369 gdrv
= to_ccwgroupdrv(dev
->driver
);
371 pr_debug("%s: device %s\n", __func__
, gdev
->dev
.bus_id
);
373 device_remove_file(dev
, &dev_attr_online
);
375 if (gdrv
&& gdrv
->remove
)
380 static struct bus_type ccwgroup_bus_type
= {
382 .match
= ccwgroup_bus_match
,
383 .uevent
= ccwgroup_uevent
,
384 .probe
= ccwgroup_probe
,
385 .remove
= ccwgroup_remove
,
389 ccwgroup_driver_register (struct ccwgroup_driver
*cdriver
)
391 /* register our new driver with the core */
392 cdriver
->driver
.bus
= &ccwgroup_bus_type
;
393 cdriver
->driver
.name
= cdriver
->name
;
395 return driver_register(&cdriver
->driver
);
399 __ccwgroup_match_all(struct device
*dev
, void *data
)
405 ccwgroup_driver_unregister (struct ccwgroup_driver
*cdriver
)
409 /* We don't want ccwgroup devices to live longer than their driver. */
410 get_driver(&cdriver
->driver
);
411 while ((dev
= driver_find_device(&cdriver
->driver
, NULL
, NULL
,
412 __ccwgroup_match_all
))) {
413 __ccwgroup_remove_symlinks(to_ccwgroupdev(dev
));
414 device_unregister(dev
);
417 put_driver(&cdriver
->driver
);
418 driver_unregister(&cdriver
->driver
);
422 ccwgroup_probe_ccwdev(struct ccw_device
*cdev
)
427 static inline struct ccwgroup_device
*
428 __ccwgroup_get_gdev_by_cdev(struct ccw_device
*cdev
)
430 struct ccwgroup_device
*gdev
;
432 if (cdev
->dev
.driver_data
) {
433 gdev
= (struct ccwgroup_device
*)cdev
->dev
.driver_data
;
434 if (get_device(&gdev
->dev
)) {
435 if (device_is_registered(&gdev
->dev
))
437 put_device(&gdev
->dev
);
445 ccwgroup_remove_ccwdev(struct ccw_device
*cdev
)
447 struct ccwgroup_device
*gdev
;
449 /* Ignore offlining errors, device is gone anyway. */
450 ccw_device_set_offline(cdev
);
451 /* If one of its devices is gone, the whole group is done for. */
452 gdev
= __ccwgroup_get_gdev_by_cdev(cdev
);
454 __ccwgroup_remove_symlinks(gdev
);
455 device_unregister(&gdev
->dev
);
456 put_device(&gdev
->dev
);
460 MODULE_LICENSE("GPL");
461 EXPORT_SYMBOL(ccwgroup_driver_register
);
462 EXPORT_SYMBOL(ccwgroup_driver_unregister
);
463 EXPORT_SYMBOL(ccwgroup_create
);
464 EXPORT_SYMBOL(ccwgroup_probe_ccwdev
);
465 EXPORT_SYMBOL(ccwgroup_remove_ccwdev
);