From 64af050c3803ed61837d9e9109c7a4e239a5c0b8 Mon Sep 17 00:00:00 2001 From: Ian Romanick Date: Wed, 12 Apr 2006 22:56:50 +0000 Subject: [PATCH] Bump to version 0.4.0. Add multiple-inclusion protection. Add new function to write masked bits to PCI config space. This mirrors functionality currently available in X.org that is slated to be removed. Gut old regex based search mechanism with a new mechanism that is modeled after the Linux kernel. In addition to searching for devices by device / vendor ID, it is possible to search for devices by their domain / bus / slot / function. Fix serious bus in the reading of ROMs and in the unmapping of regions. The main point is that the map routine depens on the pci_mem_region::memory pointer being non-NULL only when the region is mapped. Therefore, the unmap routine should set it to NULL after unmapping. Update to use new search API. --- ChangeLog | 34 +++++++++ configure.ac | 2 +- include/pciaccess.h | 53 ++++++++++++-- src/Makefile.am | 2 +- src/common_interface.c | 19 +++++ src/common_iterator.c | 188 +++++++++++++++++++++++++++---------------------- src/linux_sysfs.c | 30 ++++++-- src/scanpci.c | 2 +- 8 files changed, 232 insertions(+), 98 deletions(-) diff --git a/ChangeLog b/ChangeLog index 8494f32..3aa3d3d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,37 @@ +2006-04-12 Ian Romanick + + * configure.ac: + * src/Makefile.am: + Bump to version 0.4.0. + + * include/pciaccess.h: + Add multiple-inclusion protection. + + * src/common_interface.c: (pci_device_cfg_write_bits): + Add new function to write masked bits to PCI config space. + This mirrors functionality currently available in X.org that is slated + to be removed. + + * src/common_iterator.c: (pci_slot_match_iterator_create), + (pci_id_match_iterator_create), (pci_iterator_destroy), + (pci_device_next), (pci_device_find_by_slot): + Gut old regex based search mechanism with a new mechanism that is + modeled after the Linux kernel. In addition to searching for + devices by device / vendor ID, it is possible to search for devices + by their domain / bus / slot / function. + + * src/linux_sysfs.c: (pci_device_linux_sysfs_read_rom), + (pci_device_linux_sysfs_map_region), + (pci_device_linux_sysfs_unmap_region): + Fix serious bus in the reading of ROMs and in the unmapping of + regions. The main point is that the map routine depens on the + pci_mem_region::memory pointer being non-NULL only when the region + is mapped. Therefore, the unmap routine should set it to NULL after + unmapping. + + * src/scanpci.c: (main): + Update to use new search API. + 2006-03-27 Ian Romanick * Makefile.am: diff --git a/configure.ac b/configure.ac index 4d0e1b8..c901895 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.3.0, [none yet], libpciaccess) +AC_INIT(libpciaccess, 0.4.0, [none yet], libpciaccess) AM_INIT_AUTOMAKE([dist-bzip2]) AM_MAINTAINER_MODE diff --git a/include/pciaccess.h b/include/pciaccess.h index d4cb04d..97a19a7 100644 --- a/include/pciaccess.h +++ b/include/pciaccess.h @@ -28,6 +28,9 @@ * \author Ian Romanick */ +#ifndef PCIACCESS_H +#define PCIACCESS_H + #include typedef uint64_t pciaddr_t; @@ -35,6 +38,7 @@ typedef uint64_t pciaddr_t; struct pci_device; struct pci_device_iterator; struct pci_id_match; +struct pci_slot_match; int pci_device_read_rom( struct pci_device * dev, void * buffer ); @@ -51,16 +55,22 @@ int pci_system_init( void ); void pci_system_cleanup( void ); -struct pci_device_iterator * pci_iterator_create( const char *regex ); +struct pci_device_iterator * pci_slot_match_iterator_create( + const struct pci_slot_match * match ); + +struct pci_device_iterator * pci_id_match_iterator_create( + const struct pci_id_match * match ); void pci_iterator_destroy( struct pci_device_iterator * iter ); struct pci_device * pci_device_next( struct pci_device_iterator * iter ); +struct pci_device * pci_device_find_by_slot( uint32_t domain, uint32_t bus, + uint32_t dev, uint32_t func ); + void pci_get_strings( const struct pci_id_match * m, const char ** device_name, const char ** vendor_name, const char ** subdevice_name, const char ** subvendor_name ); -const char * pci_get_name( const struct pci_id_match * m ); const char * pci_device_get_device_name( const struct pci_device * dev ); const char * pci_device_get_subdevice_name( const struct pci_device * dev ); const char * pci_device_get_vendor_name( const struct pci_device * dev ); @@ -83,11 +93,20 @@ int pci_device_cfg_write_u16( struct pci_device * dev, const uint16_t * data, pciaddr_t offset ); int pci_device_cfg_write_u32( struct pci_device * dev, const uint32_t * data, pciaddr_t offset ); - +int pci_device_cfg_write_bits( struct pci_device * dev, uint32_t mask, + uint32_t data, pciaddr_t offset ); #define PCI_MATCH_ANY (~0) /** + * Compare two PCI ID values (either vendor or device). This is used + * internally to compare the fields of \c pci_id_match to the fields of + * \c pci_device. + */ +#define PCI_ID_COMPARE(a, b) \ + (((a) == PCI_MATCH_ANY) || ((a) == (b))) + +/** */ struct pci_id_match { /** @@ -119,6 +138,26 @@ struct pci_id_match { /** + */ +struct pci_slot_match { + /** + * \name Device slot matching controls + * + * Control the search based on the domain, bus, slot, and function of + * the device. Setting any of these fields to \c PCI_MATCH_ANY will cause + * the field to not be used in the comparison. + */ + /*@{*/ + uint32_t domain; + uint32_t bus; + uint32_t dev; + uint32_t func; + /*@}*/ + + intptr_t match_data; +}; + +/** * BAR descriptor for a PCI device. */ struct pci_mem_region { @@ -195,7 +234,9 @@ struct pci_device { /*@}*/ /** - * Device's class and subclass packed into a single 32-bit value. + * Device's class, subclass, and programming interface packed into a + * single 32-bit value. The class is at bits [23:16], subclass is at + * bits [15:8], and programming interface is at [7:0]. */ uint32_t device_class; @@ -231,7 +272,7 @@ struct pci_device { * It is the user's responsability to free this memory before destroying * the \c pci_device structure. */ - void * user_data; + intptr_t user_data; }; @@ -276,3 +317,5 @@ struct pci_agp_info { uint8_t calibration_cycle_timing; uint8_t max_requests; }; + +#endif /* PCIACCESS_H */ diff --git a/src/Makefile.am b/src/Makefile.am index f815ee5..d8d5472 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -34,7 +34,7 @@ INCLUDES = -I$(top_srcdir)/include libpciaccess_la_LIBADD = @PCIACCESS_LIBS@ -libpciaccess_la_LDFLAGS = -version-number 0:3:0 -no-undefined +libpciaccess_la_LDFLAGS = -version-number 0:4:0 -no-undefined libpciaccessincludedir = $(includedir) libpciaccessinclude_HEADERS = \ diff --git a/src/common_interface.c b/src/common_interface.c index 811d9a1..57908f4 100644 --- a/src/common_interface.c +++ b/src/common_interface.c @@ -343,3 +343,22 @@ pci_device_cfg_write_u32( struct pci_device * dev, const uint32_t * data, 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; +} diff --git a/src/common_iterator.c b/src/common_iterator.c index ffff041..0196eb2 100644 --- a/src/common_iterator.c +++ b/src/common_iterator.c @@ -30,9 +30,7 @@ */ #include -#include #include -#include #include "pciaccess.h" #include "pciaccess_private.h" @@ -44,42 +42,67 @@ */ struct pci_device_iterator { unsigned next_index; - regex_t reg; - int no_regex; + + enum { + match_any, + match_slot, + match_id + } mode; + + union { + struct pci_slot_match slot; + struct pci_id_match id; + } match; }; +/** + * Create an iterator based on a regular expression. + * + * \return + * A pointer to a fully initialized \c pci_device_iterator structure on + * success, or \c NULL on failure. + * + * \sa pci_id_match_iterator_create, pci_device_next, pci_iterator_destroy + */ +struct pci_device_iterator * +pci_slot_match_iterator_create( const struct pci_slot_match * match ) +{ + struct pci_device_iterator * iter; + + if ( pci_sys == NULL ) { + return NULL; + } + + iter = malloc( sizeof( *iter ) ); + if ( iter != NULL ) { + iter->next_index = 0; + + if ( match != NULL ) { + iter->mode = match_slot; + + (void) memcpy( & iter->match.slot, match, sizeof( *match ) ); + } + else { + iter->mode = match_any; + } + } + + return iter; +} + /** * Create an iterator based on a regular expression. - * - * The set of devices to be iterated is selected by the regular expression - * passed in \c regex. The expression matches against an extended PCI bus - * identifier string. The format of this string is - * "domain:bus:slot.function:vendor:device_id:subvendor:subdevice_id:class". - * Unlike classic X bus IDs, all values in the extened bus identifier string - * are in hexadecimal. To simplify the required regular expressions, all hex - * digits greater than 9 will be lower-case. * - * To match all devices in domain 0, the expression "0:.+" would be used. To - * match all devices by ATI, the expression ".+:1002:.+". To match all devices - * with a class of display, a class of multimedia and a subclass of video, or - * a class of processor and a subclass of coprocessor, the expression - * ".+:(03[[:hex:]]2|0400|0b40|0001)$" would be used. Since this is a fully - * function regular expression, arbitrarilly complex matches can be requested. - * - * \param pci_sys Handle for the PCI subsystem. - * \param regex Pointer to the regular expression to match against. If - * \c NULL is passed, all devices will be matched. - * * \return * A pointer to a fully initialized \c pci_device_iterator structure on * success, or \c NULL on failure. - * - * \sa pci_device_next, pci_iterator_destroy + * + * \sa pci_slot_match_iterator_create, pci_device_next, pci_iterator_destroy */ struct pci_device_iterator * -pci_iterator_create( const char * re ) +pci_id_match_iterator_create( const struct pci_id_match * match ) { struct pci_device_iterator * iter; @@ -91,21 +114,13 @@ pci_iterator_create( const char * re ) if ( iter != NULL ) { iter->next_index = 0; - /* If the caller passed a NULL or empty expression, then we don't try - * to compile the expression. Instead we set a flag that tells the - * iterator routine to iterate every device in the list. - */ - if ( (re != NULL) && (strlen( re ) > 0) ) { - int err = regcomp( & iter->reg, re, REG_EXTENDED | REG_NOSUB ); - if ( err != 0 ) { - free( iter ); - iter = NULL; - } + if ( match != NULL ) { + iter->mode = match_id; - iter->no_regex = 0; + (void) memcpy( & iter->match.id, match, sizeof( *match ) ); } else { - iter->no_regex = 1; + iter->mode = match_any; } } @@ -124,40 +139,11 @@ void pci_iterator_destroy( struct pci_device_iterator * iter ) { if ( iter != NULL ) { - if ( ! iter->no_regex ) { - regfree( & iter->reg ); - } - free( iter ); } } -static void -fill_device_string( struct pci_device_private * d ) -{ - - if ( d->device_string == NULL ) { - char * const string = malloc( 40 ); - if ( string != NULL ) { - pci_device_probe( (struct pci_device *) d ); - sprintf( string, "%04x:%02x:%02x.%u:%04x:%04x:%04x:%04x:%06x", - d->base.domain, - d->base.bus, - d->base.dev, - d->base.func, - d->base.vendor_id, - d->base.device_id, - d->base.subvendor_id, - d->base.subdevice_id, - d->base.device_class ); - - d->device_string = string; - } - } -} - - /** * Iterate to the next PCI device. * @@ -166,45 +152,77 @@ fill_device_string( struct pci_device_private * d ) * \return * A pointer to a \c pci_device, or \c NULL when all devices have been * iterated. - * - * \bug - * The only time this routine should be able to return \c NULL is when the - * end of the list is hit. However, there is a memory allocation (via - * \c fill_device_string) that can fail. If this allocation fails, \c NULL - * will be erroneously returned. What should be done here? Pre-fill the - * device strings in \c pci_iterator_create? */ struct pci_device * pci_device_next( struct pci_device_iterator * iter ) { struct pci_device_private * d = NULL; - if ( iter->no_regex ) { + switch( iter->mode ) { + case match_any: if ( iter->next_index < pci_sys->num_devices ) { d = & pci_sys->devices[ iter->next_index ]; iter->next_index++; } - } - else { + + break; + + case match_slot: { while ( iter->next_index < pci_sys->num_devices ) { struct pci_device_private * const temp = & pci_sys->devices[ iter->next_index ]; - if ( temp->device_string == NULL ) { - fill_device_string( temp ); - if ( temp->device_string == NULL ) { - break; - } + iter->next_index++; + if ( PCI_ID_COMPARE( iter->match.slot.domain, temp->base.domain ) + && PCI_ID_COMPARE( iter->match.slot.bus, temp->base.bus ) + && PCI_ID_COMPARE( iter->match.slot.dev, temp->base.dev ) + && PCI_ID_COMPARE( iter->match.slot.func, temp->base.func ) ) { + d = temp; + break; } + } + + break; + } - iter->next_index++; + case match_id: { + while ( iter->next_index < pci_sys->num_devices ) { + struct pci_device_private * const temp = + & pci_sys->devices[ iter->next_index ]; - if ( regexec( & iter->reg, temp->device_string, 0, NULL, 0 ) == 0 ) { + iter->next_index++; + if ( PCI_ID_COMPARE( iter->match.id.vendor_id, temp->base.vendor_id ) + && PCI_ID_COMPARE( iter->match.id.device_id, temp->base.device_id ) + && PCI_ID_COMPARE( iter->match.id.subvendor_id, temp->base.subvendor_id ) + && PCI_ID_COMPARE( iter->match.id.subdevice_id, temp->base.subdevice_id ) + && ((temp->base.device_class & iter->match.id.device_class_mask) + == iter->match.id.device_class) ) { d = temp; break; } } + + break; } - + } + return (struct pci_device *) d; } + + +struct pci_device * +pci_device_find_by_slot( uint32_t domain, uint32_t bus, uint32_t dev, + uint32_t func ) +{ + struct pci_device_iterator iter; + + + iter.next_index = 0; + iter.mode = match_slot; + iter.match.slot.domain = domain; + iter.match.slot.bus = bus; + iter.match.slot.dev = dev; + iter.match.slot.func = func; + + return pci_device_next( & iter ); +} \ No newline at end of file diff --git a/src/linux_sysfs.c b/src/linux_sysfs.c index d6d6b10..7008cef 100644 --- a/src/linux_sysfs.c +++ b/src/linux_sysfs.c @@ -259,6 +259,7 @@ pci_device_linux_sysfs_read_rom( struct pci_device * dev, void * buffer ) int fd; struct stat st; int err = 0; + size_t total_bytes; snprintf( name, 255, "%s/%04x:%02x:%02x.%1u/rom", @@ -288,9 +289,20 @@ pci_device_linux_sysfs_read_rom( struct pci_device * dev, void * buffer ) write( fd, "1", 1 ); lseek( fd, 0, SEEK_SET ); - if ( read( fd, buffer, st.st_size ) == -1 ) { - err = errno; + for ( total_bytes = 0 ; total_bytes < st.st_size ; /* empty */ ) { + const int bytes = read( fd, (char *) buffer + total_bytes, + st.st_size - total_bytes ); + if ( bytes == -1 ) { + err = errno; + break; + } + else if ( bytes == 0 ) { + break; + } + + total_bytes += bytes; } + lseek( fd, 0, SEEK_SET ); write( fd, "0", 1 ); @@ -440,7 +452,7 @@ pci_device_linux_sysfs_map_region( struct pci_device * dev, unsigned region, char name[256]; int fd; int err = 0; - int prot = (write_enable) ? (PROT_READ | PROT_WRITE) : PROT_READ; + const int prot = (write_enable) ? (PROT_READ | PROT_WRITE) : PROT_READ; snprintf( name, 255, "%s/%04x:%02x:%02x.%1u/resource%u", @@ -488,6 +500,14 @@ pci_device_linux_sysfs_map_region( struct pci_device * dev, unsigned region, static int pci_device_linux_sysfs_unmap_region( struct pci_device * dev, unsigned region ) { - return munmap( dev->regions[ region ].memory, - dev->regions[ region ].size ); + int err = 0; + + if ( munmap( dev->regions[ region ].memory, dev->regions[ region ].size ) + == -1 ) { + err = errno; + } + + dev->regions[ region ].memory = NULL; + + return err; } diff --git a/src/scanpci.c b/src/scanpci.c index 1c4622f..adec55d 100644 --- a/src/scanpci.c +++ b/src/scanpci.c @@ -147,7 +147,7 @@ int main( int argc, char ** argv ) pci_system_init(); - iter = pci_iterator_create( (argc > 1) ? argv[1] : NULL ); + iter = pci_slot_match_iterator_create( NULL ); while ( (dev = pci_device_next( iter )) != NULL ) { pci_device_probe( dev ); -- 2.7.4