xref: /pciutils/lib/proc.c (revision 1660c737)
1727ce158SMartin Mares /*
2727ce158SMartin Mares  *	The PCI Library -- Configuration Access via /proc/bus/pci
3727ce158SMartin Mares  *
430e9f21eSMartin Mares  *	Copyright (c) 1997--2023 Martin Mares <[email protected]>
5727ce158SMartin Mares  *
661829219SMartin Mares  *	Can be freely distributed and used under the terms of the GNU GPL v2+.
761829219SMartin Mares  *
861829219SMartin Mares  *	SPDX-License-Identifier: GPL-2.0-or-later
9727ce158SMartin Mares  */
10727ce158SMartin Mares 
11727ce158SMartin Mares #define _GNU_SOURCE
12727ce158SMartin Mares 
13727ce158SMartin Mares #include <stdio.h>
14727ce158SMartin Mares #include <string.h>
15727ce158SMartin Mares #include <unistd.h>
161579c198SPali Rohár #include <ctype.h>
17727ce158SMartin Mares #include <errno.h>
18727ce158SMartin Mares #include <fcntl.h>
19727ce158SMartin Mares #include <sys/types.h>
20727ce158SMartin Mares 
21727ce158SMartin Mares #include "internal.h"
22727ce158SMartin Mares 
23727ce158SMartin Mares static void
proc_config(struct pci_access * a)24727ce158SMartin Mares proc_config(struct pci_access *a)
25727ce158SMartin Mares {
26cb6ee324SMartin Mares   pci_define_param(a, "proc.path", PCI_PATH_PROC_BUS_PCI, "Path to the procfs bus tree");
27727ce158SMartin Mares }
28727ce158SMartin Mares 
29727ce158SMartin Mares static int
proc_detect(struct pci_access * a)30727ce158SMartin Mares proc_detect(struct pci_access *a)
31727ce158SMartin Mares {
32cb6ee324SMartin Mares   char *name = pci_get_param(a, "proc.path");
33727ce158SMartin Mares 
34727ce158SMartin Mares   if (access(name, R_OK))
35727ce158SMartin Mares     {
36727ce158SMartin Mares       a->warning("Cannot open %s", name);
37727ce158SMartin Mares       return 0;
38727ce158SMartin Mares     }
39727ce158SMartin Mares   a->debug("...using %s", name);
40727ce158SMartin Mares   return 1;
41727ce158SMartin Mares }
42727ce158SMartin Mares 
43727ce158SMartin Mares static void
proc_init(struct pci_access * a)44727ce158SMartin Mares proc_init(struct pci_access *a)
45727ce158SMartin Mares {
46727ce158SMartin Mares   a->fd = -1;
47727ce158SMartin Mares }
48727ce158SMartin Mares 
49727ce158SMartin Mares static void
proc_cleanup(struct pci_access * a)50727ce158SMartin Mares proc_cleanup(struct pci_access *a)
51727ce158SMartin Mares {
52727ce158SMartin Mares   if (a->fd >= 0)
53727ce158SMartin Mares     {
54727ce158SMartin Mares       close(a->fd);
55727ce158SMartin Mares       a->fd = -1;
56727ce158SMartin Mares     }
57727ce158SMartin Mares }
58727ce158SMartin Mares 
59727ce158SMartin Mares static void
proc_scan(struct pci_access * a)60727ce158SMartin Mares proc_scan(struct pci_access *a)
61727ce158SMartin Mares {
62727ce158SMartin Mares   FILE *f;
637f5afd83SEddie C. Dost   char buf[512];
64727ce158SMartin Mares 
65cb6ee324SMartin Mares   if (snprintf(buf, sizeof(buf), "%s/devices", pci_get_param(a, "proc.path")) == sizeof(buf))
66727ce158SMartin Mares     a->error("File name too long");
67727ce158SMartin Mares   f = fopen(buf, "r");
68727ce158SMartin Mares   if (!f)
69727ce158SMartin Mares     a->error("Cannot open %s", buf);
70727ce158SMartin Mares   while (fgets(buf, sizeof(buf)-1, f))
71727ce158SMartin Mares     {
72727ce158SMartin Mares       struct pci_dev *d = pci_alloc_dev(a);
73e95c8373SMartin Mares       unsigned int dfn, vend, cnt, known;
741579c198SPali Rohár       char *driver;
751579c198SPali Rohár       int offset;
76727ce158SMartin Mares 
779739916eSMartin Mares #define F " " PCIADDR_T_FMT
781579c198SPali Rohár       cnt = sscanf(buf, "%x %x %x" F F F F F F F F F F F F F F "%n",
79727ce158SMartin Mares 	     &dfn,
80727ce158SMartin Mares 	     &vend,
81727ce158SMartin Mares 	     &d->irq,
82727ce158SMartin Mares 	     &d->base_addr[0],
83727ce158SMartin Mares 	     &d->base_addr[1],
84727ce158SMartin Mares 	     &d->base_addr[2],
85727ce158SMartin Mares 	     &d->base_addr[3],
86727ce158SMartin Mares 	     &d->base_addr[4],
87727ce158SMartin Mares 	     &d->base_addr[5],
88e95c8373SMartin Mares 	     &d->rom_base_addr,
89e95c8373SMartin Mares 	     &d->size[0],
90e95c8373SMartin Mares 	     &d->size[1],
91e95c8373SMartin Mares 	     &d->size[2],
92e95c8373SMartin Mares 	     &d->size[3],
93e95c8373SMartin Mares 	     &d->size[4],
94e95c8373SMartin Mares 	     &d->size[5],
951579c198SPali Rohár 	     &d->rom_size,
961579c198SPali Rohár 	     &offset);
979739916eSMartin Mares #undef F
98e95c8373SMartin Mares       if (cnt != 9 && cnt != 10 && cnt != 17)
99e95c8373SMartin Mares 	a->error("proc: parse error (read only %d items)", cnt);
100727ce158SMartin Mares       d->bus = dfn >> 8U;
101727ce158SMartin Mares       d->dev = PCI_SLOT(dfn & 0xff);
102727ce158SMartin Mares       d->func = PCI_FUNC(dfn & 0xff);
103727ce158SMartin Mares       d->vendor_id = vend >> 16U;
104727ce158SMartin Mares       d->device_id = vend & 0xffff;
105caeac5c3SYu Zhao       known = 0;
106e95c8373SMartin Mares       if (!a->buscentric)
107e95c8373SMartin Mares 	{
108caeac5c3SYu Zhao 	  known |= PCI_FILL_IDENT | PCI_FILL_IRQ | PCI_FILL_BASES;
109e95c8373SMartin Mares 	  if (cnt >= 10)
110e95c8373SMartin Mares 	    known |= PCI_FILL_ROM_BASE;
111e95c8373SMartin Mares 	  if (cnt >= 17)
112e95c8373SMartin Mares 	    known |= PCI_FILL_SIZES;
113e95c8373SMartin Mares 	}
1141579c198SPali Rohár       if (cnt >= 17)
1151579c198SPali Rohár         {
1161579c198SPali Rohár           while (buf[offset] && isspace(buf[offset]))
1171579c198SPali Rohár             ++offset;
1181579c198SPali Rohár           driver = &buf[offset];
1191579c198SPali Rohár           while (buf[offset] && !isspace(buf[offset]))
1201579c198SPali Rohár             ++offset;
1211579c198SPali Rohár           buf[offset] = '\0';
1221579c198SPali Rohár           if (driver[0])
1231579c198SPali Rohár             {
1241579c198SPali Rohár               pci_set_property(d, PCI_FILL_DRIVER, driver);
1251579c198SPali Rohár               known |= PCI_FILL_DRIVER;
1261579c198SPali Rohár             }
1271579c198SPali Rohár         }
128e95c8373SMartin Mares       d->known_fields = known;
129727ce158SMartin Mares       pci_link_dev(a, d);
130727ce158SMartin Mares     }
131727ce158SMartin Mares   fclose(f);
132727ce158SMartin Mares }
133727ce158SMartin Mares 
134727ce158SMartin Mares static int
proc_setup(struct pci_dev * d,int rw)135727ce158SMartin Mares proc_setup(struct pci_dev *d, int rw)
136727ce158SMartin Mares {
137727ce158SMartin Mares   struct pci_access *a = d->access;
138727ce158SMartin Mares 
139727ce158SMartin Mares   if (a->cached_dev != d || a->fd_rw < rw)
140727ce158SMartin Mares     {
1416aea909aSMartin Mares       char buf[1024];
14211f7b31bSMartin Mares       int e;
143727ce158SMartin Mares       if (a->fd >= 0)
144727ce158SMartin Mares 	close(a->fd);
14511f7b31bSMartin Mares       e = snprintf(buf, sizeof(buf), "%s/%02x/%02x.%d",
146cb6ee324SMartin Mares 		   pci_get_param(a, "proc.path"),
14711f7b31bSMartin Mares 		   d->bus, d->dev, d->func);
14811f7b31bSMartin Mares       if (e < 0 || e >= (int) sizeof(buf))
149727ce158SMartin Mares 	a->error("File name too long");
150727ce158SMartin Mares       a->fd_rw = a->writeable || rw;
151727ce158SMartin Mares       a->fd = open(buf, a->fd_rw ? O_RDWR : O_RDONLY);
152727ce158SMartin Mares       if (a->fd < 0)
1530208ff05SMartin Mares 	{
1540208ff05SMartin Mares 	  e = snprintf(buf, sizeof(buf), "%s/%04x:%02x/%02x.%d",
1550208ff05SMartin Mares 		       pci_get_param(a, "proc.path"),
1560208ff05SMartin Mares 		       d->domain, d->bus, d->dev, d->func);
1570208ff05SMartin Mares 	  if (e < 0 || e >= (int) sizeof(buf))
1580208ff05SMartin Mares 	    a->error("File name too long");
1590208ff05SMartin Mares 	  a->fd = open(buf, a->fd_rw ? O_RDWR : O_RDONLY);
1600208ff05SMartin Mares 	}
1610208ff05SMartin Mares       if (a->fd < 0)
162727ce158SMartin Mares 	a->warning("Cannot open %s", buf);
163727ce158SMartin Mares       a->cached_dev = d;
164727ce158SMartin Mares     }
165727ce158SMartin Mares   return a->fd;
166727ce158SMartin Mares }
167727ce158SMartin Mares 
168727ce158SMartin Mares static int
proc_read(struct pci_dev * d,int pos,byte * buf,int len)169727ce158SMartin Mares proc_read(struct pci_dev *d, int pos, byte *buf, int len)
170727ce158SMartin Mares {
171727ce158SMartin Mares   int fd = proc_setup(d, 0);
172727ce158SMartin Mares   int res;
173727ce158SMartin Mares 
174727ce158SMartin Mares   if (fd < 0)
175727ce158SMartin Mares     return 0;
17630e9f21eSMartin Mares   res = pread(fd, buf, len, pos);
177727ce158SMartin Mares   if (res < 0)
178727ce158SMartin Mares     {
179727ce158SMartin Mares       d->access->warning("proc_read: read failed: %s", strerror(errno));
180727ce158SMartin Mares       return 0;
181727ce158SMartin Mares     }
182727ce158SMartin Mares   else if (res != len)
183ec25b52dSMartin Mares     return 0;
184727ce158SMartin Mares   return 1;
185727ce158SMartin Mares }
186727ce158SMartin Mares 
187727ce158SMartin Mares static int
proc_write(struct pci_dev * d,int pos,byte * buf,int len)188727ce158SMartin Mares proc_write(struct pci_dev *d, int pos, byte *buf, int len)
189727ce158SMartin Mares {
190727ce158SMartin Mares   int fd = proc_setup(d, 1);
191727ce158SMartin Mares   int res;
192727ce158SMartin Mares 
193727ce158SMartin Mares   if (fd < 0)
194727ce158SMartin Mares     return 0;
19530e9f21eSMartin Mares   res = pwrite(fd, buf, len, pos);
196727ce158SMartin Mares   if (res < 0)
197727ce158SMartin Mares     {
198727ce158SMartin Mares       d->access->warning("proc_write: write failed: %s", strerror(errno));
199727ce158SMartin Mares       return 0;
200727ce158SMartin Mares     }
201727ce158SMartin Mares   else if (res != len)
202727ce158SMartin Mares     {
20309817437SMartin Mares       d->access->warning("proc_write: tried to write %d bytes at %d, but only %d succeeded", len, pos, res);
204727ce158SMartin Mares       return 0;
205727ce158SMartin Mares     }
206727ce158SMartin Mares   return 1;
207727ce158SMartin Mares }
208727ce158SMartin Mares 
209727ce158SMartin Mares static void
proc_cleanup_dev(struct pci_dev * d)210727ce158SMartin Mares proc_cleanup_dev(struct pci_dev *d)
211727ce158SMartin Mares {
212727ce158SMartin Mares   if (d->access->cached_dev == d)
213727ce158SMartin Mares     d->access->cached_dev = NULL;
214727ce158SMartin Mares }
215727ce158SMartin Mares 
216727ce158SMartin Mares struct pci_methods pm_linux_proc = {
217*1660c737SMartin Mares   .name = "linux-proc",
218*1660c737SMartin Mares   .help = "The proc file system on Linux",
219*1660c737SMartin Mares   .config = proc_config,
220*1660c737SMartin Mares   .detect = proc_detect,
221*1660c737SMartin Mares   .init = proc_init,
222*1660c737SMartin Mares   .cleanup = proc_cleanup,
223*1660c737SMartin Mares   .scan = proc_scan,
224*1660c737SMartin Mares   .fill_info = pci_generic_fill_info,
225*1660c737SMartin Mares   .read = proc_read,
226*1660c737SMartin Mares   .write = proc_write,
227*1660c737SMartin Mares   .cleanup_dev = proc_cleanup_dev,
228727ce158SMartin Mares };
229