]>
Commit | Line | Data |
---|---|---|
070f5422 GKH |
1 | /* |
2 | * sysfs_class.c | |
3 | * | |
4 | * Generic class utility functions for libsysfs | |
5 | * | |
fe3fe3b2 | 6 | * Copyright (C) IBM Corp. 2003 |
070f5422 GKH |
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 | ||
ff44a6b0 | 26 | static void sysfs_close_cls_dev(void *dev) |
fe3fe3b2 DS |
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 | ||
070f5422 GKH |
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 | { | |
070f5422 GKH |
71 | if (cls != NULL) { |
72 | if (cls->directory != NULL) | |
73 | sysfs_close_directory(cls->directory); | |
fe3fe3b2 DS |
74 | if (cls->devices != NULL) |
75 | dlist_destroy(cls->devices); | |
070f5422 GKH |
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 | */ | |
fe3fe3b2 | 103 | static struct sysfs_directory *open_class_dir(const unsigned char *name) |
070f5422 GKH |
104 | { |
105 | struct sysfs_directory *classdir = NULL; | |
fe3fe3b2 | 106 | unsigned char classpath[SYSFS_PATH_MAX]; |
070f5422 GKH |
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) { | |
fe3fe3b2 | 115 | dprintf("Sysfs not supported on this system\n"); |
070f5422 GKH |
116 | return NULL; |
117 | } | |
118 | ||
ff44a6b0 DS |
119 | /* |
120 | * We shall now treat "block" also as a class. Hence, check here | |
121 | * if "name" is "block" and proceed accordingly | |
122 | */ | |
123 | if (strcmp(name, SYSFS_BLOCK_NAME) == 0) { | |
124 | strcat(classpath, SYSFS_BLOCK_DIR); | |
125 | } else { | |
126 | strcat(classpath, SYSFS_CLASS_DIR); | |
127 | strcat(classpath, "/"); | |
128 | strcat(classpath, name); | |
129 | } | |
070f5422 GKH |
130 | classdir = sysfs_open_directory(classpath); |
131 | if (classdir == NULL) { | |
132 | errno = EINVAL; | |
fe3fe3b2 | 133 | dprintf("Class %s not supported on this system\n", name); |
070f5422 GKH |
134 | return NULL; |
135 | } | |
136 | if ((sysfs_read_directory(classdir)) != 0) { | |
fe3fe3b2 | 137 | dprintf("Error reading %s class dir %s\n", name, classpath); |
070f5422 GKH |
138 | sysfs_close_directory(classdir); |
139 | return NULL; | |
140 | } | |
141 | ||
142 | return classdir; | |
143 | } | |
144 | ||
ff44a6b0 DS |
145 | /** |
146 | * set_classdev_classname: Grabs classname from path | |
147 | * @cdev: class device to set | |
148 | * Returns nothing | |
149 | */ | |
150 | static void set_classdev_classname(struct sysfs_class_device *cdev) | |
151 | { | |
152 | unsigned char *c = NULL, *e = NULL; | |
153 | int count = 0; | |
154 | ||
155 | c = strstr(cdev->path, SYSFS_CLASS_DIR); | |
156 | if (c == NULL) | |
157 | c = strstr(cdev->path, SYSFS_BLOCK_DIR); | |
158 | else { | |
159 | c++; | |
160 | while (c != NULL && *c != '/') | |
161 | c++; | |
162 | } | |
163 | ||
164 | if (c == NULL) | |
165 | strcpy(cdev->classname, SYSFS_UNKNOWN); | |
166 | ||
167 | else { | |
168 | c++; | |
169 | e = c; | |
170 | while (e != NULL && *e != '/' && *e != '\0') { | |
171 | e++; | |
172 | count++; | |
173 | } | |
174 | strncpy(cdev->classname, c, count); | |
175 | } | |
176 | } | |
177 | ||
070f5422 GKH |
178 | /** |
179 | * sysfs_open_class_device: Opens and populates class device | |
180 | * @path: path to class device. | |
181 | * returns struct sysfs_class_device with success and NULL with error. | |
182 | */ | |
fe3fe3b2 | 183 | struct sysfs_class_device *sysfs_open_class_device(const unsigned char *path) |
070f5422 GKH |
184 | { |
185 | struct sysfs_class_device *cdev = NULL; | |
fe3fe3b2 DS |
186 | struct sysfs_directory *dir = NULL; |
187 | struct sysfs_link *curl = NULL; | |
070f5422 GKH |
188 | struct sysfs_device *sdev = NULL; |
189 | struct sysfs_driver *drv = NULL; | |
070f5422 GKH |
190 | |
191 | if (path == NULL) { | |
192 | errno = EINVAL; | |
193 | return NULL; | |
194 | } | |
195 | cdev = alloc_class_device(); | |
196 | if (cdev == NULL) { | |
fe3fe3b2 | 197 | dprintf("calloc failed\n"); |
070f5422 GKH |
198 | return NULL; |
199 | } | |
fe3fe3b2 | 200 | if ((sysfs_get_name_from_path(path, cdev->name, SYSFS_NAME_LEN)) != 0) { |
070f5422 | 201 | errno = EINVAL; |
fe3fe3b2 | 202 | dprintf("Invalid class device path %s\n", path); |
070f5422 GKH |
203 | sysfs_close_class_device(cdev); |
204 | return NULL; | |
205 | } | |
070f5422 GKH |
206 | |
207 | dir = sysfs_open_directory(path); | |
208 | if (dir == NULL) { | |
fe3fe3b2 | 209 | dprintf("Error opening class device at %s\n", path); |
070f5422 GKH |
210 | sysfs_close_class_device(cdev); |
211 | return NULL; | |
212 | } | |
213 | if ((sysfs_read_directory(dir)) != 0) { | |
fe3fe3b2 | 214 | dprintf("Error reading class device at %s\n", path); |
070f5422 GKH |
215 | sysfs_close_directory(dir); |
216 | sysfs_close_class_device(cdev); | |
217 | return NULL; | |
218 | } | |
fe3fe3b2 | 219 | sysfs_read_all_subdirs(dir); |
070f5422 | 220 | cdev->directory = dir; |
fe3fe3b2 | 221 | strcpy(cdev->path, dir->path); |
ff44a6b0 | 222 | set_classdev_classname(cdev); |
070f5422 | 223 | |
070f5422 | 224 | /* get driver and device, if implemented */ |
fe3fe3b2 DS |
225 | if (cdev->directory->links != NULL) { |
226 | dlist_for_each_data(cdev->directory->links, curl, | |
227 | struct sysfs_link) { | |
228 | if (strncmp(curl->name, SYSFS_DEVICES_NAME, 6) == 0) { | |
229 | sdev = sysfs_open_device(curl->target); | |
230 | if (sdev != NULL) { | |
231 | cdev->sysdevice = sdev; | |
232 | if (cdev->driver != NULL) | |
233 | strncpy(sdev->driver_name, | |
234 | cdev->driver->name, | |
235 | SYSFS_NAME_LEN); | |
236 | } | |
237 | } else if (strncmp(curl->name, | |
238 | SYSFS_DRIVERS_NAME, 6) == 0) { | |
239 | drv = sysfs_open_driver(curl->target); | |
240 | if (drv != NULL) { | |
241 | cdev->driver = drv; | |
242 | if (cdev->sysdevice != NULL) { | |
243 | strncpy(cdev->sysdevice->name, | |
244 | drv->name, | |
245 | SYSFS_NAME_LEN); | |
246 | if (drv->devices == NULL) | |
247 | drv->devices = | |
248 | dlist_new | |
249 | (sizeof(struct | |
250 | sysfs_device)); | |
251 | dlist_unshift(drv->devices, | |
252 | cdev->sysdevice); | |
253 | } | |
254 | } | |
070f5422 GKH |
255 | } |
256 | } | |
070f5422 GKH |
257 | } |
258 | return cdev; | |
259 | } | |
260 | ||
070f5422 GKH |
261 | /** |
262 | * get_all_class_devices: gets all devices for class | |
263 | * @class: class to get devices for | |
264 | * returns 0 with success and -1 with failure | |
265 | */ | |
266 | static int get_all_class_devices(struct sysfs_class *cls) | |
267 | { | |
268 | struct sysfs_class_device *dev = NULL; | |
fe3fe3b2 | 269 | struct sysfs_directory *cur = NULL; |
070f5422 GKH |
270 | |
271 | if (cls == NULL || cls->directory == NULL) { | |
272 | errno = EINVAL; | |
273 | return -1; | |
274 | } | |
fe3fe3b2 DS |
275 | if (cls->directory->subdirs == NULL) |
276 | return 0; | |
277 | dlist_for_each_data(cls->directory->subdirs, cur, | |
278 | struct sysfs_directory) { | |
070f5422 GKH |
279 | dev = sysfs_open_class_device(cur->path); |
280 | if (dev == NULL) { | |
fe3fe3b2 | 281 | dprintf("Error opening device at %s\n", cur->path); |
070f5422 GKH |
282 | continue; |
283 | } | |
fe3fe3b2 DS |
284 | if (cls->devices == NULL) |
285 | cls->devices = dlist_new_with_delete | |
286 | (sizeof(struct sysfs_class_device), | |
287 | sysfs_close_cls_dev); | |
288 | dlist_unshift(cls->devices, dev); | |
070f5422 | 289 | } |
070f5422 GKH |
290 | return 0; |
291 | } | |
292 | ||
293 | /** | |
294 | * sysfs_open_class: opens specific class and all its devices on system | |
295 | * returns sysfs_class structure with success or NULL with error. | |
296 | */ | |
fe3fe3b2 | 297 | struct sysfs_class *sysfs_open_class(const unsigned char *name) |
070f5422 GKH |
298 | { |
299 | struct sysfs_class *cls = NULL; | |
300 | struct sysfs_directory *classdir = NULL; | |
301 | ||
302 | if (name == NULL) { | |
303 | errno = EINVAL; | |
304 | return NULL; | |
305 | } | |
306 | ||
307 | cls = alloc_class(); | |
308 | if (cls == NULL) { | |
fe3fe3b2 | 309 | dprintf("calloc failed\n"); |
070f5422 GKH |
310 | return NULL; |
311 | } | |
312 | strcpy(cls->name, name); | |
313 | classdir = open_class_dir(name); | |
314 | if (classdir == NULL) { | |
fe3fe3b2 | 315 | dprintf("Invalid class, %s not supported on this system\n", |
070f5422 GKH |
316 | name); |
317 | sysfs_close_class(cls); | |
318 | return NULL; | |
319 | } | |
320 | cls->directory = classdir; | |
fe3fe3b2 | 321 | strcpy(cls->path, classdir->path); |
070f5422 | 322 | if ((get_all_class_devices(cls)) != 0) { |
fe3fe3b2 | 323 | dprintf("Error reading %s class devices\n", name); |
070f5422 GKH |
324 | sysfs_close_class(cls); |
325 | return NULL; | |
326 | } | |
327 | ||
328 | return cls; | |
329 | } | |
fe3fe3b2 DS |
330 | |
331 | /** | |
332 | * sysfs_get_class_device: Get specific class device using the device's id | |
333 | * @class: class to find device on | |
334 | * @name: class name of the device | |
335 | */ | |
336 | struct sysfs_class_device *sysfs_get_class_device(struct sysfs_class *class, | |
337 | unsigned char *name) | |
338 | { | |
339 | if (class == NULL || name == NULL) { | |
340 | errno = EINVAL; | |
341 | return NULL; | |
342 | } | |
343 | ||
344 | return (struct sysfs_class_device *)dlist_find_custom(class->devices, | |
345 | name, class_name_equal); | |
346 | } | |
347 | ||
ff44a6b0 DS |
348 | /** |
349 | * get_classdev_path: given the class and a device in the class, return the | |
350 | * absolute path to the device | |
351 | * @classname: name of the class | |
352 | * @clsdev: the class device | |
353 | * @path: buffer to return path | |
354 | * @psize: size of "path" | |
355 | * Returns 0 on SUCCESS or -1 on error | |
356 | */ | |
357 | static int get_classdev_path(const unsigned char *classname, | |
358 | const unsigned char *clsdev, unsigned char *path, size_t len) | |
359 | { | |
360 | if (classname == NULL || clsdev == NULL || path == NULL) { | |
361 | errno = EINVAL; | |
362 | return -1; | |
363 | } | |
364 | if (sysfs_get_mnt_path(path, len) != 0) { | |
365 | dprintf("Error getting sysfs mount path\n"); | |
366 | return -1; | |
367 | } | |
368 | if (strcmp(classname, SYSFS_BLOCK_NAME) == 0) { | |
369 | strcat(path, SYSFS_BLOCK_DIR); | |
370 | } else { | |
371 | strcat(path, SYSFS_CLASS_DIR); | |
372 | strcat(path, "/"); | |
373 | strcat(path, classname); | |
374 | } | |
375 | strcat(path, "/"); | |
376 | strcat(path, clsdev); | |
377 | return 0; | |
378 | } | |
379 | ||
fe3fe3b2 DS |
380 | /** |
381 | * sysfs_open_class_device_by_name: Locates a specific class_device and returns it. | |
382 | * Class_device must be closed using sysfs_close_class_device | |
383 | * @classname: Class to search | |
384 | * @name: name of the class_device | |
ff44a6b0 DS |
385 | * |
386 | * NOTE: | |
387 | * Call sysfs_close_class_device() to close the class device | |
fe3fe3b2 DS |
388 | */ |
389 | struct sysfs_class_device *sysfs_open_class_device_by_name | |
ff44a6b0 | 390 | (const unsigned char *classname, const unsigned char *name) |
fe3fe3b2 | 391 | { |
ff44a6b0 DS |
392 | unsigned char devpath[SYSFS_PATH_MAX]; |
393 | struct sysfs_class_device *cdev = NULL; | |
fe3fe3b2 DS |
394 | |
395 | if (classname == NULL || name == NULL) { | |
396 | errno = EINVAL; | |
397 | return NULL; | |
398 | } | |
399 | ||
ff44a6b0 DS |
400 | memset(devpath, 0, SYSFS_PATH_MAX); |
401 | if ((get_classdev_path(classname, name, devpath, | |
402 | SYSFS_PATH_MAX)) != 0) { | |
403 | dprintf("Error getting to device %s on class %s\n", | |
404 | name, classname); | |
fe3fe3b2 DS |
405 | return NULL; |
406 | } | |
ff44a6b0 DS |
407 | |
408 | cdev = sysfs_open_class_device(devpath); | |
fe3fe3b2 DS |
409 | if (cdev == NULL) { |
410 | dprintf("Error getting class device %s from class %s\n", | |
411 | name, classname); | |
fe3fe3b2 DS |
412 | return NULL; |
413 | } | |
ff44a6b0 | 414 | return cdev; |
fe3fe3b2 DS |
415 | } |
416 | ||
417 | /** | |
418 | * sysfs_get_classdev_attributes: returns a dlist of attributes for | |
419 | * the requested class_device | |
420 | * @cdev: sysfs_class_dev for which attributes are needed | |
421 | * returns a dlist of attributes if exists, NULL otherwise | |
422 | */ | |
423 | struct dlist *sysfs_get_classdev_attributes(struct sysfs_class_device *cdev) | |
424 | { | |
425 | if (cdev == NULL || cdev->directory == NULL) | |
426 | return NULL; | |
427 | ||
428 | return (cdev->directory->attributes); | |
429 | } | |
430 | ||
fe3fe3b2 DS |
431 | /** |
432 | * sysfs_get_classdev_attr: searches class device's attributes by name | |
433 | * @clsdev: class device to look through | |
434 | * @name: attribute name to get | |
435 | * returns sysfs_attribute reference with success or NULL with error | |
436 | */ | |
437 | struct sysfs_attribute *sysfs_get_classdev_attr | |
438 | (struct sysfs_class_device *clsdev, const unsigned char *name) | |
439 | { | |
440 | struct sysfs_attribute *cur = NULL; | |
441 | ||
442 | if (clsdev == NULL || clsdev->directory == NULL || | |
443 | clsdev->directory->attributes == NULL || name == NULL) { | |
444 | errno = EINVAL; | |
445 | return NULL; | |
446 | } | |
447 | ||
448 | cur = sysfs_get_directory_attribute(clsdev->directory, | |
449 | (unsigned char *)name); | |
450 | if (cur != NULL) | |
451 | return cur; | |
452 | ||
453 | return NULL; | |
454 | } | |
455 | ||
456 | /** | |
ff44a6b0 DS |
457 | * sysfs_open_classdev_attr: read an attribute for a given class device |
458 | * @classname: name of the class on which to look | |
fe3fe3b2 DS |
459 | * @dev: class device name for which the attribute has to be read |
460 | * @attrib: attribute to read | |
ff44a6b0 DS |
461 | * Returns sysfs_attribute * on SUCCESS and NULL on error |
462 | * | |
463 | * NOTE: | |
464 | * A call to sysfs_close_attribute() is required to close the | |
465 | * attribute returned and to free memory | |
fe3fe3b2 | 466 | */ |
ff44a6b0 DS |
467 | struct sysfs_attribute *sysfs_open_classdev_attr(const unsigned char *classname, |
468 | const unsigned char *dev, const unsigned char *attrib) | |
fe3fe3b2 | 469 | { |
fe3fe3b2 | 470 | struct sysfs_attribute *attribute = NULL; |
ff44a6b0 | 471 | unsigned char path[SYSFS_PATH_MAX]; |
fe3fe3b2 | 472 | |
ff44a6b0 | 473 | if (classname == NULL || dev == NULL || attrib == NULL) { |
fe3fe3b2 | 474 | errno = EINVAL; |
ff44a6b0 | 475 | return NULL; |
fe3fe3b2 | 476 | } |
ff44a6b0 DS |
477 | memset(path, 0, SYSFS_PATH_MAX); |
478 | if ((get_classdev_path(classname, dev, path, SYSFS_PATH_MAX)) != 0) { | |
479 | dprintf("Error getting to device %s on class %s\n", | |
480 | dev, classname); | |
481 | return NULL; | |
fe3fe3b2 | 482 | } |
ff44a6b0 DS |
483 | strcat(path, "/"); |
484 | strcat(path, attrib); | |
485 | attribute = sysfs_open_attribute(path); | |
fe3fe3b2 | 486 | if (attribute == NULL) { |
ff44a6b0 DS |
487 | dprintf("Error opening attribute %s on class device %s\n", |
488 | attrib, dev); | |
489 | return NULL; | |
fe3fe3b2 | 490 | } |
ff44a6b0 DS |
491 | if ((sysfs_read_attribute(attribute)) != 0) { |
492 | dprintf("Error reading attribute %s for class device %s\n", | |
493 | attrib, dev); | |
494 | sysfs_close_attribute(attribute); | |
495 | return NULL; | |
fe3fe3b2 | 496 | } |
ff44a6b0 | 497 | return attribute; |
fe3fe3b2 | 498 | } |