The Linux PCI InterfaceSome background on PCIFeatures for driver-writerspci_dev_structThe ‘lspci’ commandAn illustrative example: vram.cinit_module()pci_find_class();Locate the VGA ‘frame buffer’How big is the frame buffer?Maximum memory-sizeProgramming algorithmLoop to find the ‘lowest 1’Checking for memory ‘wrap’Device-memory: read and writeFor copying: ram to/from vramOur ‘vram.c’ driverWarning about Red Hat 9.0Need a ‘work-around’ExercisesThe Linux PCI InterfaceAn introduction to the PCI configuration space registersSome 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 powerFeatures 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/pciAn 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 manualinit_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’ addressingMaximum 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 memoryProgramming 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 longwordLoop 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 casethe 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
View Full Document