The Linux PCI Interface An introduction to the PCI configuration space registers

advertisement
The Linux PCI Interface
An introduction to the PCI
configuration space registers
Some background on PCI
•
•
•
•
ISA: Industry Standard Architecture (1981)
PCI: Peripheral Component Interconnect
An Intel-backed industry initiative (1992-9)
Main goals:
– Improve data-xfers to/from peripheral devices
– Eliminate (or reduce) platform dependencies
– Simplify adding/removing peripheral devices
– Lower total consumption of electrical power
Features for driver-writers
•
•
•
•
•
•
Support for “auto-detection” of devices
Device configuration is “programmable”
Introduces “PCI Configuration Space”
A nonvolatile data-structure of device info
A standard “header” layout: 64 longwords
Linux provides some interface functions:
#include <linux/pci.h>
pci_dev_struct
• Linux extracts info from PCI Config. Space
• Stores the info in linked-lists of structures
• Examples:
– Name of the device’s manufacturer
– Name and release version of the product
– Hardware resources provided by the product
– System resources allocated to the product
(Linux provides “search-and-extract” routines)
The ‘lspci’ command
•
•
•
•
Linux scans PCI Configuration Space
It builds a list of ‘pci_dev_struct’ objects
It exports partial info using a ‘/proc’ file
You can view this info using a command:
$ /sbin/lspci
• Or you can directly view the /proc/pci file:
$ cat /proc/pci
An illustrative example: vram.c
•
•
•
•
Let’s write another character device-driver
It will allow read/write access to video ram
Very analogous to our prior ‘ram.c’ driver
Some differences:
– Device’s memory resides on PCI the bus
– Can safely allow writing as well as reading
– Hardware uses memory in nonstandard ways
– We really need vendor’s programmer manual
init_module()
•
•
•
•
Driver’s first job is ‘device detection’
PCI devices are identified by numbers
Device classes also have ID-numbers
VGA device-class:0x030000
– ’03’ means a ‘display device’
– ’00’ means ‘VGA compatible’
– ’00’ means ‘revision-number’
pci_find_class();
• Define the manifest constant:
#define VGA_CLASS 0x030000
• Declare a null-pointer:
struct pci_dev_struct *devp = NULL;
• Call ‘pci_find_class()’ function:
devp = pci_find_class( VGA_CLASS, devp );
• Check for ‘device-not-found’:
if ( devp == NULL ) return –ENODEV;
Locate the VGA ‘frame buffer’
• In PCI Configuration Space:
– offset 0x10: base_address0
– offset 0x14: base_address1
– offset 0x18: base_address2
–
. . . etc. . . .
(complete layout on page 475 in textbook)
A convenient Linux extraction-function:
fb_base = pci_resource_start( devp, 0 );
How big is the frame buffer?
•
•
•
•
Size of video memory varies with product
Driver needs to determine memory-size
PCI: a standard way to determine size
(But product might provide support for
larger vram than is currently installed)
• Two-step size-detection method:
– First determine maximum supported size
– Then check for ‘redundant’ addressing
Maximum memory-size
•
•
•
•
•
•
Some bits in ‘base_address0’ are ‘wired’
But other bit-values are ‘programmable’
Bits 0..3 have some special meanings
So we will need to examing bits 4..31
Find least significant ‘programmable’ bit
It tells the ‘maximum’ supported memory
Programming algorithm
•
•
•
•
•
•
•
Get configuration longword at offset 0x10
Save it (so that we can restore it later)
Write a new value: all bits set to 1’s
Read back the longword just written
The ‘hard-wired’ bits will still be 0’s
We will scan for first bit that’s ‘1’
Be sure to restore the original longword
Loop to find the ‘lowest 1’
•
•
•
•
•
Implementing the PCI size-test:
pci_write_config_dword( devp, 0x10, ~0 );
pci_read_config_dword( devp, 0x10, &val);
int
i;
for (i = 4; i < 32; i++)
if ( val & ( 1 << i ) ) break;
• fb_maxsize = ( 1 << i );
Checking for memory ‘wrap’
•
•
•
•
•
Do vram bytes have multiple addresses?
We use a ‘quick-and-dirty’ check
write to one address, read from another
If what we read didn’t change: no ‘wrap’!
Some assumptions we make:
– Memory-size will be a power of 2
– If two bytes differ, all between them do, too
• (Should we question these assumptions?)
Device-memory: read and write
•
•
•
•
The CPU understands ‘virtual’ addresses
They must be ‘mapped’ to bus addresses
The kernel must setup page-table entries
Linux kernel provides functions:
vaddr = ioremap( physaddr, memsize );
iounmap( vaddr );
Alternative: can use ‘ioremap_nocache()’
For copying: ram to/from vram
• Linux provides special ‘memcpy’ functions:
– memcpy_fromio( ram, vram, nbytes );
– memcpy_toio( vram, ram, nbytes );
Our ‘vram.c’ driver
•
•
•
•
We imitate the code in our ‘ram.c’ driver
We use ‘temporary’ mappings (one page)
Our ‘read()’ and ‘write()’ are very similar
One notable difference:
‘read()’ is supposed to return 0 in case
the file’s pointer is at the ‘end-of-file’
• (This defect should be corrected in ‘ram.c’)
Warning about Red Hat 9.0
•
•
•
•
•
Red Hat 9.0 is now available in stores
It advertises kernel version 2.4.20
But it’s not identical to 2.4.20 in our class
Our demo modules do not always work
Changes were made to kernel structures
(e.g., task_struct)
• Changes were made to exported symbols
(e.g., sys_call_table)
Need a ‘work-around’
•
•
•
•
Our ‘vram.c’ doesn’t create its device-node
Requires users to create a node manually
We ‘hard-coded’ the device major number
So decisions differ from our ‘past practice’
Exercises
•
•
•
•
•
•
•
Get ‘vram.c’ from our class website
Compile and install the ‘vram.c’ driver
Create the device special file: ‘/dev/vram’
Change file-attributes (to allow writing)
Try copying video frame-buffer to a file
Use ‘dump.cpp’ to view that binary file
Try using ‘fileview.cpp’ to view video ram
Download