]> git.ipfire.org Git - thirdparty/pciutils.git/blame - lib/sysfs.c
Fix whitespace errors
[thirdparty/pciutils.git] / lib / sysfs.c
CommitLineData
11f7b31b
MM
1/*
2 * The PCI Library -- Configuration Access via /sys/bus/pci
3 *
535fca0e 4 * Copyright (c) 2003 Matthew Wilcox <matthew@wil.cx>
cb6ee324 5 * Copyright (c) 1997--2008 Martin Mares <mj@ucw.cz>
11f7b31b
MM
6 *
7 * Can be freely distributed and used under the terms of the GNU GPL.
8 */
9
10#define _GNU_SOURCE
11
12#include <stdio.h>
13#include <stdlib.h>
14#include <string.h>
15#include <stdarg.h>
16#include <unistd.h>
17#include <errno.h>
18#include <dirent.h>
19#include <fcntl.h>
20#include <sys/types.h>
21
22#include "internal.h"
23#include "pread.h"
24
25static void
26sysfs_config(struct pci_access *a)
27{
cb6ee324 28 pci_define_param(a, "sysfs.path", PCI_PATH_SYS_BUS_PCI, "Path to the sysfs device tree");
11f7b31b
MM
29}
30
31static inline char *
32sysfs_name(struct pci_access *a)
33{
cb6ee324 34 return pci_get_param(a, "sysfs.path");
11f7b31b
MM
35}
36
37static int
38sysfs_detect(struct pci_access *a)
39{
40 if (access(sysfs_name(a), R_OK))
41 {
84c8d1bb 42 a->debug("...cannot open %s", sysfs_name(a));
11f7b31b
MM
43 return 0;
44 }
45 a->debug("...using %s", sysfs_name(a));
46 return 1;
47}
48
49static void
50sysfs_init(struct pci_access *a)
51{
52 a->fd = -1;
07ad085d 53 a->fd_vpd = -1;
11f7b31b
MM
54}
55
56static void
07ad085d 57sysfs_flush_cache(struct pci_access *a)
11f7b31b
MM
58{
59 if (a->fd >= 0)
60 {
61 close(a->fd);
62 a->fd = -1;
63 }
07ad085d
MM
64 if (a->fd_vpd >= 0)
65 {
66 close(a->fd_vpd);
67 a->fd_vpd = -1;
68 }
69 a->cached_dev = NULL;
70}
71
72static void
73sysfs_cleanup(struct pci_access *a)
74{
75 sysfs_flush_cache(a);
11f7b31b
MM
76}
77
78#define OBJNAMELEN 1024
79static void
80sysfs_obj_name(struct pci_dev *d, char *object, char *buf)
81{
82 int n = snprintf(buf, OBJNAMELEN, "%s/devices/%04x:%02x:%02x.%d/%s",
83 sysfs_name(d->access), d->domain, d->bus, d->dev, d->func, object);
84 if (n < 0 || n >= OBJNAMELEN)
85 d->access->error("File name too long");
86}
87
b1861a8d
MM
88#define OBJBUFSIZE 1024
89
11f7b31b 90static int
b1861a8d 91sysfs_get_string(struct pci_dev *d, char *object, char *buf, int mandatory)
11f7b31b
MM
92{
93 struct pci_access *a = d->access;
94 int fd, n;
b1861a8d 95 char namebuf[OBJNAMELEN];
11f7b31b
MM
96
97 sysfs_obj_name(d, object, namebuf);
98 fd = open(namebuf, O_RDONLY);
99 if (fd < 0)
b1861a8d
MM
100 {
101 if (mandatory)
102 a->error("Cannot open %s: %s", namebuf, strerror(errno));
103 return 0;
104 }
105 n = read(fd, buf, OBJBUFSIZE);
11f7b31b
MM
106 close(fd);
107 if (n < 0)
108 a->error("Error reading %s: %s", namebuf, strerror(errno));
b1861a8d 109 if (n >= OBJBUFSIZE)
11f7b31b
MM
110 a->error("Value in %s too long", namebuf);
111 buf[n] = 0;
b1861a8d
MM
112 return 1;
113}
114
115static int
116sysfs_get_value(struct pci_dev *d, char *object)
117{
118 char buf[OBJBUFSIZE];
119
120 sysfs_get_string(d, object, buf, 1);
11f7b31b
MM
121 return strtol(buf, NULL, 0);
122}
123
124static void
125sysfs_get_resources(struct pci_dev *d)
126{
127 struct pci_access *a = d->access;
128 char namebuf[OBJNAMELEN], buf[256];
129 FILE *file;
130 int i;
131
132 sysfs_obj_name(d, "resource", namebuf);
133 file = fopen(namebuf, "r");
134 if (!file)
135 a->error("Cannot open %s: %s", namebuf, strerror(errno));
9bb4b4ea 136 for (i = 0; i < 7; i++)
11f7b31b 137 {
6d143c32 138 unsigned long long start, end, size, flags;
11f7b31b
MM
139 if (!fgets(buf, sizeof(buf), file))
140 break;
6d143c32 141 if (sscanf(buf, "%llx %llx %llx", &start, &end, &flags) != 3)
11f7b31b 142 a->error("Syntax error in %s", namebuf);
00bf6625 143 if (end > start)
11f7b31b
MM
144 size = end - start + 1;
145 else
146 size = 0;
6d143c32 147 flags &= PCI_ADDR_FLAG_MASK;
9bb4b4ea 148 if (i < 6)
11f7b31b 149 {
6d143c32 150 d->base_addr[i] = start | flags;
11f7b31b
MM
151 d->size[i] = size;
152 }
153 else
154 {
6d143c32 155 d->rom_base_addr = start | flags;
11f7b31b
MM
156 d->rom_size = size;
157 }
158 }
159 fclose(file);
160}
161
162static void sysfs_scan(struct pci_access *a)
163{
73e87e96 164 char dirname[1024], buf[OBJBUFSIZE];
11f7b31b
MM
165 DIR *dir;
166 struct dirent *entry;
167 int n;
168
169 n = snprintf(dirname, sizeof(dirname), "%s/devices", sysfs_name(a));
170 if (n < 0 || n >= (int) sizeof(dirname))
171 a->error("Directory name too long");
172 dir = opendir(dirname);
173 if (!dir)
174 a->error("Cannot open %s", dirname);
175 while ((entry = readdir(dir)))
176 {
177 struct pci_dev *d;
178 unsigned int dom, bus, dev, func;
179
180 /* ".", ".." or a special non-device perhaps */
181 if (entry->d_name[0] == '.')
182 continue;
183
184 d = pci_alloc_dev(a);
185 if (sscanf(entry->d_name, "%x:%x:%x.%d", &dom, &bus, &dev, &func) < 4)
186 a->error("sysfs_scan: Couldn't parse entry name %s", entry->d_name);
187 d->domain = dom;
188 d->bus = bus;
189 d->dev = dev;
190 d->func = func;
11f7b31b
MM
191 if (!a->buscentric)
192 {
193 sysfs_get_resources(d);
84c8d1bb 194 d->irq = sysfs_get_value(d, "irq");
84c8d1bb 195 /*
c2b144ef
MM
196 * We could read these faster from the config registers, but we want to give
197 * the kernel a chance to fix up ID's and especially classes of broken devices.
84c8d1bb 198 */
11f7b31b
MM
199 d->vendor_id = sysfs_get_value(d, "vendor");
200 d->device_id = sysfs_get_value(d, "device");
c2b144ef 201 d->device_class = sysfs_get_value(d, "class") >> 8;
aecf5b35
TR
202 if (sysfs_get_string(d, "label", buf, 0))
203 d->label = pci_strdup(d->access, buf);
204
c2b144ef 205 d->known_fields = PCI_FILL_IDENT | PCI_FILL_CLASS | PCI_FILL_IRQ | PCI_FILL_BASES | PCI_FILL_ROM_BASE | PCI_FILL_SIZES;
11f7b31b
MM
206 }
207 pci_link_dev(a, d);
208 }
209 closedir(dir);
210}
211
2849a165 212static void
6171c06d 213sysfs_fill_slots(struct pci_access *a)
2849a165 214{
2849a165
AC
215 char dirname[1024];
216 DIR *dir;
217 struct dirent *entry;
218 int n;
219
220 n = snprintf(dirname, sizeof(dirname), "%s/slots", sysfs_name(a));
221 if (n < 0 || n >= (int) sizeof(dirname))
222 a->error("Directory name too long");
223 dir = opendir(dirname);
224 if (!dir)
6171c06d
MM
225 return;
226
227 while (entry = readdir(dir))
2849a165
AC
228 {
229 char namebuf[OBJNAMELEN], buf[16];
230 FILE *file;
231 unsigned int dom, bus, dev;
4134538a 232 int res = 0;
6171c06d 233 struct pci_dev *d;
2849a165
AC
234
235 /* ".", ".." or a special non-device perhaps */
236 if (entry->d_name[0] == '.')
237 continue;
238
6171c06d 239 n = snprintf(namebuf, OBJNAMELEN, "%s/%s/%s", dirname, entry->d_name, "address");
2849a165 240 if (n < 0 || n >= OBJNAMELEN)
6171c06d 241 a->error("File name too long");
2849a165 242 file = fopen(namebuf, "r");
534fbba9
MW
243 /*
244 * Old versions of Linux had a fakephp which didn't have an 'address'
245 * file. There's no useful information to be gleaned from these
246 * devices, pretend they're not there.
247 */
2849a165 248 if (!file)
534fbba9 249 continue;
6171c06d 250
4134538a
MM
251 if (!fgets(buf, sizeof(buf), file) || (res = sscanf(buf, "%x:%x:%x", &dom, &bus, &dev)) < 3)
252 {
253 /*
254 * In some cases, the slot is not tied to a specific device before
255 * a card gets inserted. This happens for example on IBM pSeries
256 * and we need not warn about it.
257 */
258 if (res != 2)
259 a->warning("sysfs_fill_slots: Couldn't parse entry address %s", buf);
260 }
6171c06d
MM
261 else
262 {
263 for (d = a->devices; d; d = d->next)
264 if (dom == d->domain && bus == d->bus && dev == d->dev && !d->phy_slot)
b1861a8d 265 d->phy_slot = pci_strdup(a, entry->d_name);
2849a165
AC
266 }
267 fclose(file);
268 }
269 closedir(dir);
270}
271
272static int
273sysfs_fill_info(struct pci_dev *d, int flags)
274{
6171c06d
MM
275 if ((flags & PCI_FILL_PHYS_SLOT) && !(d->known_fields & PCI_FILL_PHYS_SLOT))
276 {
277 struct pci_dev *pd;
278 sysfs_fill_slots(d->access);
279 for (pd = d->access->devices; pd; pd = pd->next)
280 pd->known_fields |= PCI_FILL_PHYS_SLOT;
281 }
b1861a8d
MM
282
283 if ((flags & PCI_FILL_MODULE_ALIAS) && !(d->known_fields & PCI_FILL_MODULE_ALIAS))
284 {
285 char buf[OBJBUFSIZE];
286 if (sysfs_get_string(d, "modalias", buf, 0))
287 d->module_alias = pci_strdup(d->access, buf);
288 }
289
6171c06d 290 return pci_generic_fill_info(d, flags);
2849a165
AC
291}
292
9bd5b1cf
BH
293/* Intent of the sysfs_setup() caller */
294enum
295 {
296 SETUP_READ_CONFIG = 0,
297 SETUP_WRITE_CONFIG = 1,
298 SETUP_READ_VPD = 2
299 };
300
11f7b31b 301static int
9bd5b1cf 302sysfs_setup(struct pci_dev *d, int intent)
11f7b31b
MM
303{
304 struct pci_access *a = d->access;
9bd5b1cf 305 char namebuf[OBJNAMELEN];
11f7b31b 306
07ad085d 307 if (a->cached_dev != d || (intent == SETUP_WRITE_CONFIG && !a->fd_rw))
11f7b31b 308 {
07ad085d
MM
309 sysfs_flush_cache(a);
310 a->cached_dev = d;
11f7b31b 311 }
9bd5b1cf 312
07ad085d 313 if (intent == SETUP_READ_VPD)
9bd5b1cf 314 {
07ad085d
MM
315 if (a->fd_vpd < 0)
316 {
317 sysfs_obj_name(d, "vpd", namebuf);
318 a->fd_vpd = open(namebuf, O_RDONLY);
319 /* No warning on error; vpd may be absent or accessible only to root */
320 }
321 return a->fd_vpd;
9bd5b1cf
BH
322 }
323
07ad085d 324 if (a->fd < 0)
c886948c
MM
325 {
326 sysfs_obj_name(d, "config", namebuf);
327 a->fd_rw = a->writeable || intent == SETUP_WRITE_CONFIG;
328 a->fd = open(namebuf, a->fd_rw ? O_RDWR : O_RDONLY);
329 if (a->fd < 0)
330 a->warning("Cannot open %s", namebuf);
331 a->fd_pos = 0;
332 }
07ad085d 333 return a->fd;
11f7b31b
MM
334}
335
336static int sysfs_read(struct pci_dev *d, int pos, byte *buf, int len)
337{
9bd5b1cf 338 int fd = sysfs_setup(d, SETUP_READ_CONFIG);
11f7b31b
MM
339 int res;
340
341 if (fd < 0)
342 return 0;
343 res = do_read(d, fd, buf, len, pos);
344 if (res < 0)
345 {
346 d->access->warning("sysfs_read: read failed: %s", strerror(errno));
347 return 0;
348 }
349 else if (res != len)
09817437 350 return 0;
11f7b31b
MM
351 return 1;
352}
353
354static int sysfs_write(struct pci_dev *d, int pos, byte *buf, int len)
355{
9bd5b1cf 356 int fd = sysfs_setup(d, SETUP_WRITE_CONFIG);
11f7b31b
MM
357 int res;
358
359 if (fd < 0)
360 return 0;
361 res = do_write(d, fd, buf, len, pos);
362 if (res < 0)
363 {
364 d->access->warning("sysfs_write: write failed: %s", strerror(errno));
365 return 0;
366 }
367 else if (res != len)
368 {
09817437 369 d->access->warning("sysfs_write: tried to write %d bytes at %d, but only %d succeeded", len, pos, res);
11f7b31b
MM
370 return 0;
371 }
372 return 1;
373}
374
9bd5b1cf
BH
375#ifdef PCI_HAVE_DO_READ
376
377/* pread() is not available and do_read() only works for a single fd, so we
378 * cannot implement read_vpd properly. */
379static int sysfs_read_vpd(struct pci_dev *d, int pos, byte *buf, int len)
380{
381 return 0;
382}
383
384#else /* !PCI_HAVE_DO_READ */
385
386static int sysfs_read_vpd(struct pci_dev *d, int pos, byte *buf, int len)
387{
388 int fd = sysfs_setup(d, SETUP_READ_VPD);
389 int res;
390
391 if (fd < 0)
392 return 0;
393 res = pread(fd, buf, len, pos);
394 if (res < 0)
395 {
396 d->access->warning("sysfs_read_vpd: read failed: %s", strerror(errno));
397 return 0;
398 }
399 else if (res != len)
400 return 0;
401 return 1;
402}
403
404#endif /* PCI_HAVE_DO_READ */
405
11f7b31b
MM
406static void sysfs_cleanup_dev(struct pci_dev *d)
407{
408 struct pci_access *a = d->access;
409
410 if (a->cached_dev == d)
07ad085d 411 sysfs_flush_cache(a);
11f7b31b
MM
412}
413
414struct pci_methods pm_linux_sysfs = {
9ff67879
MM
415 "linux-sysfs",
416 "The sys filesystem on Linux",
11f7b31b
MM
417 sysfs_config,
418 sysfs_detect,
419 sysfs_init,
420 sysfs_cleanup,
421 sysfs_scan,
2849a165 422 sysfs_fill_info,
11f7b31b
MM
423 sysfs_read,
424 sysfs_write,
9bd5b1cf 425 sysfs_read_vpd,
11f7b31b
MM
426 NULL, /* init_dev */
427 sysfs_cleanup_dev
428};