2 * The PCI Library -- FreeBSD /dev/pci access
4 * Copyright (c) 1999 Jari Kirma <kirma@cs.hut.fi>
5 * Updated in 2003 by Samy Al Bahra <samy@kerneled.com>
6 * Updated in 2017 by Imre Vadász <imrevdsz@gmail.com>
8 * Can be freely distributed and used under the terms of the GNU GPL.
17 #include <osreldate.h>
20 #ifdef __FreeBSD_kernel_version
21 # ifndef __FreeBSD_version
22 # define __FreeBSD_version __FreeBSD_kernel_version
26 #if __FreeBSD_version < 430000 && !defined(__DragonFly__)
27 # include <pci/pcivar.h>
28 # include <pci/pci_ioctl.h>
30 # include <sys/pciio.h>
36 fbsd_config(struct pci_access
*a
)
38 pci_define_param(a
, "fbsd.path", PCI_PATH_FBSD_DEVICE
, "Path to the FreeBSD PCI device");
42 fbsd_detect(struct pci_access
*a
)
44 char *name
= pci_get_param(a
, "fbsd.path");
46 if (access(name
, R_OK
))
48 a
->warning("Cannot open %s", name
);
51 a
->debug("...using %s", name
);
56 fbsd_init(struct pci_access
*a
)
58 char *name
= pci_get_param(a
, "fbsd.path");
64 * When opening /dev/pci as read-write fails, retry with readonly which
65 * will still allow us to gain some information via the PCIOCGETCONF and
66 * PCIOCGETBAR IOCTLs, even without generic read access to the PCI config
69 fd
= open(name
, O_RDWR
, 0);
72 fd
= open(name
, O_RDONLY
, 0);
74 a
->error("fbsd_init: %s open failed", name
);
77 a
->debug("fbsd_init: Fallback to read-only opened %s", name
);
86 fbsd_cleanup(struct pci_access
*a
)
101 fbsd_scan(struct pci_access
*a
)
103 struct pci_conf_io conf
;
104 struct pci_conf
*matches
;
109 matches
= calloc(32, sizeof(struct pci_conf
));
112 a
->error("calloc: %s", strerror(errno
));
119 conf
.pat_buf_len
= 0;
120 conf
.num_patterns
= 0;
121 conf
.patterns
= NULL
;
122 conf
.match_buf_len
= 32 * sizeof(struct pci_conf
);
123 conf
.num_matches
= 32;
124 conf
.matches
= matches
;
125 conf
.offset
= offset
;
127 if (ioctl(a
->fd_rw
>= 0 ? a
->fd_rw
: a
->fd
, PCIOCGETCONF
, &conf
) < 0)
131 a
->error("fbsd_scan: ioctl(PCIOCGETCONF) failed: %s",
134 /* PCI_GETCONF_LIST_CHANGED would require us to start over. */
135 if (conf
.status
== PCI_GETCONF_ERROR
||
136 conf
.status
== PCI_GETCONF_LIST_CHANGED
)
138 a
->error("fbsd_scan: ioctl(PCIOCGETCONF) failed");
141 for (i
= 0; i
< conf
.num_matches
; i
++)
143 t
= pci_alloc_dev(a
);
144 t
->bus
= matches
[i
].pc_sel
.pc_bus
;
145 t
->dev
= matches
[i
].pc_sel
.pc_dev
;
146 t
->dev
= matches
[i
].pc_sel
.pc_dev
;
147 t
->domain
= matches
[i
].pc_sel
.pc_domain
;
148 t
->domain_16
= matches
[i
].pc_sel
.pc_domain
;
149 t
->vendor_id
= matches
[i
].pc_vendor
;
150 t
->device_id
= matches
[i
].pc_device
;
151 t
->known_fields
= PCI_FILL_IDENT
;
152 t
->hdrtype
= matches
[i
].pc_hdr
;
155 offset
+= conf
.num_matches
;
157 while (conf
.status
== PCI_GETCONF_MORE_DEVS
);
163 fbsd_fill_info(struct pci_dev
*d
, int flags
)
165 struct pci_conf_io conf
;
166 struct pci_bar_io bar
;
167 struct pci_match_conf pattern
;
168 struct pci_conf match
;
171 if (d
->access
->fd_rw
>= 0)
172 return pci_generic_fill_info(d
, flags
);
175 * Can only handle PCI_FILL_IDENT, PCI_FILL_CLASS, PCI_FILL_BASES and
176 * PCI_FILL_SIZES requests with the PCIOCGETCONF and PCIOCGETBAR IOCTLs.
179 conf
.pat_buf_len
= sizeof(struct pci_match_conf
);
180 conf
.num_patterns
= 1;
181 conf
.patterns
= &pattern
;
182 conf
.match_buf_len
= sizeof(struct pci_conf
);
183 conf
.num_matches
= 1;
184 conf
.matches
= &match
;
189 pattern
.pc_sel
.pc_domain
= d
->domain
;
190 pattern
.pc_sel
.pc_bus
= d
->bus
;
191 pattern
.pc_sel
.pc_dev
= d
->dev
;
192 pattern
.pc_sel
.pc_func
= d
->func
;
193 pattern
.flags
= PCI_GETCONF_MATCH_DOMAIN
| PCI_GETCONF_MATCH_BUS
|
194 PCI_GETCONF_MATCH_DEV
| PCI_GETCONF_MATCH_FUNC
;
196 if (ioctl(d
->access
->fd
, PCIOCGETCONF
, &conf
) < 0)
200 d
->access
->error("fbsd_fill_info: ioctl(PCIOCGETCONF) failed: %s", strerror(errno
));
203 if (flags
& PCI_FILL_IDENT
)
205 d
->vendor_id
= match
.pc_vendor
;
206 d
->device_id
= match
.pc_device
;
208 if (flags
& PCI_FILL_CLASS
)
210 d
->device_class
= match
.pc_class
| (match
.pc_subclass
<< 8);
212 if (flags
& (PCI_FILL_BASES
| PCI_FILL_SIZES
))
214 d
->rom_base_addr
= 0;
216 for (i
= 0; i
< 6; i
++)
218 bar
.pbi_sel
.pc_domain
= d
->domain
;
219 bar
.pbi_sel
.pc_bus
= d
->bus
;
220 bar
.pbi_sel
.pc_dev
= d
->dev
;
221 bar
.pbi_sel
.pc_func
= d
->func
;
222 bar
.pbi_reg
= 0x10 + 4*i
;
226 if (ioctl(d
->access
->fd
, PCIOCGETBAR
, &bar
) < 0)
236 d
->access
->error("fbsd_fill_info: ioctl(PCIOCGETBAR) failed: %s", strerror(errno
));
240 d
->base_addr
[i
] = bar
.pbi_base
;
241 d
->size
[i
] = bar
.pbi_length
;
246 return flags
& (PCI_FILL_IDENT
| PCI_FILL_CLASS
| PCI_FILL_BASES
|
251 fbsd_read(struct pci_dev
*d
, int pos
, byte
*buf
, int len
)
255 if (d
->access
->fd_rw
< 0)
257 d
->access
->warn("fbsd_read: missing permissions");
261 if (!(len
== 1 || len
== 2 || len
== 4))
262 return pci_generic_block_read(d
, pos
, buf
, len
);
267 #if __FreeBSD_version >= 700053 || defined(__DragonFly__)
268 pi
.pi_sel
.pc_domain
= d
->domain
;
270 pi
.pi_sel
.pc_bus
= d
->bus
;
271 pi
.pi_sel
.pc_dev
= d
->dev
;
272 pi
.pi_sel
.pc_func
= d
->func
;
277 if (ioctl(d
->access
->fd_rw
, PCIOCREAD
, &pi
) < 0)
281 d
->access
->error("fbsd_read: ioctl(PCIOCREAD) failed: %s", strerror(errno
));
287 buf
[0] = (u8
) pi
.pi_data
;
290 ((u16
*) buf
)[0] = cpu_to_le16((u16
) pi
.pi_data
);
293 ((u32
*) buf
)[0] = cpu_to_le32((u32
) pi
.pi_data
);
300 fbsd_write(struct pci_dev
*d
, int pos
, byte
*buf
, int len
)
304 if (d
->access
->fd_rw
< 0)
306 d
->access
->warn("fbsd_write: missing permissions");
310 if (!(len
== 1 || len
== 2 || len
== 4))
311 return pci_generic_block_write(d
, pos
, buf
, len
);
316 #if __FreeBSD_version >= 700053 || defined(__DragonFly__)
317 pi
.pi_sel
.pc_domain
= d
->domain
;
319 pi
.pi_sel
.pc_bus
= d
->bus
;
320 pi
.pi_sel
.pc_dev
= d
->dev
;
321 pi
.pi_sel
.pc_func
= d
->func
;
332 pi
.pi_data
= le16_to_cpu(((u16
*) buf
)[0]);
335 pi
.pi_data
= le32_to_cpu(((u32
*) buf
)[0]);
339 if (ioctl(d
->access
->fd_rw
, PCIOCWRITE
, &pi
) < 0)
343 d
->access
->error("fbsd_write: ioctl(PCIOCWRITE) failed: %s", strerror(errno
));
349 struct pci_methods pm_fbsd_device
= {
351 "FreeBSD /dev/pci device",
362 NULL
/* dev_cleanup */