Linux device driver development detailed notes two

Chapter 11 Memory and I/O Access

The user space of each process is completely independent and independent of each other, and each user process has a different page table. The kernel space is
mapped by the kernel , it does not change with the process, it is fixed. The virtual address-to-physical address mapping of the kernel space is shared by all processes
. The virtual space of the kernel is independent of other programs.
11.3 Memory access
11.3.1 Dynamic application
of user space memory The function of dynamic application of memory in user space is malloc(), The use of this function on various operating systems is consistent, and
the release function of the memory requested by malloc() is free(). For Linux, the malloc() function of the C library generally
applies for memory from the kernel through two system calls, brk() and mmap().
Since the malloc algorithm of the user space C library actually has a secondary management capability, not every application and release of memory is necessarily
accompanied by a system call to the kernel. For example, the application in Listing 11.2 can call
free() immediately after it gets the memory from the kernel . Since free() calls mallopt(M_TRIM_THRESHOLD, -1) and
mallopt(M_MMAP_MAX, 0) before, this free() does not The memory will be returned to the kernel, but only to the allocation algorithm of the C library (the
memory still belongs to this process), so all subsequent dynamic memory applications and releases are performed in user mode.
Code Listing 11.2 User space memory application and mallopt

#include <malloc.h>
#include <sys/mman.h>
#define SOMESIZE (100*1024*1024) // 100MB

int main(int argc, char *argv[])
{
 unsigned char *buffer;
 int i;

 if (mlockall(MCL_CURRENT | MCL_FUTURE))
 mallopt(M_TRIM_THRESHOLD, -1);
 mallopt(M_MMAP_MAX, 0);

 buffer = malloc(SOMESIZE);
 if (!buffer)
 exit(-1);

 /*
 * Touch each page in this piece of memory to get it
 * mapped into RAM
 */
 for (i = 0; i < SOMESIZE; i += page_size)
 buffer[i] = 0;
 free(buffer);
 /* <do your RT-thing> */

 return 0;
}


In addition, the Linux kernel always uses demand paging, so when malloc() returns, although it
returns successfully , the kernel does not really give the process memory. At this time, if you read the requested memory, the content All are 0, the
mapping of this page is read-only. Only when a certain page is written, the kernel will actually give this page to the process after the page fault.

11.4 Device I/O port and I/O memory access The
device usually provides a set of registers to control the device, read and write the device, and obtain the device status, that is, the control register, the data register, and the status
register. These registers may be located in the I/O space, or they may be located in the memory space. When it is in the I/O space, it is usually called the I/O
port; when it is in the memory space, the corresponding memory space is called the I/O memory.

11.4.1 Linux I/O port and I/O memory access interface
1. I/O port
In the Linux device driver, functions provided by the Linux kernel should be used to access ports located in the I/O space. These functions include the following
Kind.
1) Read and write byte ports (8 bits wide).
unsigned inb(unsigned port);
void outb(unsigned char byte, unsigned port);
2) Read and write word port (16 bits wide).
unsigned inw(unsigned port);
void outw(unsigned short word, unsigned port);
3) Read and write long word port (32 bits wide).
unsigned inl(unsigned port);
void outl(unsigned longword, unsigned port);
4) 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);
5) insb() read count bytes from port port, and read The result is written into the memory pointed to by addr; outsb()
continuously writes count bytes in the memory pointed to by addr to the port starting with port.
6) Read and write a string of words.
void insw(unsigned port, void *addr, unsigned long count);
void outsw(unsigned port, void *addr, unsigned long count);
7) Read and write a string of long words.
void insl(unsigned port, void *addr, unsigned long count);
void outsl(unsigned port, void *addr, unsigned long count);
The type of I/O port number port in the above functions highly depends on the specific hardware platform, Therefore, only unsigned is written here.
2.
I /O memory Before accessing I /O memory in the kernel (usually the registers of the I2C, SPI, USB and other controllers in the chip or the devices on the external memory bus), you need to use the ioremap() function to set the device The physical address is mapped to the virtual address. The
prototype of ioremap() is as follows:
void *ioremap(unsigned long offset, unsigned long size);
ioremap() is similar to vmalloc(), it also needs to create a new page table, but it does not carry out the memory executed in vmalloc()
Assignment behavior. ioremap() returns a special virtual address that can be used to access a specific physical address range. This virtual
address is located in the vmalloc mapping area. The virtual address obtained by ioremap() should be released by the iounmap() function, and its prototype is as follows:
void iounmap(void * addr);
 

