]> bbs.cooldavid.org Git - net-next-2.6.git/blob - drivers/s390/cio/ccwgroup.c
17fa009d9959788783e7c821b6b04575df45b4f5
[net-next-2.6.git] / drivers / s390 / cio / ccwgroup.c
1 /*
2  *  drivers/s390/cio/ccwgroup.c
3  *  bus driver for ccwgroup
4  *
5  *    Copyright (C) 2002 IBM Deutschland Entwicklung GmbH,
6  *                       IBM Corporation
7  *    Author(s): Arnd Bergmann (arndb@de.ibm.com)
8  *               Cornelia Huck (cornelia.huck@de.ibm.com)
9  */
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>
18
19 #include <asm/ccwdev.h>
20 #include <asm/ccwgroup.h>
21
22 #define CCW_BUS_ID_SIZE         20
23
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.
29  */
30
31 /* a device matches a driver if all its slave devices match the same
32  * entry of the driver */
33 static int
34 ccwgroup_bus_match (struct device * dev, struct device_driver * drv)
35 {
36         struct ccwgroup_device *gdev;
37         struct ccwgroup_driver *gdrv;
38
39         gdev = to_ccwgroupdev(dev);
40         gdrv = to_ccwgroupdrv(drv);
41
42         if (gdev->creator_id == gdrv->driver_id)
43                 return 1;
44
45         return 0;
46 }
47 static int
48 ccwgroup_uevent (struct device *dev, struct kobj_uevent_env *env)
49 {
50         /* TODO */
51         return 0;
52 }
53
54 static struct bus_type ccwgroup_bus_type;
55
56 static void
57 __ccwgroup_remove_symlinks(struct ccwgroup_device *gdev)
58 {
59         int i;
60         char str[8];
61
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");
66         }
67         
68 }
69
70 /*
71  * Provide an 'ungroup' attribute so the user can remove group devices no
72  * longer needed or accidentially created. Saves memory :)
73  */
74 static void ccwgroup_ungroup_callback(struct device *dev)
75 {
76         struct ccwgroup_device *gdev = to_ccwgroupdev(dev);
77
78         mutex_lock(&gdev->reg_mutex);
79         if (device_is_registered(&gdev->dev)) {
80                 __ccwgroup_remove_symlinks(gdev);
81                 device_unregister(dev);
82         }
83         mutex_unlock(&gdev->reg_mutex);
84 }
85
86 static ssize_t
87 ccwgroup_ungroup_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
88 {
89         struct ccwgroup_device *gdev;
90         int rc;
91
92         gdev = to_ccwgroupdev(dev);
93
94         if (gdev->state != CCWGROUP_OFFLINE)
95                 return -EINVAL;
96
97         /* Note that we cannot unregister the device from one of its
98          * attribute methods, so we have to use this roundabout approach.
99          */
100         rc = device_schedule_callback(dev, ccwgroup_ungroup_callback);
101         if (rc)
102                 count = rc;
103         return count;
104 }
105
106 static DEVICE_ATTR(ungroup, 0200, NULL, ccwgroup_ungroup_store);
107
108 static void
109 ccwgroup_release (struct device *dev)
110 {
111         struct ccwgroup_device *gdev;
112         int i;
113
114         gdev = to_ccwgroupdev(dev);
115
116         for (i = 0; i < gdev->count; i++) {
117                 if (gdev->cdev[i]) {
118                         if (dev_get_drvdata(&gdev->cdev[i]->dev) == gdev)
119                                 dev_set_drvdata(&gdev->cdev[i]->dev, NULL);
120                         put_device(&gdev->cdev[i]->dev);
121                 }
122         }
123         kfree(gdev);
124 }
125
126 static int
127 __ccwgroup_create_symlinks(struct ccwgroup_device *gdev)
128 {
129         char str[8];
130         int i, rc;
131
132         for (i = 0; i < gdev->count; i++) {
133                 rc = sysfs_create_link(&gdev->cdev[i]->dev.kobj, &gdev->dev.kobj,
134                                        "group_device");
135                 if (rc) {
136                         for (--i; i >= 0; i--)
137                                 sysfs_remove_link(&gdev->cdev[i]->dev.kobj,
138                                                   "group_device");
139                         return rc;
140                 }
141         }
142         for (i = 0; i < gdev->count; i++) {
143                 sprintf(str, "cdev%d", i);
144                 rc = sysfs_create_link(&gdev->dev.kobj, &gdev->cdev[i]->dev.kobj,
145                                        str);
146                 if (rc) {
147                         for (--i; i >= 0; i--) {
148                                 sprintf(str, "cdev%d", i);
149                                 sysfs_remove_link(&gdev->dev.kobj, str);
150                         }
151                         for (i = 0; i < gdev->count; i++)
152                                 sysfs_remove_link(&gdev->cdev[i]->dev.kobj,
153                                                   "group_device");
154                         return rc;
155                 }
156         }
157         return 0;
158 }
159
160 static int __get_next_bus_id(const char **buf, char *bus_id)
161 {
162         int rc, len;
163         char *start, *end;
164
165         start = (char *)*buf;
166         end = strchr(start, ',');
167         if (!end) {
168                 /* Last entry. Strip trailing newline, if applicable. */
169                 end = strchr(start, '\n');
170                 if (end)
171                         *end = '\0';
172                 len = strlen(start) + 1;
173         } else {
174                 len = end - start + 1;
175                 end++;
176         }
177         if (len < CCW_BUS_ID_SIZE) {
178                 strlcpy(bus_id, start, len);
179                 rc = 0;
180         } else
181                 rc = -EINVAL;
182         *buf = end;
183         return rc;
184 }
185
186 static int __is_valid_bus_id(char bus_id[CCW_BUS_ID_SIZE])
187 {
188         int cssid, ssid, devno;
189
190         /* Must be of form %x.%x.%04x */
191         if (sscanf(bus_id, "%x.%1x.%04x", &cssid, &ssid, &devno) != 3)
192                 return 0;
193         return 1;
194 }
195
196 /**
197  * ccwgroup_create_from_string() - create and register a ccw group device
198  * @root: parent device for the new device
199  * @creator_id: identifier of creating driver
200  * @cdrv: ccw driver of slave devices
201  * @num_devices: number of slave devices
202  * @buf: buffer containing comma separated bus ids of slave devices
203  *
204  * Create and register a new ccw group device as a child of @root. Slave
205  * devices are obtained from the list of bus ids given in @buf and must all
206  * belong to @cdrv.
207  * Returns:
208  *  %0 on success and an error code on failure.
209  * Context:
210  *  non-atomic
211  */
212 int ccwgroup_create_from_string(struct device *root, unsigned int creator_id,
213                                 struct ccw_driver *cdrv, int num_devices,
214                                 const char *buf)
215 {
216         struct ccwgroup_device *gdev;
217         int rc, i;
218         char tmp_bus_id[CCW_BUS_ID_SIZE];
219         const char *curr_buf;
220
221         gdev = kzalloc(sizeof(*gdev) + num_devices * sizeof(gdev->cdev[0]),
222                        GFP_KERNEL);
223         if (!gdev)
224                 return -ENOMEM;
225
226         atomic_set(&gdev->onoff, 0);
227         mutex_init(&gdev->reg_mutex);
228         mutex_lock(&gdev->reg_mutex);
229         gdev->creator_id = creator_id;
230         gdev->count = num_devices;
231         gdev->dev.bus = &ccwgroup_bus_type;
232         gdev->dev.parent = root;
233         gdev->dev.release = ccwgroup_release;
234         device_initialize(&gdev->dev);
235
236         curr_buf = buf;
237         for (i = 0; i < num_devices && curr_buf; i++) {
238                 rc = __get_next_bus_id(&curr_buf, tmp_bus_id);
239                 if (rc != 0)
240                         goto error;
241                 if (!__is_valid_bus_id(tmp_bus_id)) {
242                         rc = -EINVAL;
243                         goto error;
244                 }
245                 gdev->cdev[i] = get_ccwdev_by_busid(cdrv, tmp_bus_id);
246                 /*
247                  * All devices have to be of the same type in
248                  * order to be grouped.
249                  */
250                 if (!gdev->cdev[i]
251                     || gdev->cdev[i]->id.driver_info !=
252                     gdev->cdev[0]->id.driver_info) {
253                         rc = -EINVAL;
254                         goto error;
255                 }
256                 /* Don't allow a device to belong to more than one group. */
257                 if (dev_get_drvdata(&gdev->cdev[i]->dev)) {
258                         rc = -EINVAL;
259                         goto error;
260                 }
261                 dev_set_drvdata(&gdev->cdev[i]->dev, gdev);
262         }
263         /* Check for sufficient number of bus ids. */
264         if (i < num_devices && !curr_buf) {
265                 rc = -EINVAL;
266                 goto error;
267         }
268         /* Check for trailing stuff. */
269         if (i == num_devices && strlen(curr_buf) > 0) {
270                 rc = -EINVAL;
271                 goto error;
272         }
273
274         dev_set_name(&gdev->dev, "%s", dev_name(&gdev->cdev[0]->dev));
275
276         rc = device_add(&gdev->dev);
277         if (rc)
278                 goto error;
279         get_device(&gdev->dev);
280         rc = device_create_file(&gdev->dev, &dev_attr_ungroup);
281
282         if (rc) {
283                 device_unregister(&gdev->dev);
284                 goto error;
285         }
286
287         rc = __ccwgroup_create_symlinks(gdev);
288         if (!rc) {
289                 mutex_unlock(&gdev->reg_mutex);
290                 put_device(&gdev->dev);
291                 return 0;
292         }
293         device_remove_file(&gdev->dev, &dev_attr_ungroup);
294         device_unregister(&gdev->dev);
295 error:
296         for (i = 0; i < num_devices; i++)
297                 if (gdev->cdev[i]) {
298                         if (dev_get_drvdata(&gdev->cdev[i]->dev) == gdev)
299                                 dev_set_drvdata(&gdev->cdev[i]->dev, NULL);
300                         put_device(&gdev->cdev[i]->dev);
301                         gdev->cdev[i] = NULL;
302                 }
303         mutex_unlock(&gdev->reg_mutex);
304         put_device(&gdev->dev);
305         return rc;
306 }
307 EXPORT_SYMBOL(ccwgroup_create_from_string);
308
309 static int __init
310 init_ccwgroup (void)
311 {
312         return bus_register (&ccwgroup_bus_type);
313 }
314
315 static void __exit
316 cleanup_ccwgroup (void)
317 {
318         bus_unregister (&ccwgroup_bus_type);
319 }
320
321 module_init(init_ccwgroup);
322 module_exit(cleanup_ccwgroup);
323
324 /************************** driver stuff ******************************/
325
326 static int
327 ccwgroup_set_online(struct ccwgroup_device *gdev)
328 {
329         struct ccwgroup_driver *gdrv;
330         int ret;
331
332         if (atomic_cmpxchg(&gdev->onoff, 0, 1) != 0)
333                 return -EAGAIN;
334         if (gdev->state == CCWGROUP_ONLINE) {
335                 ret = 0;
336                 goto out;
337         }
338         if (!gdev->dev.driver) {
339                 ret = -EINVAL;
340                 goto out;
341         }
342         gdrv = to_ccwgroupdrv (gdev->dev.driver);
343         if ((ret = gdrv->set_online ? gdrv->set_online(gdev) : 0))
344                 goto out;
345
346         gdev->state = CCWGROUP_ONLINE;
347  out:
348         atomic_set(&gdev->onoff, 0);
349         return ret;
350 }
351
352 static int
353 ccwgroup_set_offline(struct ccwgroup_device *gdev)
354 {
355         struct ccwgroup_driver *gdrv;
356         int ret;
357
358         if (atomic_cmpxchg(&gdev->onoff, 0, 1) != 0)
359                 return -EAGAIN;
360         if (gdev->state == CCWGROUP_OFFLINE) {
361                 ret = 0;
362                 goto out;
363         }
364         if (!gdev->dev.driver) {
365                 ret = -EINVAL;
366                 goto out;
367         }
368         gdrv = to_ccwgroupdrv (gdev->dev.driver);
369         if ((ret = gdrv->set_offline ? gdrv->set_offline(gdev) : 0))
370                 goto out;
371
372         gdev->state = CCWGROUP_OFFLINE;
373  out:
374         atomic_set(&gdev->onoff, 0);
375         return ret;
376 }
377
378 static ssize_t
379 ccwgroup_online_store (struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
380 {
381         struct ccwgroup_device *gdev;
382         struct ccwgroup_driver *gdrv;
383         unsigned long value;
384         int ret;
385
386         gdev = to_ccwgroupdev(dev);
387         if (!dev->driver)
388                 return count;
389
390         gdrv = to_ccwgroupdrv (gdev->dev.driver);
391         if (!try_module_get(gdrv->owner))
392                 return -EINVAL;
393
394         ret = strict_strtoul(buf, 0, &value);
395         if (ret)
396                 goto out;
397         ret = count;
398         if (value == 1)
399                 ccwgroup_set_online(gdev);
400         else if (value == 0)
401                 ccwgroup_set_offline(gdev);
402         else
403                 ret = -EINVAL;
404 out:
405         module_put(gdrv->owner);
406         return ret;
407 }
408
409 static ssize_t
410 ccwgroup_online_show (struct device *dev, struct device_attribute *attr, char *buf)
411 {
412         int online;
413
414         online = (to_ccwgroupdev(dev)->state == CCWGROUP_ONLINE);
415
416         return sprintf(buf, online ? "1\n" : "0\n");
417 }
418
419 static DEVICE_ATTR(online, 0644, ccwgroup_online_show, ccwgroup_online_store);
420
421 static int
422 ccwgroup_probe (struct device *dev)
423 {
424         struct ccwgroup_device *gdev;
425         struct ccwgroup_driver *gdrv;
426
427         int ret;
428
429         gdev = to_ccwgroupdev(dev);
430         gdrv = to_ccwgroupdrv(dev->driver);
431
432         if ((ret = device_create_file(dev, &dev_attr_online)))
433                 return ret;
434
435         ret = gdrv->probe ? gdrv->probe(gdev) : -ENODEV;
436         if (ret)
437                 device_remove_file(dev, &dev_attr_online);
438
439         return ret;
440 }
441
442 static int
443 ccwgroup_remove (struct device *dev)
444 {
445         struct ccwgroup_device *gdev;
446         struct ccwgroup_driver *gdrv;
447
448         gdev = to_ccwgroupdev(dev);
449         gdrv = to_ccwgroupdrv(dev->driver);
450
451         device_remove_file(dev, &dev_attr_online);
452
453         if (gdrv && gdrv->remove)
454                 gdrv->remove(gdev);
455         return 0;
456 }
457
458 static void ccwgroup_shutdown(struct device *dev)
459 {
460         struct ccwgroup_device *gdev;
461         struct ccwgroup_driver *gdrv;
462
463         gdev = to_ccwgroupdev(dev);
464         gdrv = to_ccwgroupdrv(dev->driver);
465         if (gdrv && gdrv->shutdown)
466                 gdrv->shutdown(gdev);
467 }
468
469 static struct bus_type ccwgroup_bus_type = {
470         .name   = "ccwgroup",
471         .match  = ccwgroup_bus_match,
472         .uevent = ccwgroup_uevent,
473         .probe  = ccwgroup_probe,
474         .remove = ccwgroup_remove,
475         .shutdown = ccwgroup_shutdown,
476 };
477
478 /**
479  * ccwgroup_driver_register() - register a ccw group driver
480  * @cdriver: driver to be registered
481  *
482  * This function is mainly a wrapper around driver_register().
483  */
484 int ccwgroup_driver_register(struct ccwgroup_driver *cdriver)
485 {
486         /* register our new driver with the core */
487         cdriver->driver.bus = &ccwgroup_bus_type;
488         cdriver->driver.name = cdriver->name;
489         cdriver->driver.owner = cdriver->owner;
490
491         return driver_register(&cdriver->driver);
492 }
493
494 static int
495 __ccwgroup_match_all(struct device *dev, void *data)
496 {
497         return 1;
498 }
499
500 /**
501  * ccwgroup_driver_unregister() - deregister a ccw group driver
502  * @cdriver: driver to be deregistered
503  *
504  * This function is mainly a wrapper around driver_unregister().
505  */
506 void ccwgroup_driver_unregister(struct ccwgroup_driver *cdriver)
507 {
508         struct device *dev;
509
510         /* We don't want ccwgroup devices to live longer than their driver. */
511         get_driver(&cdriver->driver);
512         while ((dev = driver_find_device(&cdriver->driver, NULL, NULL,
513                                          __ccwgroup_match_all))) {
514                 struct ccwgroup_device *gdev = to_ccwgroupdev(dev);
515
516                 mutex_lock(&gdev->reg_mutex);
517                 __ccwgroup_remove_symlinks(gdev);
518                 device_unregister(dev);
519                 mutex_unlock(&gdev->reg_mutex);
520                 put_device(dev);
521         }
522         put_driver(&cdriver->driver);
523         driver_unregister(&cdriver->driver);
524 }
525
526 /**
527  * ccwgroup_probe_ccwdev() - probe function for slave devices
528  * @cdev: ccw device to be probed
529  *
530  * This is a dummy probe function for ccw devices that are slave devices in
531  * a ccw group device.
532  * Returns:
533  *  always %0
534  */
535 int ccwgroup_probe_ccwdev(struct ccw_device *cdev)
536 {
537         return 0;
538 }
539
540 static struct ccwgroup_device *
541 __ccwgroup_get_gdev_by_cdev(struct ccw_device *cdev)
542 {
543         struct ccwgroup_device *gdev;
544
545         gdev = dev_get_drvdata(&cdev->dev);
546         if (gdev) {
547                 if (get_device(&gdev->dev)) {
548                         mutex_lock(&gdev->reg_mutex);
549                         if (device_is_registered(&gdev->dev))
550                                 return gdev;
551                         mutex_unlock(&gdev->reg_mutex);
552                         put_device(&gdev->dev);
553                 }
554                 return NULL;
555         }
556         return NULL;
557 }
558
559 /**
560  * ccwgroup_remove_ccwdev() - remove function for slave devices
561  * @cdev: ccw device to be removed
562  *
563  * This is a remove function for ccw devices that are slave devices in a ccw
564  * group device. It sets the ccw device offline and also deregisters the
565  * embedding ccw group device.
566  */
567 void ccwgroup_remove_ccwdev(struct ccw_device *cdev)
568 {
569         struct ccwgroup_device *gdev;
570
571         /* Ignore offlining errors, device is gone anyway. */
572         ccw_device_set_offline(cdev);
573         /* If one of its devices is gone, the whole group is done for. */
574         gdev = __ccwgroup_get_gdev_by_cdev(cdev);
575         if (gdev) {
576                 __ccwgroup_remove_symlinks(gdev);
577                 device_unregister(&gdev->dev);
578                 mutex_unlock(&gdev->reg_mutex);
579                 put_device(&gdev->dev);
580         }
581 }
582
583 MODULE_LICENSE("GPL");
584 EXPORT_SYMBOL(ccwgroup_driver_register);
585 EXPORT_SYMBOL(ccwgroup_driver_unregister);
586 EXPORT_SYMBOL(ccwgroup_probe_ccwdev);
587 EXPORT_SYMBOL(ccwgroup_remove_ccwdev);