chiark / gitweb /
[PATCH] new version of libsysfs patch
[elogind.git] / libsysfs / sysfs_class.c
1 /*
2  * sysfs_class.c
3  *
4  * Generic class utility functions for libsysfs
5  *
6  * Copyright (C) IBM Corp. 2003
7  *
8  *  This library is free software; you can redistribute it and/or
9  *  modify it under the terms of the GNU Lesser General Public
10  *  License as published by the Free Software Foundation; either
11  *  version 2.1 of the License, or (at your option) any later version.
12  *
13  *  This library is distributed in the hope that it will be useful,
14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16  *  Lesser General Public License for more details.
17  *
18  *  You should have received a copy of the GNU Lesser General Public
19  *  License along with this library; if not, write to the Free Software
20  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21  *
22  */
23 #include "libsysfs.h"
24 #include "sysfs.h"
25
26 void sysfs_close_cls_dev(void *dev)
27 {
28         sysfs_close_class_device((struct sysfs_class_device *)dev);
29 }
30
31 /**
32  * class_name_equal: compares class_devices' name
33  * @a: class_name looking for
34  * @b: sysfs_class_device being compared
35  */
36 static int class_name_equal(void *a, void *b)
37 {
38         if (a == NULL || b == NULL)
39                 return 0;
40
41         if (strcmp(((unsigned char *)a), ((struct sysfs_class_device *)b)->name)
42                 == 0)
43                 return 1;
44
45         return 0;
46 }
47
48 /**
49  * sysfs_close_class_device: closes a single class device.
50  * @dev: class device to close.
51  */
52 void sysfs_close_class_device(struct sysfs_class_device *dev)
53 {
54         if (dev != NULL) {
55                 if (dev->directory != NULL)
56                         sysfs_close_directory(dev->directory);
57                 if (dev->sysdevice != NULL)
58                         sysfs_close_device(dev->sysdevice);
59                 if (dev->driver != NULL)
60                         sysfs_close_driver(dev->driver);
61                 free(dev);
62         }
63 }
64
65 /**
66  * sysfs_close_class: close single class
67  * @class: class structure
68  */
69 void sysfs_close_class(struct sysfs_class *cls)
70 {
71         if (cls != NULL) {
72                 if (cls->directory != NULL)
73                         sysfs_close_directory(cls->directory);
74                 if (cls->devices != NULL) 
75                         dlist_destroy(cls->devices);
76                 free(cls);
77         }
78 }
79
80 /**
81  * alloc_class_device: mallocs and initializes new class device struct.
82  * returns sysfs_class_device or NULL.
83  */
84 static struct sysfs_class_device *alloc_class_device(void)
85 {
86         return (struct sysfs_class_device *)
87                                 calloc(1, sizeof(struct sysfs_class_device));
88 }
89
90 /**
91  * alloc_class: mallocs new class structure
92  * returns sysfs_class struct or NULL
93  */
94 static struct sysfs_class *alloc_class(void)
95 {
96         return (struct sysfs_class *)calloc(1, sizeof(struct sysfs_class));
97 }
98
99 /**
100  * open_class_dir: opens up sysfs class directory
101  * returns sysfs_directory struct with success and NULL with error
102  */
103 static struct sysfs_directory *open_class_dir(const unsigned char *name)
104 {
105         struct sysfs_directory *classdir = NULL;
106         unsigned char classpath[SYSFS_PATH_MAX];
107
108         if (name == NULL) {
109                 errno = EINVAL;
110                 return NULL;
111         }
112
113         memset(classpath, 0, SYSFS_PATH_MAX);
114         if ((sysfs_get_mnt_path(classpath, SYSFS_PATH_MAX)) != 0) {
115                 dprintf("Sysfs not supported on this system\n");
116                 return NULL;
117         }
118
119         strcat(classpath, SYSFS_CLASS_DIR);
120         strcat(classpath, "/");
121         strcat(classpath, name);
122         classdir = sysfs_open_directory(classpath);
123         if (classdir == NULL) {
124                 errno = EINVAL;
125                 dprintf("Class %s not supported on this system\n", name);
126                 return NULL;
127         }
128         if ((sysfs_read_directory(classdir)) != 0) {
129                 dprintf("Error reading %s class dir %s\n", name, classpath);
130                 sysfs_close_directory(classdir);
131                 return NULL;
132         }
133
134         return classdir;
135 }
136
137 /**
138  * sysfs_open_class_device: Opens and populates class device
139  * @path: path to class device.
140  * returns struct sysfs_class_device with success and NULL with error.
141  */
142 struct sysfs_class_device *sysfs_open_class_device(const unsigned char *path)
143 {
144         struct sysfs_class_device *cdev = NULL;
145         struct sysfs_directory *dir = NULL;
146         struct sysfs_link *curl = NULL;
147         struct sysfs_device *sdev = NULL;
148         struct sysfs_driver *drv = NULL;
149
150         if (path == NULL) {
151                 errno = EINVAL;
152                 return NULL;
153         }
154         cdev = alloc_class_device();
155         if (cdev == NULL) {
156                 dprintf("calloc failed\n");
157                 return NULL;
158         }
159         if ((sysfs_get_name_from_path(path, cdev->name, SYSFS_NAME_LEN)) != 0) {
160                 errno = EINVAL;
161                 dprintf("Invalid class device path %s\n", path);
162                 sysfs_close_class_device(cdev);
163                 return NULL;
164         }
165
166         dir = sysfs_open_directory(path);
167         if (dir == NULL) {
168                 dprintf("Error opening class device at %s\n", path);
169                 sysfs_close_class_device(cdev);
170                 return NULL;
171         }
172         if ((sysfs_read_directory(dir)) != 0) {
173                 dprintf("Error reading class device at %s\n", path);
174                 sysfs_close_directory(dir);
175                 sysfs_close_class_device(cdev);
176                 return NULL;
177         }
178         sysfs_read_all_subdirs(dir);
179         cdev->directory = dir;
180         strcpy(cdev->path, dir->path);
181
182         /* get driver and device, if implemented */
183         if (cdev->directory->links != NULL) {
184                 dlist_for_each_data(cdev->directory->links, curl,
185                                 struct sysfs_link) {
186                         if (strncmp(curl->name, SYSFS_DEVICES_NAME, 6) == 0) {
187                                 sdev = sysfs_open_device(curl->target);
188                                 if (sdev != NULL) {
189                                         cdev->sysdevice = sdev;
190                                         if (cdev->driver != NULL) 
191                                                 strncpy(sdev->driver_name,
192                                                         cdev->driver->name, 
193                                                         SYSFS_NAME_LEN);
194                                 }
195                         } else if (strncmp(curl->name, 
196                                                 SYSFS_DRIVERS_NAME, 6) == 0) {
197                                 drv = sysfs_open_driver(curl->target);
198                                 if (drv != NULL) {
199                                         cdev->driver = drv;
200                                         if (cdev->sysdevice != NULL) {
201                                                 strncpy(cdev->sysdevice->name,
202                                                                 drv->name, 
203                                                                 SYSFS_NAME_LEN);
204                                                 if (drv->devices == NULL)
205                                                         drv->devices = 
206                                                                 dlist_new
207                                                                 (sizeof(struct 
208                                                                 sysfs_device));
209                                                 dlist_unshift(drv->devices, 
210                                                         cdev->sysdevice);
211                                         }
212                                 }
213                         }
214                 }
215         }
216         return cdev;
217 }
218
219 /**
220  * get_all_class_devices: gets all devices for class
221  * @class: class to get devices for
222  * returns 0 with success and -1 with failure
223  */
224 static int get_all_class_devices(struct sysfs_class *cls)
225 {
226         struct sysfs_class_device *dev = NULL;
227         struct sysfs_directory *cur = NULL;
228
229         if (cls == NULL || cls->directory == NULL) {
230                 errno = EINVAL;
231                 return -1;
232         }
233         if (cls->directory->subdirs == NULL)
234                 return 0;
235         dlist_for_each_data(cls->directory->subdirs, cur, 
236                         struct sysfs_directory) {
237                 dev = sysfs_open_class_device(cur->path);
238                 if (dev == NULL) {
239                         dprintf("Error opening device at %s\n", cur->path);
240                         continue;
241                 }
242                 if (cls->devices == NULL)
243                         cls->devices = dlist_new_with_delete
244                                         (sizeof(struct sysfs_class_device),
245                                                         sysfs_close_cls_dev);
246                 dlist_unshift(cls->devices, dev);
247         }
248         return 0;
249 }
250
251 /**
252  * sysfs_open_class: opens specific class and all its devices on system
253  * returns sysfs_class structure with success or NULL with error.
254  */
255 struct sysfs_class *sysfs_open_class(const unsigned char *name)
256 {
257         struct sysfs_class *cls = NULL;
258         struct sysfs_directory *classdir = NULL;
259
260         if (name == NULL) {
261                 errno = EINVAL;
262                 return NULL;
263         }
264
265         cls = alloc_class();
266         if (cls == NULL) {
267                 dprintf("calloc failed\n");
268                 return NULL;
269         }
270         strcpy(cls->name, name);        
271         classdir = open_class_dir(name);
272         if (classdir == NULL) {
273                 dprintf("Invalid class, %s not supported on this system\n",
274                         name);
275                 sysfs_close_class(cls);
276                 return NULL;
277         }
278         cls->directory = classdir;
279         strcpy(cls->path, classdir->path);
280         if ((get_all_class_devices(cls)) != 0) {
281                 dprintf("Error reading %s class devices\n", name);
282                 sysfs_close_class(cls);
283                 return NULL;
284         }
285
286         return cls;
287 }
288
289 /**
290  * sysfs_get_class_device: Get specific class device using the device's id
291  * @class: class to find device on
292  * @name: class name of the device
293  */ 
294 struct sysfs_class_device *sysfs_get_class_device(struct sysfs_class *class,
295                                         unsigned char *name)
296 {
297         if (class == NULL || name == NULL) {
298                 errno = EINVAL;
299                 return NULL;
300         }
301
302         return (struct sysfs_class_device *)dlist_find_custom(class->devices,
303                         name, class_name_equal);
304 }
305
306 /**
307  * sysfs_open_class_device_by_name: Locates a specific class_device and returns it.
308  * Class_device must be closed using sysfs_close_class_device
309  * @classname: Class to search
310  * @name: name of the class_device
311  */
312 struct sysfs_class_device *sysfs_open_class_device_by_name
313                 (const unsigned char *classname, unsigned char *name)
314 {
315         struct sysfs_class *class = NULL;
316         struct sysfs_class_device *cdev = NULL, *rcdev = NULL;
317
318         if (classname == NULL || name == NULL) {
319                 errno = EINVAL;
320                 return NULL;
321         }
322         
323         class = sysfs_open_class(classname);
324         if (class == NULL) {
325                 dprintf("Error opening class %s\n", classname);
326                 return NULL;
327         }
328
329         cdev = sysfs_get_class_device(class, name);
330         if (cdev == NULL) {
331                 dprintf("Error getting class device %s from class %s\n",
332                                 name, classname);
333                 sysfs_close_class(class);
334                 return NULL;
335         }
336
337         rcdev = sysfs_open_class_device(cdev->directory->path);
338         if (rcdev == NULL) {
339                 dprintf("Error getting class device %s from class %s\n",
340                                 name, classname);
341                 sysfs_close_class(class);
342                 return NULL;
343         }
344         sysfs_close_class(class);
345         
346         return rcdev;
347 }
348
349 /**
350  * sysfs_get_classdev_attributes: returns a dlist of attributes for
351  *      the requested class_device
352  * @cdev: sysfs_class_dev for which attributes are needed
353  * returns a dlist of attributes if exists, NULL otherwise
354  */
355 struct dlist *sysfs_get_classdev_attributes(struct sysfs_class_device *cdev)
356 {
357         if (cdev == NULL || cdev->directory == NULL)
358                 return NULL;
359
360         return (cdev->directory->attributes);
361 }
362
363 /**
364  * sysfs_find_device_class: locates the device the device is on
365  * @bus_id: device to look for
366  * @classname: buffer to copy class name to
367  * @bsize: size of buffer
368  * returns 0 with success and -1 with error
369  */
370 int sysfs_find_device_class(const unsigned char *bus_id, 
371                                 unsigned char *classname, size_t bsize)
372 {
373         unsigned char class[SYSFS_NAME_LEN], clspath[SYSFS_NAME_LEN];
374         unsigned char *cls = NULL, *clsdev = NULL;
375         struct dlist *clslist = NULL, *clsdev_list = NULL;
376
377         if (bus_id == NULL || classname == NULL) {
378                 errno = EINVAL;
379                 return -1;
380         }
381
382         strcpy(class, SYSFS_CLASS_DIR);
383         clslist = sysfs_open_subsystem_list(class);
384         if (clslist != NULL) {
385                 dlist_for_each_data(clslist, cls, char) {
386                         memset(clspath, 0, SYSFS_NAME_LEN);
387                         strcpy(clspath, SYSFS_CLASS_DIR);
388                         strcat(clspath, "/");
389                         strcat(clspath, cls);
390                         clsdev_list = sysfs_open_subsystem_list(clspath);
391                         if (clsdev_list != NULL) {
392                                 dlist_for_each_data(clsdev_list, 
393                                                         clsdev, char) {
394                                         if (strcmp(bus_id, clsdev) == 0) {
395                                                 strncpy(classname, 
396                                                                 cls, bsize);
397                                                 sysfs_close_list(clsdev_list);
398                                                 sysfs_close_list(clslist);
399                                                 return 0;
400                                         }
401                                 }
402                                 sysfs_close_list(clsdev_list);
403                         }
404                 }
405                 sysfs_close_list(clslist);
406         }
407         return -1;
408 }
409
410 /**
411  * sysfs_get_classdev_attr: searches class device's attributes by name
412  * @clsdev: class device to look through
413  * @name: attribute name to get
414  * returns sysfs_attribute reference with success or NULL with error
415  */
416 struct sysfs_attribute *sysfs_get_classdev_attr
417                 (struct sysfs_class_device *clsdev, const unsigned char *name)
418 {
419         struct sysfs_attribute *cur = NULL;
420
421         if (clsdev == NULL || clsdev->directory == NULL ||
422                 clsdev->directory->attributes == NULL || name == NULL) {
423                 errno = EINVAL;
424                 return NULL;
425         }
426
427         cur = sysfs_get_directory_attribute(clsdev->directory,
428                                                 (unsigned char *)name);
429         if (cur != NULL)
430                 return cur;
431
432         return NULL;
433 }
434
435 /**
436  * sysfs_write_classdev_attr: modify writable attribute value for the given
437  *                              class device
438  * @dev: class device name for which the attribute has to be changed
439  * @attrib: attribute to change
440  * @value: value to change to
441  * @len: size of buffer at "value"
442  * Returns 0 on success and -1 on error
443  */
444 int sysfs_write_classdev_attr(unsigned char *dev, unsigned char *attrib,
445                                 unsigned char *value, size_t len)
446 {
447         struct sysfs_class_device *clsdev = NULL;
448         struct sysfs_attribute *attribute = NULL;
449         unsigned char class_name[SYSFS_NAME_LEN];
450
451         if (dev == NULL || attrib == NULL || value == NULL) {
452                 errno = EINVAL;
453                 return -1;
454         }
455         
456         memset(class_name, 0, SYSFS_NAME_LEN);
457         if ((sysfs_find_device_class(dev, 
458                                         class_name, SYSFS_NAME_LEN)) < 0) {
459                 dprintf("Class device %s not found\n", dev);
460                 return -1;
461         }
462         clsdev = sysfs_open_class_device_by_name(class_name, dev);
463         if (clsdev == NULL) {
464                 dprintf("Error opening %s in class %s\n", dev, class_name);
465                 return -1;
466         }
467         attribute = sysfs_get_directory_attribute(clsdev->directory, attrib);
468         if (attribute == NULL) {
469                 dprintf("Attribute %s not defined for device %s on class %s\n",
470                                 attrib, dev, class_name);
471                 sysfs_close_class_device(clsdev);
472                 return -1;
473         }
474         if ((sysfs_write_attribute(attribute, value, len)) < 0) {
475                 dprintf("Error setting %s to %s\n", attrib, value);
476                 sysfs_close_class_device(clsdev);
477                 return -1;
478         }
479         sysfs_close_class_device(clsdev);
480         return 0;
481 }
482
483 /**
484  * sysfs_read_classdev_attr: read an attribute for a given class device
485  * @dev: class device name for which the attribute has to be read
486  * @attrib: attribute to read
487  * @value: buffer to return value to user
488  * @len: size of buffer at "value"
489  * Returns 0 on success and -1 on error
490  */
491 int sysfs_read_classdev_attr(unsigned char *dev, unsigned char *attrib,
492                                 unsigned char *value, size_t len)
493 {
494         struct sysfs_class_device *clsdev = NULL;
495         struct sysfs_attribute *attribute = NULL;
496         unsigned char class_name[SYSFS_NAME_LEN];
497
498         if (dev == NULL || attrib == NULL || value == NULL) {
499                 errno = EINVAL;
500                 return -1;
501         }
502         
503         memset(class_name, 0, SYSFS_NAME_LEN);
504         if ((sysfs_find_device_class(dev, 
505                                         class_name, SYSFS_NAME_LEN)) < 0) {
506                 dprintf("Class device %s not found\n", dev);
507                 return -1;
508         }
509         clsdev = sysfs_open_class_device_by_name(class_name, dev);
510         if (clsdev == NULL) {
511                 dprintf("Error opening %s in class %s\n", dev, class_name);
512                 return -1;
513         }
514         attribute = sysfs_get_directory_attribute(clsdev->directory, attrib);
515         if (attribute == NULL) {
516                 dprintf("Attribute %s not defined for device %s on class %s\n",
517                                 attrib, dev, class_name);
518                 sysfs_close_class_device(clsdev);
519                 return -1;
520         }
521         if (attribute->len > len) {
522                 dprintf("Value length %d is greater that suppled buffer %d\n",
523                                 attribute->len, len);
524                 sysfs_close_class_device(clsdev);
525                 return -1;
526         }
527         strncpy(value, attribute->value, attribute->len);
528         value[(attribute->len)+1] = 0;
529         sysfs_close_class_device(clsdev);
530         return 0;
531 }