11.4.4 Mapping the device address to the user space
1. Memory mapping and VMA
user space are impossible and should not directly access the device. However, the mmap()
function can be implemented in the device driver , which can make the user space directly The physical address of the access device. Associate a segment of memory in user space with device memory. When a user accesses this address range in user space, 
it will actually be converted into access to the device.
This capability is very meaningful for devices such as display adapters. If the user space can directly access the video memory through the memory map,
each pixel of the screen frame will no longer need a process of copying from the user space to the kernel space.
mmap() must be mapped in units of PAGE_SIZE. In fact, memory can only be mapped in units of pages. If you want to map
an address range that is not an integer multiple of PAGE_SIZE, you must first perform page alignment and force the mapping to be a multiple of PAGE_SIZE.
As can be seen from the file_operations file operation structure, the prototype of the mmap() function in the driver is as follows:
int(*mmap)(struct file *, struct vm_area_struct*);
the mmap() function in the driver will perform mmap() in the user system It is finally called when called. The prototype of mmap() system call
is very different from the prototype of mmap() in file_operations, as shown below:
caddr_t mmap (caddr_t addr, size_t len, int prot, int flags, int fd, off_t offset) ;

When the user calls mmap(), the kernel will perform the following processing.
1) Find a piece of VMA in the virtual space of the process. 2) Map this piece of VMA.
3) If the device driver or file_operations of the file system defines the mmap() operation, call it.
4) Insert this VMA into the VMA linked list of the process.
The first parameter of mmap() function in file_operations is the VMA found in step 1).
The memory mapped by the mmap() system call can be unmapped by munmap( ). The prototype of this function is as follows:
int munmap(caddr_t addr, size_t len ​​);

Most device drivers do not need to provide the ability to map device memory to user space, because for stream-oriented
devices such as serial ports , it is meaningless to implement this kind of mapping. For display, video and other devices, the establishment of mapping can reduce the
memory copy between user space and kernel space

 

11.5 I/O memory static mapping

Establish a static mapping from the peripheral I/O memory physical address to the virtual address map_desc
map_desc structure
struct map_desc {  unsigned long virtual; /* virtual address*/  unsigned long pfn; /* __phys_to_pfn(phy_addr) */  unsigned long length; /* Size*/  unsigned int type; /* type*/ };




static struct map_desc ixdp2x01_io_desc _ _initdata = {
 .virtual = IXDP2X01_VIRT_CPLD_BASE,
 .pfn = _ _phys_to_pfn(IXDP2X01_PHYS_CPLD_BASE),
 .length = IXDP2X01_CPLD_REGION_SIZE,
 .type = MT_DEVICE
};

static void _ _init ixdp2x01_map_io(void)
{
 ixp2000_map_io();
 iotable_init(&ixdp2x01_io_desc, 1);//建立页映射

}

11.6 DMA

DMA is a hardware mechanism that allows two-way data transfer between peripherals and system memory without the involvement of the CPU. Using DMA
can make the system CPU get rid of the actual I/O data transmission process, thereby greatly improving the system throughput.

The data transmission in DMA mode is controlled by the DMA controller (DMAC). During the transmission, the CPU can perform other tasks concurrently.
When the DMA ends, DMAC informs the CPU that the data transfer has ended through an interrupt, and then the CPU executes the corresponding interrupt service routine for
post-processing.
 

Chapter 12 The Software Architecture Thoughts of Linux Device Drivers
 

Guess you like

Origin blog.csdn.net/csdn1126274345/article/details/102539664