Linux Device Driver IO operation

Source: Internet
Author: User

Each peripheral is controlled by reading and writing its registers. Peripheral registers, also known as I/O ports, typically include: control registers, status registers, and data registers.

Depending on the CPU architecture, there are two ways that the CPU addresses the IO port:

(1) I/O mapping mode (i/o-mapped)

Typically, such as the X86 processor regards the peripheral registers as a separate address space (called an I/O address space or I/O port space), the instructions that access the memory cannot be used to access these registers, but to set private instructions for the read/write of the peripheral registers, such as in and out instructions.

(2) Memory mapping mode (memory-mapped)

The CPUs of the RISC instruction system (such as ARM, PowerPC, etc.) usually implement only one physical address space, the peripheral I/O ports become part of the memory, and registers participate in the memory unified addressing. At this point, the CPU can access the peripheral I/O port as if it were accessing a memory unit without having to set up specialized peripheral I/O instructions.

In the case of multiple devices, it is possible that a port or address is reused, and unprotected direct access is an error. The operating system provides such a set of protection mechanisms that the requested IO port/address is not allowed to be applied again (Request_region is used to request IO port number, request_mem_region for IO address).

1.I/O Mapping Method (case:/DRIVER/CHAR/PC8736X_GPIO.C)

void Request_region (unsigned long from, unsigned long num, const char *name)

The base address of the From:io port. The range that the Num:io port occupies. Name: The device name that uses this IO address.

This function is used to request an IO port area. If this I/O port is not occupied, it can be used in our driver. Before you use it, you must enlist the system to prevent it from being consumed by other programs. After registering, you can see your registered IO port in the/proc/ioports file. You can safely use INB (), OUTB () and other letters to access the. Request_region () is used by the kernel to drive the "allocate" port, where it is assigned to record that the port has already been used by a process, and if other processes attempt to access it, a "busy" error is generated. Therefore, the purpose is to achieve mutually exclusive access to resources.

void Release_region (unsigned long start, unsigned long n);

After the I/O port is exhausted (possibly when the module is unloaded), you should call Release_region to return the I/O port to the system. The parameters start and n should be consistent with the previous pass to Request_region.

After the driver is successfully requested to the I/O port, the ports can be read and written. Most hardware separates the 8-bit, 16-bit, and 32-bit ports, and is not as confusing as accessing memory. The driver must call different functions to access ports of different sizes. The Linux kernel header file (System-dependent header file <asm/io.h>) defines the following inline functions to access the I/O ports:

Unsigned inb (unsigned port);
void Outb (unsigned char byte, unsigned port);
Read or write section port (8-bit width). Some systems define the port parameter as unsigned long, while some platforms define it as unsigned short. The return type of the INB is also the system-dependent
unsigned inw (unsigned port);
void outw (unsigned short word, unsigned port);
These functions access a 16-bit port (a word width);  
unsigned inl (unsigned port);
void outl (unsigned long word, unsigned port);
These functions access 32-bit ports.  

In addition to the I/O operations that pass one data at a time, some processors implement special instructions for transmitting a sequence of bytes, words, or long words to an I/O port. This is called string instructions, and they accomplish tasks faster than a C-language loop can do. If the target processor does not have instructions for string I/O, the string instruction is implemented by performing a compact loop.
The prototype of a string function is:
void InSb (unsigned port, void *addr, unsigned long count);
void outsb (unsigned port, void *addr, unsigned long count);
Reads or writes count bytes starting from the memory address addr. The data is read from or written to a single port port.
void Insw (unsigned port, void *addr, unsigned long count);
void Outsw (unsigned port, void *addr, unsigned long count);
Read or write 16-bit values to a single 16-bit port.
void inSL (unsigned port, void *addr, unsigned long count);
void Outsl (unsigned port, void *addr, unsigned long count);
Read or write 32-bit values to a single 32-bit port.

Because the processor's rate may not match the peripheral (especially the low-speed device), it can cause problems when the processor transmits data to or from the bus too quickly. The workaround is to insert a small delay if the I/O instruction is followed by another similar I/o instruction. For this reason, Linux provides a paused I/O operation function whose names are added _p, such as Inb_p, outb_p, and so on, except for the non-paused I/O operation functions (those mentioned above are non-suspended). Most systems support these functions, although they are often extended to the same code as non-paused I/O, because there is no need for additional pauses if the system uses a reasonable modern peripheral bus.

2. Memory Mapping Method (case:/SOUND/SOC/SAMSUNG/PCM.C)

