PCI device driver development under Linux

xiaoxiao2021-03-06  44

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.

转载请注明原文地址:https://www.9cbs.com/read-79301.html

New Post(0)