Accessing IO devices

Source: Internet
Author: User
Tags wrapper
I. I/O ports and I/O memory The concept of 1.1 I/O ports and memoryPeripherals are accessed by reading and writing their registers, which can be configured and run by registers.
Because it is not the processor's own register and therefore cannot be accessed directly using instructions, the registers of the peripherals need to be accessed through their addresses. The address of a peripheral register may be in the memory address space or in a separate I/O address space.
On a schema that provides a separate I/O address space for peripherals, the processor provides a separate line called I/O ports to access the I/O address space and uses special CPU instructions to access the I/O port.
For architectures that use a unified address space to access peripherals, it is sometimes necessary to simulate the use of I/O ports when accessing peripherals in order to accommodate the requirements of the bus to which the device is connected. So Linux implements the I/O port concept on all architectures.
But it should be noted that even in architectures that provide independent I/O space to access peripherals, not all peripherals use I/O ports to access peripherals, such as most PCI devices map their registers to a memory address segment, allowing access to these peripherals directly through memory. In contrast, the use of memory to access peripherals is better, because it does not require the use of special instructions, CPU access to memory is more efficient.
1.2 access to hardwareWhen accessing a hardware register, you must avoid unexpected behavior due to CPU or compiler optimizations.
Regular memory access is the reading and writing of a memory address, either by writing a data to an address or by reading a data from an address. Since memory reads and writes are too important for CPU performance, there are many mechanisms and methods for optimizing memory reads and writes, such as using caching, reordering read/write instructions, and so on.
But for the access to the hardware is different, access to the hardware registers to control the behavior of the hardware or to obtain the current state of the hardware, we do not expect to cache this operation, and hardware access is sometimes in a fixed order. Typically, for example, the transmission behavior of the network card, you must be ready to send data to tell the data submitted to the hardware, or you will certainly get not the results you expect. It is because of these special circumstances that access to the hardware cannot use the cache, and the order of instruction cannot be optimized by the compiler.
Without caching, the underlying hardware can be configured to prohibit hardware caching when accessing the I/O zone. As for banning the compiler from optimizing the order of the hardware access instructions, it needs to be implemented through a memory barrier, which provides the following macros to implement the memory barrier:
void barrier (void);
This function tells the compiler to insert a memory barrier, but the memory barrier has no effect on the hardware. The effect is that the compiled code saves all the modified values of the current CPU to memory, and then reads it again when the data is needed. It prevents compiler optimizations, but does not prevent hardware optimizations.
void Rmb (void);
void Read_barrier_depends (void);
void wmb (void);
void mb (void);
These functions are used to insert the hardware memory barrier, which is schema-dependent. Its role is as follows:
The RMB guarantees that the reading before the barrier will be completed before the read operation after the barrier is performed.
WMB guarantees that the write operation before the barrier will be completed before the write operation after the barrier is executed.
MB has both functions at the same time.
Read_barrier_depends is a special form of the reading barrier. The difference with the RMB is that it guarantees that before the completion of the read operation before the barrier, the reading associated with the barrier before the barrier and the read (i.e., the screen-dependent reading before the barrier) will not be executed.
Their SMP versions are as follows:
void Smp_rmb (void);
void Smp_read_barrier_depends (void);
void smp_wmb (void);
void smp_mb (void);
Using memory barriers can affect system performance, so they can only be used where barriers are really needed, and most kernel synchronization primitives have the effect of a memory barrier.
Two, I/O portThe I/O port is also a system resource, and the kernel provides a set of APIs to manage them.
2.1 I/O port assignmentIf you want to use the I/O port, you first need to apply the use of the I/O port to the system, which is done through the following APIs.
Request_region (Start,n,name);
This is a macro, defined in "Include/linux/ioport.h", and the parameter meaning is:
Start: Start port number
N: How many ports are used
Name: The user name for which the I/O port is being used
It returns a struct resource data structure pointer that returns NULL if the request fails.
However, when I use the I/O port, I am releasing the I/O port resources through the following APIs:
Release_region (Start,n);
The parameter meaning is the same as the parameter of the request.
2.2 Using I/O portsOnce you have applied to the I/O ports, you can use them to access the hardware, because the hardware distinguishes 8-bit, 16-bit, 32-bit ports from being able to use them as normal memory.
The kernel provides the following APIs to provide 8-bit, 16-bit, 32-bit I/O access:
U8 inb (unsigned long addr)
U16 INW (unsigned long addr)
U32 inl (unsigned long addr)
The above three are used to read the 8-bit, 16-bit, 32-bit I/O ports respectively.
void Outb (U8 B, unsigned long addr)
void Outw (U16 B, unsigned long addr)
void Outl (U32 b, unsigned long addr)
The above three are used to write 8-bit, 16-bit, 32-bit I/O ports respectively.
The kernel also provides a set of APIs to achieve a read-write sequential sequence, as follows:
void InSb (unsigned long addr, void *buffer, int count)
void Insw (unsigned long addr, void *buffer, int count)
void inSL (unsigned long addr, void *buffer, int count)
The above three functions are used to continuously read Count 8 bits, 16 bits, 32-bit values from addr to buffer. In fact, they are the corresponding wrapper for a function that reads one value at a time.
void outsb (unsigned long addr, const void *buffer, int count)
void Outsw (unsigned long addr, const void *buffer, int count)
void Outsl (unsigned long addr, const void *buffer, int count)
The above three functions are used to write the values of Count 8 bits, 16 bits, and 32 bits in the buffer into the addr respectively.
third, I/O memoryI/O memory can be a register or device memory mapped to a memory space. It is a ram-like area that can be accessed by reading and writing to the device.
Because of the differences between the schema and the bus, I/O memory may be accessed through the page table or not through the page table. If accessed through a page table, the kernel must first make these physical addresses visible to the driver (this usually requires IOREMAP to be implemented first before any I/O is made). If the page table is not required, I/O memory, like the I/O port, needs to be accessed using the appropriate function.
Regardless of whether you need to use Ioremap, do not directly use pointers to I/O memory, preferably through a system-supplied API.
3.1 I/O memory allocations and mappingsSimilar to I/O ports, I/O memory must also be allocated before use. Its prototype is defined below in "include/linux/ioport.h":
Request_mem_region (Start,n,name);
This is a macro, and its arguments have the following meanings:
Start: Start Address
N: Memory length
Name: Names of parts that request the right to use this area of memory
It returns a pointer to the struct resource data structure when the assignment succeeds, and returns null if the request fails
All I/O memory allocated by the system can be viewed through/proc/iomem.
After using I/O memory, you need to release it through the following APIs:
Release_mem_region (Start,n)
This is also a macro that frees the requested I/O memory.
The kernel also provides an additional API:
Check_mem_region (Start,n)
This macro is used to check if I/O memory is being used (that is, busy) with the length of n from start.
In addition to allocating I/O memory, we must also ensure that the specified I/O memory is accessible to the kernel. In most architectures, I/O memory is not directly read/write, the user needs to first set up a mapping, mapping using IOREMAP to complete.
I/O memory is ready to use after Ioremap is called. However, when using I/O memory, it is recommended that the access functions provided by the kernel be used instead of being used directly.
3.2 Access I/O memoryThe kernel provides a set of APIs for reading and writing I/O memory, these APIs are as follows:
Ioread8 (addr)
Ioread16 (addr);
IOREAD32 (addr);
The above three APIs are used to read 8-bit, 16-bit, 32-bit values from the specified I/O memory address. The addr value should be between the address returned by Ioremap and the Ioremap return address + the slice I/O memory size.
Iowrite8 (v, addr);
Iowrite16 (v, addr);
Iowrite32 (v, addr);
The above three APIs were used to write 8-bit, 16-bit, 32-bit v to the specified I/O memory address. Addr the meaning of the same read.
Similar to I/O port access, I/O memory access also has a wrapper version used to read and write one sequence at a time. See document "Include/asm-generic/io.h" in details.
If you want to perform an operation on a piece of I/O memory, you can use the following APIs:
Memset_io (A, B, c)
This API is used to set the content of I/O memory that is starting with a size C to B
Memcpy_fromio (A, B, c)
The API is used to copy the contents of the I/O memory, which starts with B, to the memory starting from a.
Memcpy_toio (A, B, c)
The API is used to copy the contents of the memory size C from B to the I/O memory starting from a.
If the hardware supports both the I/O ports and the I/OS memory for access, you can convert the I/O port to I/O memory by using the following APIs:
void __iomem *ioport_map (unsigned long port, unsigned int len);
It maps the Len Port starting from port to I/O memory. You can use the resulting I/O memory when the mapping is complete.
After using the I/O memory to play, you need to use the
void Ioport_unmap (void __iomem *addr) to disassociate the mappings.

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.