xref: /libpciaccess/src/linux_sysfs.c (revision 5e8d4c19)
15a04522aSIan Romanick /*
25a04522aSIan Romanick  * (C) Copyright IBM Corporation 2006
35a04522aSIan Romanick  * All Rights Reserved.
45a04522aSIan Romanick  *
55a04522aSIan Romanick  * Permission is hereby granted, free of charge, to any person obtaining a
65a04522aSIan Romanick  * copy of this software and associated documentation files (the "Software"),
75a04522aSIan Romanick  * to deal in the Software without restriction, including without limitation
85a04522aSIan Romanick  * on the rights to use, copy, modify, merge, publish, distribute, sub
95a04522aSIan Romanick  * license, and/or sell copies of the Software, and to permit persons to whom
105a04522aSIan Romanick  * the Software is furnished to do so, subject to the following conditions:
115a04522aSIan Romanick  *
125a04522aSIan Romanick  * The above copyright notice and this permission notice (including the next
135a04522aSIan Romanick  * paragraph) shall be included in all copies or substantial portions of the
145a04522aSIan Romanick  * Software.
155a04522aSIan Romanick  *
165a04522aSIan Romanick  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
175a04522aSIan Romanick  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
185a04522aSIan Romanick  * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.  IN NO EVENT SHALL
195a04522aSIan Romanick  * IBM AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
205a04522aSIan Romanick  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
215a04522aSIan Romanick  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
225a04522aSIan Romanick  * DEALINGS IN THE SOFTWARE.
235a04522aSIan Romanick  */
245a04522aSIan Romanick 
255a04522aSIan Romanick /**
265a04522aSIan Romanick  * \file linux_sysfs.c
275a04522aSIan Romanick  * Access PCI subsystem using Linux's sysfs interface.  This interface is
285a04522aSIan Romanick  * available starting somewhere in the late 2.5.x kernel phase, and is the
29d43d21c8SAlan Coopersmith  * preferred method on all 2.6.x kernels.
305a04522aSIan Romanick  *
315a04522aSIan Romanick  * \author Ian Romanick <[email protected]>
325a04522aSIan Romanick  */
335a04522aSIan Romanick 
345a04522aSIan Romanick #define _GNU_SOURCE
355a04522aSIan Romanick 
365a04522aSIan Romanick #include <stdlib.h>
375a04522aSIan Romanick #include <string.h>
385a04522aSIan Romanick #include <stdio.h>
395a04522aSIan Romanick #include <unistd.h>
405a04522aSIan Romanick #include <sys/types.h>
415a04522aSIan Romanick #include <sys/stat.h>
425a04522aSIan Romanick #include <fcntl.h>
435a04522aSIan Romanick #include <sys/mman.h>
445a04522aSIan Romanick #include <dirent.h>
455a04522aSIan Romanick #include <errno.h>
465a04522aSIan Romanick 
4708ff9f7fSIan Romanick #include "config.h"
4808ff9f7fSIan Romanick 
4908ff9f7fSIan Romanick #ifdef HAVE_MTRR
5008ff9f7fSIan Romanick #include <asm/mtrr.h>
5108ff9f7fSIan Romanick #include <sys/ioctl.h>
5208ff9f7fSIan Romanick #endif
5308ff9f7fSIan Romanick 
545a04522aSIan Romanick #include "pciaccess.h"
555a04522aSIan Romanick #include "pciaccess_private.h"
56a7faac3dSIan Romanick #include "linux_devmem.h"
575a04522aSIan Romanick 
58*5e8d4c19SAdam Jackson static const struct pci_system_methods linux_sysfs_methods;
595a04522aSIan Romanick 
605a04522aSIan Romanick #define SYS_BUS_PCI "/sys/bus/pci/devices"
615a04522aSIan Romanick 
62*5e8d4c19SAdam Jackson static int
63*5e8d4c19SAdam Jackson pci_device_linux_sysfs_read( struct pci_device * dev, void * data,
64*5e8d4c19SAdam Jackson 			     pciaddr_t offset, pciaddr_t size,
65*5e8d4c19SAdam Jackson 			     pciaddr_t * bytes_read );
665a04522aSIan Romanick 
67061a011cSIan Romanick static int populate_entries(struct pci_system * pci_sys);
685a04522aSIan Romanick 
695a04522aSIan Romanick /**
705a04522aSIan Romanick  * Attempt to access PCI subsystem using Linux's sysfs interface.
715a04522aSIan Romanick  */
72adc46f65SJulien Cristau _pci_hidden int
735a04522aSIan Romanick pci_system_linux_sysfs_create( void )
745a04522aSIan Romanick {
755a04522aSIan Romanick     int err = 0;
765a04522aSIan Romanick     struct stat st;
775a04522aSIan Romanick 
785a04522aSIan Romanick 
795a04522aSIan Romanick     /* If the directory "/sys/bus/pci/devices" exists, then the PCI subsystem
805a04522aSIan Romanick      * can be accessed using this interface.
815a04522aSIan Romanick      */
825a04522aSIan Romanick 
835a04522aSIan Romanick     if ( stat( SYS_BUS_PCI, & st ) == 0 ) {
845a04522aSIan Romanick 	pci_sys = calloc( 1, sizeof( struct pci_system ) );
855a04522aSIan Romanick 	if ( pci_sys != NULL ) {
865a04522aSIan Romanick 	    pci_sys->methods = & linux_sysfs_methods;
874586bb67SAdam Jackson #ifdef HAVE_MTRR
884586bb67SAdam Jackson 	    pci_sys->mtrr_fd = open("/proc/mtrr", O_WRONLY);
894586bb67SAdam Jackson #endif
90061a011cSIan Romanick 	    err = populate_entries(pci_sys);
915a04522aSIan Romanick 	}
925a04522aSIan Romanick 	else {
935a04522aSIan Romanick 	    err = ENOMEM;
945a04522aSIan Romanick 	}
955a04522aSIan Romanick     }
965a04522aSIan Romanick     else {
975a04522aSIan Romanick 	err = errno;
985a04522aSIan Romanick     }
995a04522aSIan Romanick 
1005a04522aSIan Romanick     return err;
1015a04522aSIan Romanick }
1025a04522aSIan Romanick 
1035a04522aSIan Romanick 
1045a04522aSIan Romanick /**
1055a04522aSIan Romanick  * Filter out the names "." and ".." from the scanned sysfs entries.
1065a04522aSIan Romanick  *
1075a04522aSIan Romanick  * \param d  Directory entry being processed by \c scandir.
1085a04522aSIan Romanick  *
1095a04522aSIan Romanick  * \return
1105a04522aSIan Romanick  * Zero if the entry name matches either "." or "..", non-zero otherwise.
1115a04522aSIan Romanick  *
1125a04522aSIan Romanick  * \sa scandir, populate_entries
1135a04522aSIan Romanick  */
1145a04522aSIan Romanick static int
1155a04522aSIan Romanick scan_sys_pci_filter( const struct dirent * d )
1165a04522aSIan Romanick {
1175a04522aSIan Romanick     return !((strcmp( d->d_name, "." ) == 0)
1185a04522aSIan Romanick 	     || (strcmp( d->d_name, ".." ) == 0));
1195a04522aSIan Romanick }
1205a04522aSIan Romanick 
1215a04522aSIan Romanick 
122061a011cSIan Romanick int
1235a04522aSIan Romanick populate_entries( struct pci_system * p )
1245a04522aSIan Romanick {
1255a04522aSIan Romanick     struct dirent ** devices;
1265a04522aSIan Romanick     int n;
1275a04522aSIan Romanick     int i;
12808ff9f7fSIan Romanick     int err = 0;
1295a04522aSIan Romanick 
1305a04522aSIan Romanick 
1315a04522aSIan Romanick     n = scandir( SYS_BUS_PCI, & devices, scan_sys_pci_filter, alphasort );
1325a04522aSIan Romanick     if ( n > 0 ) {
1335a04522aSIan Romanick 	p->num_devices = n;
1345a04522aSIan Romanick 	p->devices = calloc( n, sizeof( struct pci_device_private ) );
1355a04522aSIan Romanick 
136061a011cSIan Romanick 	if (p->devices != NULL) {
1375a04522aSIan Romanick 	    for (i = 0 ; i < n ; i++) {
138061a011cSIan Romanick 		uint8_t config[48];
139061a011cSIan Romanick 		pciaddr_t bytes;
1405a04522aSIan Romanick 		unsigned dom, bus, dev, func;
141061a011cSIan Romanick 		struct pci_device_private *device =
142061a011cSIan Romanick 			(struct pci_device_private *) &p->devices[i];
1435a04522aSIan Romanick 
1445a04522aSIan Romanick 
1455a04522aSIan Romanick 		sscanf(devices[i]->d_name, "%04x:%02x:%02x.%1u",
1465a04522aSIan Romanick 		       & dom, & bus, & dev, & func);
1475a04522aSIan Romanick 
148061a011cSIan Romanick 		device->base.domain = dom;
149061a011cSIan Romanick 		device->base.bus = bus;
150061a011cSIan Romanick 		device->base.dev = dev;
151061a011cSIan Romanick 		device->base.func = func;
152061a011cSIan Romanick 
153061a011cSIan Romanick 
154061a011cSIan Romanick 		err = pci_device_linux_sysfs_read(& device->base, config, 0,
155061a011cSIan Romanick 						  48, & bytes);
156061a011cSIan Romanick 		if ((bytes == 48) && !err) {
157061a011cSIan Romanick 		    device->base.vendor_id = (uint16_t)config[0]
158061a011cSIan Romanick 			+ ((uint16_t)config[1] << 8);
159061a011cSIan Romanick 		    device->base.device_id = (uint16_t)config[2]
160061a011cSIan Romanick 			+ ((uint16_t)config[3] << 8);
161061a011cSIan Romanick 		    device->base.device_class = (uint32_t)config[9]
162061a011cSIan Romanick 			+ ((uint32_t)config[10] << 8)
163061a011cSIan Romanick 			+ ((uint32_t)config[11] << 16);
164061a011cSIan Romanick 		    device->base.revision = config[8];
165061a011cSIan Romanick 		    device->base.subvendor_id = (uint16_t)config[44]
166061a011cSIan Romanick 			+ ((uint16_t)config[45] << 8);
167061a011cSIan Romanick 		    device->base.subdevice_id = (uint16_t)config[46]
168061a011cSIan Romanick 			+ ((uint16_t)config[47] << 8);
169061a011cSIan Romanick 		}
170061a011cSIan Romanick 
171061a011cSIan Romanick 		if (err) {
172061a011cSIan Romanick 		    break;
1735a04522aSIan Romanick 		}
1745a04522aSIan Romanick 	    }
1755a04522aSIan Romanick 	}
176061a011cSIan Romanick 	else {
177061a011cSIan Romanick 	    err = ENOMEM;
178061a011cSIan Romanick 	}
179061a011cSIan Romanick     }
180061a011cSIan Romanick 
181cdbdfaf6SKeith Packard     for (i = 0; i < n; i++)
182cdbdfaf6SKeith Packard 	free(devices[i]);
183cdbdfaf6SKeith Packard     free(devices);
184cdbdfaf6SKeith Packard 
185061a011cSIan Romanick     if (err) {
186061a011cSIan Romanick 	free(p->devices);
187061a011cSIan Romanick 	p->devices = NULL;
188061a011cSIan Romanick     }
189061a011cSIan Romanick 
190061a011cSIan Romanick     return err;
191061a011cSIan Romanick }
1925a04522aSIan Romanick 
1935a04522aSIan Romanick 
1945a04522aSIan Romanick static int
1955a04522aSIan Romanick pci_device_linux_sysfs_probe( struct pci_device * dev )
1965a04522aSIan Romanick {
1975a04522aSIan Romanick     char     name[256];
1985a04522aSIan Romanick     uint8_t  config[256];
1995a04522aSIan Romanick     char     resource[512];
2005a04522aSIan Romanick     int fd;
2015a04522aSIan Romanick     pciaddr_t bytes;
2025a04522aSIan Romanick     unsigned i;
2035a04522aSIan Romanick     int err;
2045a04522aSIan Romanick 
2055a04522aSIan Romanick 
2065a04522aSIan Romanick     err = pci_device_linux_sysfs_read( dev, config, 0, 256, & bytes );
2075a04522aSIan Romanick     if ( bytes >= 64 ) {
2082ba1a0e4SIan Romanick 	struct pci_device_private *priv = (struct pci_device_private *) dev;
2092ba1a0e4SIan Romanick 
2105a04522aSIan Romanick 	dev->irq = config[60];
2112ba1a0e4SIan Romanick 	priv->header_type = config[14];
2122ba1a0e4SIan Romanick 
2135a04522aSIan Romanick 
2145a04522aSIan Romanick 	/* The PCI config registers can be used to obtain information
2155a04522aSIan Romanick 	 * about the memory and I/O regions for the device.  However,
2165a04522aSIan Romanick 	 * doing so requires some tricky parsing (to correctly handle
2175a04522aSIan Romanick 	 * 64-bit memory regions) and requires writing to the config
2185a04522aSIan Romanick 	 * registers.  Since we'd like to avoid having to deal with the
2195a04522aSIan Romanick 	 * parsing issues and non-root users can write to PCI config
2205a04522aSIan Romanick 	 * registers, we use a different file in the device's sysfs
2215a04522aSIan Romanick 	 * directory called "resource".
2225a04522aSIan Romanick 	 *
2235a04522aSIan Romanick 	 * The resource file contains all of the needed information in
2245a04522aSIan Romanick 	 * a format that is consistent across all platforms.  Each BAR
2255a04522aSIan Romanick 	 * and the expansion ROM have a single line of data containing
2265a04522aSIan Romanick 	 * 3, 64-bit hex values:  the first address in the region,
2275a04522aSIan Romanick 	 * the last address in the region, and the region's flags.
2285a04522aSIan Romanick 	 */
2295a04522aSIan Romanick 	snprintf( name, 255, "%s/%04x:%02x:%02x.%1u/resource",
2305a04522aSIan Romanick 		  SYS_BUS_PCI,
2315a04522aSIan Romanick 		  dev->domain,
2325a04522aSIan Romanick 		  dev->bus,
2335a04522aSIan Romanick 		  dev->dev,
2345a04522aSIan Romanick 		  dev->func );
2355a04522aSIan Romanick 	fd = open( name, O_RDONLY );
2365a04522aSIan Romanick 	if ( fd != -1 ) {
2375a04522aSIan Romanick 	    char * next;
2385a04522aSIan Romanick 	    pciaddr_t  low_addr;
2395a04522aSIan Romanick 	    pciaddr_t  high_addr;
2405a04522aSIan Romanick 	    pciaddr_t  flags;
2415a04522aSIan Romanick 
2425a04522aSIan Romanick 
2435a04522aSIan Romanick 	    bytes = read( fd, resource, 512 );
2445a04522aSIan Romanick 	    resource[511] = '\0';
2455a04522aSIan Romanick 
2465a04522aSIan Romanick 	    close( fd );
2475a04522aSIan Romanick 
2485a04522aSIan Romanick 	    next = resource;
2495a04522aSIan Romanick 	    for ( i = 0 ; i < 6 ; i++ ) {
2505a04522aSIan Romanick 
2515a04522aSIan Romanick 		dev->regions[i].base_addr = strtoull( next, & next, 16 );
2525a04522aSIan Romanick 		high_addr = strtoull( next, & next, 16 );
2535a04522aSIan Romanick 		flags = strtoull( next, & next, 16 );
2545a04522aSIan Romanick 
2555a04522aSIan Romanick 		if ( dev->regions[i].base_addr != 0 ) {
2565a04522aSIan Romanick 		    dev->regions[i].size = (high_addr
2575a04522aSIan Romanick 					    - dev->regions[i].base_addr) + 1;
2585a04522aSIan Romanick 
2595a04522aSIan Romanick 		    dev->regions[i].is_IO = (flags & 0x01);
2605a04522aSIan Romanick 		    dev->regions[i].is_64 = (flags & 0x04);
2615a04522aSIan Romanick 		    dev->regions[i].is_prefetchable = (flags & 0x08);
2625a04522aSIan Romanick 		}
2635a04522aSIan Romanick 	    }
2645a04522aSIan Romanick 
2655a04522aSIan Romanick 	    low_addr = strtoull( next, & next, 16 );
2665a04522aSIan Romanick 	    high_addr = strtoull( next, & next, 16 );
2675a04522aSIan Romanick 	    flags = strtoull( next, & next, 16 );
2685a04522aSIan Romanick 	    if ( low_addr != 0 ) {
269a7faac3dSIan Romanick 		priv->rom_base = low_addr;
2705a04522aSIan Romanick 		dev->rom_size = (high_addr - low_addr) + 1;
2715a04522aSIan Romanick 	    }
2725a04522aSIan Romanick 	}
2735a04522aSIan Romanick     }
2745a04522aSIan Romanick 
2755a04522aSIan Romanick     return err;
2765a04522aSIan Romanick }
2775a04522aSIan Romanick 
2785a04522aSIan Romanick 
2795a04522aSIan Romanick static int
2805a04522aSIan Romanick pci_device_linux_sysfs_read_rom( struct pci_device * dev, void * buffer )
2815a04522aSIan Romanick {
2825a04522aSIan Romanick     char name[256];
2835a04522aSIan Romanick     int fd;
2845a04522aSIan Romanick     struct stat  st;
2855a04522aSIan Romanick     int err = 0;
286a5c86202SStuart Bennett     size_t rom_size;
28764af050cSIan Romanick     size_t total_bytes;
2885a04522aSIan Romanick 
2895a04522aSIan Romanick 
2905a04522aSIan Romanick     snprintf( name, 255, "%s/%04x:%02x:%02x.%1u/rom",
2915a04522aSIan Romanick 	      SYS_BUS_PCI,
2925a04522aSIan Romanick 	      dev->domain,
2935a04522aSIan Romanick 	      dev->bus,
2945a04522aSIan Romanick 	      dev->dev,
2955a04522aSIan Romanick 	      dev->func );
2965a04522aSIan Romanick 
2975a04522aSIan Romanick     fd = open( name, O_RDWR );
2985a04522aSIan Romanick     if ( fd == -1 ) {
29957cf6f1fSDave Airlie #ifdef LINUX_ROM
300a7faac3dSIan Romanick 	/* If reading the ROM using sysfs fails, fall back to the old
301a7faac3dSIan Romanick 	 * /dev/mem based interface.
30257cf6f1fSDave Airlie 	 * disable this for newer kernels using configure
303a7faac3dSIan Romanick 	 */
304a7faac3dSIan Romanick 	return pci_device_linux_devmem_read_rom(dev, buffer);
30557cf6f1fSDave Airlie #else
30657cf6f1fSDave Airlie 	return errno;
30757cf6f1fSDave Airlie #endif
3085a04522aSIan Romanick     }
3095a04522aSIan Romanick 
3105a04522aSIan Romanick 
3115a04522aSIan Romanick     if ( fstat( fd, & st ) == -1 ) {
3125a04522aSIan Romanick 	close( fd );
3135a04522aSIan Romanick 	return errno;
3145a04522aSIan Romanick     }
3155a04522aSIan Romanick 
316a5c86202SStuart Bennett     rom_size = st.st_size;
317a5c86202SStuart Bennett     if ( rom_size == 0 )
318a5c86202SStuart Bennett 	rom_size = 0x10000;
3195a04522aSIan Romanick 
3205a04522aSIan Romanick     /* This is a quirky thing on Linux.  Even though the ROM and the file
3215a04522aSIan Romanick      * for the ROM in sysfs are read-only, the string "1" must be written to
3225a04522aSIan Romanick      * the file to enable the ROM.  After the data has been read, "0" must be
3235a04522aSIan Romanick      * written to the file to disable the ROM.
3245a04522aSIan Romanick      */
3255a04522aSIan Romanick     write( fd, "1", 1 );
3265a04522aSIan Romanick     lseek( fd, 0, SEEK_SET );
3275a04522aSIan Romanick 
328a5c86202SStuart Bennett     for ( total_bytes = 0 ; total_bytes < rom_size ; /* empty */ ) {
32964af050cSIan Romanick 	const int bytes = read( fd, (char *) buffer + total_bytes,
330a5c86202SStuart Bennett 				rom_size - total_bytes );
33164af050cSIan Romanick 	if ( bytes == -1 ) {
3325a04522aSIan Romanick 	    err = errno;
33364af050cSIan Romanick 	    break;
3345a04522aSIan Romanick 	}
33564af050cSIan Romanick 	else if ( bytes == 0 ) {
33664af050cSIan Romanick 	    break;
33764af050cSIan Romanick 	}
33864af050cSIan Romanick 
33964af050cSIan Romanick 	total_bytes += bytes;
34064af050cSIan Romanick     }
34164af050cSIan Romanick 
3425a04522aSIan Romanick 
3435a04522aSIan Romanick     lseek( fd, 0, SEEK_SET );
3445a04522aSIan Romanick     write( fd, "0", 1 );
3455a04522aSIan Romanick 
3465a04522aSIan Romanick     close( fd );
3475a04522aSIan Romanick     return err;
3485a04522aSIan Romanick }
3495a04522aSIan Romanick 
3505a04522aSIan Romanick 
3515a04522aSIan Romanick static int
3525a04522aSIan Romanick pci_device_linux_sysfs_read( struct pci_device * dev, void * data,
3535a04522aSIan Romanick 			     pciaddr_t offset, pciaddr_t size,
3545a04522aSIan Romanick 			     pciaddr_t * bytes_read )
3555a04522aSIan Romanick {
3565a04522aSIan Romanick     char name[256];
3575a04522aSIan Romanick     pciaddr_t temp_size = size;
3585a04522aSIan Romanick     int err = 0;
3595a04522aSIan Romanick     int fd;
3605cf29b06SKeith Packard     char *data_bytes = data;
3615a04522aSIan Romanick 
3625a04522aSIan Romanick     if ( bytes_read != NULL ) {
3635a04522aSIan Romanick 	*bytes_read = 0;
3645a04522aSIan Romanick     }
3655a04522aSIan Romanick 
3665a04522aSIan Romanick     /* Each device has a directory under sysfs.  Within that directory there
3675a04522aSIan Romanick      * is a file named "config".  This file used to access the PCI config
3685a04522aSIan Romanick      * space.  It is used here to obtain most of the information about the
3695a04522aSIan Romanick      * device.
3705a04522aSIan Romanick      */
3715a04522aSIan Romanick     snprintf( name, 255, "%s/%04x:%02x:%02x.%1u/config",
3725a04522aSIan Romanick 	      SYS_BUS_PCI,
3735a04522aSIan Romanick 	      dev->domain,
3745a04522aSIan Romanick 	      dev->bus,
3755a04522aSIan Romanick 	      dev->dev,
3765a04522aSIan Romanick 	      dev->func );
3775a04522aSIan Romanick 
3785a04522aSIan Romanick     fd = open( name, O_RDONLY );
3795a04522aSIan Romanick     if ( fd == -1 ) {
3805a04522aSIan Romanick 	return errno;
3815a04522aSIan Romanick     }
3825a04522aSIan Romanick 
3835a04522aSIan Romanick 
3845a04522aSIan Romanick     while ( temp_size > 0 ) {
3855cf29b06SKeith Packard 	const ssize_t bytes = pread64( fd, data_bytes, temp_size, offset );
3865a04522aSIan Romanick 
3875a04522aSIan Romanick 	/* If zero bytes were read, then we assume it's the end of the
3885a04522aSIan Romanick 	 * config file.
3895a04522aSIan Romanick 	 */
3905a04522aSIan Romanick 	if ( bytes <= 0 ) {
3915a04522aSIan Romanick 	    err = errno;
3925a04522aSIan Romanick 	    break;
3935a04522aSIan Romanick 	}
3945a04522aSIan Romanick 
3955a04522aSIan Romanick 	temp_size -= bytes;
3965a04522aSIan Romanick 	offset += bytes;
3975cf29b06SKeith Packard 	data_bytes += bytes;
3985a04522aSIan Romanick     }
3995a04522aSIan Romanick 
4005a04522aSIan Romanick     if ( bytes_read != NULL ) {
4015a04522aSIan Romanick 	*bytes_read = size - temp_size;
4025a04522aSIan Romanick     }
4035a04522aSIan Romanick 
4045a04522aSIan Romanick     close( fd );
4055a04522aSIan Romanick     return err;
4065a04522aSIan Romanick }
4075a04522aSIan Romanick 
4085a04522aSIan Romanick 
4095a04522aSIan Romanick static int
4105a04522aSIan Romanick pci_device_linux_sysfs_write( struct pci_device * dev, const void * data,
4115a04522aSIan Romanick 			     pciaddr_t offset, pciaddr_t size,
4125a04522aSIan Romanick 			     pciaddr_t * bytes_written )
4135a04522aSIan Romanick {
4145a04522aSIan Romanick     char name[256];
4155a04522aSIan Romanick     pciaddr_t temp_size = size;
4165a04522aSIan Romanick     int err = 0;
4175a04522aSIan Romanick     int fd;
4185cf29b06SKeith Packard     const char *data_bytes = data;
4195a04522aSIan Romanick 
4205a04522aSIan Romanick     if ( bytes_written != NULL ) {
4215a04522aSIan Romanick 	*bytes_written = 0;
4225a04522aSIan Romanick     }
4235a04522aSIan Romanick 
4245a04522aSIan Romanick     /* Each device has a directory under sysfs.  Within that directory there
4255a04522aSIan Romanick      * is a file named "config".  This file used to access the PCI config
4265a04522aSIan Romanick      * space.  It is used here to obtain most of the information about the
4275a04522aSIan Romanick      * device.
4285a04522aSIan Romanick      */
4295a04522aSIan Romanick     snprintf( name, 255, "%s/%04x:%02x:%02x.%1u/config",
4305a04522aSIan Romanick 	      SYS_BUS_PCI,
4315a04522aSIan Romanick 	      dev->domain,
4325a04522aSIan Romanick 	      dev->bus,
4335a04522aSIan Romanick 	      dev->dev,
4345a04522aSIan Romanick 	      dev->func );
4355a04522aSIan Romanick 
4365a04522aSIan Romanick     fd = open( name, O_WRONLY );
4375a04522aSIan Romanick     if ( fd == -1 ) {
4385a04522aSIan Romanick 	return errno;
4395a04522aSIan Romanick     }
4405a04522aSIan Romanick 
4415a04522aSIan Romanick 
4425a04522aSIan Romanick     while ( temp_size > 0 ) {
4435cf29b06SKeith Packard 	const ssize_t bytes = pwrite64( fd, data_bytes, temp_size, offset );
4445a04522aSIan Romanick 
4455a04522aSIan Romanick 	/* If zero bytes were written, then we assume it's the end of the
4465a04522aSIan Romanick 	 * config file.
4475a04522aSIan Romanick 	 */
4485a04522aSIan Romanick 	if ( bytes <= 0 ) {
4495a04522aSIan Romanick 	    err = errno;
4505a04522aSIan Romanick 	    break;
4515a04522aSIan Romanick 	}
4525a04522aSIan Romanick 
4535a04522aSIan Romanick 	temp_size -= bytes;
4545a04522aSIan Romanick 	offset += bytes;
4555cf29b06SKeith Packard 	data_bytes += bytes;
4565a04522aSIan Romanick     }
4575a04522aSIan Romanick 
4585a04522aSIan Romanick     if ( bytes_written != NULL ) {
4595a04522aSIan Romanick 	*bytes_written = size - temp_size;
4605a04522aSIan Romanick     }
4615a04522aSIan Romanick 
4625a04522aSIan Romanick     close( fd );
4635a04522aSIan Romanick     return err;
4645a04522aSIan Romanick }
4655a04522aSIan Romanick 
4667282b53cSJesse Barnes static int
4677282b53cSJesse Barnes pci_device_linux_sysfs_map_range_wc(struct pci_device *dev,
4687282b53cSJesse Barnes 				    struct pci_device_mapping *map)
4697282b53cSJesse Barnes {
4707282b53cSJesse Barnes     char name[256];
4717282b53cSJesse Barnes     int fd;
4727282b53cSJesse Barnes     const int prot = ((map->flags & PCI_DEV_MAP_FLAG_WRITABLE) != 0)
4737282b53cSJesse Barnes         ? (PROT_READ | PROT_WRITE) : PROT_READ;
4747282b53cSJesse Barnes     const int open_flags = ((map->flags & PCI_DEV_MAP_FLAG_WRITABLE) != 0)
4757282b53cSJesse Barnes         ? O_RDWR : O_RDONLY;
4767282b53cSJesse Barnes     const off_t offset = map->base - dev->regions[map->region].base_addr;
4777282b53cSJesse Barnes 
4787282b53cSJesse Barnes     snprintf(name, 255, "%s/%04x:%02x:%02x.%1u/resource%u_wc",
4797282b53cSJesse Barnes 	     SYS_BUS_PCI,
4807282b53cSJesse Barnes 	     dev->domain,
4817282b53cSJesse Barnes 	     dev->bus,
4827282b53cSJesse Barnes 	     dev->dev,
4837282b53cSJesse Barnes 	     dev->func,
4847282b53cSJesse Barnes 	     map->region);
4857282b53cSJesse Barnes     fd = open(name, open_flags);
4867282b53cSJesse Barnes     if (fd == -1)
4877282b53cSJesse Barnes 	    return errno;
4887282b53cSJesse Barnes 
4897282b53cSJesse Barnes     map->memory = mmap(NULL, map->size, prot, MAP_SHARED, fd, offset);
4907282b53cSJesse Barnes     if (map->memory == MAP_FAILED) {
4917282b53cSJesse Barnes         map->memory = NULL;
4927282b53cSJesse Barnes 	close(fd);
4937282b53cSJesse Barnes 	return errno;
4947282b53cSJesse Barnes     }
4957282b53cSJesse Barnes 
4967282b53cSJesse Barnes     close(fd);
4977282b53cSJesse Barnes 
4987282b53cSJesse Barnes     return 0;
4997282b53cSJesse Barnes }
5005a04522aSIan Romanick 
5015a04522aSIan Romanick /**
5025a04522aSIan Romanick  * Map a memory region for a device using the Linux sysfs interface.
5035a04522aSIan Romanick  *
5045a04522aSIan Romanick  * \param dev   Device whose memory region is to be mapped.
50508ff9f7fSIan Romanick  * \param map   Parameters of the mapping that is to be created.
5065a04522aSIan Romanick  *
5075a04522aSIan Romanick  * \return
5085a04522aSIan Romanick  * Zero on success or an \c errno value on failure.
5095a04522aSIan Romanick  *
51008ff9f7fSIan Romanick  * \sa pci_device_map_rrange, pci_device_linux_sysfs_unmap_range
5115a04522aSIan Romanick  *
5125a04522aSIan Romanick  * \todo
5135a04522aSIan Romanick  * Some older 2.6.x kernels don't implement the resourceN files.  On those
5145a04522aSIan Romanick  * systems /dev/mem must be used.  On these systems it is also possible that
5155a04522aSIan Romanick  * \c mmap64 may need to be used.
5165a04522aSIan Romanick  */
5175a04522aSIan Romanick static int
51808ff9f7fSIan Romanick pci_device_linux_sysfs_map_range(struct pci_device *dev,
51908ff9f7fSIan Romanick                                  struct pci_device_mapping *map)
5205a04522aSIan Romanick {
5215a04522aSIan Romanick     char name[256];
5225a04522aSIan Romanick     int fd;
5235a04522aSIan Romanick     int err = 0;
52408ff9f7fSIan Romanick     const int prot = ((map->flags & PCI_DEV_MAP_FLAG_WRITABLE) != 0)
52508ff9f7fSIan Romanick         ? (PROT_READ | PROT_WRITE) : PROT_READ;
52608ff9f7fSIan Romanick     const int open_flags = ((map->flags & PCI_DEV_MAP_FLAG_WRITABLE) != 0)
52708ff9f7fSIan Romanick         ? O_RDWR : O_RDONLY;
52808ff9f7fSIan Romanick     const off_t offset = map->base - dev->regions[map->region].base_addr;
52908ff9f7fSIan Romanick #ifdef HAVE_MTRR
53008ff9f7fSIan Romanick     struct mtrr_sentry sentry = {
53108ff9f7fSIan Romanick 	.base = map->base,
53208ff9f7fSIan Romanick         .size = map->size,
53308ff9f7fSIan Romanick 	.type = MTRR_TYPE_UNCACHABLE
53408ff9f7fSIan Romanick     };
53508ff9f7fSIan Romanick #endif
5365a04522aSIan Romanick 
5377282b53cSJesse Barnes     /* For WC mappings, try sysfs resourceN_wc file first */
5387282b53cSJesse Barnes     if ((map->flags & PCI_DEV_MAP_FLAG_WRITE_COMBINE) &&
5397282b53cSJesse Barnes 	!pci_device_linux_sysfs_map_range_wc(dev, map))
5407282b53cSJesse Barnes 	    return 0;
5417282b53cSJesse Barnes 
5425a04522aSIan Romanick     snprintf(name, 255, "%s/%04x:%02x:%02x.%1u/resource%u",
5435a04522aSIan Romanick              SYS_BUS_PCI,
5445a04522aSIan Romanick              dev->domain,
5455a04522aSIan Romanick              dev->bus,
5465a04522aSIan Romanick              dev->dev,
5475a04522aSIan Romanick              dev->func,
54808ff9f7fSIan Romanick              map->region);
5495a04522aSIan Romanick 
55008ff9f7fSIan Romanick     fd = open(name, open_flags);
5515a04522aSIan Romanick     if (fd == -1) {
5525a04522aSIan Romanick         return errno;
5535a04522aSIan Romanick     }
5545a04522aSIan Romanick 
5555a04522aSIan Romanick 
55608ff9f7fSIan Romanick     map->memory = mmap(NULL, map->size, prot, MAP_SHARED, fd, offset);
55708ff9f7fSIan Romanick     if (map->memory == MAP_FAILED) {
55808ff9f7fSIan Romanick         map->memory = NULL;
5595a04522aSIan Romanick 	close(fd);
560e3adc06bSEric Anholt 	return errno;
561e3adc06bSEric Anholt     }
56208ff9f7fSIan Romanick 
56308ff9f7fSIan Romanick #ifdef HAVE_MTRR
56408ff9f7fSIan Romanick     if ((map->flags & PCI_DEV_MAP_FLAG_CACHABLE) != 0) {
56508ff9f7fSIan Romanick         sentry.type = MTRR_TYPE_WRBACK;
56608ff9f7fSIan Romanick     } else if ((map->flags & PCI_DEV_MAP_FLAG_WRITE_COMBINE) != 0) {
56708ff9f7fSIan Romanick         sentry.type = MTRR_TYPE_WRCOMB;
5685a04522aSIan Romanick     }
5695a04522aSIan Romanick 
5705cf29b06SKeith Packard     if (pci_sys->mtrr_fd != -1 && sentry.type != MTRR_TYPE_UNCACHABLE) {
57108ff9f7fSIan Romanick 	if (ioctl(pci_sys->mtrr_fd, MTRRIOC_ADD_ENTRY, &sentry) < 0) {
57208ff9f7fSIan Romanick 	    /* FIXME: Should we report an error in this case?
5735a04522aSIan Romanick 	     */
57408ff9f7fSIan Romanick 	    fprintf(stderr, "error setting MTRR "
57508ff9f7fSIan Romanick 		    "(base = 0x%08lx, size = 0x%08x, type = %u) %s (%d)\n",
57608ff9f7fSIan Romanick 		    sentry.base, sentry.size, sentry.type,
57708ff9f7fSIan Romanick 		    strerror(errno), errno);
57808ff9f7fSIan Romanick /*            err = errno;*/
57964af050cSIan Romanick 	}
580b30d4582SKeith Packard 	/* KLUDGE ALERT -- rewrite the PTEs to turn off the CD and WT bits */
581b30d4582SKeith Packard 	mprotect (map->memory, map->size, PROT_NONE);
582e3adc06bSEric Anholt 	err = mprotect (map->memory, map->size, PROT_READ|PROT_WRITE);
583e3adc06bSEric Anholt 
584e3adc06bSEric Anholt 	if (err != 0) {
585e3adc06bSEric Anholt 	    fprintf(stderr, "mprotect(PROT_READ | PROT_WRITE) failed: %s\n",
586e3adc06bSEric Anholt 		    strerror(errno));
587f49f6671SStefan Dirsch 	    fprintf(stderr, "remapping without mprotect performance kludge.\n");
588e3adc06bSEric Anholt 
589e3adc06bSEric Anholt 	    munmap(map->memory, map->size);
590e3adc06bSEric Anholt 	    map->memory = mmap(NULL, map->size, prot, MAP_SHARED, fd, offset);
591e3adc06bSEric Anholt 	    if (map->memory == MAP_FAILED) {
592e3adc06bSEric Anholt 		map->memory = NULL;
593e3adc06bSEric Anholt 		close(fd);
594e3adc06bSEric Anholt 		return errno;
595e3adc06bSEric Anholt 	    }
596e3adc06bSEric Anholt 	}
59708ff9f7fSIan Romanick     }
59808ff9f7fSIan Romanick #endif
59964af050cSIan Romanick 
600e3adc06bSEric Anholt     close(fd);
601e3adc06bSEric Anholt 
602e3adc06bSEric Anholt     return 0;
6035a04522aSIan Romanick }
6045cf29b06SKeith Packard 
6055cf29b06SKeith Packard /**
6065cf29b06SKeith Packard  * Unmap a memory region for a device using the Linux sysfs interface.
6075cf29b06SKeith Packard  *
6085cf29b06SKeith Packard  * \param dev   Device whose memory region is to be unmapped.
6095cf29b06SKeith Packard  * \param map   Parameters of the mapping that is to be destroyed.
6105cf29b06SKeith Packard  *
6115cf29b06SKeith Packard  * \return
6125cf29b06SKeith Packard  * Zero on success or an \c errno value on failure.
6135cf29b06SKeith Packard  *
6145cf29b06SKeith Packard  * \sa pci_device_map_rrange, pci_device_linux_sysfs_map_range
6155cf29b06SKeith Packard  *
6165cf29b06SKeith Packard  * \todo
6175cf29b06SKeith Packard  * Some older 2.6.x kernels don't implement the resourceN files.  On those
6185cf29b06SKeith Packard  * systems /dev/mem must be used.  On these systems it is also possible that
6195cf29b06SKeith Packard  * \c mmap64 may need to be used.
6205cf29b06SKeith Packard  */
6215cf29b06SKeith Packard static int
6225cf29b06SKeith Packard pci_device_linux_sysfs_unmap_range(struct pci_device *dev,
6235cf29b06SKeith Packard 				   struct pci_device_mapping *map)
6245cf29b06SKeith Packard {
6255cf29b06SKeith Packard     int err = 0;
6265cf29b06SKeith Packard #ifdef HAVE_MTRR
6275cf29b06SKeith Packard     struct mtrr_sentry sentry = {
6285cf29b06SKeith Packard 	.base = map->base,
6295cf29b06SKeith Packard         .size = map->size,
6305cf29b06SKeith Packard 	.type = MTRR_TYPE_UNCACHABLE
6315cf29b06SKeith Packard     };
6325cf29b06SKeith Packard #endif
6335cf29b06SKeith Packard 
6345cf29b06SKeith Packard     err = pci_device_generic_unmap_range (dev, map);
6355cf29b06SKeith Packard     if (err)
6365cf29b06SKeith Packard 	return err;
6375cf29b06SKeith Packard 
6385cf29b06SKeith Packard #ifdef HAVE_MTRR
6395cf29b06SKeith Packard     if ((map->flags & PCI_DEV_MAP_FLAG_CACHABLE) != 0) {
6405cf29b06SKeith Packard         sentry.type = MTRR_TYPE_WRBACK;
6415cf29b06SKeith Packard     } else if ((map->flags & PCI_DEV_MAP_FLAG_WRITE_COMBINE) != 0) {
6425cf29b06SKeith Packard         sentry.type = MTRR_TYPE_WRCOMB;
6435cf29b06SKeith Packard     }
6445cf29b06SKeith Packard 
6455cf29b06SKeith Packard     if (pci_sys->mtrr_fd != -1 && sentry.type != MTRR_TYPE_UNCACHABLE) {
6465cf29b06SKeith Packard 	if (ioctl(pci_sys->mtrr_fd, MTRRIOC_DEL_ENTRY, &sentry) < 0) {
6475cf29b06SKeith Packard 	    /* FIXME: Should we report an error in this case?
6485cf29b06SKeith Packard 	     */
6495cf29b06SKeith Packard 	    fprintf(stderr, "error setting MTRR "
6505cf29b06SKeith Packard 		    "(base = 0x%08lx, size = 0x%08x, type = %u) %s (%d)\n",
6515cf29b06SKeith Packard 		    sentry.base, sentry.size, sentry.type,
6525cf29b06SKeith Packard 		    strerror(errno), errno);
6535cf29b06SKeith Packard /*            err = errno;*/
6545cf29b06SKeith Packard 	}
6555cf29b06SKeith Packard     }
6565cf29b06SKeith Packard #endif
6575cf29b06SKeith Packard 
6585cf29b06SKeith Packard     return err;
6595cf29b06SKeith Packard }
6604bc9292fSDave Airlie 
6614bc9292fSDave Airlie static void pci_device_linux_sysfs_enable(struct pci_device *dev)
6624bc9292fSDave Airlie {
6634bc9292fSDave Airlie     char name[256];
6644bc9292fSDave Airlie     int fd;
6654bc9292fSDave Airlie 
6664bc9292fSDave Airlie     snprintf( name, 255, "%s/%04x:%02x:%02x.%1u/enable",
6674bc9292fSDave Airlie 	      SYS_BUS_PCI,
6684bc9292fSDave Airlie 	      dev->domain,
6694bc9292fSDave Airlie 	      dev->bus,
6704bc9292fSDave Airlie 	      dev->dev,
6714bc9292fSDave Airlie 	      dev->func );
6724bc9292fSDave Airlie 
6734bc9292fSDave Airlie     fd = open( name, O_RDWR );
6744bc9292fSDave Airlie     if (fd == -1)
6754bc9292fSDave Airlie        return;
6764bc9292fSDave Airlie 
6774bc9292fSDave Airlie     write( fd, "1", 1 );
6784bc9292fSDave Airlie     close(fd);
6794bc9292fSDave Airlie }
680b2838fb6SDave Airlie 
681b2838fb6SDave Airlie static int pci_device_linux_sysfs_boot_vga(struct pci_device *dev)
682b2838fb6SDave Airlie {
683b2838fb6SDave Airlie     char name[256];
684b2838fb6SDave Airlie     char reply[3];
685b2838fb6SDave Airlie     int fd, bytes_read;
686b2838fb6SDave Airlie     int ret = 0;
687b2838fb6SDave Airlie 
688b2838fb6SDave Airlie     snprintf( name, 255, "%s/%04x:%02x:%02x.%1u/boot_vga",
689b2838fb6SDave Airlie 	      SYS_BUS_PCI,
690b2838fb6SDave Airlie 	      dev->domain,
691b2838fb6SDave Airlie 	      dev->bus,
692b2838fb6SDave Airlie 	      dev->dev,
693b2838fb6SDave Airlie 	      dev->func );
694b2838fb6SDave Airlie 
6958ba6b02eSAaron Plattner     fd = open( name, O_RDONLY );
696b2838fb6SDave Airlie     if (fd == -1)
697b2838fb6SDave Airlie        return 0;
698b2838fb6SDave Airlie 
699b2838fb6SDave Airlie     bytes_read = read(fd, reply, 1);
700b2838fb6SDave Airlie     if (bytes_read != 1)
701b2838fb6SDave Airlie 	goto out;
702b2838fb6SDave Airlie     if (reply[0] == '1')
703b2838fb6SDave Airlie 	ret = 1;
704b2838fb6SDave Airlie out:
705b2838fb6SDave Airlie     close(fd);
706b2838fb6SDave Airlie     return ret;
707b2838fb6SDave Airlie }
7085d1bdf0cSDave Airlie 
7095d1bdf0cSDave Airlie static int pci_device_linux_sysfs_has_kernel_driver(struct pci_device *dev)
7105d1bdf0cSDave Airlie {
7115d1bdf0cSDave Airlie     char name[256];
7125d1bdf0cSDave Airlie     struct stat dummy;
7135d1bdf0cSDave Airlie     int ret;
7145d1bdf0cSDave Airlie 
7155d1bdf0cSDave Airlie     snprintf( name, 255, "%s/%04x:%02x:%02x.%1u/driver",
7165d1bdf0cSDave Airlie 	      SYS_BUS_PCI,
7175d1bdf0cSDave Airlie 	      dev->domain,
7185d1bdf0cSDave Airlie 	      dev->bus,
7195d1bdf0cSDave Airlie 	      dev->dev,
7205d1bdf0cSDave Airlie 	      dev->func );
7215d1bdf0cSDave Airlie 
7225d1bdf0cSDave Airlie     ret = stat(name, &dummy);
7235d1bdf0cSDave Airlie     if (ret < 0)
7245d1bdf0cSDave Airlie 	return 0;
7255d1bdf0cSDave Airlie     return 1;
7265d1bdf0cSDave Airlie }
727*5e8d4c19SAdam Jackson 
728*5e8d4c19SAdam Jackson static struct pci_io_handle *
729*5e8d4c19SAdam Jackson pci_device_linux_sysfs_open_device_io(struct pci_io_handle *ret,
730*5e8d4c19SAdam Jackson 				      struct pci_device *dev, int bar,
731*5e8d4c19SAdam Jackson 				      pciaddr_t base, pciaddr_t size)
732*5e8d4c19SAdam Jackson {
733*5e8d4c19SAdam Jackson     char name[PATH_MAX];
734*5e8d4c19SAdam Jackson 
735*5e8d4c19SAdam Jackson     snprintf(name, PATH_MAX, "%s/%04x:%02x:%02x.%1u/resource%d",
736*5e8d4c19SAdam Jackson 	     SYS_BUS_PCI, dev->domain, dev->bus, dev->dev, dev->func, bar);
737*5e8d4c19SAdam Jackson 
738*5e8d4c19SAdam Jackson     ret->fd = open(name, O_RDWR);
739*5e8d4c19SAdam Jackson 
740*5e8d4c19SAdam Jackson     if (ret->fd < 0)
741*5e8d4c19SAdam Jackson 	return NULL;
742*5e8d4c19SAdam Jackson 
743*5e8d4c19SAdam Jackson     ret->base = base;
744*5e8d4c19SAdam Jackson     ret->size = size;
745*5e8d4c19SAdam Jackson 
746*5e8d4c19SAdam Jackson     return ret;
747*5e8d4c19SAdam Jackson }
748*5e8d4c19SAdam Jackson 
749*5e8d4c19SAdam Jackson static struct pci_io_handle *
750*5e8d4c19SAdam Jackson pci_device_linux_sysfs_open_legacy_io(struct pci_io_handle *ret,
751*5e8d4c19SAdam Jackson 				      struct pci_device *dev, pciaddr_t base,
752*5e8d4c19SAdam Jackson 				      pciaddr_t size)
753*5e8d4c19SAdam Jackson {
754*5e8d4c19SAdam Jackson     char name[PATH_MAX];
755*5e8d4c19SAdam Jackson 
756*5e8d4c19SAdam Jackson     /* First check if there's a legacy io method for the device */
757*5e8d4c19SAdam Jackson     while (dev) {
758*5e8d4c19SAdam Jackson 	snprintf(name, PATH_MAX, "/sys/class/pci_bus/%04x:%02x/legacy_io",
759*5e8d4c19SAdam Jackson 		 dev->domain, dev->bus);
760*5e8d4c19SAdam Jackson 
761*5e8d4c19SAdam Jackson 	ret->fd = open(name, O_RDWR);
762*5e8d4c19SAdam Jackson 	if (ret->fd >= 0)
763*5e8d4c19SAdam Jackson 	    break;
764*5e8d4c19SAdam Jackson 
765*5e8d4c19SAdam Jackson 	dev = pci_device_get_parent_bridge(dev);
766*5e8d4c19SAdam Jackson     }
767*5e8d4c19SAdam Jackson 
768*5e8d4c19SAdam Jackson     /* If not, /dev/port is the best we can do */
769*5e8d4c19SAdam Jackson     if (!dev)
770*5e8d4c19SAdam Jackson 	ret->fd = open("/dev/port", O_RDWR);
771*5e8d4c19SAdam Jackson 
772*5e8d4c19SAdam Jackson     if (ret->fd < 0)
773*5e8d4c19SAdam Jackson 	return NULL;
774*5e8d4c19SAdam Jackson 
775*5e8d4c19SAdam Jackson     ret->base = base;
776*5e8d4c19SAdam Jackson     ret->size = size;
777*5e8d4c19SAdam Jackson 
778*5e8d4c19SAdam Jackson     return ret;
779*5e8d4c19SAdam Jackson }
780*5e8d4c19SAdam Jackson 
781*5e8d4c19SAdam Jackson static void
782*5e8d4c19SAdam Jackson pci_device_linux_sysfs_close_io(struct pci_device *dev,
783*5e8d4c19SAdam Jackson 				struct pci_io_handle *handle)
784*5e8d4c19SAdam Jackson {
785*5e8d4c19SAdam Jackson     close(handle->fd);
786*5e8d4c19SAdam Jackson }
787*5e8d4c19SAdam Jackson 
788*5e8d4c19SAdam Jackson static uint32_t
789*5e8d4c19SAdam Jackson pci_device_linux_sysfs_read32(struct pci_io_handle *handle, uint32_t port)
790*5e8d4c19SAdam Jackson {
791*5e8d4c19SAdam Jackson     uint32_t ret;
792*5e8d4c19SAdam Jackson 
793*5e8d4c19SAdam Jackson     pread(handle->fd, &ret, 4, port + handle->base);
794*5e8d4c19SAdam Jackson 
795*5e8d4c19SAdam Jackson     return ret;
796*5e8d4c19SAdam Jackson }
797*5e8d4c19SAdam Jackson 
798*5e8d4c19SAdam Jackson static uint16_t
799*5e8d4c19SAdam Jackson pci_device_linux_sysfs_read16(struct pci_io_handle *handle, uint32_t port)
800*5e8d4c19SAdam Jackson {
801*5e8d4c19SAdam Jackson     uint16_t ret;
802*5e8d4c19SAdam Jackson 
803*5e8d4c19SAdam Jackson     pread(handle->fd, &ret, 2, port + handle->base);
804*5e8d4c19SAdam Jackson 
805*5e8d4c19SAdam Jackson     return ret;
806*5e8d4c19SAdam Jackson }
807*5e8d4c19SAdam Jackson 
808*5e8d4c19SAdam Jackson static uint8_t
809*5e8d4c19SAdam Jackson pci_device_linux_sysfs_read8(struct pci_io_handle *handle, uint32_t port)
810*5e8d4c19SAdam Jackson {
811*5e8d4c19SAdam Jackson     uint8_t ret;
812*5e8d4c19SAdam Jackson 
813*5e8d4c19SAdam Jackson     pread(handle->fd, &ret, 1, port + handle->base);
814*5e8d4c19SAdam Jackson 
815*5e8d4c19SAdam Jackson     return ret;
816*5e8d4c19SAdam Jackson }
817*5e8d4c19SAdam Jackson 
818*5e8d4c19SAdam Jackson static void
819*5e8d4c19SAdam Jackson pci_device_linux_sysfs_write32(struct pci_io_handle *handle, uint32_t port,
820*5e8d4c19SAdam Jackson 			       uint32_t data)
821*5e8d4c19SAdam Jackson {
822*5e8d4c19SAdam Jackson     pwrite(handle->fd, &data, 4, port + handle->base);
823*5e8d4c19SAdam Jackson }
824*5e8d4c19SAdam Jackson 
825*5e8d4c19SAdam Jackson static void
826*5e8d4c19SAdam Jackson pci_device_linux_sysfs_write16(struct pci_io_handle *handle, uint32_t port,
827*5e8d4c19SAdam Jackson 			       uint16_t data)
828*5e8d4c19SAdam Jackson {
829*5e8d4c19SAdam Jackson     pwrite(handle->fd, &data, 2, port + handle->base);
830*5e8d4c19SAdam Jackson }
831*5e8d4c19SAdam Jackson 
832*5e8d4c19SAdam Jackson static void
833*5e8d4c19SAdam Jackson pci_device_linux_sysfs_write8(struct pci_io_handle *handle, uint32_t port,
834*5e8d4c19SAdam Jackson 			      uint8_t data)
835*5e8d4c19SAdam Jackson {
836*5e8d4c19SAdam Jackson     pwrite(handle->fd, &data, 1, port + handle->base);
837*5e8d4c19SAdam Jackson }
838*5e8d4c19SAdam Jackson 
839*5e8d4c19SAdam Jackson static const struct pci_system_methods linux_sysfs_methods = {
840*5e8d4c19SAdam Jackson     .destroy = NULL,
841*5e8d4c19SAdam Jackson     .destroy_device = NULL,
842*5e8d4c19SAdam Jackson     .read_rom = pci_device_linux_sysfs_read_rom,
843*5e8d4c19SAdam Jackson     .probe = pci_device_linux_sysfs_probe,
844*5e8d4c19SAdam Jackson     .map_range = pci_device_linux_sysfs_map_range,
845*5e8d4c19SAdam Jackson     .unmap_range = pci_device_linux_sysfs_unmap_range,
846*5e8d4c19SAdam Jackson 
847*5e8d4c19SAdam Jackson     .read = pci_device_linux_sysfs_read,
848*5e8d4c19SAdam Jackson     .write = pci_device_linux_sysfs_write,
849*5e8d4c19SAdam Jackson 
850*5e8d4c19SAdam Jackson     .fill_capabilities = pci_fill_capabilities_generic,
851*5e8d4c19SAdam Jackson     .enable = pci_device_linux_sysfs_enable,
852*5e8d4c19SAdam Jackson     .boot_vga = pci_device_linux_sysfs_boot_vga,
853*5e8d4c19SAdam Jackson     .has_kernel_driver = pci_device_linux_sysfs_has_kernel_driver,
854*5e8d4c19SAdam Jackson 
855*5e8d4c19SAdam Jackson     .open_device_io = pci_device_linux_sysfs_open_device_io,
856*5e8d4c19SAdam Jackson     .open_legacy_io = pci_device_linux_sysfs_open_legacy_io,
857*5e8d4c19SAdam Jackson     .close_io = pci_device_linux_sysfs_close_io,
858*5e8d4c19SAdam Jackson     .read32 = pci_device_linux_sysfs_read32,
859*5e8d4c19SAdam Jackson     .read16 = pci_device_linux_sysfs_read16,
860*5e8d4c19SAdam Jackson     .read8 = pci_device_linux_sysfs_read8,
861*5e8d4c19SAdam Jackson     .write32 = pci_device_linux_sysfs_write32,
862*5e8d4c19SAdam Jackson     .write16 = pci_device_linux_sysfs_write16,
863*5e8d4c19SAdam Jackson     .write8 = pci_device_linux_sysfs_write8,
864*5e8d4c19SAdam Jackson };
865