summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJames Cloos <cloos@jhcloos.com>2007-09-03 06:15:58 -0400
committerJames Cloos <cloos@jhcloos.com>2007-09-03 06:15:58 -0400
commit9d1596cba90c8fd273e9d1d5488747cc0f34fdc7 (patch)
tree2e45baf3a3e2edbfe3f9cf469fd0853d2f49fe2c
parentb1e911784d314fdbd8d938e5fe3671bec128fb61 (diff)
parent5cf29b06b9b5806056a0b04160b2286eb4158748 (diff)
Merge branch 'master' of ssh://git.freedesktop.org/git/xorg/lib/libpciaccess
-rw-r--r--configure.ac8
-rw-r--r--include/pciaccess.h60
-rw-r--r--src/Makefile.am5
-rw-r--r--src/common_interface.c199
-rw-r--r--src/common_map.c54
-rw-r--r--src/freebsd_pci.c64
-rw-r--r--src/linux_sysfs.c164
-rw-r--r--src/pciaccess_private.h30
-rw-r--r--src/scanpci.c4
-rw-r--r--src/solx_devfs.c64
10 files changed, 440 insertions, 212 deletions
diff --git a/configure.ac b/configure.ac
index d5c0585..133963d 100644
--- a/configure.ac
+++ b/configure.ac
@@ -41,7 +41,7 @@ dnl refers to ${prefix}. Thus we have to use `eval' twice.
AC_PREREQ([2.57])
-AC_INIT(libpciaccess, 0.8.0, [none yet], libpciaccess)
+AC_INIT(libpciaccess, 0.10.0, [none yet], libpciaccess)
AM_INIT_AUTOMAKE([dist-bzip2])
AM_MAINTAINER_MODE
@@ -87,6 +87,12 @@ AM_CONDITIONAL(LINUX, [test "x$linux" = xyes])
AM_CONDITIONAL(FREEBSD, [test "x$freebsd" = xyes])
AM_CONDITIONAL(SOLARIS, [test "x$solaris" = xyes])
+AC_CHECK_FILE([/usr/include/asm/mtrr.h],
+ [have_mtrr_h="yes"], [have_mtrr_h="no"])
+if test "x$have_mtrr_h" = xyes; then
+ AC_DEFINE(HAVE_MTRR, 1, [Use MTRRs on mappings])
+fi
+
AC_SUBST(PCIACCESS_CFLAGS)
AC_SUBST(PCIACCESS_LIBS)
diff --git a/include/pciaccess.h b/include/pciaccess.h
index 4f383ab..4bd3672 100644
--- a/include/pciaccess.h
+++ b/include/pciaccess.h
@@ -33,6 +33,12 @@
#include <inttypes.h>
+#if __GNUC__ >= 3
+#define __deprecated __attribute__((deprecated))
+#else
+#define __deprecated
+#endif
+
typedef uint64_t pciaddr_t;
struct pci_device;
@@ -42,17 +48,24 @@ struct pci_slot_match;
int pci_device_read_rom(struct pci_device *dev, void *buffer);
-int pci_device_map_region(struct pci_device *dev, unsigned region,
- int write_enable);
+int __deprecated pci_device_map_region(struct pci_device *dev,
+ unsigned region, int write_enable);
-int pci_device_unmap_region(struct pci_device *dev, unsigned region);
+int __deprecated pci_device_unmap_region(struct pci_device *dev,
+ unsigned region);
-int pci_device_map_memory_range(struct pci_device *dev, pciaddr_t base,
- pciaddr_t size, int write_enable, void **addr);
+int pci_device_map_range(struct pci_device *dev, pciaddr_t base,
+ pciaddr_t size, unsigned map_flags, void **addr);
-int pci_device_unmap_memory_range(struct pci_device *dev, void *memory,
+int pci_device_unmap_range(struct pci_device *dev, void *memory,
pciaddr_t size);
+int __deprecated pci_device_map_memory_range(struct pci_device *dev,
+ pciaddr_t base, pciaddr_t size, int write_enable, void **addr);
+
+int __deprecated pci_device_unmap_memory_range(struct pci_device *dev,
+ void *memory, pciaddr_t size);
+
int pci_device_probe(struct pci_device *dev);
const struct pci_agp_info *pci_device_get_agp_info(struct pci_device *dev);
@@ -111,6 +124,16 @@ int pci_device_cfg_write_u32(struct pci_device *dev, uint32_t data,
int pci_device_cfg_write_bits(struct pci_device *dev, uint32_t mask,
uint32_t data, pciaddr_t offset);
+/**
+ * \name Mapping flags passed to \c pci_device_map_range
+ */
+/*@{*/
+#define PCI_DEV_MAP_FLAG_WRITABLE (1U<<0)
+#define PCI_DEV_MAP_FLAG_WRITE_COMBINE (1U<<1)
+#define PCI_DEV_MAP_FLAG_CACHABLE (1U<<2)
+/*@}*/
+
+
#define PCI_MATCH_ANY (~0)
/**
@@ -178,10 +201,35 @@ struct pci_slot_match {
struct pci_mem_region {
/**
* When the region is mapped, this is the pointer to the memory.
+ *
+ * This field is \b only set when the deprecated \c pci_device_map_region
+ * interface is used. Use \c pci_device_map_range instead.
+ *
+ * \deprecated
*/
void *memory;
+
+ /**
+ * Base physical address of the region within its bus / domain.
+ *
+ * \warning
+ * This address is really only useful to other devices in the same
+ * domain. It's probably \b not the address applications will ever
+ * use.
+ *
+ * \warning
+ * Most (all?) platform back-ends leave this field unset.
+ */
pciaddr_t bus_addr;
+
+
+ /**
+ * Base physical address of the region from the CPU's point of view.
+ *
+ * This address is typically passed to \c pci_device_map_range to create
+ * a mapping of the region to the CPU's virtual address space.
+ */
pciaddr_t base_addr;
diff --git a/src/Makefile.am b/src/Makefile.am
index 760711c..e743dbf 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -26,7 +26,7 @@ AM_CFLAGS = @PCIACCESS_CFLAGS@
lib_LTLIBRARIES = libpciaccess.la
if LINUX
-OS_SUPPORT = linux_sysfs.c linux_devmem.c
+OS_SUPPORT = linux_sysfs.c linux_devmem.c linux_devmem.h
endif
if FREEBSD
@@ -43,6 +43,7 @@ libpciaccess_la_SOURCES = common_bridge.c \
common_interface.c \
common_capability.c \
common_device_name.c \
+ common_map.c \
pciaccess_private.h \
$(OS_SUPPORT)
@@ -50,7 +51,7 @@ INCLUDES = -I$(top_srcdir)/include
libpciaccess_la_LIBADD = @PCIACCESS_LIBS@
-libpciaccess_la_LDFLAGS = -version-number 0:8:0 -no-undefined
+libpciaccess_la_LDFLAGS = -version-number 0:10:0 -no-undefined
libpciaccessincludedir = $(includedir)
libpciaccessinclude_HEADERS = \
diff --git a/src/common_interface.c b/src/common_interface.c
index 3b20937..9c29622 100644
--- a/src/common_interface.c
+++ b/src/common_interface.c
@@ -30,6 +30,7 @@
*/
#include <stdlib.h>
+#include <string.h>
#include <errno.h>
#include "pciaccess.h"
@@ -131,25 +132,27 @@ pci_device_probe( struct pci_device * dev )
* \return
* Zero on success or an \c errno value on failure.
*
- * \sa pci_device_unmap_region
+ * \sa pci_device_map_range, pci_device_unmap_range
+ * \deprecated
*/
int
-pci_device_map_region( struct pci_device * dev, unsigned region,
- int write_enable )
+pci_device_map_region(struct pci_device * dev, unsigned region,
+ int write_enable)
{
- if ( dev == NULL ) {
- return EFAULT;
- }
+ const unsigned map_flags =
+ (write_enable) ? PCI_DEV_MAP_FLAG_WRITABLE : 0;
- if ( (region > 5) || (dev->regions[ region ].size == 0) ) {
- return ENOENT;
+ if ((region > 5) || (dev->regions[region].size == 0)) {
+ return ENOENT;
}
- if ( dev->regions[ region ].memory != NULL ) {
- return 0;
+ if (dev->regions[region].memory != NULL) {
+ return 0;
}
- return (pci_sys->methods->map)( dev, region, write_enable );
+ return pci_device_map_range(dev, dev->regions[region].base_addr,
+ dev->regions[region].size, map_flags,
+ &dev->regions[region].memory);
}
@@ -163,58 +166,90 @@ pci_device_map_region( struct pci_device * dev, unsigned region,
* \param dev Device whose memory region is to be mapped.
* \param base Base address of the range to be mapped.
* \param size Size of the range to be mapped.
- * \param write_enable Map for writing (non-zero).
+ * \param map_flags Flag bits controlling how the mapping is accessed.
* \param addr Location to store the mapped address.
*
* \return
* Zero on success or an \c errno value on failure.
*
- * \sa pci_device_unmap_memory_range, pci_device_map_region
+ * \sa pci_device_unmap_range
*/
int
-pci_device_map_memory_range(struct pci_device *dev, pciaddr_t base,
- pciaddr_t size, int write_enable,
- void **addr)
+pci_device_map_range(struct pci_device *dev, pciaddr_t base,
+ pciaddr_t size, unsigned map_flags,
+ void **addr)
{
+ struct pci_device_private *const devp =
+ (struct pci_device_private *) dev;
+ struct pci_device_mapping *mappings;
unsigned region;
+ unsigned i;
int err = 0;
*addr = NULL;
if (dev == NULL) {
- return EFAULT;
+ return EFAULT;
}
for (region = 0; region < 6; region++) {
- const struct pci_mem_region const* r = &dev->regions[region];
+ const struct pci_mem_region const* r = &dev->regions[region];
- if (r->size != 0) {
- if ((r->base_addr <= base) && ((r->base_addr + r->size) > base)) {
- if ((base + size) > (r->base_addr + r->size)) {
- return E2BIG;
- }
+ if (r->size != 0) {
+ if ((r->base_addr <= base) && ((r->base_addr + r->size) > base)) {
+ if ((base + size) > (r->base_addr + r->size)) {
+ return E2BIG;
+ }
- break;
- }
- }
+ break;
+ }
+ }
}
if (region > 5) {
- return ENOENT;
+ return ENOENT;
+ }
+
+ /* Make sure that there isn't already a mapping with the same base and
+ * size.
+ */
+ for (i = 0; i < devp->num_mappings; i++) {
+ if ((devp->mappings[i].base == base)
+ && (devp->mappings[i].size == size)) {
+ return EINVAL;
+ }
}
+
+ mappings = realloc(devp->mappings,
+ (sizeof(devp->mappings[0]) * (devp->num_mappings + 1)));
+ if (mappings == NULL) {
+ return ENOMEM;
+ }
+
+ mappings[devp->num_mappings].base = base;
+ mappings[devp->num_mappings].size = size;
+ mappings[devp->num_mappings].region = region;
+ mappings[devp->num_mappings].flags = map_flags;
+ mappings[devp->num_mappings].memory = NULL;
+
if (dev->regions[region].memory == NULL) {
- err = (*pci_sys->methods->map)(dev, region, write_enable);
+ err = (*pci_sys->methods->map_range)(dev,
+ &mappings[devp->num_mappings]);
}
-
- if (err == 0) {
- const pciaddr_t offset = base - dev->regions[region].base_addr;
- *addr = ((uint8_t *)dev->regions[region].memory) + offset;
+ if (err == 0) {
+ *addr = mappings[devp->num_mappings].memory;
+ devp->num_mappings++;
+ } else {
+ mappings = realloc(devp->mappings,
+ (sizeof(devp->mappings[0]) * devp->num_mappings));
}
+ devp->mappings = mappings;
+
return err;
}
@@ -231,24 +266,29 @@ pci_device_map_memory_range(struct pci_device *dev, pciaddr_t base,
* \return
* Zero on success or an \c errno value on failure.
*
- * \sa pci_device_map_region
+ * \sa pci_device_map_range, pci_device_unmap_range
+ * \deprecated
*/
int
pci_device_unmap_region( struct pci_device * dev, unsigned region )
{
- if ( dev == NULL ) {
- return EFAULT;
+ int err;
+
+ if (dev == NULL) {
+ return EFAULT;
}
- if ( (region > 5) || (dev->regions[ region ].size == 0) ) {
- return ENOENT;
+ if ((region > 5) || (dev->regions[region].size == 0)) {
+ return ENOENT;
}
- if ( dev->regions[ region ].memory == NULL ) {
- return 0;
+ err = pci_device_unmap_range(dev, dev->regions[region].memory,
+ dev->regions[region].size);
+ if (!err) {
+ dev->regions[region].memory = NULL;
}
-
- return (pci_sys->methods->unmap)( dev, region );
+
+ return err;
}
@@ -265,41 +305,74 @@ pci_device_unmap_region( struct pci_device * dev, unsigned region )
* \return
* Zero on success or an \c errno value on failure.
*
- * \sa pci_device_map_memory_range, pci_device_unmap_region
+ * \sa pci_device_map_range, pci_device_unmap_range
+ * \deprecated
*/
int
pci_device_unmap_memory_range(struct pci_device *dev, void *memory,
- pciaddr_t size)
+ pciaddr_t size)
{
- unsigned region;
+ return pci_device_unmap_range(dev, memory, size);
+}
+
+
+/**
+ * Unmap the specified memory range so that it can no longer be accessed by the CPU.
+ *
+ * Unmaps the specified memory range that was previously mapped via
+ * \c pci_device_map_memory_range.
+ *
+ * \param dev Device whose memory is to be unmapped.
+ * \param memory Pointer to the base of the mapped range.
+ * \param size Size, in bytes, of the range to be unmapped.
+ *
+ * \return
+ * Zero on success or an \c errno value on failure.
+ *
+ * \sa pci_device_map_range
+ */
+int
+pci_device_unmap_range(struct pci_device *dev, void *memory,
+ pciaddr_t size)
+{
+ struct pci_device_private *const devp =
+ (struct pci_device_private *) dev;
+ unsigned i;
+ int err;
if (dev == NULL) {
- return EFAULT;
+ return EFAULT;
}
- for (region = 0; region < 6; region++) {
- const struct pci_mem_region const* r = &dev->regions[region];
- const uint8_t *const mem = r->memory;
-
- if (r->size != 0) {
- if ((mem <= memory) && ((mem + r->size) > memory)) {
- if ((memory + size) > (mem + r->size)) {
- return E2BIG;
- }
-
- break;
- }
- }
+ for (i = 0; i < devp->num_mappings; i++) {
+ if ((devp->mappings[i].memory == memory)
+ && (devp->mappings[i].size == size)) {
+ break;
+ }
}
- if (region > 5) {
- return ENOENT;
+ if (i == devp->num_mappings) {
+ return ENOENT;
+ }
+
+
+ err = (*pci_sys->methods->unmap_range)(dev, &devp->mappings[i]);
+ if (!err) {
+ const unsigned entries_to_move = (devp->num_mappings - i) - 1;
+
+ if (entries_to_move > 0) {
+ (void) memmove(&devp->mappings[i],
+ &devp->mappings[i + 1],
+ entries_to_move * sizeof(devp->mappings[0]));
+ }
+
+ devp->num_mappings--;
+ devp->mappings = realloc(devp->mappings,
+ (sizeof(devp->mappings[0]) * devp->num_mappings));
}
- return (dev->regions[region].memory != NULL)
- ? (*pci_sys->methods->unmap)(dev, region)
- : 0;
+ return err;
}
diff --git a/src/common_map.c b/src/common_map.c
new file mode 100644
index 0000000..a64577b
--- /dev/null
+++ b/src/common_map.c
@@ -0,0 +1,54 @@
+/*
+ * (C) Copyright IBM Corporation 2007
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * on the rights to use, copy, modify, merge, publish, distribute, sub
+ * license, and/or sell copies of the Software, and to permit persons to whom
+ * the Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
+ * IBM AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include <sys/mman.h>
+#include <errno.h>
+
+#include "pciaccess.h"
+#include "pciaccess_private.h"
+
+/**
+ * \file common_map.c
+ * Platform independent memory map routines.
+ *
+ * \author Ian Romanick <idr@us.ibm.com>
+ */
+
+/**
+ * Unmap the specified region using the munmap.
+ *
+ * \param dev Device whose memory region is to be mapped.
+ * \param map Memory mapping that is to be undone.
+ *
+ * \return
+ * Zero on success or an \c errno value on failure.
+ *
+ * \sa pci_device_unmap_range
+ */
+int
+pci_device_generic_unmap_range(struct pci_device *dev,
+ struct pci_device_mapping *map)
+{
+ return (munmap(map->memory, map->size) == -1) ? errno : 0;
+}
diff --git a/src/freebsd_pci.c b/src/freebsd_pci.c
index e83cc00..dd7635f 100644
--- a/src/freebsd_pci.c
+++ b/src/freebsd_pci.c
@@ -67,60 +67,34 @@ struct freebsd_pci_system {
/**
* Map a memory region for a device using /dev/mem.
- *
- * \param dev Device whose memory region is to be mapped.
- * \param region Region, on the range [0, 5], that is to be mapped.
- * \param write_enable Map for writing (non-zero).
- *
+ *
+ * \param dev Device whose memory region is to be mapped.
+ * \param map Parameters of the mapping that is to be created.
+ *
* \return
* Zero on success or an \c errno value on failure.
*/
static int
-pci_device_freebsd_map( struct pci_device *dev, unsigned region,
- int write_enable )
+pci_device_freebsd_map_range(struct pci_device *dev,
+ struct pci_device_mapping *map)
{
- int fd, err = 0, prot;
-
- fd = open( "/dev/mem", write_enable ? O_RDWR : O_RDONLY );
- if ( fd == -1 )
+ const int prot = ((map->flags & PCI_DEV_MAP_FLAG_WRITABLE) != 0)
+ ? (PROT_READ | PROT_WRITE) : PROT_READ;
+ const int open_flags = ((map->flags & PCI_DEV_MAP_FLAG_WRITABLE) != 0)
+ ? O_RDWR : O_RDONLY;
+ int fd, err = 0;
+
+ fd = open("/dev/mem", open_flags);
+ if (fd == -1)
return errno;
- prot = write_enable ? (PROT_READ | PROT_WRITE) : PROT_READ;
- dev->regions[ region ].memory = mmap( NULL, dev->regions[ region ].size,
- prot, MAP_SHARED, fd,
- dev->regions[ region ].base_addr);
-
- if ( dev->regions[ region ].memory == MAP_FAILED ) {
- close( fd );
- dev->regions[ region ].memory = NULL;
- err = errno;
- }
-
- close( fd );
-
- return err;
-}
-
-/**
- * Unmap the specified region.
- *
- * \param dev Device whose memory region is to be unmapped.
- * \param region Region, on the range [0, 5], that is to be unmapped.
- *
- * \return
- * Zero on success or an \c errno value on failure.
- */
-static int
-pci_device_freebsd_unmap( struct pci_device * dev, unsigned region )
-{
- int err = 0;
+ map->memory = mmap(NULL, map->size, prot, MAP_SHARED, fd, map->base);
- if ( munmap( dev->regions[ region ].memory,
- dev->regions[ region ].size ) == -1) {
+ if (map->memory == MAP_FAILED) {
err = errno;
}
- dev->regions[ region ].memory = NULL;
+ close(fd);
return err;
}
@@ -387,8 +361,8 @@ static const struct pci_system_methods freebsd_pci_methods = {
.destroy_device = NULL, /* nothing to do for this */
.read_rom = pci_device_freebsd_read_rom,
.probe = pci_device_freebsd_probe,
- .map = pci_device_freebsd_map,
- .unmap = pci_device_freebsd_unmap,
+ .map_range = pci_device_freebsd_map_range,
+ .unmap_range = pci_device_generic_unmap_range,
.read = pci_device_freebsd_read,
.write = pci_device_freebsd_write,
.fill_capabilities = pci_fill_capabilities_generic,
diff --git a/src/linux_sysfs.c b/src/linux_sysfs.c
index ba5dce2..3671f28 100644
--- a/src/linux_sysfs.c
+++ b/src/linux_sysfs.c
@@ -44,6 +44,13 @@
#include <dirent.h>
#include <errno.h>
+#include "config.h"
+
+#ifdef HAVE_MTRR
+#include <asm/mtrr.h>
+#include <sys/ioctl.h>
+#endif
+
#include "pciaccess.h"
#include "pciaccess_private.h"
#include "linux_devmem.h"
@@ -53,11 +60,11 @@ static int pci_device_linux_sysfs_read_rom( struct pci_device * dev,
static int pci_device_linux_sysfs_probe( struct pci_device * dev );
-static int pci_device_linux_sysfs_map_region( struct pci_device * dev,
- unsigned region, int write_enable );
+static int pci_device_linux_sysfs_map_range(struct pci_device *dev,
+ struct pci_device_mapping *map);
-static int pci_device_linux_sysfs_unmap_region( struct pci_device * dev,
- unsigned region );
+static int pci_device_linux_sysfs_unmap_range(struct pci_device *dev,
+ struct pci_device_mapping *map);
static int pci_device_linux_sysfs_read( struct pci_device * dev, void * data,
pciaddr_t offset, pciaddr_t size, pciaddr_t * bytes_read );
@@ -71,8 +78,8 @@ static const struct pci_system_methods linux_sysfs_methods = {
.destroy_device = NULL,
.read_rom = pci_device_linux_sysfs_read_rom,
.probe = pci_device_linux_sysfs_probe,
- .map = pci_device_linux_sysfs_map_region,
- .unmap = pci_device_linux_sysfs_unmap_region,
+ .map_range = pci_device_linux_sysfs_map_range,
+ .unmap_range = pci_device_linux_sysfs_unmap_range,
.read = pci_device_linux_sysfs_read,
.write = pci_device_linux_sysfs_write,
@@ -114,6 +121,10 @@ pci_system_linux_sysfs_create( void )
err = errno;
}
+#ifdef HAVE_MTRR
+ pci_sys->mtrr_fd = open("/proc/mtrr", O_WRONLY);
+#endif
+
return err;
}
@@ -142,7 +153,7 @@ populate_entries( struct pci_system * p )
struct dirent ** devices;
int n;
int i;
- int err;
+ int err = 0;
n = scandir( SYS_BUS_PCI, & devices, scan_sys_pci_filter, alphasort );
@@ -361,7 +372,7 @@ pci_device_linux_sysfs_read( struct pci_device * dev, void * data,
pciaddr_t temp_size = size;
int err = 0;
int fd;
-
+ char *data_bytes = data;
if ( bytes_read != NULL ) {
*bytes_read = 0;
@@ -386,7 +397,7 @@ pci_device_linux_sysfs_read( struct pci_device * dev, void * data,
while ( temp_size > 0 ) {
- const ssize_t bytes = pread64( fd, data, temp_size, offset );
+ const ssize_t bytes = pread64( fd, data_bytes, temp_size, offset );
/* If zero bytes were read, then we assume it's the end of the
* config file.
@@ -398,7 +409,7 @@ pci_device_linux_sysfs_read( struct pci_device * dev, void * data,
temp_size -= bytes;
offset += bytes;
- data += bytes;
+ data_bytes += bytes;
}
if ( bytes_read != NULL ) {
@@ -419,7 +430,7 @@ pci_device_linux_sysfs_write( struct pci_device * dev, const void * data,
pciaddr_t temp_size = size;
int err = 0;
int fd;
-
+ const char *data_bytes = data;
if ( bytes_written != NULL ) {
*bytes_written = 0;
@@ -444,7 +455,7 @@ pci_device_linux_sysfs_write( struct pci_device * dev, const void * data,
while ( temp_size > 0 ) {
- const ssize_t bytes = pwrite64( fd, data, temp_size, offset );
+ const ssize_t bytes = pwrite64( fd, data_bytes, temp_size, offset );
/* If zero bytes were written, then we assume it's the end of the
* config file.
@@ -456,7 +467,7 @@ pci_device_linux_sysfs_write( struct pci_device * dev, const void * data,
temp_size -= bytes;
offset += bytes;
- data += bytes;
+ data_bytes += bytes;
}
if ( bytes_written != NULL ) {
@@ -471,14 +482,13 @@ pci_device_linux_sysfs_write( struct pci_device * dev, const void * data,
/**
* Map a memory region for a device using the Linux sysfs interface.
*
- * \param dev Device whose memory region is to be mapped.
- * \param region Region, on the range [0, 5], that is to be mapped.
- * \param write_enable Map for writing (non-zero).
+ * \param dev Device whose memory region is to be mapped.
+ * \param map Parameters of the mapping that is to be created.
*
* \return
* Zero on success or an \c errno value on failure.
*
- * \sa pci_device_map_region, pci_device_linux_sysfs_unmap_region
+ * \sa pci_device_map_rrange, pci_device_linux_sysfs_unmap_range
*
* \todo
* Some older 2.6.x kernels don't implement the resourceN files. On those
@@ -486,51 +496,80 @@ pci_device_linux_sysfs_write( struct pci_device * dev, const void * data,
* \c mmap64 may need to be used.
*/
static int
-pci_device_linux_sysfs_map_region( struct pci_device * dev, unsigned region,
- int write_enable )
+pci_device_linux_sysfs_map_range(struct pci_device *dev,
+ struct pci_device_mapping *map)
{
char name[256];
int fd;
int err = 0;
- const int prot = (write_enable) ? (PROT_READ | PROT_WRITE) : PROT_READ;
-
+ const int prot = ((map->flags & PCI_DEV_MAP_FLAG_WRITABLE) != 0)
+ ? (PROT_READ | PROT_WRITE) : PROT_READ;
+ const int open_flags = ((map->flags & PCI_DEV_MAP_FLAG_WRITABLE) != 0)
+ ? O_RDWR : O_RDONLY;
+ const off_t offset = map->base - dev->regions[map->region].base_addr;
+#ifdef HAVE_MTRR
+ struct mtrr_sentry sentry = {
+ .base = map->base,
+ .size = map->size,
+ .type = MTRR_TYPE_UNCACHABLE
+ };
+#endif
+
+ snprintf(name, 255, "%s/%04x:%02x:%02x.%1u/resource%u",
+ SYS_BUS_PCI,
+ dev->domain,
+ dev->bus,
+ dev->dev,
+ dev->func,
+ map->region);
+
+ fd = open(name, open_flags);
+ if (fd == -1) {
+ return errno;
+ }
- snprintf( name, 255, "%s/%04x:%02x:%02x.%1u/resource%u",
- SYS_BUS_PCI,
- dev->domain,
- dev->bus,
- dev->dev,
- dev->func,
- region );
- fd = open( name, (write_enable) ? O_RDWR : O_RDONLY );
- if ( fd == -1 ) {
- return errno;
+ map->memory = mmap(NULL, map->size, prot, MAP_SHARED, fd, offset);
+ if (map->memory == MAP_FAILED) {
+ err = errno;
+ map->memory = NULL;
}
+ close(fd);
- dev->regions[ region ].memory = mmap( NULL, dev->regions[ region ].size,
- prot, MAP_SHARED, fd, 0 );
- if ( dev->regions[ region ].memory == MAP_FAILED ) {
- err = errno;
- dev->regions[ region ].memory = NULL;
+#ifdef HAVE_MTRR
+ if ((map->flags & PCI_DEV_MAP_FLAG_CACHABLE) != 0) {
+ sentry.type = MTRR_TYPE_WRBACK;
+ } else if ((map->flags & PCI_DEV_MAP_FLAG_WRITE_COMBINE) != 0) {
+ sentry.type = MTRR_TYPE_WRCOMB;
}
- close( fd );
+ if (pci_sys->mtrr_fd != -1 && sentry.type != MTRR_TYPE_UNCACHABLE) {
+ if (ioctl(pci_sys->mtrr_fd, MTRRIOC_ADD_ENTRY, &sentry) < 0) {
+ /* FIXME: Should we report an error in this case?
+ */
+ fprintf(stderr, "error setting MTRR "
+ "(base = 0x%08lx, size = 0x%08x, type = %u) %s (%d)\n",
+ sentry.base, sentry.size, sentry.type,
+ strerror(errno), errno);
+/* err = errno;*/
+ }
+ }
+#endif
+
return err;
}
-
/**
- * Unmap the specified region using the Linux sysfs interface.
- *
- * \param dev Device whose memory region is to be mapped.
- * \param region Region, on the range [0, 5], that is to be mapped.
- *
+ * Unmap a memory region for a device using the Linux sysfs interface.
+ *
+ * \param dev Device whose memory region is to be unmapped.
+ * \param map Parameters of the mapping that is to be destroyed.
+ *
* \return
* Zero on success or an \c errno value on failure.
*
- * \sa pci_device_unmap_region, pci_device_linux_sysfs_map_region
+ * \sa pci_device_map_rrange, pci_device_linux_sysfs_map_range
*
* \todo
* Some older 2.6.x kernels don't implement the resourceN files. On those
@@ -538,16 +577,41 @@ pci_device_linux_sysfs_map_region( struct pci_device * dev, unsigned region,
* \c mmap64 may need to be used.
*/
static int
-pci_device_linux_sysfs_unmap_region( struct pci_device * dev, unsigned region )
+pci_device_linux_sysfs_unmap_range(struct pci_device *dev,
+ struct pci_device_mapping *map)
{
int err = 0;
-
- if ( munmap( dev->regions[ region ].memory, dev->regions[ region ].size )
- == -1 ) {
- err = errno;
+#ifdef HAVE_MTRR
+ struct mtrr_sentry sentry = {
+ .base = map->base,
+ .size = map->size,
+ .type = MTRR_TYPE_UNCACHABLE
+ };
+#endif
+
+ err = pci_device_generic_unmap_range (dev, map);
+ if (err)
+ return err;
+
+#ifdef HAVE_MTRR
+ if ((map->flags & PCI_DEV_MAP_FLAG_CACHABLE) != 0) {
+ sentry.type = MTRR_TYPE_WRBACK;
+ } else if ((map->flags & PCI_DEV_MAP_FLAG_WRITE_COMBINE) != 0) {
+ sentry.type = MTRR_TYPE_WRCOMB;
}
- dev->regions[ region ].memory = NULL;
+ if (pci_sys->mtrr_fd != -1 && sentry.type != MTRR_TYPE_UNCACHABLE) {
+ if (ioctl(pci_sys->mtrr_fd, MTRRIOC_DEL_ENTRY, &sentry) < 0) {
+ /* FIXME: Should we report an error in this case?
+ */
+ fprintf(stderr, "error setting MTRR "
+ "(base = 0x%08lx, size = 0x%08x, type = %u) %s (%d)\n",
+ sentry.base, sentry.size, sentry.type,
+ strerror(errno), errno);
+/* err = errno;*/
+ }
+ }
+#endif
return err;
}
diff --git a/src/pciaccess_private.h b/src/pciaccess_private.h
index afbeb75..9eb6062 100644
--- a/src/pciaccess_private.h
+++ b/src/pciaccess_private.h
@@ -30,15 +30,20 @@
*/
+struct pci_device_mapping;
+
int pci_fill_capabilities_generic( struct pci_device * dev );
+int pci_device_generic_unmap_range(struct pci_device *dev,
+ struct pci_device_mapping *map);
struct pci_system_methods {
void (*destroy)( void );
void (*destroy_device)( struct pci_device * dev );
int (*read_rom)( struct pci_device * dev, void * buffer );
int (*probe)( struct pci_device * dev );
- int (*map)( struct pci_device * dev, unsigned region, int write_enable );
- int (*unmap)( struct pci_device * dev, unsigned region );
+ int (*map_range)(struct pci_device *dev, struct pci_device_mapping *map);
+ int (*unmap_range)(struct pci_device * dev,
+ struct pci_device_mapping *map);
int (*read)(struct pci_device * dev, void * data, pciaddr_t offset,
pciaddr_t size, pciaddr_t * bytes_read );
@@ -49,6 +54,14 @@ struct pci_system_methods {
int (*fill_capabilities)( struct pci_device * dev );
};
+struct pci_device_mapping {
+ pciaddr_t base;
+ pciaddr_t size;
+ unsigned region;
+ unsigned flags;
+ void *memory;
+};
+
struct pci_device_private {
struct pci_device base;
const char * device_string;
@@ -76,7 +89,14 @@ struct pci_device_private {
struct pci_pcmcia_bridge_info * pcmcia;
} bridge;
/*@}*/
-
+
+ /**
+ * \name Mappings active on this device.
+ */
+ /*@{*/
+ struct pci_device_mapping *mappings;
+ unsigned num_mappings;
+ /*@}*/
};
@@ -98,6 +118,10 @@ struct pci_system {
* Array of known devices.
*/
struct pci_device_private * devices;
+
+#ifdef HAVE_MTRR
+ int mtrr_fd;
+#endif
};
extern struct pci_system * pci_sys;
diff --git a/src/scanpci.c b/src/scanpci.c
index 2c49d80..eb6e94f 100644
--- a/src/scanpci.c
+++ b/src/scanpci.c
@@ -128,10 +128,10 @@ print_pci_device( struct pci_device * dev, int verbose )
pci_device_probe( dev );
for ( i = 0 ; i < 6 ; i++ ) {
if ( dev->regions[i].base_addr != 0 ) {
- printf( " BASE%u 0x%08x addr 0x%08x %s",
+ printf( " BASE%u 0x%08x SIZE %d %s",
i,
- 0,
(intptr_t) dev->regions[i].base_addr,
+ (size_t) dev->regions[i].size,
(dev->regions[i].is_IO) ? "I/O" : "MEM" );
if ( ! dev->regions[i].is_IO ) {
diff --git a/src/solx_devfs.c b/src/solx_devfs.c
index 058b604..a500327 100644
--- a/src/solx_devfs.c
+++ b/src/solx_devfs.c
@@ -144,8 +144,8 @@ static const struct pci_system_methods solx_devfs_methods = {
.destroy_device = NULL,
.read_rom = pci_device_solx_devfs_read_rom,
.probe = pci_device_solx_devfs_probe,
- .map = pci_device_solx_devfs_map_region,
- .unmap = pci_device_solx_devfs_unmap_region,
+ .map_range = pci_device_solx_devfs_map_range,
+ .unmap_range = pci_device_generic_unmap_range,
.read = pci_device_solx_devfs_read,
.write = pci_device_solx_devfs_write,
@@ -836,55 +836,39 @@ pci_device_solx_devfs_write( struct pci_device * dev, const void * data,
}
-/*
- * Solaris Version
+/**
+ * Map a memory region for a device using /dev/xsvc.
+ *
+ * \param dev Device whose memory region is to be mapped.
+ * \param map Parameters of the mapping that is to be created.
+ *
+ * \return
+ * Zero on success or an \c errno value on failure.
*/
static int
-pci_device_solx_devfs_map_region( struct pci_device * dev, unsigned region,
- int write_enable )
+pci_device_solx_devfs_map_range(struct pci_device *dev,
+ struct pci_device_mapping *map)
{
+ const int prot = ((map->flags & PCI_DEV_MAP_FLAG_WRITABLE) != 0)
+ ? (PROT_READ | PROT_WRITE) : PROT_READ;
+ int err;
+
if (xsvc_fd < 0) {
if ((xsvc_fd = open("/dev/xsvc", O_RDWR)) < 0) {
- (void) fprintf(stderr, "can not open xsvc driver\n");
-
- return (-1);
+ (void) fprintf(stderr, "can not open xsvc driver\n");
+ return errno;
}
}
- dev->regions[region].memory = mmap(NULL, dev->regions[region].size,
- (write_enable) ? (PROT_READ | PROT_WRITE) : PROT_READ, MAP_SHARED,
- xsvc_fd, dev->regions[region].base_addr);
+ map->memory = mmap(NULL, map->size, prot, MAP_SHARED, xsvs_fd,
+ map->base);
+ if (map->memory == MAP_FAILED) {
+ err = errno;
- if (dev->regions[region].memory == MAP_FAILED) {
- dev->regions[region].memory = 0;
-
(void) fprintf(stderr, "map rom region =%x failed",
- dev->regions[region].base_addr);
- return (-1);
+ map->base);
}
-
- /*
- * Still used xsvc to do the user space mapping
- */
- return (0);
-}
-
-
-/*
- * Solaris version
- */
-static int
-pci_device_solx_devfs_unmap_region( struct pci_device * dev, unsigned region )
-{
- int err = 0;
- if ( munmap( dev->regions[ region ].memory, dev->regions[ region ].size )
- == -1 ) {
- err = errno;
- }
-
- dev->regions[ region ].memory = NULL;
-
- return (err);
+ return err;
}