PCI device memory access

Source: Internet
Author: User

> Http://blog.chinaunix.net/u2/67414/showart_1657718.html



To see the actual running effect, we select the 8139too Nic as an example to crop the relevant code from the Linux driver of the NIC.
The driver of a PCI device must describe itself to the PCI core in the kernel. At the same time, it must also tell the PCI core which devices can be driven by itself. The following describes two important data structures.
Struct pci_device_id {
_ U32 vendor, device;/* vendor and device ID or pci_any_id */
_ U32 subvendor, subdevice;/* subsystem ID's or pci_any_id */
_ U32 class, class_mask;/* (class, subclass, prog-If) triplet */
Kernel_ulong_t driver_data;/* data private to the driver */
};

Struct pci_driver {
Struct list_head node;
Char * Name;
Struct module * owner;
Const struct pci_device_id * id_table; // list of device IDs that the driver can manipulate.
INT (* probe) (struct pci_dev * Dev, const struct pci_device_id * ID); // Insert a new device
Void (* remove) (struct pci_dev * Dev); // remove the device.
INT (* suspend) (struct pci_dev * Dev, pm_message_t State );
INT (* resume) (struct pci_dev * Dev );
INT (* enable_wake) (struct pci_dev * Dev, pci_power_t state, int enable );
Void (* shutdown) (struct pci_dev * Dev );
Struct device_driver driver;
Struct pci_dynids dynids;
};

Pci_device_id uniquely identifies a PCI device. Several of its members are represented in sequence: vendor number, device number, sub-vendor number, sub-device Number, category, category mask (class can be divided into base class, sub-
Class), private data. Each driver of a PCI device has an array of pci_device_id, which is used to tell the PCI core which devices can be driven by itself. 8139too driver
The program defines its pci_device_id array as follows:
Static struct pci_device_id rtl8139_pci_tbl [];

