When writing the driver program, I/O port and I/O mem are rarely noticed. Although some non-conforming code can achieve the ultimate goal, this is extremely not recommended.
In combination, we thoroughly describe the relationship between IO ports, Io memory, and memory. The main memory is 16 MB of SDRAM. The peripherals are video capture cards with 16 MB of SDRAM as the buffer zone.
1. When the CPU is in the i386 architecture, in the i386 series, the memory and external Io are independently addressing. The memory space of mem is 32-bit addressable to 4 GB, And the IO space is 16-bit addressable to 64 K.
2. In the Linux kernel, the IO port used to access peripherals must be addressed through the IO port. However, I/O mem access is too long. The external mem cannot be accessed in the same way as the primary memory. Although the size is equal, the external mem is not registered in the system. Access to the external Io mem must be mapped to the memory space of the kernel through Remap. To achieve the same interface identity, the kernel provides a ing function from IO port to Io mem. After the ing, the IO port can be regarded as io mem, which can be accessed by Io mem.
3. CPU usage in arm or PPC Architecture
In this type of embedded processor, IO port addressing adopts memory ing, that is, Io bus is mem bus. If the addressing capability of the system is 32 bits, the IO port + MEM (including Io MEm) can reach 4 GB.
When accessing such Io ports, we can also use the IO port special addressing method. As for how the kernel is implemented when addressing the IO port, this is done during the internal nuclear transfer. In the processor of this architecture, I/O port is still supported, which is completely a problem left over from the i386 architecture. We will not discuss it here. The method for accessing Io mem is the same as that for i386.
Note:The Linux Kernel provides me with full support for IO port and IO mem. However, the driver under the driver directory seldom organizes IO port and IO mem resources according to this specification. The most critical issue for access between the two is the location of the address. In the C language, volatile can be used. Many codes use the volatile keyword when accessing registers in the I/O port. Although the function can be implemented, it is not recommended. Just like the simplest latency is the while, but it is definitely avoided in a multitasking system!
Generally, the CPU (such as arm and PowerPC) of a CPU of a server-defined identity management system only implements one physical address space, and the peripheral I/O port is a part of the memory. In this case, the CPU can access the peripheral I/O port as it accesses a memory unit, without the need to set up a dedicated peripheral I/O command.
However, the hardware implementation differences between the two are completely transparent to the software, driver developers can regard memory ing I/O Ports and peripheral memory as "I/O memory" resources.
Generally, when the system is running, the physical address of the peripheral I/O memory resources is known and determined by the hardware design. However, the CPU generally does not pre-define the virtual address range for the physical addresses of these known peripheral I/O memory resources, and the driver cannot directly access the I/O memory resources through the physical address, instead, you must map them to the core virtual address space (through the page table) before you can access these I/o memory resources by visiting the Internal commands based on the core virtual address range obtained by the ing. Linux in Io. the H header file declares the function ioremap (), which is used to map the physical addresses of I/O memory resources to the core virtual address space (3 GB-4 GB). The prototype is as follows:
Void * ioremap (unsigned long phys_addr, unsigned long size, unsigned long flags );
The iounmap function is used to cancel the Iing of ioremap (). The prototype is as follows:
Void iounmap (void * ADDR );
Both functions are implemented in the mm/ioremap. c file.
After ing the physical address of the I/O memory resources into the core virtual address, theoretically we can directly read and write the I/O memory resources like the read/write Ram. To ensure the cross-platform portability of the driver, we should use special functions in Linux to access the I/O memory resources, instead of accessing the resources by pointing to the core virtual address. For example, on the X86 platform, read/write I/O functions are as follows:
# Define readb (ADDR) (* (volatile unsigned char *) _ io_virt (ADDR ))
# Define readw (ADDR) (* (volatile unsigned short *) _ io_virt (ADDR ))
# Define readl (ADDR) (* (volatile unsigned int *) _ io_virt (ADDR ))
# Define writeb (B, ADDR) (* (volatile unsigned char *) _ io_virt (ADDR) = (B ))
# Define writew (B, ADDR) (* (volatile unsigned short *) _ io_virt (ADDR) = (B ))
# Define writel (B, ADDR) (* (volatile unsigned int *) _ io_virt (ADDR) = (B ))
# Define memset_io (a, B, c) memset (_ io_virt (a), (B), (c ))
# Define memcpy_fromio (a, B, c) memcpy (a) ,__ io_virt (B), (c ))
# Define memcpy_toio (a, B, c) memcpy (_ io_virt (a), (B), (c ))
Finally, we need to emphasizeMMAPFunction implementation method. UseMMAPIng a device means associating an address in the user space to the device memory. This means that as long as the program reads or writes data within the allocated address range, it actually accesses the device.
In the Linux source code, I searched for text containing "ioremap" and found that there were quite a few ioremap objects. Therefore, the author tries to find the physical address translation from the I/O operation to the real location of the virtual address, and finds that Linux has a statement to replace ioremap, but this conversion process is indispensable.
CPUThere are two addressing methods for the physical address of the peripheral Port:
One isIoMemory ing.
In Linux, Io Ports Based on Io ing and memory ing are collectively referred to as IO region ).
Io region is still an IO resource, so it can still be described by the resource structure type.
Linux Io region management:
1) request_region ()
Allocate the I/O port of a given range to an I/O device.
2) check_region ()
Check whether the IO port in a given range is idle or whether some of the ports have been allocated to an IO Device.
3) release_region ()
Releases the I/O port allocated to an I/O device in the specified range.
In Linux, you can use the following auxiliary functions to access the I/O port:
INB (), inw (), INL (), outb (), outw (), outl ()
"B" "W" and "L" represent 8-bit, 16-bit, and 32-bit respectively.
Access to Io memory resources
1) request_mem_region ()
Request to allocate the specified Io memory resources.
2) check_mem_region ()
Check whether the specified Io memory is occupied.
3) release_mem_region ()
Releases the specified Io memory.
The start address parameter passed to the function is the physical address of the memory area (the preceding function parameter table is omitted ).
Driver developers can regard Io ports mapped to memory and peripheral memory as IO memory resources.
Ioremap () is used to map the physical addresses of Io resources to the virtual address space (3 GB-4 GB) of the kernel. the ADDR parameter is a pointer to the virtual address of the kernel.
In Linux, you can use the following auxiliary functions to access Io memory resources:
Readb (), readw (), readl (), writeb (), writew (), writel ().
Linux in the kernel/resource. the global variables ioport_resource and iomem_resource are defined in file C, to describe the entire IO port space based on Io ing and IO memory resource space based on memory ing (including IO port and peripheral memory ).
Memory ing (IoAddress and memory address)
Differences between memory and I/O ing in the ARM architecture
(1) Io and memory space:
I/O space exists in the x86 processor. I/O space is relative to the memory space, and it is accessed through specific commands in and out. The port number identifies the peripheral Register address. The in and out commands in Intel syntax are as follows:
In accumulators, {port number │ DX}
Out {port │ DX}, accumulators
Currently, most embedded controllers such as arm and PowerPC do not provide I/O space, but only have memory space. The memory space can be accessed directly through addresses and pointers. the variables and other data used in programs and programs are stored in the memory space.
Even though I/O space is provided in the x86 processor, If We design our own circuit board, Peripherals can still be mounted only to the memory space. In this case, the CPU can access the peripheral I/O ports as it accesses a memory unit without the need to set up dedicated I/O commands. Therefore, the memory space is required, and the I/O space is optional.
(2) INB and outb:
In Linux device drivers, you should use the functions provided by the Linux kernel to access the ports located in the I/O space. These functions include:
· Read/write byte port (8-Bit Width)
Unsigned INB (unsigned port );
Void outb (unsigned char byte, unsigned port );
· Read/write port (16-Bit Width)
Unsigned inw (unsigned port );
Void outw (unsigned short word, unsigned port );
· Read/write long-character port (32-Bit Width)
Unsigned INL (unsigned port );
Void outl (unsigned longword, unsigned port );
· Read and write a string of bytes
Void InSb (unsigned port, void * ADDR, unsigned Long Count );
Void outsb (unsigned port, void * ADDR, unsigned Long Count );
· InSb () reads count byte ports from the port and writes the read results to the memory directed by ADDR; outsb () write the Count bytes of the memory that ADDR points to into the port that starts from the port consecutively.
· Read and write a string of characters
Void insw (unsigned port, void * ADDR, unsigned Long Count );
Void outsw (unsigned port, void * ADDR, unsigned Long Count );
· Read and write a string of long characters
Void insl (unsigned port, void * ADDR, unsigned Long Count );
Void outsl (unsigned port, void * ADDR, unsigned Long Count );
In the above functions, the type of the I/O port is highly dependent on the specific hardware platform, so only unsigned is written.
(3) readb and writeb:
After the physical address of the device is mapped to a virtual address, you can directly access these addresses through pointers, however, engineers should use the following functions of the Linux kernel to read and write virtual addresses mapped to the device memory. These functions include:
· Read I/O memory
Unsigned int ioread8 (void * ADDR );
Unsigned int ioread16 (void * ADDR );
Unsigned int ioread32 (void * ADDR );
Functions of earlier versions corresponding to the above functions are (these functions are still supported in Linux 2.6 ):
Unsigned readb (Address );
Unsigned readw (Address );
Unsigned readl (Address );
· Write I/O memory
Void iowrite8 (u8 value, void * ADDR );
Void iowrite16 (2010value, void * ADDR );
Void iowrite32 (u32 value, void * ADDR );
Functions of earlier versions corresponding to the above functions are (these functions are still supported in Linux 2.6 ):
Void writeb (unsigned value, address );
Void writew (unsigned value, address );
Void writel (unsigned value, address );
(4) map the I/O port to "memory space ":
Void * ioport_map (unsigned long port, unsigned int count );
This function can be used to remap count consecutive I/O ports starting with 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. When you no longer need this ing, you need to call the following function to undo it:
Void ioport_unmap (void * ADDR );
In fact, by analyzing the source code of ioport_map (), we can find that the so-called ing to memory space is actually an "illusion" for developers and is not mapped to the kernel virtual address, only to allow engineers to access the I/O port using a unified I/O memory access interface.
11.2.7 I/O space ing
Many hardware devices have their own memory, which is usually called I/O space. For example, all new graphics cards have several MB of RAM, which is called memory, used to store the screen images to be displayed on the screen.
1. Address ing
Depending on the device and bus types, the I/O space in the PC architecture can be mapped between three different physical address ranges:
(1) For most devices connected to the ISA bus
I/O space is usually mapped to the physical address range from 0xa0000 to 0xfffff, which leaves a space between 640k and 1 MB. This is called a "hole ".
(2) For some old devices that use the VESA Local Bus (VLB)
This is a dedicated bus mainly used by graphics cards: the I/O space is mapped to the address range from 0xe00000 to 0xffffff, that is, between 14 MB and 16 Mb. Because these devices make page table initialization more complex, such devices are no longer produced.
(3) For devices connected to the PCI bus
The I/O space is mapped to a large physical address range, located at the top of the ram physical address. Processing of such devices is relatively simple.
2. AccessI/OSpace
How does the kernel access an I/O space unit? Let's start with the PC architecture. This problem can be easily solved, and we will discuss other architectures further.
Do not forget that the kernel program acts on the virtual address, so the I/O space unit must represent an address larger than page_offset. In subsequent discussions, we assume that page_offset is equal to 0xc0000000, that is, the kernel virtual address is 4th GB.
The kernel driver must convert the physical address of the I/O space unit to the virtual address of the kernel space. In the PC architecture, the 32-bit physical address and the 0xc0000000 constant can be simply calculated or obtained. For example, if the kernel needs to store the values of I/O units whose physical address is 0x000b0fe4 in T1 and the values of I/O units whose physical address is 0xfc000000 in T2, you can use the following expression to complete this function:
T1 = * (unsigned char *) (0xc00b0fe4 ));
T2 = * (unsigned char *) (0xfc000000 ));
In chapter 6, we have introduced that in the initialization phase, the kernel has mapped available Ram physical addresses to the initial part of the 4G virtual address space. Therefore, the paging mechanism maps the virtual address 0xc00b0fe4 in the first statement to the original I/O physical address 0x000b0fe4, which falls in the "Isa hole" from 640k to 1mb. This is exactly what we expected.
However, for the second statement, there is a problem because its I/o physical address exceeds the maximum physical address of the system Ram. Therefore, the virtual address 0xfc000000 does not need to correspond to the physical address 0xfc000000. In this case, to include a virtual address mapped to this I/O physical address in the kernel page table, you must modify the page table by calling ioremap () function. The ioremap () and vmalloc () functions are similar. They call get_vm_area () to create a new vm_struct descriptor. The virtual address range described in this parameter is the size of the requested I/O space. Then, the ioremap () function updates the corresponding page table items of all processes as appropriate.
Therefore, the correct form of the second statement should be:
Io_mem = ioremap (0xfb000000, 0x200000 );
T2 = * (unsigned char *) (io_mem + 0x100000 ));
The first statement establishes a 2 MB virtual address range, starting from 0xfb000000; the second statement reads the memory unit of the address 0xfc000000. To cancel the ing later, the iounmap () function must be used.
Now let's take a look at the architecture apart from PC. In this case, the corresponding virtual address obtained by adding the I/O physical address with the 0xc0000000 constant is not always correct. To improve the portability of the kernel, Linux specifically includes the following macros to access the I/O space:
Readb, readw, readl
Read 1, 2, or 4 bytes from an I/O space unit.
Writeb, writew, writel
Write 1, 2, or 4 bytes to an I/O space unit.
Memcpy_fromio, memcpy_toio
Copy a data block from an I/O space unit to the dynamic memory. Another function copies a data block from the dynamic memory to an I/O space unit.
Memset_io
Fill an I/O space area with a fixed value
The following method is recommended for access to 0xfc000000 I/O units:
Io_mem = ioremap (0xfb000000, 0x200000 );
T2 = readb (io_mem + 0x100000 );
Using these macros, you can hide the differences in the methods used to access the I/O space on different platforms.
Article Source: http://www.360doc.com/content/10/1011/07/1317564_60018145.shtml
In essence, I/O ports are the registers of I/O Ports in Linux drivers, and I/O ports are controlled through operation registers. Io memory refers to some devices that map Io registers to a memory area, because no special commands are required to access the memory.
[Reprint] I/O port and I/O memory