Sie sind auf Seite 1von 20

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 devices 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

Lets write another character device-driver


It will allow read/write access to video ram
Very analogous to our prior ram.c driver
Some differences:
Devices memory resides on PCI the bus
Can safely allow writing as well as reading
Hardware uses memory in nonstandard ways
We really need vendors programmer manual

init_module()

Drivers 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 1s
Read back the longword just written
The hard-wired bits will still be 0s
We will scan for first bit thats 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 didnt 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 files 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 its 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 doesnt 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

Das könnte Ihnen auch gefallen