]>
Commit | Line | Data |
---|---|---|
93afd047 MT |
1 | #include <stdio.h> |
2 | #include <stdlib.h> | |
3 | #include <string.h> | |
4 | #include <unistd.h> | |
5 | #include <errno.h> | |
6 | #include <fcntl.h> | |
7 | #include <ctype.h> | |
8 | #include <sys/stat.h> | |
9 | #include <sys/types.h> | |
10 | typedef unsigned long kernel_ulong_t; | |
11 | #include <linux/types.h> | |
12 | #ifdef __UCLIBC__ | |
13 | #include <linux/pci.h> | |
14 | #else | |
15 | #include <sys/pci.h> | |
16 | #endif | |
17 | ||
18 | #include "hd.h" | |
19 | #include "hd_int.h" | |
20 | #include "hddb.h" | |
21 | #include "pci.h" | |
22 | ||
23 | /* | |
24 | * linux/ioport.h | |
25 | */ | |
26 | #define IORESOURCE_BITS 0x000000ff | |
27 | #define IORESOURCE_IO 0x00000100 | |
28 | #define IORESOURCE_MEM 0x00000200 | |
29 | #define IORESOURCE_IRQ 0x00000400 | |
30 | #define IORESOURCE_DMA 0x00000800 | |
31 | #define IORESOURCE_PREFETCH 0x00001000 | |
32 | #define IORESOURCE_READONLY 0x00002000 | |
33 | #define IORESOURCE_CACHEABLE 0x00004000 | |
34 | #define IORESOURCE_DISABLED 0x10000000 | |
35 | ||
36 | ||
37 | /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | |
38 | * pci stuff | |
39 | * | |
40 | * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | |
41 | */ | |
42 | ||
43 | static struct sysfs_attribute *hd_read_single_sysfs_attribute(char *path, char *name); | |
44 | static void get_pci_data(hd_data_t *hd_data); | |
45 | static void add_pci_data(hd_data_t *hd_data); | |
46 | static void add_driver_info(hd_data_t *hd_data); | |
47 | static pci_t *add_pci_entry(hd_data_t *hd_data, pci_t *new_pci); | |
48 | static unsigned char pci_cfg_byte(pci_t *pci, int fd, unsigned idx); | |
49 | static void dump_pci_data(hd_data_t *hd_data); | |
50 | ||
51 | void hd_scan_sysfs_pci(hd_data_t *hd_data) | |
52 | { | |
53 | if(!hd_probe_feature(hd_data, pr_pci)) return; | |
54 | ||
55 | hd_data->module = mod_pci; | |
56 | ||
57 | /* some clean-up */ | |
58 | remove_hd_entries(hd_data); | |
59 | hd_data->pci = NULL; | |
60 | ||
61 | PROGRESS(1, 0, "sysfs drivers"); | |
62 | ||
63 | hd_sysfs_driver_list(hd_data); | |
64 | ||
65 | PROGRESS(2, 0, "get sysfs pci data"); | |
66 | ||
67 | get_pci_data(hd_data); | |
68 | if(hd_data->debug) dump_pci_data(hd_data); | |
69 | ||
70 | add_pci_data(hd_data); | |
71 | } | |
72 | ||
73 | ||
74 | /* | |
75 | * sysfs_get_device_attr() reads *all* device attributes, then returns the | |
76 | * requested one. | |
77 | * | |
78 | * This leads to problems where some attribute *must not* be read. | |
79 | */ | |
80 | struct sysfs_attribute *hd_read_single_sysfs_attribute(char *path, char *name) | |
81 | { | |
82 | char *attr_path = NULL; | |
83 | struct sysfs_attribute *attr; | |
84 | ||
85 | str_printf(&attr_path, 0, "%s/%s", path, name); | |
86 | attr = sysfs_open_attribute(attr_path); | |
87 | free_mem(attr_path); | |
88 | ||
89 | sysfs_read_attribute(attr); | |
90 | ||
91 | return attr; | |
92 | } | |
93 | ||
94 | ||
95 | /* | |
96 | * Get the (raw) PCI data, taken from /sys/bus/pci/. | |
97 | * | |
98 | * Note: non-root users can only read the first 64 bytes (of 256) | |
99 | * of the device headers. | |
100 | */ | |
101 | void get_pci_data(hd_data_t *hd_data) | |
102 | { | |
103 | uint64_t ul0, ul1, ul2; | |
104 | unsigned u, u0, u1, u2, u3; | |
105 | unsigned char nxt; | |
106 | str_list_t *sl; | |
107 | char *s; | |
108 | pci_t *pci; | |
109 | int fd; | |
110 | ||
111 | struct sysfs_bus *sf_bus; | |
112 | struct dlist *sf_dev_list; | |
113 | struct sysfs_device *sf_dev; | |
114 | struct sysfs_attribute *attr; | |
115 | ||
116 | sf_bus = sysfs_open_bus("pci"); | |
117 | ||
118 | if(!sf_bus) { | |
119 | ADD2LOG("sysfs: no such bus: pci\n"); | |
120 | return; | |
121 | } | |
122 | ||
123 | sf_dev_list = sysfs_get_bus_devices(sf_bus); | |
124 | if(sf_dev_list) dlist_for_each_data(sf_dev_list, sf_dev, struct sysfs_device) { | |
125 | ADD2LOG( | |
126 | " pci device: name = %s, bus_id = %s, bus = %s\n path = %s\n", | |
127 | sf_dev->name, | |
128 | sf_dev->bus_id, | |
129 | sf_dev->bus, | |
130 | hd_sysfs_id(sf_dev->path) | |
131 | ); | |
132 | ||
133 | if(sscanf(sf_dev->bus_id, "%x:%x:%x.%x", &u0, &u1, &u2, &u3) != 4) continue; | |
134 | ||
135 | pci = add_pci_entry(hd_data, new_mem(sizeof *pci)); | |
136 | ||
137 | pci->sysfs_id = new_str(sf_dev->path); | |
138 | pci->sysfs_bus_id = new_str(sf_dev->bus_id); | |
139 | ||
140 | pci->bus = (u0 << 8) + u1; | |
141 | pci->slot = u2; | |
142 | pci->func = u3; | |
143 | ||
144 | if(hd_attr_uint(attr = hd_read_single_sysfs_attribute(sf_dev->path, "class"), &ul0, 0)) { | |
145 | ADD2LOG(" class = 0x%x\n", (unsigned) ul0); | |
146 | pci->prog_if = ul0 & 0xff; | |
147 | pci->sub_class = (ul0 >> 8) & 0xff; | |
148 | pci->base_class = (ul0 >> 16) & 0xff; | |
149 | } | |
150 | sysfs_close_attribute(attr); | |
151 | ||
152 | if(hd_attr_uint(attr = hd_read_single_sysfs_attribute(sf_dev->path, "vendor"), &ul0, 0)) { | |
153 | ADD2LOG(" vendor = 0x%x\n", (unsigned) ul0); | |
154 | pci->vend = ul0 & 0xffff; | |
155 | } | |
156 | sysfs_close_attribute(attr); | |
157 | ||
158 | if(hd_attr_uint(attr = hd_read_single_sysfs_attribute(sf_dev->path, "device"), &ul0, 0)) { | |
159 | ADD2LOG(" device = 0x%x\n", (unsigned) ul0); | |
160 | pci->dev = ul0 & 0xffff; | |
161 | } | |
162 | sysfs_close_attribute(attr); | |
163 | ||
164 | if(hd_attr_uint(attr = hd_read_single_sysfs_attribute(sf_dev->path, "subsystem_vendor"), &ul0, 0)) { | |
165 | ADD2LOG(" subvendor = 0x%x\n", (unsigned) ul0); | |
166 | pci->sub_vend = ul0 & 0xffff; | |
167 | } | |
168 | sysfs_close_attribute(attr); | |
169 | ||
170 | if(hd_attr_uint(attr = hd_read_single_sysfs_attribute(sf_dev->path, "subsystem_device"), &ul0, 0)) { | |
171 | ADD2LOG(" subdevice = 0x%x\n", (unsigned) ul0); | |
172 | pci->sub_dev = ul0 & 0xffff; | |
173 | } | |
174 | sysfs_close_attribute(attr); | |
175 | ||
176 | if(hd_attr_uint(attr = hd_read_single_sysfs_attribute(sf_dev->path, "irq"), &ul0, 0)) { | |
177 | ADD2LOG(" irq = %d\n", (unsigned) ul0); | |
178 | pci->irq = ul0; | |
179 | } | |
180 | sysfs_close_attribute(attr); | |
181 | ||
182 | sl = hd_attr_list(attr = hd_read_single_sysfs_attribute(sf_dev->path, "resource")); | |
183 | for(u = 0; sl; sl = sl->next, u++) { | |
184 | if( | |
185 | sscanf(sl->str, "0x%"SCNx64" 0x%"SCNx64" 0x%"SCNx64, &ul0, &ul1, &ul2) == 3 && | |
186 | ul1 && | |
187 | u < sizeof pci->base_addr / sizeof *pci->base_addr | |
188 | ) { | |
189 | ADD2LOG(" res[%u] = 0x%"PRIx64" 0x%"PRIx64" 0x%"PRIx64"\n", u, ul0, ul1, ul2); | |
190 | pci->base_addr[u] = ul0; | |
191 | pci->base_len[u] = ul1 + 1 - ul0; | |
192 | pci->addr_flags[u] = ul2; | |
193 | } | |
194 | } | |
195 | sysfs_close_attribute(attr); | |
196 | ||
197 | s = NULL; | |
198 | str_printf(&s, 0, "%s/config", sf_dev->path); | |
199 | if((fd = open(s, O_RDONLY)) != -1) { | |
200 | pci->data_len = pci->data_ext_len = read(fd, pci->data, 0x40); | |
201 | ADD2LOG(" config[%u]\n", pci->data_len); | |
202 | ||
203 | if(pci->data_len >= 0x40) { | |
204 | pci->hdr_type = pci->data[PCI_HEADER_TYPE] & 0x7f; | |
205 | pci->cmd = pci->data[PCI_COMMAND] + (pci->data[PCI_COMMAND + 1] << 8); | |
206 | ||
207 | if(pci->hdr_type == 1 || pci->hdr_type == 2) { /* PCI or CB bridge */ | |
208 | pci->secondary_bus = pci->data[PCI_SECONDARY_BUS]; | |
209 | /* PCI_SECONDARY_BUS == PCI_CB_CARD_BUS */ | |
210 | } | |
211 | ||
212 | for(u = 0; u < sizeof pci->base_addr / sizeof *pci->base_addr; u++) { | |
213 | if((pci->addr_flags[u] & IORESOURCE_IO)) { | |
214 | if(!(pci->cmd & PCI_COMMAND_IO)) pci->addr_flags[u] |= IORESOURCE_DISABLED; | |
215 | } | |
216 | ||
217 | if((pci->addr_flags[u] & IORESOURCE_MEM)) { | |
218 | if(!(pci->cmd & PCI_COMMAND_MEMORY)) pci->addr_flags[u] |= IORESOURCE_DISABLED; | |
219 | } | |
220 | } | |
221 | ||
222 | /* let's get through the capability list */ | |
223 | if( | |
224 | pci->hdr_type == PCI_HEADER_TYPE_NORMAL && | |
225 | (nxt = pci->data[PCI_CAPABILITY_LIST]) | |
226 | ) { | |
227 | /* | |
228 | * Cut out after 16 capabilities to avoid infinite recursion due | |
229 | * to (potentially) malformed data. 16 is more or less | |
230 | * arbitrary, though (the capabilities are bits in a byte, so 8 entries | |
231 | * should suffice). | |
232 | */ | |
233 | for(u = 0; u < 16 && nxt && nxt <= 0xfe; u++) { | |
234 | switch(pci_cfg_byte(pci, fd, nxt)) { | |
235 | case PCI_CAP_ID_PM: | |
236 | pci->flags |= (1 << pci_flag_pm); | |
237 | break; | |
238 | ||
239 | case PCI_CAP_ID_AGP: | |
240 | pci->flags |= (1 << pci_flag_agp); | |
241 | break; | |
242 | } | |
243 | nxt = pci_cfg_byte(pci, fd, nxt + 1); | |
244 | } | |
245 | } | |
246 | } | |
247 | ||
248 | close(fd); | |
249 | } | |
250 | s = free_mem(s); | |
251 | ||
252 | pci->rev = pci->data[PCI_REVISION_ID]; | |
253 | ||
254 | if((pci->addr_flags[6] & IORESOURCE_MEM)) { | |
255 | if(!(pci->data[PCI_ROM_ADDRESS] & PCI_ROM_ADDRESS_ENABLE)) { | |
256 | pci->addr_flags[6] |= IORESOURCE_DISABLED; | |
257 | } | |
258 | } | |
259 | ||
260 | pci->flags |= (1 << pci_flag_ok); | |
261 | } | |
262 | ||
263 | sysfs_close_bus(sf_bus); | |
264 | } | |
265 | ||
266 | ||
267 | void add_pci_data(hd_data_t *hd_data) | |
268 | { | |
269 | hd_t *hd, *hd2; | |
270 | pci_t *pci, *pnext; | |
271 | hd_res_t *res; | |
272 | unsigned u; | |
273 | char *s, *t; | |
274 | ||
275 | PROGRESS(4, 0, "build list"); | |
276 | ||
277 | for(pci = hd_data->pci; pci; pci = pnext) { | |
278 | pnext = pci->next; | |
279 | hd = add_hd_entry(hd_data, __LINE__, 0); | |
280 | ||
281 | hd->sysfs_id = new_str(hd_sysfs_id(pci->sysfs_id)); | |
282 | s = hd_sysfs_find_driver(hd_data, hd->sysfs_id, 1); | |
283 | if(s) add_str_list(&hd->drivers, s); | |
284 | ||
285 | if(pci->sysfs_bus_id && *pci->sysfs_bus_id) { | |
286 | hd->sysfs_bus_id = new_str(pci->sysfs_bus_id); | |
287 | } | |
288 | ||
289 | hd->bus.id = bus_pci; | |
290 | hd->slot = pci->slot + (pci->bus << 8); | |
291 | hd->func = pci->func; | |
292 | hd->base_class.id = pci->base_class; | |
293 | hd->sub_class.id = pci->sub_class; | |
294 | hd->prog_if.id = pci->prog_if; | |
295 | ||
296 | /* fix up old VGA's entries */ | |
297 | if(hd->base_class.id == bc_none && hd->sub_class.id == 0x01) { | |
298 | hd->base_class.id = bc_display; | |
299 | hd->sub_class.id = sc_dis_vga; | |
300 | } | |
301 | ||
302 | if(pci->dev || pci->vend) { | |
303 | hd->device.id = MAKE_ID(TAG_PCI, pci->dev); | |
304 | hd->vendor.id = MAKE_ID(TAG_PCI, pci->vend); | |
305 | } | |
306 | if(pci->sub_dev || pci->sub_vend) { | |
307 | hd->sub_device.id = MAKE_ID(TAG_PCI, pci->sub_dev); | |
308 | hd->sub_vendor.id = MAKE_ID(TAG_PCI, pci->sub_vend); | |
309 | } | |
310 | hd->revision.id = pci->rev; | |
311 | ||
312 | if((u = device_class(hd_data, hd->vendor.id, hd->device.id))) { | |
313 | hd->base_class.id = u >> 8; | |
314 | hd->sub_class.id = u & 0xff; | |
315 | } | |
316 | ||
317 | for(u = 0; u < sizeof pci->base_addr / sizeof *pci->base_addr; u++) { | |
318 | if((pci->addr_flags[u] & IORESOURCE_IO)) { | |
319 | res = new_mem(sizeof *res); | |
320 | res->io.type = res_io; | |
321 | res->io.enabled = pci->addr_flags[u] & IORESOURCE_DISABLED ? 0 : 1; | |
322 | res->io.base = pci->base_addr[u]; | |
323 | res->io.range = pci->base_len[u]; | |
324 | res->io.access = pci->addr_flags[u] & IORESOURCE_READONLY ? acc_ro : acc_rw; | |
325 | add_res_entry(&hd->res, res); | |
326 | } | |
327 | ||
328 | if((pci->addr_flags[u] & IORESOURCE_MEM)) { | |
329 | res = new_mem(sizeof *res); | |
330 | res->mem.type = res_mem; | |
331 | res->mem.enabled = pci->addr_flags[u] & IORESOURCE_DISABLED ? 0 : 1; | |
332 | res->mem.base = pci->base_addr[u]; | |
333 | res->mem.range = pci->base_len[u]; | |
334 | res->mem.access = pci->addr_flags[u] & IORESOURCE_READONLY ? acc_ro : acc_rw; | |
335 | res->mem.prefetch = pci->addr_flags[u] & IORESOURCE_PREFETCH ? flag_yes : flag_no; | |
336 | add_res_entry(&hd->res, res); | |
337 | } | |
338 | } | |
339 | ||
340 | if(pci->irq) { | |
341 | res = new_mem(sizeof *res); | |
342 | res->irq.type = res_irq; | |
343 | res->irq.enabled = 1; | |
344 | res->irq.base = pci->irq; | |
345 | add_res_entry(&hd->res, res); | |
346 | } | |
347 | ||
348 | hd->detail = new_mem(sizeof *hd->detail); | |
349 | hd->detail->type = hd_detail_pci; | |
350 | hd->detail->pci.data = pci; | |
351 | if(pci->flags & (1 << pci_flag_agp)) hd->is.agp = 1; | |
352 | pci->next = NULL; | |
353 | } | |
354 | hd_data->pci = NULL; | |
355 | ||
356 | for(hd = hd_data->hd; hd; hd = hd->next) { | |
357 | if(hd->bus.id == bus_pci && hd->sysfs_id) { | |
358 | s = new_str(hd->sysfs_id); | |
359 | ||
360 | if((t = strrchr(s, '/'))) { | |
361 | *t = 0; | |
362 | if((hd2 = hd_find_sysfs_id(hd_data, s))) { | |
363 | hd->attached_to = hd2->idx; | |
364 | } | |
365 | } | |
366 | free_mem(s); | |
367 | } | |
368 | } | |
369 | ||
370 | add_driver_info(hd_data); | |
371 | } | |
372 | ||
373 | ||
374 | /* | |
375 | * Add driver info in some special cases. | |
376 | */ | |
377 | void add_driver_info(hd_data_t *hd_data) | |
378 | { | |
379 | hd_t *hd; | |
380 | hd_res_t *res; | |
381 | ||
382 | for(hd = hd_data->hd; hd; hd = hd->next) { | |
383 | if(hd->bus.id != bus_pci) continue; | |
384 | ||
385 | if( | |
386 | ( | |
387 | hd->base_class.id == bc_serial && | |
388 | hd->sub_class.id == sc_ser_fire | |
389 | ) || | |
390 | ( | |
391 | hd->base_class.id == bc_serial && | |
392 | hd->sub_class.id == sc_ser_usb | |
393 | ) | |
394 | ) { | |
395 | for(res = hd->res; res; res = res->next) { | |
396 | if(res->any.type == res_irq) break; | |
397 | } | |
398 | if(!res) hd->is.notready = 1; | |
399 | continue; | |
400 | } | |
401 | } | |
402 | } | |
403 | ||
404 | ||
405 | #if 1 | |
406 | /* | |
407 | * Store a raw PCI entry; just for convenience. | |
408 | */ | |
409 | pci_t *add_pci_entry(hd_data_t *hd_data, pci_t *new_pci) | |
410 | { | |
411 | pci_t **pci = &hd_data->pci; | |
412 | ||
413 | while(*pci) pci = &(*pci)->next; | |
414 | ||
415 | return *pci = new_pci; | |
416 | } | |
417 | ||
418 | #else | |
419 | ||
420 | /* | |
421 | * Store a raw PCI entry; just for convenience. | |
422 | * | |
423 | * Reverse order. | |
424 | */ | |
425 | pci_t *add_pci_entry(hd_data_t *hd_data, pci_t *new_pci) | |
426 | { | |
427 | new_pci->next = hd_data->pci; | |
428 | ||
429 | return hd_data->pci = new_pci; | |
430 | } | |
431 | #endif | |
432 | ||
433 | ||
434 | /* | |
435 | * get a byte from pci config space | |
436 | */ | |
437 | unsigned char pci_cfg_byte(pci_t *pci, int fd, unsigned idx) | |
438 | { | |
439 | unsigned char uc; | |
440 | ||
441 | if(idx >= sizeof pci->data) return 0; | |
442 | if(idx < pci->data_len) return pci->data[idx]; | |
443 | if(idx < pci->data_ext_len && pci->data[idx]) return pci->data[idx]; | |
444 | if(lseek(fd, idx, SEEK_SET) != (off_t) idx) return 0; | |
445 | if(read(fd, &uc, 1) != 1) return 0; | |
446 | pci->data[idx] = uc; | |
447 | ||
448 | if(idx >= pci->data_ext_len) pci->data_ext_len = idx + 1; | |
449 | ||
450 | return uc; | |
451 | } | |
452 | /* | |
453 | * Add a dump of all raw PCI data to the global log. | |
454 | */ | |
455 | void dump_pci_data(hd_data_t *hd_data) | |
456 | { | |
457 | pci_t *pci; | |
458 | char *s = NULL; | |
459 | char buf[32]; | |
460 | int i, j; | |
461 | ||
462 | ADD2LOG("---------- PCI raw data ----------\n"); | |
463 | ||
464 | for(pci = hd_data->pci; pci; pci = pci->next) { | |
465 | ||
466 | if(!(pci->flags & (1 << pci_flag_ok))) str_printf(&s, -1, "oops"); | |
467 | if(pci->flags & (1 << pci_flag_pm)) str_printf(&s, -1, ",pm"); | |
468 | if(pci->flags & (1 << pci_flag_agp)) str_printf(&s, -1, ",agp"); | |
469 | if(!s) str_printf(&s, 0, "%s", ""); | |
470 | ||
471 | *buf = 0; | |
472 | if(pci->secondary_bus) { | |
473 | sprintf(buf, "->%02x", pci->secondary_bus); | |
474 | } | |
475 | ||
476 | ADD2LOG( | |
477 | "bus %02x%s, slot %02x, func %x, vend:dev:s_vend:s_dev:rev %04x:%04x:%04x:%04x:%02x\n", | |
478 | pci->bus, buf, pci->slot, pci->func, pci->vend, pci->dev, pci->sub_vend, pci->sub_dev, pci->rev | |
479 | ); | |
480 | ADD2LOG( | |
481 | "class %02x, sub_class %02x prog_if %02x, hdr %x, flags <%s>, irq %u\n", | |
482 | pci->base_class, pci->sub_class, pci->prog_if, pci->hdr_type, *s == ',' ? s + 1 : s, pci->irq | |
483 | ); | |
484 | ||
485 | s = free_mem(s); | |
486 | ||
487 | for(i = 0; i < 6; i++) { | |
488 | if(pci->base_addr[i] || pci->base_len[i]) | |
489 | ADD2LOG(" addr%d %08"PRIx64", size %08"PRIx64"\n", i, pci->base_addr[i], pci->base_len[i]); | |
490 | } | |
491 | if(pci->rom_base_addr) | |
492 | ADD2LOG(" rom %08"PRIx64"\n", pci->rom_base_addr); | |
493 | ||
494 | if(pci->log) ADD2LOG("%s", pci->log); | |
495 | ||
496 | for(i = 0; (unsigned) i < pci->data_ext_len; i += 0x10) { | |
497 | ADD2LOG(" %02x: ", i); | |
498 | j = pci->data_ext_len - i; | |
499 | hexdump(&hd_data->log, 1, j > 0x10 ? 0x10 : j, pci->data + i); | |
500 | ADD2LOG("\n"); | |
501 | } | |
502 | ||
503 | if(pci->next) ADD2LOG("\n"); | |
504 | } | |
505 | ||
506 | ADD2LOG("---------- PCI raw data end ----------\n"); | |
507 | } | |
508 | ||
509 | ||
510 | /* | |
511 | * Parse attribute and return integer value. | |
512 | */ | |
513 | int hd_attr_uint(struct sysfs_attribute *attr, uint64_t *u, int base) | |
514 | { | |
515 | char *s; | |
516 | uint64_t u2; | |
517 | int ok; | |
518 | ||
519 | if(!(s = hd_attr_str(attr))) return 0; | |
520 | ||
521 | u2 = strtoull(s, &s, base); | |
522 | ok = !*s || isspace(*s) ? 1 : 0; | |
523 | ||
524 | if(ok && u) *u = u2; | |
525 | ||
526 | return ok; | |
527 | } | |
528 | ||
529 | ||
530 | /* | |
531 | * Return attribute as string list. | |
532 | */ | |
533 | str_list_t *hd_attr_list(struct sysfs_attribute *attr) | |
534 | { | |
535 | static str_list_t *sl = NULL; | |
536 | ||
537 | free_str_list(sl); | |
538 | ||
539 | return sl = hd_split('\n', hd_attr_str(attr)); | |
540 | } | |
541 | ||
542 | ||
543 | /* | |
544 | * Return attribute as string. | |
545 | */ | |
546 | char *hd_attr_str(struct sysfs_attribute *attr) | |
547 | { | |
548 | return attr ? attr->value : NULL; | |
549 | } | |
550 | ||
551 | ||
552 | /* | |
553 | * Remove leading "/sys" from path. | |
554 | */ | |
555 | char *hd_sysfs_id(char *path) | |
556 | { | |
557 | if(!path || !*path) return NULL; | |
558 | ||
559 | return strchr(path + 1, '/'); | |
560 | } | |
561 | ||
562 | ||
563 | /* | |
564 | * Convert '!' to '/'. | |
565 | */ | |
566 | char *hd_sysfs_name2_dev(char *str) | |
567 | { | |
568 | static char *s = NULL; | |
569 | ||
570 | if(!str) return NULL; | |
571 | ||
572 | free_mem(s); | |
573 | s = str = new_str(str); | |
574 | ||
575 | while(*str) { | |
576 | if(*str == '!') *str = '/'; | |
577 | str++; | |
578 | } | |
579 | ||
580 | return s; | |
581 | } | |
582 | ||
583 | ||
584 | /* | |
585 | * Convert '/' to '!'. | |
586 | */ | |
587 | char *hd_sysfs_dev2_name(char *str) | |
588 | { | |
589 | static char *s = NULL; | |
590 | ||
591 | if(!str) return NULL; | |
592 | ||
593 | free_mem(s); | |
594 | s = str = new_str(str); | |
595 | ||
596 | while(*str) { | |
597 | if(*str == '/') *str = '!'; | |
598 | str++; | |
599 | } | |
600 | ||
601 | return s; | |
602 | } | |
603 | ||
604 |