Although I/O ports are popular in the x86 world, the primary mechanism used to communicate with devices is through memory-mapped registers and device memory, both called I/O memory, because the difference between registers and memory is transparent to the software.

I/O memory is just a ram-like area where the processor accesses the zone through the bus for access to the device. Similarly, the reading and writing of this area is a marginal effect.

Depending on the computer system and the bus, I/O memory can be divided into or not accessible through the page table. In the Case of page table access, the kernel must first rearrange the physical address to make it visible to the driver, which means that you must call Ioremap before any I/O is done, and if the page table is not required, the I/O memory area is similar to the I/O port, and you can read and write them directly using the appropriate I/O functions.

Because of the marginal effect, it is discouraged to use I/O memory pointers directly, regardless of whether or not ioremap are required, instead use specialized I/O memory manipulation functions. These I/O memory operation functions are not only secure on all platforms, but also optimize the use of pointers to manipulate I/O memory directly.

The I/O memory area must be allocated before use. The interface that allocates the memory area is (defined in <linux/ioport.h>):
struct Resource *request_mem_region (unsigned long start, unsigned long len, char *name);
This function allocates a Len byte memory area, starting from start. If all goes well, a non-NULL pointer returns; Otherwise, the return value is NULL. All the I/O memory allocations are listed in/proc/iomem.
The memory area should be freed when it is no longer needed:
void Release_mem_region (unsigned long start, unsigned long len);
There is also an old function to check the availability of I/O memory areas:
int check_mem_region (unsigned long start, unsigned long len);
However, for check_region, this function is unsafe and should be avoided.

Allocating I/O memory is not the only required step before accessing I/O memory, and you must also ensure that the kernel has access to that I/O memory. Accessing I/O memory is not just a simple dereference pointer, and in many systems I/O memory cannot be accessed directly in this way. Therefore, a mapping must also be set through the Ioremap function.

#include <asm/io.h>
void *ioremap (unsigned long phys_addr, unsigned long size);

The ioremap is used to map the I/O memory area to a virtual address. The parameter phys_addr is the I/O memory start address to be mapped, the size of the I/O memory to be mapped, the virtual address to which the return value is mapped

void *ioremap_nocache (unsigned long phys_addr, unsigned long size);

Ioremap_nocache is a non-cached version of Ioremap. In fact, in most systems, the Ioremap is the same as the Ioremap_nocache implementation, because all I/O memory is in the memory address space without cache
void Iounmap (void * addr);

Iounmap for releasing mappings that are no longer needed

After Ioremap (and Iounmap), the device driver can access any I/O memory address. Note that the address returned by Ioremap cannot be directly dereferenced, instead, the access function provided by the kernel should be used.

unsigned int ioread8 (void *addr);
unsigned int ioread16 (void *addr);
unsigned int ioread32 (void *addr);
Here, the addr should be the address obtained from the Ioremap (perhaps with an integral type offset); The return value is read from the given I/O memory.
There is a similar series of functions to write I/O Memory:
void Iowrite8 (U8 value, void *addr);
void Iowrite16 (U16 value, void *addr);
void Iowrite32 (u32 value, void *addr);
If you have to read and write a series of values into a given I/O memory address, you can use the duplicate versions of these functions:
void Ioread8_rep (void *addr, void *buf, unsigned long count);
void Ioread16_rep (void *addr, void *buf, unsigned long count);

void Ioread32_rep (void *addr, void *buf, unsigned long count);
void Iowrite8_rep (void *addr, const void *buf, unsigned long count);
void Iowrite16_rep (void *addr, const void *buf, unsigned long count);
void Iowrite32_rep (void *addr, const void *buf, unsigned long count);
These functions read or write the count value from the given buf to the given addr. Note that count is expressed as the size of the data being written; Ioread32_rep reads the count 32-bit value starting from BUF.

If you browse through the kernel source, you can see many calls to the old set of functions when using I/O memory. These functions can still work, but their use in the new code is discouraged. They are less secure, among other things, because they do not perform the same type checking. However, we describe them here:
unsigned readb (address);
unsigned readw (address);
unsigned readl (address);
These macro definitions are used to obtain 8-bit, 16-bit, and 32-bit data values from I/O memory.
void Writeb (unsigned value, address);
void Writew (unsigned value, address);
void Writel (unsigned value, address);
Like the previous functions, these functions (macros) are used to write 8-bit, 16-bit, and 32-bit data items.

Linux Device Driver IO operation

Related Article

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.