The following is an article I recently read when I wrote a pci driver. I hope it will be helpful to you. Hi, I was a beginner in linux.
The driver is also difficult, and many people are asked about it shamelessly. I would like to express my gratitude and sorry to these experts, especially unix1998. The level of helplessness is limited. It may be wrong in some places. If
If you are not reading the original text smoothly, read the original text.
Original article:/Documentation/pci.txt
There is also a good article about pci device drivers:
Development of PCI driver in Linux
Http://www-900.ibm.com/developerWorks/cn/linux/l-pci/index.shtml? Ca = dwcn-isc & me = ccid
How to Write linux pci device drivers
Original file:/Documentation/pci.txt translated by terrace
PCI bus is widely used and surprising. Different pci devices have different requirements and problems. Therefore, it is very important to support the pci layer in the Linux kernel. This document is intended to solve various problems in pci processing for driver designers.
0. Structure of the Pci Device Driver
There are now two styles of pci driver structures: the new style Driver (that is, the pci layer is used for a large number of device detection)
Working and supports hot swapping) and old-style drivers (that is, the driver itself detects the device ). Unless you have a good reason, do not use the old style to write the driver. When the driver finds the driver, perform the following steps:
Enable Device
Access device configuration space
Detect Device resources (such as base address and interrupt number)
Allocate device resources
Communicate with devices
Most of the above steps will be discussed below. For more information, see. It has good comments.
If the pci subsystem is not configured (that is, CONFIG_PCI is not set), most of the functions described below are defined as inline functions, which are either empty, either return the corresponding error code to avoid excessive conditional macros ifdefs in the driver.
1. New drivers
The new driver only calls pci_register_driver during initialization, and uses a structure pointer pointing to struct pci_driver during the call. The pointer contains the following components:
Name driver name
Id_table refers to a pointer to the driver-related device ID table. Most drivers should use MODULE_DEVICE_TABLE (pci ,...) Export the device ID table. Set it to NULL when prob () is called to enable the system to detect all pci devices.
Probe points to the device detection function prob ()
Pointer. This function is used when the pci device ID matches the device ID table and is not processed by other drivers.
When a new device is inserted. When calling the command, input a pointer to struct
The pci_driver structure pointer and the device ID table matching the device are used as parameters. If it succeeds (the driver detects the pci device), 0 is returned; otherwise, a negative error code is returned. This function is always
It is called between contexts, so it can enter sleep state.
The remove Pointer Points to the remove () function of a device. This function is called when a pci device is detached (such as logging out of the device driver or manually pulling out the device. Like probe, this function can also be sleep.
The save_state is the status of the device that is saved before the device is paused.
Suspend transfers the device to a low-power state and stops.
Sesume wakes up a paused device (in low power consumption status.
Enable_wake allows the device to initiate a wake-up event to recover from a low-power state.
(For more information about pci power management and related functions, see the Documentation/power/pci.txt file)
The ID table is an array of the struct pci_device_id type. The array ends when every item of this type is NULL. Struct pci_device_id contains the following components:
The vendor ID and device ID (or PCI_ANY_ID) of the device)
Subvendor: the ID number of the sub-vendor corresponding to the subdevice device and the ID number of the sub-device (or PCI_ANY_ID)
Class, the class corresponding to the class_mask device. The class mask indicates the bits in the class used in the comparison.
The private data of the driver_data device.
When a device driver exists, you only need to call pci_unregister_driver to call remove () at the pci layer ()
The function automatically detaches all the drivers.
Add the following appropriate macros before the initial and cleanup functions (this macro is defined in ):
_ Init initialization tag, placed before the initialization function in the driver.
_ Exit End flag will be ignored in a non-modular driver.
_ Devinit device initialization tag. If the CONFIG_HOTPLUG option is selected during kernel compilation, it is equivalent to _ init.
_ Devexit is the same as _ exit.
General tips:
Mark _ init/_ exit before the module_init ()/module_exit () function;
The struct pci_driver should not mark any of the above marks;
The ID table array should be marked with _ devinitdata;
The probe () and remove () functions should mark _ devinit/exit;
If you are sure that it is not a driver that supports hot swapping, you can only use _ init/exit _ initdata/exitdata;
A pointer to a function marked with _ devexit must be generated by _ devexit_p (function_name.
It generates the corresponding function name. If the _ devexit function is not marked, NULL is generated.
2. Old-style pci device drivers
The old-style pci device driver does not use pci_register_driver () to detect the device. Instead, you need to manually (by the driver) use the following structure to detect the device:
Detect a device based on the vendor ID and device ID:
Struct pci_dev * dev = NULL;
While (dev = pci_find_device (VENDOR_ID, DEVICE_ID, dev ))
Configure_device (dev );
Detect devices by class ID:
Pci_find_class (CLASS_ID, dev );
Detect a device based on the vendor ID, device ID, subsystem vendor ID, and device ID:
Pci_find_subsys (VENDOR_ID, DEVICE_ID, SUBSYS_VENDOR_ID, SUBSYS_DEVICE_ID, dev );
You can replace VENDOR_ID and DEVICE_ID with the constant PCI_ANY_ID as the wildcard.
You can search for all devices. If you want to detect devices with more complex conditions, You have to traverse all the known
Pci device:
Struct pci_dev * dev;
Pci_for_each_dev (dev ){
... Do anything you want with dev...
}
For upward compatibility, you can also use pci_for_each_dev_reverse (dev) to traverse the device table.
3. Enable the device
Before you perform any operations on the specified device, you must call pci_enable_device () to enable the I/O of the device.
And memory resources, insufficient resources are allocated. If necessary, You must wake up a device in the paused status. Note that this operation may fail.
If you want to set the device to work in the bus master device mode, calling pci_set_master () will call the PCI_COMMAND
The bus master device in the register allows location 1 and modifies the value in the latency counter.
If you want to use pci memory to write invalid transactions, call pci_set_swi () to store the PCI_COMMAND
Mem_Wr_Inval position 1, and check whether the cache row length register (CACHE_LINE_REGISTER) is set correctly. Check the return value of pci_set_swi (). Not all devices support invalid pci memory writes.
4. How to access the pci device configuration space
You must use pci _ (read | write) _ config _ (byte | word | dword) to access the pci device (by struct pci_dev *
. If all the above functions are successful, 0 is returned, and the error code is returned. Most drivers expect that they will not fail to access valid pci devices.
If you want to access the standard space of the pci configuration header, you can use a symbolic constant to indicate the Register address and bit settings. These symbolic constants are defined in.
To access the pci extended space capability register, you only need to call pci_find_capability () for each specific capability to find the corresponding register block.
5. Addressing and interruption
Do not read the memory and port base addresses and interrupt numbers from the pci configuration space, but use the phase
Value because they may be mapped to the kernel. For more information about how to access device memory, see Documentation/IO-mapping.txt files. You also need to call
The request_region () function and request_mem_region () apply for the IO address and device memory address range respectively to ensure that no other driver uses the device.
All interrupt handlers must use the shared interrupt number. You can also use the devid parameter to map the interrupt number IRQs to the device data structure.
6. other useful functions
Pci_find_slot () detects the corresponding pci device based on the bus number and Slot Number of the device
Pci_set_power_state () sets the pci Power Management Status
Pci_find_capability () identifies the specified capability in the device capability table
Pci_module_init () inline function to ensure proper driver initialization and error handling
Pci_resource_start () returns the bus start address in the pci address range.
Pci_resource_end () returns the bus end address in the pci address range.
Pci_resource_len () returns the length of the pci address range, in bytes.
Pci_set_drvdata () sets a Private Data Pointer for pci_dev
Pci_get_drvdata () obtains its private data pointer from the pci_dev structure.
Pci_set_mwi () Enable invalid device memory write transactions
Pci_clear_mwi () disables invalid device memory write transactions.
7. Other prompts
When the pci slot is displayed to the user (for example, the driver wants to tell the user the device card it finds ),
Pci_dev-> slot_name.
Always use a pointer to struct pci_dev to reference a pci device. All pci-layer interface functions use this pointer. Do not use bus/slot numbers/function numbers. They have special purposes-it is very complicated to say that systems with multiple main bus are used.
8. Obsolete Functions
The following interface functions are reserved only for the sake of upward compatibility. Do not use them in your new driver:
Pcibios_present () was used before. Now you do not have to test whether the pci subsystem exists. If the pci sub-system does not exist, the pci device table is empty and all the function of the probe device returns NULL.
Pcibios _ (read | write) _ * has been replaced by the corresponding pci _ (read | write) _ *
Pcibios_find _ * has been replaced by the corresponding pci_find _ *