]>
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); | |
5d4754f1 DS |
61 | if (dev->parent != NULL) |
62 | sysfs_close_class_device(dev->parent); | |
070f5422 GKH |
63 | free(dev); |
64 | } | |
65 | } | |
66 | ||
67 | /** | |
68 | * sysfs_close_class: close single class | |
69 | * @class: class structure | |
70 | */ | |
71 | void sysfs_close_class(struct sysfs_class *cls) | |
72 | { | |
070f5422 GKH |
73 | if (cls != NULL) { |
74 | if (cls->directory != NULL) | |
75 | sysfs_close_directory(cls->directory); | |
fe3fe3b2 DS |
76 | if (cls->devices != NULL) |
77 | dlist_destroy(cls->devices); | |
070f5422 GKH |
78 | free(cls); |
79 | } | |
80 | } | |
81 | ||
82 | /** | |
83 | * alloc_class_device: mallocs and initializes new class device struct. | |
84 | * returns sysfs_class_device or NULL. | |
85 | */ | |
86 | static struct sysfs_class_device *alloc_class_device(void) | |
87 | { | |
88 | return (struct sysfs_class_device *) | |
89 | calloc(1, sizeof(struct sysfs_class_device)); | |
90 | } | |
91 | ||
92 | /** | |
93 | * alloc_class: mallocs new class structure | |
94 | * returns sysfs_class struct or NULL | |
95 | */ | |
96 | static struct sysfs_class *alloc_class(void) | |
97 | { | |
98 | return (struct sysfs_class *)calloc(1, sizeof(struct sysfs_class)); | |
99 | } | |
100 | ||
ff44a6b0 DS |
101 | /** |
102 | * set_classdev_classname: Grabs classname from path | |
103 | * @cdev: class device to set | |
104 | * Returns nothing | |
105 | */ | |
106 | static void set_classdev_classname(struct sysfs_class_device *cdev) | |
107 | { | |
108 | unsigned char *c = NULL, *e = NULL; | |
109 | int count = 0; | |
110 | ||
5d4754f1 DS |
111 | c = strstr(cdev->path, SYSFS_CLASS_NAME); |
112 | if (c == NULL) { | |
113 | c = strstr(cdev->path, SYSFS_BLOCK_NAME); | |
114 | } else { | |
115 | c = strstr(c, "/"); | |
ff44a6b0 DS |
116 | } |
117 | ||
5d4754f1 | 118 | if (c == NULL) |
ff44a6b0 | 119 | strcpy(cdev->classname, SYSFS_UNKNOWN); |
ff44a6b0 | 120 | else { |
5d4754f1 DS |
121 | if (*c == '/') |
122 | c++; | |
ff44a6b0 DS |
123 | e = c; |
124 | while (e != NULL && *e != '/' && *e != '\0') { | |
125 | e++; | |
126 | count++; | |
127 | } | |
128 | strncpy(cdev->classname, c, count); | |
129 | } | |
130 | } | |
131 | ||
070f5422 | 132 | /** |
bcbe2d8e | 133 | * sysfs_open_class_device_path: Opens and populates class device |
070f5422 GKH |
134 | * @path: path to class device. |
135 | * returns struct sysfs_class_device with success and NULL with error. | |
136 | */ | |
bcbe2d8e DS |
137 | struct sysfs_class_device *sysfs_open_class_device_path |
138 | (const unsigned char *path) | |
070f5422 GKH |
139 | { |
140 | struct sysfs_class_device *cdev = NULL; | |
070f5422 GKH |
141 | |
142 | if (path == NULL) { | |
143 | errno = EINVAL; | |
144 | return NULL; | |
145 | } | |
5d4754f1 DS |
146 | if ((sysfs_path_is_dir(path)) != 0) { |
147 | dprintf("%s is not a valid path to a class device\n", path); | |
148 | return NULL; | |
149 | } | |
070f5422 GKH |
150 | cdev = alloc_class_device(); |
151 | if (cdev == NULL) { | |
fe3fe3b2 | 152 | dprintf("calloc failed\n"); |
070f5422 GKH |
153 | return NULL; |
154 | } | |
fe3fe3b2 | 155 | if ((sysfs_get_name_from_path(path, cdev->name, SYSFS_NAME_LEN)) != 0) { |
070f5422 | 156 | errno = EINVAL; |
5d4754f1 | 157 | dprintf("Error getting class device name\n"); |
070f5422 GKH |
158 | sysfs_close_class_device(cdev); |
159 | return NULL; | |
160 | } | |
070f5422 | 161 | |
5d4754f1 | 162 | strcpy(cdev->path, path); |
616a7078 AM |
163 | if ((sysfs_remove_trailing_slash(cdev->path)) != 0) { |
164 | dprintf("Invalid path to class device %s\n", cdev->path); | |
165 | sysfs_close_class_device(cdev); | |
166 | return NULL; | |
167 | } | |
ff44a6b0 | 168 | set_classdev_classname(cdev); |
070f5422 | 169 | |
070f5422 GKH |
170 | return cdev; |
171 | } | |
172 | ||
070f5422 | 173 | /** |
5d4754f1 | 174 | * sysfs_get_class_devices: gets all devices for class |
070f5422 | 175 | * @class: class to get devices for |
5d4754f1 | 176 | * returns dlist of class_devices with success and NULL with error |
070f5422 | 177 | */ |
5d4754f1 | 178 | struct dlist *sysfs_get_class_devices(struct sysfs_class *cls) |
070f5422 GKH |
179 | { |
180 | struct sysfs_class_device *dev = NULL; | |
fe3fe3b2 | 181 | struct sysfs_directory *cur = NULL; |
070f5422 | 182 | |
5d4754f1 | 183 | if (cls == NULL) { |
070f5422 | 184 | errno = EINVAL; |
5d4754f1 | 185 | return NULL; |
070f5422 | 186 | } |
616a7078 AM |
187 | |
188 | if (cls->devices != NULL) | |
189 | return cls->devices; | |
190 | ||
5d4754f1 DS |
191 | if (cls->directory == NULL) { |
192 | cls->directory = sysfs_open_directory(cls->path); | |
193 | if (cls->directory == NULL) | |
194 | return NULL; | |
195 | } | |
196 | ||
bcbe2d8e | 197 | if ((sysfs_read_dir_subdirs(cls->directory)) != 0) |
5d4754f1 DS |
198 | return NULL; |
199 | ||
bcbe2d8e DS |
200 | if (cls->directory->subdirs != NULL) { |
201 | dlist_for_each_data(cls->directory->subdirs, cur, | |
202 | struct sysfs_directory) { | |
203 | dev = sysfs_open_class_device_path(cur->path); | |
204 | if (dev == NULL) { | |
205 | dprintf("Error opening device at %s\n", | |
206 | cur->path); | |
207 | continue; | |
208 | } | |
209 | if (cls->devices == NULL) | |
210 | cls->devices = dlist_new_with_delete | |
fe3fe3b2 DS |
211 | (sizeof(struct sysfs_class_device), |
212 | sysfs_close_cls_dev); | |
bcbe2d8e DS |
213 | dlist_unshift(cls->devices, dev); |
214 | } | |
070f5422 | 215 | } |
5d4754f1 | 216 | return cls->devices; |
070f5422 GKH |
217 | } |
218 | ||
219 | /** | |
220 | * sysfs_open_class: opens specific class and all its devices on system | |
221 | * returns sysfs_class structure with success or NULL with error. | |
222 | */ | |
fe3fe3b2 | 223 | struct sysfs_class *sysfs_open_class(const unsigned char *name) |
070f5422 GKH |
224 | { |
225 | struct sysfs_class *cls = NULL; | |
5d4754f1 | 226 | unsigned char classpath[SYSFS_PATH_MAX]; |
070f5422 GKH |
227 | |
228 | if (name == NULL) { | |
229 | errno = EINVAL; | |
230 | return NULL; | |
231 | } | |
232 | ||
5d4754f1 DS |
233 | memset(classpath, 0, SYSFS_PATH_MAX); |
234 | if ((sysfs_get_mnt_path(classpath, SYSFS_PATH_MAX)) != 0) { | |
235 | dprintf("Sysfs not supported on this system\n"); | |
236 | return NULL; | |
237 | } | |
5d4754f1 DS |
238 | |
239 | /* | |
240 | * We shall now treat "block" also as a class. Hence, check here | |
241 | * if "name" is "block" and proceed accordingly | |
242 | */ | |
243 | if (strcmp(name, SYSFS_BLOCK_NAME) == 0) { | |
616a7078 | 244 | strcat(classpath, "/"); |
5d4754f1 DS |
245 | strcat(classpath, SYSFS_BLOCK_NAME); |
246 | } else { | |
616a7078 | 247 | strcat(classpath, "/"); |
5d4754f1 DS |
248 | strcat(classpath, SYSFS_CLASS_NAME); |
249 | strcat(classpath, "/"); | |
250 | strcat(classpath, name); | |
251 | } | |
252 | if ((sysfs_path_is_dir(classpath)) != 0) { | |
253 | dprintf("Class %s not found on the system\n", name); | |
254 | return NULL; | |
255 | } | |
256 | ||
070f5422 GKH |
257 | cls = alloc_class(); |
258 | if (cls == NULL) { | |
fe3fe3b2 | 259 | dprintf("calloc failed\n"); |
070f5422 GKH |
260 | return NULL; |
261 | } | |
262 | strcpy(cls->name, name); | |
5d4754f1 | 263 | strcpy(cls->path, classpath); |
616a7078 AM |
264 | if ((sysfs_remove_trailing_slash(cls->path)) != 0) { |
265 | dprintf("Invalid path to class device %s\n", cls->path); | |
266 | sysfs_close_class(cls); | |
267 | return NULL; | |
268 | } | |
5d4754f1 | 269 | |
070f5422 GKH |
270 | return cls; |
271 | } | |
fe3fe3b2 DS |
272 | |
273 | /** | |
274 | * sysfs_get_class_device: Get specific class device using the device's id | |
275 | * @class: class to find device on | |
276 | * @name: class name of the device | |
277 | */ | |
278 | struct sysfs_class_device *sysfs_get_class_device(struct sysfs_class *class, | |
279 | unsigned char *name) | |
280 | { | |
281 | if (class == NULL || name == NULL) { | |
282 | errno = EINVAL; | |
283 | return NULL; | |
284 | } | |
285 | ||
5d4754f1 DS |
286 | if (class->devices == NULL) { |
287 | class->devices = sysfs_get_class_devices(class); | |
616a7078 | 288 | if (class->devices == NULL) |
5d4754f1 DS |
289 | return NULL; |
290 | } | |
fe3fe3b2 DS |
291 | return (struct sysfs_class_device *)dlist_find_custom(class->devices, |
292 | name, class_name_equal); | |
293 | } | |
294 | ||
5d4754f1 DS |
295 | /** |
296 | * sysfs_get_classdev_device: returns the sysfs_device corresponding to | |
297 | * sysfs_class_device, if present | |
298 | * @clsdev: class device whose sysfs_device is required | |
299 | * Returns sysfs_device on success, NULL on error or if device is not | |
300 | * implemented | |
301 | */ | |
302 | struct sysfs_device *sysfs_get_classdev_device | |
303 | (struct sysfs_class_device *clsdev) | |
304 | { | |
305 | struct sysfs_link *devlink = NULL; | |
616a7078 | 306 | unsigned char devpath[SYSFS_PATH_MAX]; |
5d4754f1 DS |
307 | |
308 | if (clsdev == NULL) { | |
309 | errno = EINVAL; | |
310 | return NULL; | |
311 | } | |
616a7078 AM |
312 | strcpy(devpath, clsdev->path); |
313 | strcat(devpath, "/device"); | |
314 | if ((sysfs_path_is_link(devpath)) != 0) { | |
315 | if (clsdev->sysdevice != NULL) { | |
316 | sysfs_close_device(clsdev->sysdevice); | |
317 | clsdev->sysdevice = NULL; | |
318 | } | |
319 | return NULL; | |
320 | } | |
5d4754f1 DS |
321 | |
322 | if (clsdev->directory == NULL) { | |
323 | clsdev->directory = sysfs_open_directory(clsdev->path); | |
324 | if (clsdev->directory == NULL) | |
325 | return NULL; | |
326 | } | |
327 | devlink = sysfs_get_directory_link(clsdev->directory, "device"); | |
616a7078 AM |
328 | if (devlink == NULL) { |
329 | if (clsdev->sysdevice != NULL) { | |
330 | dprintf("Device link no longer exists\n"); | |
331 | sysfs_close_device(clsdev->sysdevice); | |
332 | clsdev->sysdevice = NULL; | |
333 | } | |
5d4754f1 | 334 | return NULL; |
616a7078 AM |
335 | } |
336 | ||
337 | if (clsdev->sysdevice != NULL) { | |
338 | if (!strncmp(devlink->target, clsdev->sysdevice->path, | |
339 | SYSFS_PATH_MAX)) | |
340 | /* sysdevice hasn't changed */ | |
341 | return (clsdev->sysdevice); | |
342 | else | |
343 | /* come here only if the device link for has changed */ | |
344 | sysfs_close_device(clsdev->sysdevice); | |
345 | } | |
5d4754f1 | 346 | |
bcbe2d8e | 347 | clsdev->sysdevice = sysfs_open_device_path(devlink->target); |
5d4754f1 DS |
348 | if (clsdev->sysdevice == NULL) |
349 | return NULL; | |
350 | if (clsdev->driver != NULL) | |
351 | strcpy(clsdev->sysdevice->driver_name, clsdev->driver->name); | |
352 | ||
353 | return (clsdev->sysdevice); | |
354 | } | |
355 | ||
356 | /** | |
357 | * sysfs_get_classdev_driver: returns the sysfs_driver corresponding to | |
358 | * sysfs_class_device, if present | |
359 | * @clsdev: class device whose sysfs_device is required | |
360 | * Returns sysfs_driver on success, NULL on error or if driver is not | |
361 | * implemented | |
362 | */ | |
363 | struct sysfs_driver *sysfs_get_classdev_driver | |
364 | (struct sysfs_class_device *clsdev) | |
365 | { | |
366 | struct sysfs_link *drvlink = NULL; | |
616a7078 | 367 | unsigned char drvpath[SYSFS_PATH_MAX]; |
5d4754f1 DS |
368 | |
369 | if (clsdev == NULL) { | |
370 | errno = EINVAL; | |
371 | return NULL; | |
372 | } | |
616a7078 AM |
373 | strcpy(drvpath, clsdev->path); |
374 | strcat(drvpath, "/driver"); | |
375 | if ((sysfs_path_is_link(drvpath)) != 0) { | |
376 | if (clsdev->driver != NULL) { | |
377 | sysfs_close_driver(clsdev->driver); | |
378 | clsdev->driver = NULL; | |
379 | } | |
380 | return NULL; | |
381 | } | |
382 | ||
5d4754f1 DS |
383 | if (clsdev->directory == NULL) { |
384 | clsdev->directory = sysfs_open_directory(clsdev->path); | |
385 | if (clsdev->directory == NULL) | |
386 | return NULL; | |
387 | } | |
388 | drvlink = sysfs_get_directory_link(clsdev->directory, "driver"); | |
616a7078 AM |
389 | if (drvlink == NULL) { |
390 | if (clsdev->driver != NULL) { | |
391 | dprintf("Driver link no longer exists\n"); | |
392 | sysfs_close_driver(clsdev->driver); | |
393 | clsdev->driver = NULL; | |
394 | } | |
395 | return NULL; | |
396 | } | |
397 | if (clsdev->driver != NULL) { | |
398 | if (!strncmp(drvlink->target, clsdev->driver->path, | |
399 | SYSFS_PATH_MAX)) | |
400 | /* driver hasn't changed */ | |
401 | return (clsdev->driver); | |
402 | else | |
403 | /* come here only if the device link for has changed */ | |
404 | sysfs_close_driver(clsdev->driver); | |
5d4754f1 | 405 | } |
616a7078 AM |
406 | |
407 | clsdev->driver = sysfs_open_driver_path(drvlink->target); | |
408 | if (clsdev->driver == NULL) | |
409 | return NULL; | |
410 | if (clsdev->sysdevice != NULL) | |
411 | strcpy(clsdev->sysdevice->driver_name, clsdev->driver->name); | |
412 | ||
5d4754f1 DS |
413 | return (clsdev->driver); |
414 | } | |
616a7078 AM |
415 | |
416 | /** | |
5d4754f1 DS |
417 | * get_blockdev_parent: Get the parent class device for a "block" subsystem |
418 | * device if present | |
419 | * @clsdev: block subsystem class device whose parent needs to be found | |
420 | * Returns 0 on success and 1 on error | |
421 | */ | |
422 | static int get_blockdev_parent(struct sysfs_class_device *clsdev) | |
423 | { | |
616a7078 | 424 | unsigned char parent_path[SYSFS_PATH_MAX], *c = NULL; |
5d4754f1 | 425 | |
616a7078 | 426 | strcpy(parent_path, clsdev->path); |
5d4754f1 DS |
427 | c = strstr(parent_path, SYSFS_BLOCK_NAME); |
428 | if (c == NULL) { | |
616a7078 | 429 | dprintf("Class device %s does not belong to BLOCK subsystem\n", |
5d4754f1 DS |
430 | clsdev->name); |
431 | return 1; | |
432 | } | |
616a7078 | 433 | |
5d4754f1 DS |
434 | c += strlen(SYSFS_BLOCK_NAME); |
435 | if (*c == '/') | |
436 | c++; | |
437 | else | |
438 | goto errout; | |
616a7078 AM |
439 | |
440 | /* validate whether the given class device is a partition or not */ | |
441 | if ((strncmp(c, clsdev->name, strlen(clsdev->name))) == 0) { | |
442 | dprintf("%s not a partition\n", clsdev->name); | |
443 | return 1; | |
444 | } | |
445 | ||
446 | c = strchr(c, '/'); | |
5d4754f1 DS |
447 | if (c == NULL) |
448 | goto errout; | |
449 | ||
450 | *c = '\0'; | |
616a7078 | 451 | |
bcbe2d8e | 452 | clsdev->parent = sysfs_open_class_device_path(parent_path); |
5d4754f1 DS |
453 | if (clsdev->parent == NULL) { |
454 | dprintf("Error opening the parent class device at %s\n", | |
455 | parent_path); | |
456 | return 1; | |
457 | } | |
458 | return 0; | |
459 | ||
460 | errout: | |
461 | dprintf("Invalid path %s\n", clsdev->path); | |
462 | return 1; | |
463 | } | |
464 | ||
465 | /** | |
466 | * sysfs_get_classdev_parent: Retrieves the parent of a class device. | |
467 | * eg., when working with hda1, this function can be used to retrieve the | |
468 | * sysfs_class_device for hda | |
469 | * | |
470 | * @clsdev: class device whose parent details are required. | |
471 | * Returns sysfs_class_device of the parent on success, NULL on failure | |
472 | */ | |
473 | struct sysfs_class_device *sysfs_get_classdev_parent | |
474 | (struct sysfs_class_device *clsdev) | |
475 | { | |
476 | if (clsdev == NULL) { | |
477 | errno = EINVAL; | |
478 | return NULL; | |
479 | } | |
480 | if (clsdev->parent != NULL) | |
481 | return (clsdev->parent); | |
482 | ||
483 | /* | |
484 | * As of now, only block devices have a parent child heirarchy in sysfs | |
485 | * We do not know, if, in the future, more classes will have a similar | |
486 | * structure. Hence, we now call a specialized function for block and | |
487 | * later we can add support functions for other subsystems as required. | |
488 | */ | |
489 | if (!(strcmp(clsdev->classname, SYSFS_BLOCK_NAME))) { | |
490 | if ((get_blockdev_parent(clsdev)) == 0) | |
491 | return (clsdev->parent); | |
492 | } | |
493 | return NULL; | |
494 | } | |
495 | ||
ff44a6b0 DS |
496 | /** |
497 | * get_classdev_path: given the class and a device in the class, return the | |
498 | * absolute path to the device | |
499 | * @classname: name of the class | |
500 | * @clsdev: the class device | |
501 | * @path: buffer to return path | |
502 | * @psize: size of "path" | |
503 | * Returns 0 on SUCCESS or -1 on error | |
504 | */ | |
505 | static int get_classdev_path(const unsigned char *classname, | |
506 | const unsigned char *clsdev, unsigned char *path, size_t len) | |
507 | { | |
508 | if (classname == NULL || clsdev == NULL || path == NULL) { | |
509 | errno = EINVAL; | |
510 | return -1; | |
511 | } | |
512 | if (sysfs_get_mnt_path(path, len) != 0) { | |
513 | dprintf("Error getting sysfs mount path\n"); | |
514 | return -1; | |
515 | } | |
516 | if (strcmp(classname, SYSFS_BLOCK_NAME) == 0) { | |
616a7078 | 517 | strcat(path, "/"); |
edcd3364 | 518 | strcat(path, SYSFS_BLOCK_NAME); |
ff44a6b0 | 519 | } else { |
616a7078 | 520 | strcat(path, "/"); |
edcd3364 | 521 | strcat(path, SYSFS_CLASS_NAME); |
ff44a6b0 DS |
522 | strcat(path, "/"); |
523 | strcat(path, classname); | |
524 | } | |
525 | strcat(path, "/"); | |
526 | strcat(path, clsdev); | |
527 | return 0; | |
528 | } | |
529 | ||
fe3fe3b2 | 530 | /** |
bcbe2d8e | 531 | * sysfs_open_class_device: Locates a specific class_device and returns it. |
fe3fe3b2 DS |
532 | * Class_device must be closed using sysfs_close_class_device |
533 | * @classname: Class to search | |
534 | * @name: name of the class_device | |
ff44a6b0 DS |
535 | * |
536 | * NOTE: | |
537 | * Call sysfs_close_class_device() to close the class device | |
fe3fe3b2 | 538 | */ |
bcbe2d8e | 539 | struct sysfs_class_device *sysfs_open_class_device |
ff44a6b0 | 540 | (const unsigned char *classname, const unsigned char *name) |
fe3fe3b2 | 541 | { |
ff44a6b0 DS |
542 | unsigned char devpath[SYSFS_PATH_MAX]; |
543 | struct sysfs_class_device *cdev = NULL; | |
fe3fe3b2 DS |
544 | |
545 | if (classname == NULL || name == NULL) { | |
546 | errno = EINVAL; | |
547 | return NULL; | |
548 | } | |
549 | ||
ff44a6b0 DS |
550 | memset(devpath, 0, SYSFS_PATH_MAX); |
551 | if ((get_classdev_path(classname, name, devpath, | |
552 | SYSFS_PATH_MAX)) != 0) { | |
553 | dprintf("Error getting to device %s on class %s\n", | |
554 | name, classname); | |
fe3fe3b2 DS |
555 | return NULL; |
556 | } | |
ff44a6b0 | 557 | |
bcbe2d8e | 558 | cdev = sysfs_open_class_device_path(devpath); |
fe3fe3b2 DS |
559 | if (cdev == NULL) { |
560 | dprintf("Error getting class device %s from class %s\n", | |
561 | name, classname); | |
fe3fe3b2 DS |
562 | return NULL; |
563 | } | |
ff44a6b0 | 564 | return cdev; |
fe3fe3b2 DS |
565 | } |
566 | ||
567 | /** | |
568 | * sysfs_get_classdev_attributes: returns a dlist of attributes for | |
569 | * the requested class_device | |
570 | * @cdev: sysfs_class_dev for which attributes are needed | |
571 | * returns a dlist of attributes if exists, NULL otherwise | |
572 | */ | |
573 | struct dlist *sysfs_get_classdev_attributes(struct sysfs_class_device *cdev) | |
574 | { | |
5d4754f1 | 575 | if (cdev == NULL) |
fe3fe3b2 DS |
576 | return NULL; |
577 | ||
5d4754f1 DS |
578 | if (cdev->directory == NULL) { |
579 | cdev->directory = sysfs_open_directory(cdev->path); | |
580 | if (cdev->directory == NULL) | |
581 | return NULL; | |
582 | } | |
583 | if (cdev->directory->attributes == NULL) { | |
616a7078 | 584 | if ((sysfs_read_dir_attributes(cdev->directory)) != 0) |
5d4754f1 | 585 | return NULL; |
5d4754f1 | 586 | } |
fe3fe3b2 DS |
587 | return (cdev->directory->attributes); |
588 | } | |
589 | ||
616a7078 AM |
590 | /** |
591 | * sysfs_refresh_clsassdev_attributes: refreshes the driver's list of attributes | |
592 | * @clsdev: sysfs_class_device whose attributes to refresh | |
593 | * | |
594 | * NOTE: Upon return, prior references to sysfs_attributes for this classdev | |
595 | * _may_ not be valid | |
596 | * | |
597 | * Returns list of attributes on success and NULL on failure | |
598 | */ | |
599 | struct dlist *sysfs_refresh_classdev_attributes | |
600 | (struct sysfs_class_device *clsdev) | |
601 | { | |
602 | if (clsdev == NULL) { | |
603 | errno = EINVAL; | |
604 | return NULL; | |
605 | } | |
606 | ||
607 | if (clsdev->directory == NULL) | |
608 | return (sysfs_get_classdev_attributes(clsdev)); | |
609 | ||
610 | if ((sysfs_refresh_dir_attributes(clsdev->directory)) != 0) { | |
611 | dprintf("Error refreshing class_device attributes\n"); | |
612 | return NULL; | |
613 | } | |
614 | ||
615 | return (clsdev->directory->attributes); | |
616 | } | |
617 | ||
fe3fe3b2 DS |
618 | /** |
619 | * sysfs_get_classdev_attr: searches class device's attributes by name | |
620 | * @clsdev: class device to look through | |
621 | * @name: attribute name to get | |
622 | * returns sysfs_attribute reference with success or NULL with error | |
623 | */ | |
624 | struct sysfs_attribute *sysfs_get_classdev_attr | |
625 | (struct sysfs_class_device *clsdev, const unsigned char *name) | |
626 | { | |
627 | struct sysfs_attribute *cur = NULL; | |
5d4754f1 DS |
628 | struct sysfs_directory *sdir = NULL; |
629 | struct dlist *attrlist = NULL; | |
630 | ||
631 | if (clsdev == NULL || name == NULL) { | |
fe3fe3b2 DS |
632 | errno = EINVAL; |
633 | return NULL; | |
634 | } | |
bcbe2d8e | 635 | |
5d4754f1 DS |
636 | /* |
637 | * First, see if it's in the current directory. Then look at | |
638 | * subdirs since class devices can have subdirs of attributes. | |
639 | */ | |
640 | attrlist = sysfs_get_classdev_attributes(clsdev); | |
bcbe2d8e DS |
641 | if (attrlist != NULL) { |
642 | cur = sysfs_get_directory_attribute(clsdev->directory, | |
fe3fe3b2 | 643 | (unsigned char *)name); |
bcbe2d8e DS |
644 | if (cur != NULL) |
645 | return cur; | |
646 | } | |
fe3fe3b2 | 647 | |
5d4754f1 DS |
648 | if (clsdev->directory->subdirs == NULL) |
649 | if ((sysfs_read_dir_subdirs(clsdev->directory)) != 0 || | |
650 | clsdev->directory->subdirs == NULL) | |
651 | return NULL; | |
652 | ||
bcbe2d8e DS |
653 | if (clsdev->directory->subdirs != NULL) { |
654 | dlist_for_each_data(clsdev->directory->subdirs, sdir, | |
655 | struct sysfs_directory) { | |
656 | if ((sysfs_path_is_dir(sdir->path)) != 0) | |
657 | continue; | |
616a7078 | 658 | cur = sysfs_get_directory_attribute(sdir, |
bcbe2d8e | 659 | (unsigned char *)name); |
616a7078 AM |
660 | if (cur == NULL) |
661 | continue; | |
bcbe2d8e | 662 | } |
5d4754f1 | 663 | } |
bcbe2d8e | 664 | return cur; |
fe3fe3b2 DS |
665 | } |
666 | ||
667 | /** | |
ff44a6b0 DS |
668 | * sysfs_open_classdev_attr: read an attribute for a given class device |
669 | * @classname: name of the class on which to look | |
fe3fe3b2 DS |
670 | * @dev: class device name for which the attribute has to be read |
671 | * @attrib: attribute to read | |
ff44a6b0 DS |
672 | * Returns sysfs_attribute * on SUCCESS and NULL on error |
673 | * | |
674 | * NOTE: | |
675 | * A call to sysfs_close_attribute() is required to close the | |
676 | * attribute returned and to free memory | |
fe3fe3b2 | 677 | */ |
ff44a6b0 DS |
678 | struct sysfs_attribute *sysfs_open_classdev_attr(const unsigned char *classname, |
679 | const unsigned char *dev, const unsigned char *attrib) | |
fe3fe3b2 | 680 | { |
fe3fe3b2 | 681 | struct sysfs_attribute *attribute = NULL; |
ff44a6b0 | 682 | unsigned char path[SYSFS_PATH_MAX]; |
fe3fe3b2 | 683 | |
ff44a6b0 | 684 | if (classname == NULL || dev == NULL || attrib == NULL) { |
fe3fe3b2 | 685 | errno = EINVAL; |
ff44a6b0 | 686 | return NULL; |
fe3fe3b2 | 687 | } |
ff44a6b0 DS |
688 | memset(path, 0, SYSFS_PATH_MAX); |
689 | if ((get_classdev_path(classname, dev, path, SYSFS_PATH_MAX)) != 0) { | |
690 | dprintf("Error getting to device %s on class %s\n", | |
691 | dev, classname); | |
692 | return NULL; | |
fe3fe3b2 | 693 | } |
ff44a6b0 DS |
694 | strcat(path, "/"); |
695 | strcat(path, attrib); | |
696 | attribute = sysfs_open_attribute(path); | |
fe3fe3b2 | 697 | if (attribute == NULL) { |
ff44a6b0 DS |
698 | dprintf("Error opening attribute %s on class device %s\n", |
699 | attrib, dev); | |
700 | return NULL; | |
fe3fe3b2 | 701 | } |
ff44a6b0 DS |
702 | if ((sysfs_read_attribute(attribute)) != 0) { |
703 | dprintf("Error reading attribute %s for class device %s\n", | |
704 | attrib, dev); | |
705 | sysfs_close_attribute(attribute); | |
706 | return NULL; | |
fe3fe3b2 | 707 | } |
ff44a6b0 | 708 | return attribute; |
fe3fe3b2 | 709 | } |
bcbe2d8e | 710 |