This array is initialized as a set of NICs of the 8139 series. When the PCI core obtains this array, it compares each entry in the array with the data read in the PCI configuration space, to the driver
Find the correct device. Pci_driver represents a PCI driver. The member id_talbe is the pointer to the pci_device_id array. Name is the driver
The probe completes the probe, that is, compare the pci_device_id array with the data in the kernel. Remove to remove the driver. Key members
.
The driver registers itself with the kernel through pci_module_init (we sometimes see the pci_register_driver function, which is actually the same. In the kernel code, we can see that it is just a simple # define ):
Pci_module_init (& pci_driver );
After the function is called, if the device identified in the pci_device_id array exists in the system and the device does not have a driver, the driver will be installed. The following shows the PCI device initialization code cropped from the 8139too driver code:
Pci_driver.h:

/* Pci_driver.h
* Helinqiang@hotmail.com
* 2006-3-5
*/
# Ifndef pci_driver_h
# Define pci_driver_h

# Include <Linux/mod_devicetable.h> // For struct pci_device_id
# Include <Linux/module. h> // For module_device_table
# Include <Linux/PCI. h> // For struct pci_driver

# Define drv_name "8139too"
# Define drv_version "0.9.27"
# Define rtl8139_driver_name drv_name "Fast Ethernet driver" drv_version

Typedef Enum {
Rtl8139 = 0,
Rtl8129,
} Board_t;

Static struct pci_device_id rtl8139_pci_tbl [] = {
{0x10ec, 0x8139, pci_any_id, pci_any_id, 0, 0, rtl8139 },
{0x10ec, 0x8138, pci_any_id, pci_any_id, 0, 0, rtl8139 },
{0x1113, 0x1211, pci_any_id, pci_any_id, 0, 0, rtl8139 },
{0x1500, 0x1360, pci_any_id, pci_any_id, 0, 0, rtl8139 },
{0x4033, 0x1360, pci_any_id, pci_any_id, 0, 0, rtl8139 },
{0x1186, 0x1300, pci_any_id, pci_any_id, 0, 0, rtl8139 },
{0x1186, 0x1340, pci_any_id, pci_any_id, 0, 0, rtl8139 },
{0x13d1, 0xab06, pci_any_id, pci_any_id, 0, 0, rtl8139 },
{0x1259, 0xa117, pci_any_id, pci_any_id, 0, 0, rtl8139 },
{0x1259, 0xa11e, pci_any_id, pci_any_id, 0, 0, rtl8139 },
{0x14ea, 0xab06, pci_any_id, pci_any_id, 0, 0, rtl8139 },
{0x14ea, 0xab07, pci_any_id, pci_any_id, 0, 0, rtl8139 },
{0x11db, 0x1234, pci_any_id, pci_any_id, 0, 0, rtl8139 },
{0x1432, 0x9130, pci_any_id, pci_any_id, 0, 0, rtl8139 },
{0x02ac, 0x1012, pci_any_id, pci_any_id, 0, 0, rtl8139 },
{0x018a, 0x0106, pci_any_id, pci_any_id, 0, 0, rtl8139 },
{0x126c, 0x1211, pci_any_id, pci_any_id, 0, 0, rtl8139 },
{0x1743, 0x8139, pci_any_id, pci_any_id, 0, 0, rtl8139 },
{0x021b, 0x8139, pci_any_id, pci_any_id, 0, 0, rtl8139 },

# Ifdef config_sh_secureedge5410
/* Bogus 8139 silicon reports 8129 without external prom */
{0x10ec, 0x8129, pci_any_id, pci_any_id, 0, 0, rtl8139 },
# Endif
# Ifdef config_8139too_8129
{0x10ec, 0x8129, pci_any_id, pci_any_id, 0, 0, rtl8129 },
# Endif
/* Some crazy cards Report invalid vendor IDs like
* 0x0001 here. The other IDs are valid and constant,
* So we simply don't match on the main vendor ID.
*/
{Pci_any_id, 0x8139, 0x10ec, 0x8139, 0, 0, rtl8139 },
{Pci_any_id, 0x8139, 0x1186, 0x1300, 0, 0, rtl8139 },
{Pci_any_id, 0x8139, 0x13d1, 0xab06, 0, 0, rtl8139 },
{0 ,}
};
Module_device_table (PCI, rtl8139_pci_tbl );
Static int _ devinit rtl8139_init_one (struct pci_dev * pdev, const struct pci_device_id * ID );
Static void _ devexit rtl8139_remove_one (struct pci_dev * pdev );

Static struct pci_driver rtl8139_pci_driver = {
. Name = drv_name,
. Id_table = rtl8139_pci_tbl,
. Probe = rtl8139_init_one,
. Remove = _ devexit_p (rtl8139_remove_one ),
};

# Endif // pci_driver_h

Pci_driver.c:
/* Pci_driver.c
* Helinqiang@hotmail.com
* 2006-3-5
*/

# Include "pci_driver.h"

# Include <Linux/init. h>

Module_author ("linqiang he, Hangzhou China ");
Module_license ("dual BSD/GPL ");

Static int _ init rtl8139_init_module (void)
{
/* When we're a module, we always print a version message,
* Even if no 8139 board is found.
*/
# Ifdef Module
Printk (kern_info rtl8139_driver_name "/N ");
# Endif

Return pci_module_init (& rtl8139_pci_driver );
}

Static void _ exit rtl8139_cleanup_module (void)
{
Pci_unregister_driver (& rtl8139_pci_driver );
}

Module_init (rtl8139_init_module );
Module_exit (rtl8139_cleanup_module );

Int _ devinit rtl8139_init_one (struct pci_dev * pdev, const struct pci_device_id * ID)
{
// Various debugging codes can be inserted here, which will be described in detail below.
Return 0;
}

Void _ devexit rtl8139_remove_one (struct pci_dev * pdev)
{
}

After the driver is successfully registered, rtl8139_init_one will be called. In this function, We can insert some print output statements to see the configuration address space and I/O address areas of PCI.
First, insert the following statement:
2010vendor, device;
Pci_read_config_word (pdev, 0, & vendor );
Pci_read_config_word (pdev, 2, & device );
Printk (kern_info "% x, % x/N", vendor, device );
This Code reads the first four digits of the IP address space configured for the NIC device, which is the vendor ID and device ID of the device. The output is as follows:
Mar 9 21:44:39 localhost kernel: 10ec, 8139
10ec and 8139 are the vendor and device numbers of my network card.
Insert the following code:
U32 addr1, addr2, addr3, addr4, addr5, addr6;
Pci_read_config_dword (pdev, 16, & addr1 );
Pci_read_config_dword (pdev, 20, & addr2 );
Pci_read_config_dword (pdev, 24, & addr3 );
Pci_read_config_dword (pdev, 28, & addr4 );
Pci_read_config_dword (pdev, 32, & addr5 );
Pci_read_config_dword (pdev, 36, & addr6 );
Printk (kern_info "% x, % x/N", addr1, addr2, addr3, addr4, addr5, addr6 );
This Code reads the starting location of the six I/O address areas of the NIC device. The output is as follows:
Mar 9 21:55:06 localhost kernel: 3401, e0000800
It can be seen that the device only uses the first two I/O address areas, respectively, to identify its I/O port areas and memory address space.
In addition, the MAC address of the NIC can be printed directly. Not detailed.

Next, we can insert some different debugging code in the source code given above in rtl8139_init_one to observe the actions of the device driver module in the kernel.
8139too
The first six bytes of the device memory of the NIC are the 48-bit MAC address of the NIC. You can obtain the MAC address by accessing the device memory. In
Rtl8139_init_one inserts code to access the device memory in four different ways. The first is implemented by accessing the I/O memory, and the last three are implemented by accessing the I/O port.
First:
Unsigned long mmio_start, addr1, addr2;
Void _ iomem * ioaddr;
Mmio_start = pci_resource_start (pdev, 1 );
Ioaddr = pci_iomap (pdev, 1, 0 );
Addr1 = ioread32 (ioaddr );
Addr2 = ioread32 (ioaddr + 4 );
Printk (kern_info "mmio start: % LX/N", mmio_start );
Printk (kern_info "ioaddr: % P/N", ioaddr );
Printk (kern_info "% 02lx. % 02lx. % 02lx. % 02lx. % 02lx. % 02lx/N ",
(Addr1) & 0xff,
(Addr1> 8) & 0xff,
(Addr1> 16) & 0xff,
(Addr1> 24) & 0xff,
(Addr2) & 0xff,
(Addr2> 8) & 0xff );
Running result:
Mar 10 22:34:56 localhost kernel: mmio start: e0000800
Mar 10 22:34:56 localhost kernel: ioaddr: f8aa6800
Mar 10 22:34:56 localhost kernel: 0020.2.3f.ac.41.9d

Second:

Unsigned long pio_start, pio_len, addr1, addr2;
Void _ iomem * ioaddr;
Pio_start = pci_resource_start (pdev, 0 );
Pio_len = pci_resource_len (pdev, 0 );
Ioaddr = ioport_map (pio_start, pio_len );
Addr1 = ioread32 (ioaddr );
Addr2 = ioread32 (ioaddr + 4 );
Printk (kern_info "Pio start: % LX/N", pio_start );
Printk (kern_info "ioaddr: % P/N", ioaddr );
Printk (kern_info "% 02lx. % 02lx. % 02lx. % 02lx. % 02lx. % 02lx/N ",
(Addr1) & 0xff,
(Addr1> 8) & 0xff,
(Addr1> 16) & 0xff,
(Addr1> 24) & 0xff,
(Addr2) & 0xff,
(Addr2> 8) & 0xff );
Running result:
Mar 10 22:30:52 localhost kernel: Pio start: 3400
Mar 10 22:30:52 localhost kernel: ioaddr: 00013400
Mar 10 22:30:52 localhost kernel: 0020.2.3f.ac.41.9d

Third:

Unsigned long pio_start, addr1, addr2;
Pio_start = pci_resource_start (pdev, 0 );
Addr1 = INL (pio_start );
Addr2 = INL (pio_start + 4 );
Printk (kern_info "Port IO start: % LX/N", pio_start );
Printk (kern_info "% 02lx. % 02lx. % 02lx. % 02lx. % 02lx. % 02lx/N ",
(Addr1) & 0xff,
(Addr1> 8) & 0xff,
(Addr1> 16) & 0xff,
(Addr1> 24) & 0xff,
(Addr2) & 0xff,
(Addr2> 8) & 0xff );
Running result:
Mar 10 22:36:18 localhost kernel: port I/O start: 3400
Mar 10 22:36:18 localhost kernel: 0020.2.3f.ac.41.9d

Fourth:

Unsigned long pio_start;
U8 addr1, addr2, addr3, addr4, addr5, addr6;
Pio_start = pci_resource_start (pdev, 0 );
Addr1 = INB (pio_start );
Addr2 = INB (pio_start + 1 );
Addr3 = INB (pio_start + 2 );
Addr4 = INB (pio_start + 3 );
Addr5 = INB (pio_start + 4 );
Addr6 = INB (pio_start + 5 );
Printk (kern_info "Port IO start: % LX/N", pio_start );
Printk (kern_info "% 02x. % 02x. % 02x. % 02x. % 02x. % 02x/N ",
Addr1, addr2, addr3, addr4, addr5, addr6 );
Running result:
Mar 10 22:37:19 localhost kernel: port I/O start: 3400
Mar 10 22:37:19 localhost kernel: 0020.2.3f.ac.41.9d


Supplement: >>>>>>>>>>>>>>>>>>>>>>>>

I/O ports are drivers and many devices
Communication between Linux instances
The kernel provides an operation interface for I/O port allocation. However, for PCI devices, its configuration address space has already specified the I/O port range, no additional allocation operation is required. Linux Kernel
Provides the following inline functions to access the I/O port:
Unsigned INB (unsigned port );
Void outb (unsigned char byte, unsigned port );
Unsigned inw (unsigned port );
Void outw (unsigned short word, unsigned port );
Unsigned INL (unsigned port );
Void outl (unsigned longword, unsigned port );
Next we will focus on the ioport_map function introduced by the 2.6 kernel:
Void * ioport_map (unsigned long port, unsigned int count );
Using this function, you can remap the Count continuous ports starting with the port to a "memory space ". Then, you can access these I/O ports at the address they return, just like accessing the I/O memory. If this ing is not required, you need to call the following function to cancel the ing:
Void iport_unmap
(Void * ADDR );
In addition to the I/O port, another major mechanism for communication with devices is to use registers or device memory mapped to the memory. These two types are called I/O memory, because the difference between registers and memory is
Is transparent.
For allocated I/O memory, direct access to the I/O memory pointer is generally not encouraged. It is best to access it through a page table and using a packaging function. To access through a page table, you need to map the allocated I/O memory to ensure that the I/O memory is accessible to the kernel. The function for completing the I/O memory ing is ioremap.
# Include <ASM/IO. h>
Void * ioremap (unsigned long phys_addr, unsigned long size );



Name


Pci_iomap-
Create a virtual mapping cookie for a PCI bar


Synopsis


void __iomem * pci_iomap
(

Struct pci_dev *Dev
, IntBar
, Unsigned longMaxlen
)
;

Arguments


dev



PCI device that owns the bar


bar



Bar number


maxlen



Length of the memory to map


Description



Using this function you will get a _ iomem address to your device bar.
You can access it using ioread * () and iowrite * (). These functions hide
The details if this is a mmio or Pio address space and will just do what
You have CT from them in the correct way.


maxlen

Specifies the maximum length to map. If you want to get access
The complete bar without checking for its length first, pass0

Here.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.