]>
Commit | Line | Data |
---|---|---|
070f5422 GKH |
1 | /* |
2 | * sysfs_device.c | |
3 | * | |
4 | * Generic device 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 | /** |
616a7078 AM |
27 | * sysfs_get_device_bus: retrieves the bus name the device is on, checks path |
28 | * to bus' link to make sure it has correct device. | |
ff44a6b0 DS |
29 | * @dev: device to get busname. |
30 | * returns 0 with success and -1 with error. | |
31 | */ | |
616a7078 | 32 | int sysfs_get_device_bus(struct sysfs_device *dev) |
ff44a6b0 | 33 | { |
5d4754f1 DS |
34 | unsigned char subsys[SYSFS_NAME_LEN], path[SYSFS_PATH_MAX]; |
35 | unsigned char target[SYSFS_PATH_MAX], *bus = NULL, *c = NULL; | |
36 | struct dlist *buslist = NULL; | |
ff44a6b0 DS |
37 | |
38 | if (dev == NULL) { | |
39 | errno = EINVAL; | |
40 | return -1; | |
41 | } | |
42 | ||
5d4754f1 DS |
43 | memset(subsys, 0, SYSFS_NAME_LEN); |
44 | strcat(subsys, "/"); | |
45 | strcpy(subsys, SYSFS_BUS_NAME); /* subsys = /bus */ | |
ff44a6b0 DS |
46 | buslist = sysfs_open_subsystem_list(subsys); |
47 | if (buslist != NULL) { | |
48 | dlist_for_each_data(buslist, bus, char) { | |
5d4754f1 DS |
49 | memset(path, 0, SYSFS_PATH_MAX); |
50 | strcpy(path, dev->path); | |
51 | c = strstr(path, "/devices"); | |
52 | if (c == NULL) { | |
53 | dprintf("Invalid path to device %s\n", path); | |
54 | sysfs_close_list(buslist); | |
55 | return -1; | |
56 | } | |
57 | *c = '\0'; | |
58 | strcat(path, "/"); | |
59 | strcat(path, SYSFS_BUS_NAME); | |
60 | strcat(path, "/"); | |
61 | strcat(path, bus); | |
62 | strcat(path, "/"); | |
63 | strcat(path, SYSFS_DEVICES_NAME); | |
64 | strcat(path, "/"); | |
65 | strcat(path, dev->bus_id); | |
66 | if ((sysfs_path_is_link(path)) == 0) { | |
67 | memset(target, 0, SYSFS_PATH_MAX); | |
68 | if ((sysfs_get_link(path, target, | |
69 | SYSFS_PATH_MAX)) != 0) { | |
70 | dprintf("Error getting link target\n"); | |
71 | sysfs_close_list(buslist); | |
72 | return -1; | |
73 | } | |
74 | if (!(strncmp(target, dev->path, | |
75 | SYSFS_PATH_MAX))) { | |
76 | strcpy(dev->bus, bus); | |
77 | sysfs_close_list(buslist); | |
78 | return 0; | |
79 | } | |
80 | } | |
ff44a6b0 DS |
81 | } |
82 | sysfs_close_list(buslist); | |
83 | } | |
84 | return -1; | |
85 | } | |
86 | ||
fe3fe3b2 DS |
87 | /** |
88 | * sysfs_close_device_tree: closes every device in the supplied tree, | |
89 | * closing children only. | |
90 | * @devroot: device root of tree. | |
91 | */ | |
92 | static void sysfs_close_device_tree(struct sysfs_device *devroot) | |
93 | { | |
94 | if (devroot != NULL) { | |
95 | if (devroot->children != NULL) { | |
96 | struct sysfs_device *child = NULL; | |
97 | ||
98 | dlist_for_each_data(devroot->children, child, | |
99 | struct sysfs_device) { | |
100 | sysfs_close_device_tree(child); | |
101 | } | |
102 | } | |
103 | sysfs_close_device(devroot); | |
104 | } | |
105 | } | |
106 | ||
fe3fe3b2 DS |
107 | /** |
108 | * sysfs_close_dev_tree: routine for dlist integration | |
109 | */ | |
110 | static void sysfs_close_dev_tree(void *dev) | |
111 | { | |
112 | sysfs_close_device_tree((struct sysfs_device *)dev); | |
113 | } | |
114 | ||
070f5422 GKH |
115 | /** |
116 | * sysfs_close_device: closes and cleans up a device | |
117 | * @dev = device to clean up | |
118 | */ | |
119 | void sysfs_close_device(struct sysfs_device *dev) | |
120 | { | |
121 | if (dev != NULL) { | |
bcbe2d8e DS |
122 | if (dev->parent != NULL) |
123 | sysfs_close_device(dev->parent); | |
070f5422 GKH |
124 | if (dev->directory != NULL) |
125 | sysfs_close_directory(dev->directory); | |
fe3fe3b2 DS |
126 | if (dev->children != NULL && dev->children->count == 0) |
127 | dlist_destroy(dev->children); | |
070f5422 GKH |
128 | free(dev); |
129 | } | |
130 | } | |
131 | ||
132 | /** | |
133 | * alloc_device: allocates and initializes device structure | |
134 | * returns struct sysfs_device | |
135 | */ | |
136 | static struct sysfs_device *alloc_device(void) | |
137 | { | |
138 | return (struct sysfs_device *)calloc(1, sizeof(struct sysfs_device)); | |
139 | } | |
140 | ||
141 | /** | |
5d4754f1 DS |
142 | * open_device_dir: opens up sysfs_directory for specific root dev |
143 | * @name: name of root | |
144 | * returns struct sysfs_directory with success and NULL with error | |
070f5422 | 145 | */ |
5d4754f1 | 146 | static struct sysfs_directory *open_device_dir(const unsigned char *path) |
070f5422 | 147 | { |
5d4754f1 | 148 | struct sysfs_directory *rdir = NULL; |
070f5422 | 149 | |
5d4754f1 | 150 | if (path == NULL) { |
070f5422 GKH |
151 | errno = EINVAL; |
152 | return NULL; | |
153 | } | |
070f5422 | 154 | |
5d4754f1 DS |
155 | rdir = sysfs_open_directory(path); |
156 | if (rdir == NULL) { | |
157 | errno = EINVAL; | |
158 | dprintf ("Device %s not supported on this system\n", path); | |
159 | return NULL; | |
160 | } | |
bcbe2d8e | 161 | if ((sysfs_read_dir_subdirs(rdir)) != 0) { |
5d4754f1 DS |
162 | dprintf ("Error reading device at dir %s\n", path); |
163 | sysfs_close_directory(rdir); | |
164 | return NULL; | |
165 | } | |
166 | ||
167 | return rdir; | |
070f5422 GKH |
168 | } |
169 | ||
170 | /** | |
bcbe2d8e | 171 | * sysfs_open_device_path: opens and populates device structure |
070f5422 GKH |
172 | * @path: path to device, this is the /sys/devices/ path |
173 | * returns sysfs_device structure with success or NULL with error | |
174 | */ | |
bcbe2d8e | 175 | struct sysfs_device *sysfs_open_device_path(const unsigned char *path) |
070f5422 GKH |
176 | { |
177 | struct sysfs_device *dev = NULL; | |
070f5422 GKH |
178 | |
179 | if (path == NULL) { | |
180 | errno = EINVAL; | |
181 | return NULL; | |
182 | } | |
5d4754f1 DS |
183 | if ((sysfs_path_is_dir(path)) != 0) { |
184 | dprintf("Incorrect path to device: %s\n", path); | |
185 | return NULL; | |
186 | } | |
070f5422 GKH |
187 | dev = alloc_device(); |
188 | if (dev == NULL) { | |
fe3fe3b2 | 189 | dprintf("Error allocating device at %s\n", path); |
070f5422 GKH |
190 | return NULL; |
191 | } | |
5d4754f1 DS |
192 | if ((sysfs_get_name_from_path(path, dev->bus_id, |
193 | SYSFS_NAME_LEN)) != 0) { | |
070f5422 | 194 | errno = EINVAL; |
5d4754f1 | 195 | dprintf("Error getting device bus_id\n"); |
070f5422 GKH |
196 | sysfs_close_device(dev); |
197 | return NULL; | |
198 | } | |
5d4754f1 | 199 | strcpy(dev->path, path); |
616a7078 AM |
200 | if ((sysfs_remove_trailing_slash(dev->path)) != 0) { |
201 | dprintf("Invalid path to device %s\n", dev->path); | |
202 | sysfs_close_device(dev); | |
203 | return NULL; | |
204 | } | |
fe3fe3b2 DS |
205 | /* |
206 | * The "name" attribute no longer exists... return the device's | |
207 | * sysfs representation instead, in the "dev->name" field, which | |
208 | * implies that the dev->name and dev->bus_id contain same data. | |
209 | */ | |
5d4754f1 | 210 | strncpy(dev->name, dev->bus_id, SYSFS_NAME_LEN); |
ff44a6b0 | 211 | |
616a7078 AM |
212 | if (sysfs_get_device_bus(dev) != 0) |
213 | dprintf("Could not get device bus\n"); | |
070f5422 GKH |
214 | |
215 | return dev; | |
216 | } | |
217 | ||
218 | /** | |
fe3fe3b2 DS |
219 | * sysfs_open_device_tree: opens root device and all of its children, |
220 | * creating a tree of devices. Only opens children. | |
221 | * @path: sysfs path to devices | |
222 | * returns struct sysfs_device and its children with success or NULL with | |
223 | * error. | |
070f5422 | 224 | */ |
fe3fe3b2 | 225 | static struct sysfs_device *sysfs_open_device_tree(const unsigned char *path) |
070f5422 | 226 | { |
fe3fe3b2 DS |
227 | struct sysfs_device *rootdev = NULL, *new = NULL; |
228 | struct sysfs_directory *cur = NULL; | |
229 | ||
230 | if (path == NULL) { | |
231 | errno = EINVAL; | |
232 | return NULL; | |
233 | } | |
bcbe2d8e | 234 | rootdev = sysfs_open_device_path(path); |
fe3fe3b2 DS |
235 | if (rootdev == NULL) { |
236 | dprintf("Error opening root device at %s\n", path); | |
237 | return NULL; | |
238 | } | |
5d4754f1 DS |
239 | if (rootdev->directory == NULL) { |
240 | rootdev->directory = open_device_dir(rootdev->path); | |
241 | if (rootdev->directory == NULL) | |
242 | return NULL; | |
243 | } | |
fe3fe3b2 DS |
244 | if (rootdev->directory->subdirs != NULL) { |
245 | dlist_for_each_data(rootdev->directory->subdirs, cur, | |
246 | struct sysfs_directory) { | |
247 | new = sysfs_open_device_tree(cur->path); | |
248 | if (new == NULL) { | |
249 | dprintf("Error opening device tree at %s\n", | |
250 | cur->path); | |
251 | sysfs_close_device_tree(rootdev); | |
252 | return NULL; | |
070f5422 | 253 | } |
fe3fe3b2 DS |
254 | if (rootdev->children == NULL) |
255 | rootdev->children = dlist_new_with_delete | |
256 | (sizeof(struct sysfs_device), | |
bcbe2d8e | 257 | sysfs_close_dev_tree); |
fe3fe3b2 | 258 | dlist_unshift(rootdev->children, new); |
070f5422 | 259 | } |
070f5422 | 260 | } |
fe3fe3b2 DS |
261 | |
262 | return rootdev; | |
070f5422 GKH |
263 | } |
264 | ||
265 | /** | |
fe3fe3b2 DS |
266 | * sysfs_close_root_device: closes root and all devices |
267 | * @root: root device to close | |
070f5422 | 268 | */ |
fe3fe3b2 | 269 | void sysfs_close_root_device(struct sysfs_root_device *root) |
070f5422 | 270 | { |
fe3fe3b2 DS |
271 | if (root != NULL) { |
272 | if (root->devices != NULL) | |
273 | dlist_destroy(root->devices); | |
274 | if (root->directory != NULL) | |
275 | sysfs_close_directory(root->directory); | |
276 | free(root); | |
070f5422 GKH |
277 | } |
278 | } | |
279 | ||
280 | /** | |
5d4754f1 | 281 | * sysfs_get_root_devices: opens up all the devices under this root device |
fe3fe3b2 | 282 | * @root: root device to open devices for |
5d4754f1 | 283 | * returns dlist of devices with success and NULL with error |
fe3fe3b2 | 284 | */ |
5d4754f1 | 285 | struct dlist *sysfs_get_root_devices(struct sysfs_root_device *root) |
fe3fe3b2 DS |
286 | { |
287 | struct sysfs_device *dev = NULL; | |
070f5422 GKH |
288 | struct sysfs_directory *cur = NULL; |
289 | ||
5d4754f1 | 290 | if (root == NULL) { |
fe3fe3b2 | 291 | errno = EINVAL; |
5d4754f1 | 292 | return NULL; |
fe3fe3b2 | 293 | } |
5d4754f1 DS |
294 | if (root->directory == NULL) { |
295 | root->directory = open_device_dir(root->path); | |
296 | if (root->directory == NULL) | |
297 | return NULL; | |
298 | } | |
299 | ||
fe3fe3b2 DS |
300 | if (root->directory->subdirs == NULL) |
301 | return 0; | |
302 | ||
303 | dlist_for_each_data(root->directory->subdirs, cur, | |
304 | struct sysfs_directory) { | |
305 | dev = sysfs_open_device_tree(cur->path); | |
306 | if (dev == NULL) { | |
307 | dprintf ("Error opening device at %s\n", cur->path); | |
308 | continue; | |
309 | } | |
310 | if (root->devices == NULL) | |
311 | root->devices = dlist_new_with_delete | |
312 | (sizeof(struct sysfs_device), | |
313 | sysfs_close_dev_tree); | |
314 | dlist_unshift(root->devices, dev); | |
315 | } | |
316 | ||
5d4754f1 | 317 | return root->devices; |
fe3fe3b2 DS |
318 | } |
319 | ||
320 | /** | |
321 | * sysfs_open_root_device: opens sysfs devices root and all of its | |
322 | * devices. | |
323 | * @name: name of /sys/devices/root to open | |
324 | * returns struct sysfs_root_device if success and NULL with error | |
325 | */ | |
326 | struct sysfs_root_device *sysfs_open_root_device(const unsigned char *name) | |
327 | { | |
328 | struct sysfs_root_device *root = NULL; | |
5d4754f1 | 329 | unsigned char rootpath[SYSFS_PATH_MAX]; |
fe3fe3b2 DS |
330 | |
331 | if (name == NULL) { | |
070f5422 GKH |
332 | errno = EINVAL; |
333 | return NULL; | |
334 | } | |
fe3fe3b2 | 335 | |
5d4754f1 DS |
336 | memset(rootpath, 0, SYSFS_PATH_MAX); |
337 | if (sysfs_get_mnt_path(rootpath, SYSFS_PATH_MAX) != 0) { | |
338 | dprintf ("Sysfs not supported on this system\n"); | |
fe3fe3b2 DS |
339 | return NULL; |
340 | } | |
5d4754f1 | 341 | |
616a7078 | 342 | strcat(rootpath, "/"); |
5d4754f1 DS |
343 | strcat(rootpath, SYSFS_DEVICES_NAME); |
344 | strcat(rootpath, "/"); | |
345 | strcat(rootpath, name); | |
346 | if ((sysfs_path_is_dir(rootpath)) != 0) { | |
347 | errno = EINVAL; | |
348 | dprintf("Invalid root device: %s\n", name); | |
fe3fe3b2 DS |
349 | return NULL; |
350 | } | |
5d4754f1 DS |
351 | root = (struct sysfs_root_device *)calloc |
352 | (1, sizeof(struct sysfs_root_device)); | |
353 | if (root == NULL) { | |
354 | dprintf("calloc failure\n"); | |
fe3fe3b2 DS |
355 | return NULL; |
356 | } | |
bcbe2d8e | 357 | strcpy(root->name, name); |
5d4754f1 | 358 | strcpy(root->path, rootpath); |
616a7078 AM |
359 | if ((sysfs_remove_trailing_slash(root->path)) != 0) { |
360 | dprintf("Invalid path to root device %s\n", root->path); | |
361 | sysfs_close_root_device(root); | |
362 | return NULL; | |
363 | } | |
fe3fe3b2 DS |
364 | return root; |
365 | } | |
366 | ||
367 | /** | |
368 | * sysfs_get_device_attributes: returns a dlist of attributes corresponding to | |
369 | * the specific device | |
370 | * @device: struct sysfs_device * for which attributes are to be returned | |
371 | */ | |
372 | struct dlist *sysfs_get_device_attributes(struct sysfs_device *device) | |
373 | { | |
5d4754f1 | 374 | if (device == NULL) |
fe3fe3b2 DS |
375 | return NULL; |
376 | ||
5d4754f1 DS |
377 | if (device->directory == NULL) { |
378 | device->directory = sysfs_open_directory(device->path); | |
379 | if (device->directory == NULL) | |
380 | return NULL; | |
381 | } | |
382 | if (device->directory->attributes == NULL) { | |
383 | if ((sysfs_read_dir_attributes(device->directory)) != 0) | |
384 | return NULL; | |
5d4754f1 | 385 | } |
fe3fe3b2 DS |
386 | return (device->directory->attributes); |
387 | } | |
388 | ||
616a7078 AM |
389 | /** |
390 | * sysfs_refresh_device_attributes: refreshes the device's list of attributes | |
391 | * @device: sysfs_device whose attributes to refresh | |
392 | * | |
393 | * NOTE: Upon return, prior references to sysfs_attributes for this device | |
394 | * _may_ not be valid | |
395 | * | |
396 | * Returns list of attributes on success and NULL on failure | |
397 | */ | |
398 | struct dlist *sysfs_refresh_device_attributes(struct sysfs_device *device) | |
399 | { | |
400 | if (device == NULL) { | |
401 | errno = EINVAL; | |
402 | return NULL; | |
403 | } | |
404 | ||
405 | if (device->directory == NULL) | |
406 | return (sysfs_get_device_attributes(device)); | |
407 | ||
408 | if ((sysfs_refresh_dir_attributes(device->directory)) != 0) { | |
409 | dprintf("Error refreshing device attributes\n"); | |
410 | return NULL; | |
411 | } | |
412 | ||
413 | return (device->directory->attributes); | |
414 | } | |
415 | ||
5d4754f1 DS |
416 | /** |
417 | * sysfs_get_device_attr: searches dev's attributes by name | |
418 | * @dev: device to look through | |
419 | * @name: attribute name to get | |
420 | * returns sysfs_attribute reference with success or NULL with error. | |
421 | */ | |
422 | struct sysfs_attribute *sysfs_get_device_attr(struct sysfs_device *dev, | |
423 | const unsigned char *name) | |
424 | { | |
5d4754f1 DS |
425 | struct dlist *attrlist = NULL; |
426 | ||
427 | if (dev == NULL || name == NULL) { | |
428 | errno = EINVAL; | |
429 | return NULL; | |
430 | } | |
616a7078 | 431 | |
5d4754f1 DS |
432 | attrlist = sysfs_get_device_attributes(dev); |
433 | if (attrlist == NULL) | |
434 | return NULL; | |
435 | ||
616a7078 AM |
436 | return sysfs_get_directory_attribute(dev->directory, |
437 | (unsigned char *)name); | |
5d4754f1 DS |
438 | } |
439 | ||
fe3fe3b2 DS |
440 | /** |
441 | * get_device_absolute_path: looks up the bus the device is on, gets | |
442 | * absolute path to the device | |
443 | * @device: device for which path is needed | |
444 | * @path: buffer to store absolute path | |
445 | * @psize: size of "path" | |
446 | * Returns 0 on success -1 on failure | |
447 | */ | |
ff44a6b0 DS |
448 | static int get_device_absolute_path(const unsigned char *device, |
449 | const unsigned char *bus, unsigned char *path, size_t psize) | |
fe3fe3b2 | 450 | { |
5d4754f1 | 451 | unsigned char bus_path[SYSFS_PATH_MAX]; |
fe3fe3b2 DS |
452 | |
453 | if (device == NULL || path == NULL) { | |
454 | errno = EINVAL; | |
455 | return -1; | |
456 | } | |
457 | ||
5d4754f1 | 458 | memset(bus_path, 0, SYSFS_PATH_MAX); |
fe3fe3b2 DS |
459 | if (sysfs_get_mnt_path(bus_path, SYSFS_PATH_MAX) != 0) { |
460 | dprintf ("Sysfs not supported on this system\n"); | |
461 | return -1; | |
462 | } | |
616a7078 | 463 | strcat(bus_path, "/"); |
edcd3364 | 464 | strcat(bus_path, SYSFS_BUS_NAME); |
fe3fe3b2 | 465 | strcat(bus_path, "/"); |
ff44a6b0 | 466 | strcat(bus_path, bus); |
5d4754f1 DS |
467 | strcat(bus_path, "/"); |
468 | strcat(bus_path, SYSFS_DEVICES_NAME); | |
fe3fe3b2 DS |
469 | strcat(bus_path, "/"); |
470 | strcat(bus_path, device); | |
471 | /* | |
472 | * We now are at /sys/bus/"bus_name"/devices/"device" which is a link. | |
473 | * Now read this link to reach to the device. | |
474 | */ | |
475 | if ((sysfs_get_link(bus_path, path, SYSFS_PATH_MAX)) != 0) { | |
476 | dprintf("Error getting to device %s\n", device); | |
477 | return -1; | |
478 | } | |
479 | return 0; | |
480 | } | |
481 | ||
482 | /** | |
bcbe2d8e | 483 | * sysfs_open_device: open a device by id (use the "bus" subsystem) |
ff44a6b0 DS |
484 | * @bus_id: bus_id of the device to open - has to be the "bus_id" in |
485 | * /sys/bus/xxx/devices | |
486 | * @bus: bus the device belongs to | |
ff44a6b0 DS |
487 | * returns struct sysfs_device if found, NULL otherwise |
488 | * NOTE: | |
489 | * 1. Use sysfs_close_device to close the device | |
490 | * 2. Bus the device is on must be supplied | |
491 | * Use sysfs_find_device_bus to get the bus name | |
492 | */ | |
bcbe2d8e | 493 | struct sysfs_device *sysfs_open_device(const unsigned char *bus_id, |
5d4754f1 | 494 | const unsigned char *bus) |
fe3fe3b2 | 495 | { |
ff44a6b0 DS |
496 | char sysfs_path[SYSFS_PATH_MAX]; |
497 | struct sysfs_device *device = NULL; | |
fe3fe3b2 | 498 | |
ff44a6b0 | 499 | if (bus_id == NULL || bus == NULL) { |
fe3fe3b2 | 500 | errno = EINVAL; |
ff44a6b0 | 501 | return NULL; |
fe3fe3b2 | 502 | } |
ff44a6b0 DS |
503 | memset(sysfs_path, 0, SYSFS_PATH_MAX); |
504 | if ((get_device_absolute_path(bus_id, bus, sysfs_path, | |
505 | SYSFS_PATH_MAX)) != 0) { | |
506 | dprintf("Error getting to device %s\n", bus_id); | |
507 | return NULL; | |
070f5422 | 508 | } |
ff44a6b0 | 509 | |
bcbe2d8e | 510 | device = sysfs_open_device_path(sysfs_path); |
ff44a6b0 DS |
511 | if (device == NULL) { |
512 | dprintf("Error opening device %s\n", bus_id); | |
513 | return NULL; | |
fe3fe3b2 | 514 | } |
ff44a6b0 DS |
515 | |
516 | return device; | |
fe3fe3b2 | 517 | } |
070f5422 | 518 | |
bcbe2d8e DS |
519 | /** |
520 | * sysfs_get_device_parent: opens up given device's parent and returns a | |
521 | * reference to its sysfs_device | |
522 | * @dev: sysfs_device whose parent is requested | |
523 | * Returns sysfs_device of the parent on success and NULL on failure | |
524 | */ | |
525 | struct sysfs_device *sysfs_get_device_parent(struct sysfs_device *dev) | |
526 | { | |
527 | unsigned char ppath[SYSFS_PATH_MAX], *tmp = NULL; | |
528 | ||
529 | if (dev == NULL) { | |
530 | errno = EINVAL; | |
531 | return NULL; | |
532 | } | |
533 | ||
534 | if (dev->parent != NULL) | |
535 | return (dev->parent); | |
536 | ||
537 | memset(ppath, 0, SYSFS_PATH_MAX); | |
538 | strcpy(ppath, dev->path); | |
539 | tmp = strrchr(ppath, '/'); | |
540 | if (tmp == NULL) { | |
541 | dprintf("Invalid path to device %s\n", ppath); | |
542 | return NULL; | |
543 | } | |
544 | if (*(tmp +1) == '\0') { | |
545 | *tmp = '\0'; | |
546 | tmp = strrchr(tmp, '/'); | |
547 | if (tmp == NULL) { | |
548 | dprintf("Invalid path to device %s\n", ppath); | |
549 | return NULL; | |
550 | } | |
551 | } | |
552 | *tmp = '\0'; | |
553 | ||
554 | /* | |
555 | * All "devices" have the "detach_state" attribute - validate here | |
556 | */ | |
557 | strcat(ppath, "/detach_state"); | |
558 | if ((sysfs_path_is_file(ppath)) != 0) { | |
559 | dprintf("Device at %s does not have a parent\n", dev->path); | |
560 | return NULL; | |
561 | } | |
562 | tmp = strrchr(ppath, '/'); | |
563 | *tmp = '\0'; | |
564 | dev->parent = sysfs_open_device_path(ppath); | |
565 | if (dev->parent == NULL) { | |
566 | dprintf("Error opening device %s's parent at %s\n", | |
567 | dev->bus_id, ppath); | |
568 | return NULL; | |
569 | } | |
570 | return (dev->parent); | |
571 | } | |
572 | ||
ff44a6b0 DS |
573 | /* |
574 | * sysfs_open_device_attr: open the given device's attribute | |
575 | * @bus: Bus on which to look | |
576 | * @dev_id: device for which attribute is required | |
577 | * @attrname: name of the attribute to look for | |
578 | * Returns struct sysfs_attribute on success and NULL on failure | |
579 | * | |
580 | * NOTE: | |
581 | * A call to sysfs_close_attribute() is required to close | |
582 | * the attribute returned and free memory. | |
583 | */ | |
584 | struct sysfs_attribute *sysfs_open_device_attr(const unsigned char *bus, | |
585 | const unsigned char *bus_id, const unsigned char *attrib) | |
fe3fe3b2 DS |
586 | { |
587 | struct sysfs_attribute *attribute = NULL; | |
588 | unsigned char devpath[SYSFS_PATH_MAX]; | |
ff44a6b0 DS |
589 | |
590 | if (bus == NULL || bus_id == NULL || attrib == NULL) { | |
fe3fe3b2 | 591 | errno = EINVAL; |
ff44a6b0 | 592 | return NULL; |
fe3fe3b2 | 593 | } |
ff44a6b0 | 594 | |
fe3fe3b2 | 595 | memset(devpath, 0, SYSFS_PATH_MAX); |
ff44a6b0 DS |
596 | if ((get_device_absolute_path(bus_id, bus, devpath, |
597 | SYSFS_PATH_MAX)) != 0) { | |
598 | dprintf("Error getting to device %s\n", bus_id); | |
599 | return NULL; | |
fe3fe3b2 DS |
600 | } |
601 | strcat(devpath, "/"); | |
602 | strcat(devpath, attrib); | |
603 | attribute = sysfs_open_attribute(devpath); | |
604 | if (attribute == NULL) { | |
605 | dprintf("Error opening attribute %s for device %s\n", | |
ff44a6b0 DS |
606 | attrib, bus_id); |
607 | return NULL; | |
fe3fe3b2 DS |
608 | } |
609 | if ((sysfs_read_attribute(attribute)) != 0) { | |
610 | dprintf("Error reading attribute %s for device %s\n", | |
ff44a6b0 | 611 | attrib, bus_id); |
fe3fe3b2 | 612 | sysfs_close_attribute(attribute); |
ff44a6b0 | 613 | return NULL; |
fe3fe3b2 | 614 | } |
ff44a6b0 | 615 | return attribute; |
070f5422 | 616 | } |
ff44a6b0 | 617 |