upload tizen2.0 source
[framework/uifw/xorg/lib/libpciaccess.git] / src / linux_sysfs.c
index 1832ee7..d52503e 100644 (file)
@@ -1,6 +1,7 @@
 /*
  * (C) Copyright IBM Corporation 2006
  * All Rights Reserved.
+ * Copyright 2012 Red Hat, Inc.
  *
  * Permission is hereby granted, free of charge, to any person obtaining a
  * copy of this software and associated documentation files (the "Software"),
 #include <dirent.h>
 #include <errno.h>
 
+#if defined(__i386__) || defined(__x86_64__) || defined(__arm__)
+#include <sys/io.h>
+#else
+#define inb(x) -1
+#define inw(x) -1
+#define inl(x) -1
+#define outb(x,y) do {} while (0)
+#define outw(x,y) do {} while (0)
+#define outl(x,y) do {} while (0)
+#define iopl(x) -1
+#endif
+
 #include "config.h"
 
 #ifdef HAVE_MTRR
@@ -79,13 +92,13 @@ pci_system_linux_sysfs_create( void )
     /* If the directory "/sys/bus/pci/devices" exists, then the PCI subsystem
      * can be accessed using this interface.
      */
-    
+
     if ( stat( SYS_BUS_PCI, & st ) == 0 ) {
        pci_sys = calloc( 1, sizeof( struct pci_system ) );
        if ( pci_sys != NULL ) {
            pci_sys->methods = & linux_sysfs_methods;
 #ifdef HAVE_MTRR
-           pci_sys->mtrr_fd = open("/proc/mtrr", O_WRONLY);
+           pci_sys->mtrr_fd = open("/proc/mtrr", O_WRONLY | O_CLOEXEC);
 #endif
            err = populate_entries(pci_sys);
        }
@@ -114,7 +127,7 @@ pci_system_linux_sysfs_create( void )
 static int
 scan_sys_pci_filter( const struct dirent * d )
 {
-    return !((strcmp( d->d_name, "." ) == 0) 
+    return !((strcmp( d->d_name, "." ) == 0)
             || (strcmp( d->d_name, ".." ) == 0));
 }
 
@@ -122,7 +135,7 @@ scan_sys_pci_filter( const struct dirent * d )
 int
 populate_entries( struct pci_system * p )
 {
-    struct dirent ** devices;
+    struct dirent ** devices = NULL;
     int n;
     int i;
     int err = 0;
@@ -219,7 +232,7 @@ pci_device_linux_sysfs_probe( struct pci_device * dev )
         * parsing issues and non-root users can write to PCI config
         * registers, we use a different file in the device's sysfs
         * directory called "resource".
-        * 
+        *
         * The resource file contains all of the needed information in
         * a format that is consistent across all platforms.  Each BAR
         * and the expansion ROM have a single line of data containing
@@ -232,7 +245,7 @@ pci_device_linux_sysfs_probe( struct pci_device * dev )
                  dev->bus,
                  dev->dev,
                  dev->func );
-       fd = open( name, O_RDONLY );
+       fd = open( name, O_RDONLY | O_CLOEXEC);
        if ( fd != -1 ) {
            char * next;
            pciaddr_t  low_addr;
@@ -251,9 +264,9 @@ pci_device_linux_sysfs_probe( struct pci_device * dev )
                dev->regions[i].base_addr = strtoull( next, & next, 16 );
                high_addr = strtoull( next, & next, 16 );
                flags = strtoull( next, & next, 16 );
-                   
+
                if ( dev->regions[i].base_addr != 0 ) {
-                   dev->regions[i].size = (high_addr 
+                   dev->regions[i].size = (high_addr
                                            - dev->regions[i].base_addr) + 1;
 
                    dev->regions[i].is_IO = (flags & 0x01);
@@ -293,8 +306,8 @@ pci_device_linux_sysfs_read_rom( struct pci_device * dev, void * buffer )
              dev->bus,
              dev->dev,
              dev->func );
-    
-    fd = open( name, O_RDWR );
+
+    fd = open( name, O_RDWR | O_CLOEXEC);
     if ( fd == -1 ) {
 #ifdef LINUX_ROM
        /* If reading the ROM using sysfs fails, fall back to the old
@@ -338,7 +351,7 @@ pci_device_linux_sysfs_read_rom( struct pci_device * dev, void * buffer )
 
        total_bytes += bytes;
     }
-       
+
 
     lseek( fd, 0, SEEK_SET );
     write( fd, "0", 1 );
@@ -375,7 +388,7 @@ pci_device_linux_sysfs_read( struct pci_device * dev, void * data,
              dev->dev,
              dev->func );
 
-    fd = open( name, O_RDONLY );
+    fd = open( name, O_RDONLY | O_CLOEXEC);
     if ( fd == -1 ) {
        return errno;
     }
@@ -387,7 +400,9 @@ pci_device_linux_sysfs_read( struct pci_device * dev, void * data,
        /* If zero bytes were read, then we assume it's the end of the
         * config file.
         */
-       if ( bytes <= 0 ) {
+       if (bytes == 0)
+           break;
+       if ( bytes < 0 ) {
            err = errno;
            break;
        }
@@ -396,7 +411,7 @@ pci_device_linux_sysfs_read( struct pci_device * dev, void * data,
        offset += bytes;
        data_bytes += bytes;
     }
-    
+
     if ( bytes_read != NULL ) {
        *bytes_read = size - temp_size;
     }
@@ -433,7 +448,7 @@ pci_device_linux_sysfs_write( struct pci_device * dev, const void * data,
              dev->dev,
              dev->func );
 
-    fd = open( name, O_WRONLY );
+    fd = open( name, O_WRONLY | O_CLOEXEC);
     if ( fd == -1 ) {
        return errno;
     }
@@ -445,7 +460,9 @@ pci_device_linux_sysfs_write( struct pci_device * dev, const void * data,
        /* If zero bytes were written, then we assume it's the end of the
         * config file.
         */
-       if ( bytes <= 0 ) {
+       if ( bytes == 0 )
+           break;
+       if ( bytes < 0 ) {
            err = errno;
            break;
        }
@@ -454,7 +471,7 @@ pci_device_linux_sysfs_write( struct pci_device * dev, const void * data,
        offset += bytes;
        data_bytes += bytes;
     }
-    
+
     if ( bytes_written != NULL ) {
        *bytes_written = size - temp_size;
     }
@@ -469,9 +486,9 @@ pci_device_linux_sysfs_map_range_wc(struct pci_device *dev,
 {
     char name[256];
     int fd;
-    const int prot = ((map->flags & PCI_DEV_MAP_FLAG_WRITABLE) != 0) 
+    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) 
+    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;
 
@@ -482,7 +499,7 @@ pci_device_linux_sysfs_map_range_wc(struct pci_device *dev,
             dev->dev,
             dev->func,
             map->region);
-    fd = open(name, open_flags);
+    fd = open(name, open_flags | O_CLOEXEC);
     if (fd == -1)
            return errno;
 
@@ -500,10 +517,10 @@ pci_device_linux_sysfs_map_range_wc(struct pci_device *dev,
 
 /**
  * Map a memory region for a device using the Linux sysfs interface.
- * 
+ *
  * \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.
  *
@@ -521,9 +538,9 @@ pci_device_linux_sysfs_map_range(struct pci_device *dev,
     char name[256];
     int fd;
     int err = 0;
-    const int prot = ((map->flags & PCI_DEV_MAP_FLAG_WRITABLE) != 0) 
+    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) 
+    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
@@ -547,7 +564,7 @@ pci_device_linux_sysfs_map_range(struct pci_device *dev,
              dev->func,
              map->region);
 
-    fd = open(name, open_flags);
+    fd = open(name, open_flags | O_CLOEXEC);
     if (fd == -1) {
         return errno;
     }
@@ -604,10 +621,10 @@ pci_device_linux_sysfs_map_range(struct pci_device *dev,
 
 /**
  * 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.
  *
@@ -634,7 +651,7 @@ pci_device_linux_sysfs_unmap_range(struct pci_device *dev,
     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;
@@ -669,8 +686,8 @@ static void pci_device_linux_sysfs_enable(struct pci_device *dev)
              dev->bus,
              dev->dev,
              dev->func );
-    
-    fd = open( name, O_RDWR );
+
+    fd = open( name, O_RDWR | O_CLOEXEC);
     if (fd == -1)
        return;
 
@@ -691,8 +708,8 @@ static int pci_device_linux_sysfs_boot_vga(struct pci_device *dev)
              dev->bus,
              dev->dev,
              dev->func );
-    
-    fd = open( name, O_RDONLY );
+
+    fd = open( name, O_RDONLY | O_CLOEXEC);
     if (fd == -1)
        return 0;
 
@@ -718,7 +735,7 @@ static int pci_device_linux_sysfs_has_kernel_driver(struct pci_device *dev)
              dev->bus,
              dev->dev,
              dev->func );
-    
+
     ret = stat(name, &dummy);
     if (ret < 0)
        return 0;
@@ -735,7 +752,7 @@ pci_device_linux_sysfs_open_device_io(struct pci_io_handle *ret,
     snprintf(name, PATH_MAX, "%s/%04x:%02x:%02x.%1u/resource%d",
             SYS_BUS_PCI, dev->domain, dev->bus, dev->dev, dev->func, bar);
 
-    ret->fd = open(name, O_RDWR);
+    ret->fd = open(name, O_RDWR | O_CLOEXEC);
 
     if (ret->fd < 0)
        return NULL;
@@ -758,19 +775,24 @@ pci_device_linux_sysfs_open_legacy_io(struct pci_io_handle *ret,
        snprintf(name, PATH_MAX, "/sys/class/pci_bus/%04x:%02x/legacy_io",
                 dev->domain, dev->bus);
 
-       ret->fd = open(name, O_RDWR);
+       ret->fd = open(name, O_RDWR | O_CLOEXEC);
        if (ret->fd >= 0)
            break;
 
        dev = pci_device_get_parent_bridge(dev);
     }
 
-    /* If not, /dev/port is the best we can do */
-    if (!dev)
-       ret->fd = open("/dev/port", O_RDWR);
+    /*
+     * You would think you'd want to use /dev/port here.  Don't make that
+     * mistake, /dev/port only does byte-wide i/o cycles which means it
+     * doesn't work.  If you think this is stupid, well, you're right.
+     */
 
-    if (ret->fd < 0)
-       return NULL;
+    /* If we've no other choice, iopl */
+    if (ret->fd < 0) {
+       if (iopl(3))
+           return NULL;
+    }
 
     ret->base = base;
     ret->size = size;
@@ -782,7 +804,8 @@ static void
 pci_device_linux_sysfs_close_io(struct pci_device *dev,
                                struct pci_io_handle *handle)
 {
-    close(handle->fd);
+    if (handle->fd > -1)
+       close(handle->fd);
 }
 
 static uint32_t
@@ -790,7 +813,10 @@ pci_device_linux_sysfs_read32(struct pci_io_handle *handle, uint32_t port)
 {
     uint32_t ret;
 
-    pread(handle->fd, &ret, 4, port + handle->base);
+    if (handle->fd > -1)
+       pread(handle->fd, &ret, 4, port + handle->base);
+    else
+       ret = inl(port + handle->base);
 
     return ret;
 }
@@ -800,7 +826,10 @@ pci_device_linux_sysfs_read16(struct pci_io_handle *handle, uint32_t port)
 {
     uint16_t ret;
 
-    pread(handle->fd, &ret, 2, port + handle->base);
+    if (handle->fd > -1)
+       pread(handle->fd, &ret, 2, port + handle->base);
+    else
+       ret = inw(port + handle->base);
 
     return ret;
 }
@@ -810,7 +839,10 @@ pci_device_linux_sysfs_read8(struct pci_io_handle *handle, uint32_t port)
 {
     uint8_t ret;
 
-    pread(handle->fd, &ret, 1, port + handle->base);
+    if (handle->fd > -1)
+       pread(handle->fd, &ret, 1, port + handle->base);
+    else
+       ret = inb(port + handle->base);
 
     return ret;
 }
@@ -819,25 +851,93 @@ static void
 pci_device_linux_sysfs_write32(struct pci_io_handle *handle, uint32_t port,
                               uint32_t data)
 {
-    pwrite(handle->fd, &data, 4, port + handle->base);
+    if (handle->fd > -1)
+       pwrite(handle->fd, &data, 4, port + handle->base);
+    else
+       outl(data, port + handle->base);
 }
 
 static void
 pci_device_linux_sysfs_write16(struct pci_io_handle *handle, uint32_t port,
                               uint16_t data)
 {
-    pwrite(handle->fd, &data, 2, port + handle->base);
+    if (handle->fd > -1)
+       pwrite(handle->fd, &data, 2, port + handle->base);
+    else
+       outw(data, port + handle->base);
 }
 
 static void
 pci_device_linux_sysfs_write8(struct pci_io_handle *handle, uint32_t port,
                              uint8_t data)
 {
-    pwrite(handle->fd, &data, 1, port + handle->base);
+    if (handle->fd > -1)
+       pwrite(handle->fd, &data, 1, port + handle->base);
+    else
+       outb(data, port + handle->base);
+}
+
+static int
+pci_device_linux_sysfs_map_legacy(struct pci_device *dev, pciaddr_t base,
+                                 pciaddr_t size, unsigned map_flags, void **addr)
+{
+    char name[PATH_MAX];
+    int flags = O_RDONLY;
+    int prot = PROT_READ;
+    int fd;
+    int ret=0;
+
+    if (map_flags & PCI_DEV_MAP_FLAG_WRITABLE) {
+       flags = O_RDWR; /* O_RDWR != O_WRONLY | O_RDONLY */;
+       prot |= PROT_WRITE;
+    }
+
+    /* First check if there's a legacy memory method for the device */
+    while (dev) {
+       snprintf(name, PATH_MAX, "/sys/class/pci_bus/%04x:%02x/legacy_mem",
+                dev->domain, dev->bus);
+
+       fd = open(name, flags | O_CLOEXEC);
+       if (fd >= 0)
+           break;
+
+       dev = pci_device_get_parent_bridge(dev);
+    }
+
+    /* If not, /dev/mem is the best we can do */
+    if (!dev)
+       fd = open("/dev/mem", flags | O_CLOEXEC);
+
+    if (fd < 0)
+       return errno;
+
+    *addr = mmap(NULL, size, prot, MAP_SHARED, fd, base);
+    if (*addr == MAP_FAILED) {
+       ret = errno;
+    }
+
+    close(fd);
+    return ret;
+}
+
+static int
+pci_device_linux_sysfs_unmap_legacy(struct pci_device *dev, void *addr, pciaddr_t size)
+{
+    return munmap(addr, size);
+}
+
+
+static void
+pci_system_linux_destroy(void)
+{
+#ifdef HAVE_MTRR
+       if (pci_sys->mtrr_fd != -1)
+               close(pci_sys->mtrr_fd);
+#endif
 }
 
 static const struct pci_system_methods linux_sysfs_methods = {
-    .destroy = NULL,
+    .destroy = pci_system_linux_destroy,
     .destroy_device = NULL,
     .read_rom = pci_device_linux_sysfs_read_rom,
     .probe = pci_device_linux_sysfs_probe,
@@ -861,4 +961,7 @@ static const struct pci_system_methods linux_sysfs_methods = {
     .write32 = pci_device_linux_sysfs_write32,
     .write16 = pci_device_linux_sysfs_write16,
     .write8 = pci_device_linux_sysfs_write8,
+
+    .map_legacy = pci_device_linux_sysfs_map_legacy,
+    .unmap_legacy = pci_device_linux_sysfs_unmap_legacy,
 };