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