Just reviewed on BBS, maybe I have to do related things after the New Year, so I will collect some information first. LINUX PCI device driver development (ZZ)
1. Key data structure
There are three address spaces on the PCI device: the I / O space of the PCI, the storage space of the PCI, and the configuration space of the PCI. CPU can
Access all address space on the PCI device, where I / O space and storage are available to the device driver, and
The configuration space is used by the PCI initialization code in the Linux kernel. The kernel is responsible for all PCI devices at startup.
Initialization, configure all PCI devices, including interrupt numbers, and I / O base sites, and listening to file / proc / pci
There is a found PCI device, as well as the parameters and properties of these devices.
Linux drivers usually use structures to represent a device, and variables in the structure represent a certain
Specific equipment, this variable stores all information related to the device. Good drivers should be able to drive multiple same
Equipment, each device is divided, and if structured is used to represent all the drive
The program-driven device can simply use the array subscript to represent the secondary device number.
In the PCI driver, the following critical data structures play a very core role:
PCI_Driver
This data structure is in file include / Linux / pci.h, which is a new PCI after Linux kernel version 2.4.
The device driver is added, the most important is the ID_TABLE structure used to identify the device, and for detection
The function of the device probe () and unloading device REMOVE ():
Struct PCI_Driver {
Struct list_head node;
Char * name;
Const struct pci_device_id * id_table;
INT (* probe) (Struct PCI_DEV * DEV, Const Struct PCI_Device_id * id);
Void (* remove) (Struct PCI_DEV * DEV);
INT (* save_state) (Struct PCI_DEV * DEV, U32 State);
INT (* Suspend) (Struct PCI_Dev * DEV, U32 STATE);
INT (* resume) (Struct PCI_DEV * DEV);
INT (* enable_wake) (Struct PCI_DEV * DEV, U32 State, Int Enable);
}
PCI_DEV
This data structure is also in the file include / Linux / pci.h, which describes almost all PCI devices.
Hardware information, including vendor ID, device ID, various resources, etc.
Struct pci_dev {
Struct List_head global_list;
Struct List_Head Bus_List;
Struct pci_bus * bus;
Struct PCI_Bus * Subordinate;
Void * sysdata;
Struct proc_dir_entry * procent;
Unsigned int devfn;
UNSIGNED Short Vendor;
Unsigned short device;
UNSIGNED SHORT SUBSYSTEM_VENDOR;
UNSIGNED SHORT SUBSYSTEM_DEVICE;
Unsigned int class;
U8 HDR_TYPE;
U8 ROM_BASE_REG;
Struct PCI_Driver * driver;
Void * driver_data; u64 dma_mask;
U32 Current_State;
Unsigned short vendor_compatible [device_count_compatible];
Unsigned short device_compatible [device_count_compatible];
Unsigned int IRQ;
Struct resource resource [device_count_resource];
Struct Resource DMA_RESOURCE [Device_count_dma];
Struct resource Irq_resource [device_count_irq];
CHAR Name [80];
Char slot_name [8];
int Active;
int RO;
UNSIGNED SHORT Regs;
INT (* preted) (Struct PCI_DEV * DEV);
INT (* ACTIVATE) (Struct PCI_DEV * DEV);
INT (* deactivate) (Struct PCI_Dev * dev);
}
2. Basic framework
When you implement a PCI device driver in a module, you are usually at least the following sections: initializing device module
, Device open module, data read / write and control module, interrupt processing module, device release module, device unloading module
. The basic framework of a typical PCI device driver is given below, from it is not difficult to experience these key modules
How to organize it.
/ * Indicate which PCI device for this driver is available * /
Static struct pci_device_id demo_pci_tbl [] __INITDATA = {
{PCI_vendor_id_demo, PCI_Device_id_Demo,
PCI_Any_ID, PCI_Any_ID, 0, 0, Demo},
{0,}
}
/ * Data structure for specific PCI devices * /
Struct demo_card {
Unsigned int magic;
/ * Use the chain list to save all the same PCI device * /
Struct Demo_Card * Next;
/ * ... * /
}
/ * Interrupt Processing Module * /
Static Void Demo_Interrupt (int IRQ, Void * dev_id, struct pt_regs * regs)
{
/ * ... * /
}
/ * Device file operation interface * /
Static struct file_operations demo_fops = {
Owner: this_Module, / * DEMO_FOPS Device Module * /
Read: Demo_read, / * Read Device Operation * /
Write: demo_write, / * write device operation * /
IOCTL: DEMO_IOCTL, / * Control Equipment Operation * /
MMAP: DEMO_MMAP, / * Memory Heavy Mappings * /
Open: Demo_open, / * Open the device operation * /
Release: demo_release / * Release Device Operation * /
/ * ... * /
}
/ * Device module information * /
STATIC STRUCT PCI_Driver Demo_PCI_Driver = {
Name: demo_module_name, / * Device Module Name * /
ID_TABLE: DEMO_PCI_TBL, / * List of devices that can be driven * /
Probe: Demo_Probe, / * Find and initialize the device * /
REMOVE: DEMO_REMOVE / * Uninstall device module * // * ... * /
}
Static int __init demo_init_module (void)
{
/ * ... * /
}
Static void __exit demo_cleanup_module (void)
{
PCI_unregister_driver (& demo_pci_driver);
}
/ * Load driver module portfolio * /
Module_init (Demo_init_module);
/ * Uninstall driver module entry * /
Module_exit (Demo_cleanup_module);
The above code gives a model of a typical PCI device driver, which is a relatively fixed mode. need
Note that the functions or data structures related to loading and unloading modules should be plus __INIT, __EXIT, etc.
The flag is separated from the normal function. After constructing such a frame, how is the next job?
Complete each function module within the frame.
3. Initializing equipment module
Under the Linux system, you want to complete the initialization of a PCI device, you need to complete the following work:
Check if the PCI bus is supported by Linux kernel;
Check if the device is inserted on a bus slot, if it is, save the location of the slot it occupies.
Read the information in the configuration header is provided to the driver.
When the Linux kernel starts and completes the initialization operation of all PCI devices, login, and allocating resources, etc.
In order to establish a topological structure of all PCI devices in the system, thereafter, when the PCI driver needs to initialize the device
When you usually call the following code:
Static int __init demo_init_module (void)
{
/ * Check if the system supports the PCI bus * /
IF (! pci_present ())
Return -EnodeV;
/ * Register hardware driver * /
IF (! PCI_REGISTER_DRIVER (& demo_pci_driver) {
PCI_unregister_driver (& demo_pci_driver);
Return -EnodeV;
}
/ * ... * /
Return 0;
}
The driver first calls the function PCI_Present () check if the PCI bus has been supported by Linux kernel, if
Support the PCI bus structure, the return value of this function is 0, and if the driver is called when the function is called
Non-0 return values, then drivers must have to stop their own tasks. In the previous kernel in 2.4, it is necessary
Handmade the PCI_Find_Device () function to find the PCI device, but better after 2.4 is to call PCI_R.
The EGISTER_DRIVER () function is registered with the PCI device driver, and a PCI_Driver structure needs to be provided.
The Probe detect routine given in this structure will be responsible for completing the detection of hardware.
Static int __init demo_probe (Struct PCI_DEV * PCI_DEV, Const Struct
PCI_Device_id * pci_id)
{
Struct demo_card * card;
/ * Start PCI device * /
IF (PCI_Enable_Device (PCI_DEV))
Return-EiO;
/ * Device DMA ID * /
IF (PCI_SET_DMA_MASK (PCI_DEV, DEMO_DMA_MASK) {
Return -EnodeV;
}
/ * Dynamic application memory in the kernel space * /
IF ((card = kmalloc (sizeof (struct demo_card), gfp_kernel) == NULL) {
Printk (kern_err "pci_demo: out of memory / n");
Return -ENMEM;
}
MEMSET (Card, 0, SizeOf (* Card));
/ * Read PCI configuration information * /
Card-> IOBASE = PCI_Resource_Start (PCI_DEV, 1);
CARD-> PCI_DEV = PCI_DEV;
Card-> PCI_ID = PCI_ID-> Device;
CARD-> IRQ = PCI_DEV-> IRQ;
Card-> next = devs;
Card-> Magic = DEMO_CARD_MAGIC;
/ * Set to the bus master DMA mode * /
PCI_set_master (pci_dev);
/ * Application I / O resource * /
Request_Region (Card-> IOBASE, 64, CARD_NAMES [PCI_ID-> Driver_Data]);
Return 0;
}
4. Open the device module
In this module, the application interruption is mainly implemented, the read and write mode, and the application of the device are controlled. Applicable control
When the right is right, the non-blocking method is busy back, otherwise the process takes the initiative to accept the schedule, enter the sleep state, waiting for others
The process releases the control of the device.
Static int Demo_open (struct inode * inode, struct file * file)
{
/ * Application Interruption, registration interrupt handler * /
Request_irq (Card-> IRQ, & demo_interrupt, sa_shirq,
CARD_NAMES [PCI_ID-> Driver_Data], CARD) {
/ * Check read and write mode * /
IF (file-> f_mode & fmode_read) {
/ * ... * /
}
IF (file-> f_mode & fmode_write) {
/ * ... * /
}
/ * Apply for the control of the equipment * /
Down (& Card-> Open_SEM);
While (card-> open_mode & file-> f_mode) {
IF (file-> f_flags & o_nonblock) {
/ * NONBLOCK mode, return to -ebusy * /
Up (& Card-> Open_SEM);
Return -ebusy;
} else {
/ * Waiting for scheduling, get control * /
CARD-> Open_MODE | = f_mode & (fmode_read | fmode_write);
Up (& Card-> Open_SEM);
/ * Device Opening Count increased by 1 * /
MOD_INC_USE_COUNT;
/ * ... * /
}
}
}
5. Data reading and control information module
The PCI device driver can provide the application to the application through the function demo_ioc () in the DEMO_FOPS structure.
The hardware is controlled. For example, by reading a data from the I / O register and transmits to the user
Room:
Static Int Demo_iocTl (Struct Inode * Inode, Struct File * file, unsigned int
Cmd, unsigned long arg)
{
/ * ... * /
Switch (cmd) {
Case Demo_rdata:
/ * Read 4 bytes from I / O port * /
Val = Iobae 0x10);
/ * Transfer read data to user space * /
Return 0;}
/ * ... * /
}
In fact, in Demo_FOPS, it is also possible to implement operations such as Demo_READ (), Demo_mmap (), Linux
The source code of many device drivers is available in the driver directory in the nuclear source code. Looking there, similar examples can be found.
child. In addition to the I / O instruction, in addition to the I / O instruction, there is access to the I / O instruction. These
The operation of the memory can operate as ordinary memory after remapping the I / O memory, on the other hand
Let the device transmit data to the system memory in a manner that passes the main DMA (Bus Master DMA).
6. Interrupt Processing Module
The PC's interrupt resource is limited, only 0 to 15 interrupt numbers, so most external devices are in the form of sharing.
Please interrupt the number. When the interrupt occurs, the interrupt handler is first responsible for identifying the interrupt, then
One step process.
Static Void Demo_Interrupt (int IRQ, Void * dev_id, struct pt_regs * regs)
{
Struct demo_card * card = (struct demo_card *) dev_id;
U32 status;
Spin_lock (& Card-> Lock);
/ * Identification Interrupt * /
Status = inl (Card-> IOBASE GLOB_STA);
IF (! (status & int_mask)
{
Spin_unlock (& Card-> Lock);
Return; / * NOT for US * /
}
/ * Tell the device has received the interrupt * /
OUTL (Status & Int_mask, Card-> IOBASE GLOB_STA);
Spin_unlock (& Card-> Lock);
/ * Other further processes, such as updating DMA buffer pointers, etc. * /
}
7. Release device module
The release device module is mainly responsible for the release of the control of the device, the release of the memory and interruption, etc.
Good and open the device module:
Static Int Demo_release (Struct Inode * Inode, Struct File * file)
{
/ * ... * /
/ * Release the control of the device * /
Card-> Open_MODE & = (Fmode_Read | FMODE_WRITE);
/ * Wake Other Processes waiting for access to control * /
Wake_up (& Card-> Open_WAIT);
Up (& Card-> Open_SEM);
/ * Release interruption * /
Free_irq (Card-> IRQ, CARD);
/ * Device Opening Count increased by 1 * /
MOD_DEC_USE_COUNT;
/ * ... * /
}
8. Uninstall device module
The uninstall device module corresponds to the initialization device module, which is relatively simple, mainly to call function P
CI_UNREGISTER_DRIVER () logout device drivers from Linux kernels:
Static void __exit demo_cleanup_module (void)
{
PCI_unregister_driver (& demo_pci_driver);
}
summary
The PCI bus is not only a wide range of computer bus standards, but also a compatibility and most complete meter.
Computer bus. LINUX as a new operating system, its development prospect is unaffected, and is also the total PCI
The lines are possible to interconnect with various new devices. Since the Linux source is open, therefore gives anything connected to the PCI bus
The device's written driver is relatively easy. This article describes how to compile the PCI driver under Linux, and the kernel version is 2.4.