Edit

IABSD.fr/xenocara/lib/libpciaccess/src/common_interface.c

Branch :

  • Show log

    Commit

  • Author : matthieu
    Date : 2026-05-16 14:40:55
    Hash : 50816655
    Message : Update to libpciaccess 0.19

  • lib/libpciaccess/src/common_interface.c
  • /*
     * (C) Copyright IBM Corporation 2006
     * 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.
     */
    
    /**
     * \file common_interface.c
     * Platform independent interface glue.
     *
     * \author Ian Romanick <idr@us.ibm.com>
     */
    #ifdef HAVE_CONFIG_H
    #include "config.h"
    #endif
    
    #include <stdlib.h>
    #include <string.h>
    #include <errno.h>
    
    #include "pciaccess.h"
    #include "pciaccess_private.h"
    
    #if defined(__linux__) || defined(__GLIBC__) || defined(__CYGWIN__)
    #include <byteswap.h>
    
    #if __BYTE_ORDER == __BIG_ENDIAN
    # define LETOH_16(x)   bswap_16(x)
    # define HTOLE_16(x)   bswap_16(x)
    # define LETOH_32(x)   bswap_32(x)
    # define HTOLE_32(x)   bswap_32(x)
    #else
    # define LETOH_16(x)   (x)
    # define HTOLE_16(x)   (x)
    # define LETOH_32(x)   (x)
    # define HTOLE_32(x)   (x)
    #endif /* linux */
    
    #elif defined(__sun)
    
    #include <sys/byteorder.h>
    
    #ifdef _BIG_ENDIAN
    # define LETOH_16(x)   BSWAP_16(x)
    # define HTOLE_16(x)   BSWAP_16(x)
    # define LETOH_32(x)   BSWAP_32(x)
    # define HTOLE_32(x)   BSWAP_32(x)
    #else
    # define LETOH_16(x)   (x)
    # define HTOLE_16(x)   (x)
    # define LETOH_32(x)   (x)
    # define HTOLE_32(x)   (x)
    #endif /* Solaris */
    
    #elif defined(__APPLE__)
    #include <libkern/OSByteOrder.h>
    
    #define htobe16(x) OSSwapHostToBigInt16(x)
    #define htole16(x) OSSwapHostToLittleInt16(x)
    #define be16toh(x) OSSwapBigToHostInt16(x)
    #define le16toh(x) OSSwapLittleToHostInt16(x)
    
    #define htobe32(x) OSSwapHostToBigInt32(x)
    #define htole32(x) OSSwapHostToLittleInt32(x)
    #define be32toh(x) OSSwapBigToHostInt32(x)
    #define le32toh(x) OSSwapLittleToHostInt32(x)
    
    #define htobe64(x) OSSwapHostToBigInt64(x)
    #define htole64(x) OSSwapHostToLittleInt64(x)
    #define be64toh(x) OSSwapBigToHostInt64(x)
    #define le64toh(x) OSSwapLittleToHostInt64(x)
    
    #else
    
    #include <sys/endian.h>
    
    #define HTOLE_16(x)	htole16(x)
    #define HTOLE_32(x)	htole32(x)
    
    #if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__NetBSD__)
    #define LETOH_16(x)	le16toh(x)
    #define LETOH_32(x)	le32toh(x)
    #else
    #define LETOH_16(x)	letoh16(x)
    #define LETOH_32(x)	letoh32(x)
    #endif
    
    #endif /* others */
    
    /**
     * Read a device's expansion ROM.
     *
     * Reads the device's expansion ROM and stores the data in the memory pointed
     * to by \c buffer.  The buffer must be at least \c pci_device::rom_size
     * bytes.
     *
     * \param dev    Device whose expansion ROM is to be read.
     * \param buffer Memory in which to store the ROM.
     *
     * \return
     * Zero on success or an \c errno value on failure.
     */
    int
    pci_device_read_rom( struct pci_device * dev, void * buffer )
    {
        if ( (dev == NULL) || (buffer == NULL) ) {
    	return EFAULT;
        }
    
    
        return (pci_sys->methods->read_rom)( dev, buffer );
    }
    
    /**
     * Probe a PCI (VGA) device to determine if its the boot VGA device
     *
     * \param dev    Device whose VGA status to query
     * \return
     * Zero if not the boot VGA, 1 if the boot VGA.
     */
    int
    pci_device_is_boot_vga( struct pci_device * dev )
    {
    	if (!pci_sys->methods->boot_vga)
    		return 0;
    	return pci_sys->methods->boot_vga( dev );
    }
    
    /**
     * Probe a PCI (Display) device to determine if its the boot display device
     *
     * \param dev    Device whose display status to query
     * \return
     * Zero if not the boot display, 1 if the boot display.
     */
    int
    pci_device_is_boot_display( struct pci_device * dev )
    {
    	if (!pci_sys->methods->boot_display)
    		return 0;
    	return pci_sys->methods->boot_display( dev );
    }
    
    /**
     * Probe a PCI device to determine if a kernel driver is attached.
     *
     * \param dev Device to query
     * \return
     * Zero if no driver attached, 1 if attached kernel drviver
     */
    int
    pci_device_has_kernel_driver( struct pci_device * dev )
    {
    	if (!pci_sys->methods->has_kernel_driver)
    		return 0;
    	return pci_sys->methods->has_kernel_driver( dev );
    }
    
    /**
     * Probe a PCI device to learn information about the device.
     *
     * Probes a PCI device to learn various information about the device.  Before
     * calling this function, the only public fields in the \c pci_device
     * structure that have valid values are \c pci_device::domain,
     * \c pci_device::bus, \c pci_device::dev, and \c pci_device::func.
     *
     * \param dev  Device to be probed.
     *
     * \return
     * Zero on success or an \c errno value on failure.
     */
    int
    pci_device_probe( struct pci_device * dev )
    {
        if ( dev == NULL ) {
    	return EFAULT;
        }
    
    
        return (pci_sys->methods->probe)( dev );
    }
    
    
    /**
     * Map the specified BAR so that it can be accessed by the CPU.
     *
     * Maps the specified BAR for access by the processor.  The pointer to the
     * mapped region is stored in the \c pci_mem_region::memory pointer for the
     * BAR.
     *
     * \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).
     *
     * \return
     * Zero on success or an \c errno value on failure.
     *
     * \sa pci_device_map_range, pci_device_unmap_range
     * \deprecated
     */
    int
    pci_device_map_region(struct pci_device * dev, unsigned region,
                          int write_enable)
    {
        const unsigned map_flags =
            (write_enable) ? PCI_DEV_MAP_FLAG_WRITABLE : 0;
    
        if ((region > 5) || (dev->regions[region].size == 0))  {
            return ENOENT;
        }
    
        if (dev->regions[region].memory != NULL) {
            return 0;
        }
    
        return pci_device_map_range(dev, dev->regions[region].base_addr,
                                    dev->regions[region].size, map_flags,
                                    &dev->regions[region].memory);
    }
    
    
    /**
     * Map the specified memory range so that it can be accessed by the CPU.
     *
     * Maps the specified memory range for access by the processor.  The pointer
     * to the mapped region is stored in \c addr.  In addition, the
     * \c pci_mem_region::memory pointer for the BAR will be updated.
     *
     * \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 addr         Location to store the mapped address.
     *
     * \return
     * Zero on success or an \c errno value on failure.
     *
     * \sa pci_device_map_range
     */
    int pci_device_map_memory_range(struct pci_device *dev,
    				pciaddr_t base, pciaddr_t size,
    				int write_enable, void **addr)
    {
        return pci_device_map_range(dev, base, size,
    				(write_enable) ? PCI_DEV_MAP_FLAG_WRITABLE : 0,
    				addr);
    }
    
    
    /**
     * Map the specified memory range so that it can be accessed by the CPU.
     *
     * Maps the specified memory range for access by the processor.  The pointer
     * to the mapped region is stored in \c addr.  In addition, the
     * \c pci_mem_region::memory pointer for the BAR will be updated.
     *
     * \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 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_range
     */
    int
    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;
        }
    
    
        for (region = 0; region < 6; 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;
                    }
    
                    break;
                }
            }
        }
    
        if (region > 5) {
            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_range)(dev,
                                                 &mappings[devp->num_mappings]);
        }
    
        if (err == 0) {
            *addr =  mappings[devp->num_mappings].memory;
            devp->num_mappings++;
        } else {
            mappings = realloc(mappings,
                               (sizeof(mappings[0]) * devp->num_mappings));
        }
    
        devp->mappings = mappings;
    
        return err;
    }
    
    
    /**
     * Unmap the specified BAR so that it can no longer be accessed by the CPU.
     *
     * Unmaps the specified BAR that was previously mapped via
     * \c pci_device_map_region.
     *
     * \param dev          Device whose memory region is to be mapped.
     * \param region       Region, on the range [0, 5], that is to be mapped.
     *
     * \return
     * Zero on success or an \c errno value on failure.
     *
     * \sa pci_device_map_range, pci_device_unmap_range
     * \deprecated
     */
    int
    pci_device_unmap_region( struct pci_device * dev, unsigned region )
    {
        int err;
    
        if (dev == NULL) {
            return EFAULT;
        }
    
        if ((region > 5) || (dev->regions[region].size == 0)) {
            return ENOENT;
        }
    
        err = pci_device_unmap_range(dev, dev->regions[region].memory,
                                     dev->regions[region].size);
        if (!err) {
            dev->regions[region].memory = NULL;
        }
    
        return err;
    }
    
    
    /**
     * 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, pci_device_unmap_range
     * \deprecated
     */
    int
    pci_device_unmap_memory_range(struct pci_device *dev, void *memory,
                                  pciaddr_t size)
    {
        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;
        }
    
        for (i = 0; i < devp->num_mappings; i++) {
            if ((devp->mappings[i].memory == memory)
                && (devp->mappings[i].size == size)) {
                break;
            }
        }
    
        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 err;
    }
    
    
    /**
     * Read arbitrary bytes from device's PCI config space
     *
     * Reads data from the device's PCI configuration space.  As with the system
     * read command, less data may be returned, without an error, than was
     * requested.  This is particularly the case if a non-root user tries to read
     * beyond the first 64-bytes of configuration space.
     *
     * \param dev         Device whose PCI configuration data is to be read.
     * \param data        Location to store the data
     * \param offset      Initial byte offset to read
     * \param size        Total number of bytes to read
     * \param bytes_read  Location to store the actual number of bytes read.  This
     *                    pointer may be \c NULL.
     *
     * \returns
     * Zero on success or an errno value on failure.
     *
     * \note
     * Data read from PCI configuration space using this routine is \b not
     * byte-swapped to the host's byte order.  PCI configuration data is always
     * stored in little-endian order, and that is what this routine returns.
     */
    int
    pci_device_cfg_read( struct pci_device * dev, void * data,
    		     pciaddr_t offset, pciaddr_t size,
    		     pciaddr_t * bytes_read )
    {
        pciaddr_t  scratch;
    
        if ( (dev == NULL) || (data == NULL) ) {
    	return EFAULT;
        }
    
        return pci_sys->methods->read( dev, data, offset, size,
    				   (bytes_read == NULL)
    				   ? & scratch : bytes_read );
    }
    
    
    int
    pci_device_cfg_read_u8( struct pci_device * dev, uint8_t * data,
    			pciaddr_t offset )
    {
        pciaddr_t bytes;
        int err = pci_device_cfg_read( dev, data, offset, 1, & bytes );
    
        if ( (err == 0) && (bytes != 1) ) {
    	err = ENXIO;
        }
    
        return err;
    }
    
    
    int
    pci_device_cfg_read_u16( struct pci_device * dev, uint16_t * data,
    			 pciaddr_t offset )
    {
        pciaddr_t bytes;
        int err = pci_device_cfg_read( dev, data, offset, 2, & bytes );
    
        if ( (err == 0) && (bytes != 2) ) {
    	err = ENXIO;
        }
    
        *data = LETOH_16( *data );
        return err;
    }
    
    
    int
    pci_device_cfg_read_u32( struct pci_device * dev, uint32_t * data,
    			 pciaddr_t offset )
    {
        pciaddr_t bytes;
        int err = pci_device_cfg_read( dev, data, offset, 4, & bytes );
    
        if ( (err == 0) && (bytes != 4) ) {
    	err = ENXIO;
        }
    
        *data = LETOH_32( *data );
        return err;
    }
    
    
    /**
     * Write arbitrary bytes to device's PCI config space
     *
     * Writes data to the device's PCI configuration space.  As with the system
     * write command, less data may be written, without an error, than was
     * requested.
     *
     * \param dev         Device whose PCI configuration data is to be written.
     * \param data        Location of the source data
     * \param offset      Initial byte offset to write
     * \param size        Total number of bytes to write
     * \param bytes_read  Location to store the actual number of bytes written.
     *                    This pointer may be \c NULL.
     *
     * \returns
     * Zero on success or an errno value on failure.
     *
     * \note
     * Data written to PCI configuration space using this routine is \b not
     * byte-swapped from the host's byte order.  PCI configuration data is always
     * stored in little-endian order, so data written with this routine should be
     * put in that order in advance.
     */
    int
    pci_device_cfg_write( struct pci_device * dev, const void * data,
    		      pciaddr_t offset, pciaddr_t size,
    		      pciaddr_t * bytes_written )
    {
        pciaddr_t  scratch;
    
        if ( (dev == NULL) || (data == NULL) ) {
    	return EFAULT;
        }
    
        return pci_sys->methods->write( dev, data, offset, size,
    				    (bytes_written == NULL)
    				    ? & scratch : bytes_written );
    }
    
    
    int
    pci_device_cfg_write_u8(struct pci_device *dev, uint8_t data,
    			pciaddr_t offset)
    {
        pciaddr_t bytes;
        int err = pci_device_cfg_write(dev, & data, offset, 1, & bytes);
    
        if ( (err == 0) && (bytes != 1) ) {
    	err = ENOSPC;
        }
    
    
        return err;
    }
    
    
    int
    pci_device_cfg_write_u16(struct pci_device *dev, uint16_t data,
    			 pciaddr_t offset)
    {
        pciaddr_t bytes;
        const uint16_t temp = HTOLE_16(data);
        int err = pci_device_cfg_write( dev, & temp, offset, 2, & bytes );
    
        if ( (err == 0) && (bytes != 2) ) {
    	err = ENOSPC;
        }
    
    
        return err;
    }
    
    
    int
    pci_device_cfg_write_u32(struct pci_device *dev, uint32_t data,
    			 pciaddr_t offset)
    {
        pciaddr_t bytes;
        const uint32_t temp = HTOLE_32(data);
        int err = pci_device_cfg_write( dev, & temp, offset, 4, & bytes );
    
        if ( (err == 0) && (bytes != 4) ) {
    	err = ENOSPC;
        }
    
    
        return err;
    }
    
    
    int
    pci_device_cfg_write_bits( struct pci_device * dev, uint32_t mask,
    			   uint32_t data, pciaddr_t offset )
    {
        uint32_t  temp;
        int err;
    
        err = pci_device_cfg_read_u32( dev, & temp, offset );
        if ( ! err ) {
    	temp &= ~mask;
    	temp |= data;
    
    	err = pci_device_cfg_write_u32(dev, temp, offset);
        }
    
        return err;
    }
    
    void
    pci_device_enable(struct pci_device *dev)
    {
        if (dev == NULL) {
    	return;
        }
    
        if (pci_sys->methods->enable)
    	pci_sys->methods->enable(dev);
    }
    
    void
    pci_device_disable(struct pci_device *dev)
    {
    	if (dev == NULL)
    		return;
    
    	if (pci_sys->methods->disable)
    		pci_sys->methods->disable(dev);
    }
    
    /**
     * Map the legacy memory space for the PCI domain containing \c dev.
     *
     * \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 map_flags    Flag bits controlling how the mapping is accessed.
     * \param addr         Location to store the mapped address.
     *
     * \returns
     * Zero on success or an \c errno value on failure.
     */
    int
    pci_device_map_legacy(struct pci_device *dev, pciaddr_t base, pciaddr_t size,
    		      unsigned map_flags, void **addr)
    {
        if (base > 0x100000 || base + size > 0x100000)
    	return EINVAL;
    
        if (!pci_sys->methods->map_legacy)
    	return ENOSYS;
    
        return pci_sys->methods->map_legacy(dev, base, size, map_flags, addr);
    }
    
    /**
     * Unmap the legacy memory space for the PCI domain containing \c dev.
     *
     * \param dev          Device whose memory region is to be unmapped.
     * \param addr         Location of the mapped address.
     * \param size         Size of the range to be unmapped.
     *
     * \returns
     * Zero on success or an \c errno value on failure.
     */
    int
    pci_device_unmap_legacy(struct pci_device *dev, void *addr, pciaddr_t size)
    {
        if (!pci_sys->methods->unmap_legacy)
    	return ENOSYS;
    
        return pci_sys->methods->unmap_legacy(dev, addr, size);
    }