xref: /pciutils/lib/rt-thread-smart-dm.c (revision 021d41cf)
1 /*
2  *  The PCI Library -- Direct Configuration access via RT-Thread Smart DM Ports
3  *
4  *  Copyright (c) 2024 RT-Thread Development Team
5  *
6  *  Can be freely distributed and used under the terms of the GNU GPL v2+.
7  *
8  *  SPDX-License-Identifier: GPL-2.0-or-later
9  */
10 
11 #include <ctype.h>
12 #include <errno.h>
13 #include <fcntl.h>
14 #include <stdio.h>
15 #include <string.h>
16 #include <unistd.h>
17 #include <stdint.h>
18 
19 #include "internal.h"
20 
21 static void
rt_thread_smart_dm_config(struct pci_access * a)22 rt_thread_smart_dm_config(struct pci_access *a)
23 {
24   pci_define_param(a, "rt-thread-smart-dm.path", PCI_PATH_RT_THREAD_SMART_DM,
25     "Path to the RT-Thread Smart PCI device");
26 }
27 
28 static int
rt_thread_smart_dm_detect(struct pci_access * a)29 rt_thread_smart_dm_detect(struct pci_access *a)
30 {
31   char *name = pci_get_param(a, "rt-thread-smart-dm.path");
32 
33   if (access(name, R_OK))
34     {
35       a->warning("Cannot open %s", name);
36       return 0;
37     }
38 
39   a->debug("...using %s", name);
40   return 1;
41 }
42 
43 static void
rt_thread_smart_dm_init(struct pci_access * a)44 rt_thread_smart_dm_init(struct pci_access *a)
45 {
46   a->fd = -1;
47 }
48 
49 static void
rt_thread_smart_dm_cleanup(struct pci_access * a UNUSED)50 rt_thread_smart_dm_cleanup(struct pci_access *a UNUSED)
51 {
52   if (a->fd >= 0)
53     {
54       close(a->fd);
55       a->fd = -1;
56     }
57 }
58 
59 static void
rt_thread_smart_dm_scan(struct pci_access * a)60 rt_thread_smart_dm_scan(struct pci_access *a)
61 {
62   FILE *f;
63   char buf[512];
64 
65   if (snprintf(buf, sizeof(buf), "%s/devices", pci_get_param(a, "rt-thread-smart-dm.path")) == sizeof(buf))
66     a->error("File name too long");
67 
68   f = fopen(buf, "r");
69   if (!f)
70     a->error("Cannot open %s", buf);
71 
72   while (fgets(buf, sizeof(buf)-1, f))
73     {
74       struct pci_dev *d = pci_alloc_dev(a);
75       unsigned int dfn, vend, cnt, known;
76       char *driver;
77       int offset;
78 
79 #define F " %016" PCI_U64_FMT_X
80       cnt = sscanf(buf, "%x %x %x" F F F F F F F F F F F F F F "%n",
81              &dfn,
82              &vend,
83              &d->irq,
84              &d->base_addr[0],
85              &d->base_addr[1],
86              &d->base_addr[2],
87              &d->base_addr[3],
88              &d->base_addr[4],
89              &d->base_addr[5],
90              &d->rom_base_addr,
91              &d->size[0],
92              &d->size[1],
93              &d->size[2],
94              &d->size[3],
95              &d->size[4],
96              &d->size[5],
97              &d->rom_size,
98              &offset);
99 #undef F
100       if (cnt != 9 && cnt != 10 && cnt != 17)
101         a->error("proc: parse error (read only %d items)", cnt);
102       d->bus = dfn >> 8U;
103       d->dev = PCI_SLOT(dfn & 0xff);
104       d->func = PCI_FUNC(dfn & 0xff);
105       d->vendor_id = vend >> 16U;
106       d->device_id = vend & 0xffff;
107       known = 0;
108       if (!a->buscentric)
109         {
110           known |= PCI_FILL_IDENT | PCI_FILL_IRQ | PCI_FILL_BASES;
111           if (cnt >= 10)
112             known |= PCI_FILL_ROM_BASE;
113           if (cnt >= 17)
114             known |= PCI_FILL_SIZES;
115         }
116       if (cnt >= 17)
117         {
118           while (buf[offset] && isspace(buf[offset]))
119             ++offset;
120           driver = &buf[offset];
121           while (buf[offset] && !isspace(buf[offset]))
122             ++offset;
123           buf[offset] = '\0';
124           if (driver[0])
125             {
126               pci_set_property(d, PCI_FILL_DRIVER, driver);
127               known |= PCI_FILL_DRIVER;
128             }
129         }
130       d->known_fields = known;
131       pci_link_dev(a, d);
132     }
133 
134   fclose(f);
135 }
136 
137 static int
rt_thread_smart_dm_setup(struct pci_dev * d,int rw)138 rt_thread_smart_dm_setup(struct pci_dev *d, int rw)
139 {
140   struct pci_access *a = d->access;
141 
142   if (a->cached_dev != d || a->fd_rw < rw)
143     {
144       char buf[1024];
145       int e;
146       if (a->fd >= 0)
147         close(a->fd);
148       e = snprintf(buf, sizeof(buf), "%s/%04x:%02x:%02x.%u",
149                    pci_get_param(a, "rt-thread-smart-dm.path"),
150                    d->domain, d->bus, d->dev, d->func);
151       if (e < 0 || e >= (int) sizeof(buf))
152         a->error("File name too long");
153       a->fd_rw = a->writeable || rw;
154       a->fd = open(buf, a->fd_rw ? O_RDWR : O_RDONLY);
155       if (a->fd < 0)
156         a->warning("Cannot open %s", buf);
157       a->cached_dev = d;
158     }
159   return a->fd;
160 }
161 
162 static int
rt_thread_smart_dm_read(struct pci_dev * d,int pos,byte * buf,int len)163 rt_thread_smart_dm_read(struct pci_dev *d, int pos, byte *buf, int len)
164 {
165   int fd = rt_thread_smart_dm_setup(d, 0);
166   int res;
167 
168   if (fd < 0)
169     return 0;
170   res = pread(fd, buf, len, pos);
171   if (res < 0)
172     {
173       d->access->warning("%s: read failed: %s", __func__, strerror(errno));
174       return 0;
175     }
176   else if (res != len)
177     return 0;
178   return 1;
179 }
180 
181 static int
rt_thread_smart_dm_write(struct pci_dev * d,int pos,byte * buf,int len)182 rt_thread_smart_dm_write(struct pci_dev *d, int pos, byte *buf, int len)
183 {
184   int fd = rt_thread_smart_dm_setup(d, 1);
185   int res;
186 
187   if (fd < 0)
188     return 0;
189   res = pwrite(fd, buf, len, pos);
190   if (res < 0)
191     {
192       d->access->warning("%s: write failed: %s", __func__, strerror(errno));
193       return 0;
194     }
195   else if (res != len)
196     {
197       d->access->warning("%s: tried to write %d bytes at %d, but only %d succeeded", __func__, len, pos, res);
198       return 0;
199     }
200   return 1;
201 }
202 
203 static void
rt_thread_smart_dm_cleanup_dev(struct pci_dev * d)204 rt_thread_smart_dm_cleanup_dev(struct pci_dev *d)
205 {
206   if (d->access->cached_dev == d)
207     d->access->cached_dev = NULL;
208 }
209 
210 struct pci_methods pm_rt_thread_smart_dm = {
211   .name = "rt-thread-smart-dm",
212   .help = "RT-Thrad Smart /proc/pci device",
213   .config = rt_thread_smart_dm_config,
214   .detect = rt_thread_smart_dm_detect,
215   .init = rt_thread_smart_dm_init,
216   .cleanup = rt_thread_smart_dm_cleanup,
217   .scan = rt_thread_smart_dm_scan,
218   .fill_info = pci_generic_fill_info,
219   .read = rt_thread_smart_dm_read,
220   .write = rt_thread_smart_dm_write,
221   .cleanup_dev = rt_thread_smart_dm_cleanup_dev,
222 };
223