Advanced Linux Driver (3) - Interrupt and Clock Mechanism


foreword

Interrupt and clock mechanism are two important technologies in Linux driver. Using these techniques, drivers can be helped to complete tasks more efficiently. In the process of writing device drivers, in order to let the system know what the hardware is doing, interrupts must be used. The device can do almost nothing without interrupts. This chapter will explain the interrupt and clock mechanism in detail.

Interrupt Brief

This section will briefly analyze interrupt related concepts and classify interrupts. According to different interrupt types, the method of writing interrupt driver is also different. The following will mainly introduce the basic concept and common classification of interrupts.

concept of interruption

中断It is a very important concept in computer. Without interrupts, devices and programs cannot use the computer's CPU resources efficiently.
1.什么是中断
Here I use a scientific essay "Overall Planning Method" written by the famous mathematician Hua Luogeng as a metaphor - making a pot of tea.
The situation at that time was: there was no boiling water; the kettle needed to be washed, the teapot and cups needed to be washed; what to do? The most time-saving method is to wash the kettle, fill it with cold water, and put it on the fire; while waiting for the water to boil, wash the teapot, wash the teacup, and take the tea leaves; wait for the water to boil, make tea and drink.
In the absence of interruption, the computer can only handle a linear process, which either only boils water, or only washes the teapot, or processes the event of washing the teapot after boiling the water, which is obviously a waste of time. The process of making tea and drinking water without using the interrupt mode and using the interrupt mode is shown in the figure below.
insert image description here
Interrupts are introduced into computers because it is more efficient to use them. In the process of boiling water, the short-term tasks of washing the teapot, washing the teacup, and taking the tea leaves have the advantage that the event of washing the teapot can be executed as soon as possible, so that the task of making tea and drinking can be completed as quickly as possible. Correspondingly, in the process of executing the program on the computer, due to a special situation (or called "event"), the running program is temporarily suspended, and the processing of this special event is executed, and then returns to the computer after the processing is completed. When the original program continues to execute downward, this process is interrupted.
2.中断在Linux中的实现
Interrupts are only implemented through signals in Linux. When the hardware needs to notify the processor of an event, it sends a signal to the processor. For example, when the user presses the answer key on the phone's keypad, a signal is sent to the phone's processor. After the processor receives this signal, it will call the speaker and microphone driver, so that the user can talk.
Usually, a driver only needs to apply for an interrupt and add an interrupt handler. The arrival of the interrupt and the call of the interrupt handler are all done by the kernel framework. This reduces a lot of burden on the programmer. The programmer only needs to ensure that the correct interrupt number is applied and the correct interrupt processing function is written.
说明: Most mobile phones use ARM processors. For readers who are just getting started with the driver, they don't know what processor to choose to learn. One of the most popular processors today is the ARM processor. It is widely used in equipment such as digital audio players, digital set-top boxes, game consoles, digital cameras and printers.

Macro Classification of Disruptions

In the Linux operating system, the classification of interrupts is very complicated. According to different angles, interrupts can be divided into different types. The relationships among the various types are not independent of each other, but often cross each other. Macroscopically, it can be divided into two categories 硬中断和软中断:
1.硬中断
Hard interrupts are interrupts generated by system hardware. System hardware often causes external events. External events are random and sudden, so hard interrupts are also random and sudden. For example, when a user uses a mobile phone, it is normally in a standby state, and the CPU handles clock and power management issues in the standby state. When the GSM module of the mobile phone receives an incoming call request, it will send a hardware interrupt request to the CPU through the interrupt line connected to the CPU. After the CPU receives the interrupt, it will immediately process the predefined interrupt handler. The interrupt handler will call the ringtone driver or the motor driver to make the mobile phone ring or vibrate, and wait for the user to answer the call.
The reason hardware interrupts are random and sudden is that the phone has no way of predicting when a call will come. In addition, hard interrupts can be masked. Many mobile phones now have airplane mode, which can automatically block incoming calls while on the plane.
2.软中断
A soft interrupt is generated when an interrupt instruction is executed. Soft interrupts do not use external facilities to add interrupt request signals, so the occurrence of interrupts is not random but arranged by the program. Soft interrupt instructions are often used in assembler programming, for example int n, n must be an interrupt vector.
There are two sources for the processor to receive soft interrupts. One is that the processor executes an incorrect instruction code, such as a division by zero error; the other is an interrupt generated by software, such as the process scheduling is the soft interrupt method used.

Location Classification of Interrupt Generation

From the position where the interrupt is generated, the interrupt can be divided into external interrupt and internal interrupt.
1.外部中断
External interrupts generally refer to interrupt requests issued by computer peripherals, such as keyboard interrupts, printer interrupts, and timer interrupts. External interrupts can be masked programmatically.
2.内部中断
Internal interrupt refers to the interrupt caused by hardware error (such as sudden power failure, parity check, etc.) or operation error (division by zero, operation overflow, single-step interrupt, etc.). Internal interrupts are non-maskable interrupts. Normally, most internal interrupts are handled by the Linux kernel, so driver programmers often don't need to care about these issues.

Synchronous and Asynchronous Interrupts

From the perspective of instruction execution, interrupts can be divided into synchronous interrupts and asynchronous interrupts.
1.同步中断
Synchronous interrupts are controlled by the CPU during instruction execution, and the CPU issues an interrupt after executing an instruction. That is to say, during the execution of the instruction, even if there is an interrupt, as long as the instruction has not been executed, the CPU will not execute the interrupt. Synchronous interrupts are generally caused by program errors, such as page fault interrupts in memory management, division by 0 errors, etc. When the CPU decides to handle the synchronous interrupt, it will call the exception handling function to make the system recover from the wrong state. When the error is unrecoverable, phenomena such as crashes and blue screens will appear. The previous versions of the Windows system often had a blue screen phenomenon, because it was impossible to recover from the exception.
2.异步中断
Asynchronous interrupts are randomly generated by hardware devices, and the clock synchronization with the processor is not considered when generating interrupts. This type of interrupt can be generated at any time. For example, in the network card driver, when the network card receives the data packet, it will send an asynchronous interrupt event to the CPU, indicating that the data arrives, and the CPU does not know when the event will be received. The execution sequence of the interrupt processing function of the asynchronous interrupt and the kernel is executed asynchronously, and there is no necessary connection between the two, and they will not affect each other.

Interrupt summary

The above 4 sections classify interrupts in Linux from different angles, but this is not a strict classification. For example, a hard interrupt can be an external interrupt or an asynchronous interrupt, and a soft interrupt can be an internal interrupt or a synchronous interrupt, as shown in the figure below:
insert image description here

Interrupt implementation process

The implementation process of interruption is a relatively complicated process. It involves concepts such as interrupt signal lines and interrupt controllers. First introduce the concept of interrupt signal line.

Interrupt signal line (IRQ)

中断信号线It is a general term for interrupt input lines and interrupt output lines. An interrupt input line refers to a pin that receives an interrupt signal. The interrupt output line refers to the pin that sends the interrupt signal line. Each peripheral that can generate interrupts has one or more interrupt output lines ( Interrupt ReQuestIRQ for short), which are used to notify the processor to generate an interrupt. Correspondingly, the processor also has a set of interrupt input lines for receiving interrupt signals from external devices connected to it.
As shown in the figure below, peripheral 1, peripheral 2, and peripheral 3 are all connected to different interrupt input lines on the ARM processor through their own interrupt output lines. Each IRQ line is numbered, generally starting from 0, and the number can also be called an interrupt number. In the process of writing a device driver, the interrupt number often needs to be specified by the driver developer. At this time, you can check the schematic diagram of the hardware development board to find the connection relationship between the device and the ARM processor. If it is connected to the interrupt line 0, then the interrupt number is 0.
insert image description here

interrupt controller

中断控制器Located between the ARM processor core and the interrupt source. External interrupt sources send interrupts to the interrupt controller. The interrupt controller judges according to the priority, and then sends the interrupt request to the ARM processor core through the pin. The interrupt controller inside the ARM processor is shown in the figure below.
insert image description here
When external devices generate interrupts at the same time, the interrupt priority generation logic will determine which interrupt will be executed. As in the interrupt mask register in the above figure, when the mask bit is 1, it means that the corresponding interrupt is disabled; when the mask bit is 0, it means that the corresponding interrupt can be executed normally. The meaning of mask bit 0/1 may be different in different processors.

interrupt handling

The entire process of Linux handling interrupts is shown in the figure below.
insert image description here

  • The peripheral generates an interrupt signal, which is sent to the interrupt controller as an electrical signal through the interrupt line.
  • The interrupt controller keeps checking the IRQ line to see if a signal is generated. If one or more IRQ lines generate signals, the interrupt controller will first process the IRQ line with a smaller interrupt number, which has a higher priority.
  • The interrupt controller stores the received interrupt number in I/Oport A, which is directly connected to the data bus of the CPU. In this way, the CPU can read the interrupt number in port A through the data bus.
  • When everything is ready, the interrupt controller sends a signal to the INTR pin of the CPU. At this time, the CPU will analyze the signal at an appropriate moment in the instruction cycle to determine the type of interrupt.
  • If the interrupt is caused by an external device, an acknowledge signal is sent to port B of the interrupt controller. Port B is set to an interrupt pending value, indicating that the CPU is executing the interrupt, and the interrupt is not allowed to be generated again at this time.
  • The CPU determines the corresponding interrupt processing function according to the interrupt number.

Interrupted installation and release

Interrupts should be installed when the device requires interrupt functionality. If the driver programmer does not notify the Linux kernel that an interrupt needs to be used by installing an interrupt, the kernel will simply answer and ignore the interrupt.
1.申请中断线
Applying for an interrupt line allows the kernel to know which interrupt number and which interrupt handler the peripheral should use. Requesting an interrupt line occurs when interaction with external devices is required. The Linux kernel provides request_irq()a function to apply for an interrupt line. In Linux2.6.29, this function is <kernel/irq/Mabage.c>implemented by .

int request_irq(unsigned int irq,
irq_handler_t handler,
	unsigned long irqflags,
const char *devname,
void *dev_id);
  • irqIndicates the interrupt number to apply for, and the interrupt number is determined by the hardware schematic diagram of the development board.
  • handlerIndicates the interrupt handler function pointer to be registered. When an interrupt occurs, the kernel will automatically call this function to handle the interrupt.
  • irqflagsRepresents attributes about interrupt handling. Through this flag, the kernel can determine how the interrupt should be handled. This part of knowledge will be explained in detail in the upper and lower half of the interrupt mechanism.
  • devnameIndicates the device name, which will be /proc/interruptsdisplayed in . interruptsThe correspondence between devices and interrupt numbers is recorded.
  • dev_idThis pointer is set up for shared interrupt lines. If you don't need to share the interrupt line, just set the pointer to NULL.
    request_irq()The function returns 0 on success and -EINVALor on error _NOMEM. <include/asm-generic/Errno-base.h>Macros are explicitly defined in header files EINVAL和ENOMEM.
#define ENOMEM    12 /*Out of memory*/
#define EINVAL    22 /*Invalid argument */

ENOMEMMacro indicates insufficient memory. Such errors often occur in embedded systems due to limited memory resources. EINVALMacro represents an invalid argument. If this return value appears, then you should check request_irq()whether the passed parameters are correct.
说明:How to know the execution process and return value of a function, the best way is to use Source Insightthe tools introduced earlier to view the kernel source code. This can help readers gain a deeper understanding of how the content is implemented.
2.释放中断线
When the device does not need the interrupt line, it needs to release the interrupt line. The interrupt signal is very scarce, for example, the S3C2440 processor has 24 external interrupt lines ( EINT). Some readers may doubt that 24 external interrupt lines are already a lot, but they are far from enough. For example, design a cell phone keypad in such a way that interrupt lines are not shared. Number keys will occupy 10 interrupt lines, answer and answer will occupy two interrupt lines, and other function keys will occupy several interrupt lines. In this example, only the keyboard takes up more than a dozen interrupt lines, and the remaining dozen are used by other external devices of the mobile phone. That is to say, the interrupt signal line is far from enough.
so Linux内核设计者都建议当中断不再使用时,就应该释放该中断信号线. However, from the perspective of application, the keyboard of the mobile phone should be valid when the mobile phone is turned on, and the use of the keyboard device must be realized by means of an interrupt line, so the interrupt line cannot be released when the phone is turned on. Generally, only the start button is effective when shutting down, and the shutdown task is not completed through the operating system, so when shutting down, the interrupt line can be released. The validity period of the interrupt should be in the whole operating cycle of the mobile phone.
The implementation function for releasing the interrupt line is free_irq().

void free_irq(unsigned int irq, void *dev_id);
  • irqIndicates the interrupt number of the release request.
  • dev_idThis pointer is set up for shared interrupt lines. This parameter will be described in the "Shared Interrupt" section. It should be noted that the interrupt can be used by other devices only when the interrupt line is released.

Key interrupt instance

Now that you know enough about interrupts, here's a setup driver. When the key is pressed, the case driver prints the prompt information of the key pressed.
As a driver developer, the first thing to do is to understand the circuit diagram. In actual project development, hardware design is sometimes very complicated. At this time, the driver developer should communicate more with the hardware developer to master enough hardware knowledge to avoid writing wrong drivers.

Key Device Schematic

First of all, you should carefully understand the schematic diagram of the button device. This is the most basic quality as a driver developer. The button device is a very simple device in the actual project, and the hardware schematic diagram is also very simple. The schematic diagram of this example can be mini2440downloaded for free from the official website of the development board ( http://www.arm9.net). The schematic diagram of the button is as follows.
insert image description here
Here is a brief introduction to the working principle of the circuit diagram. K1 to K6 are 6 buttons, one end of which is grounded, and the other end is respectively connected to the EINT13, EINT14, EINT15, EINT19 pins of the S3C2440 processor. EIN means external interrupt ( External Interrupt). Among them, EINT8 and EINT19 are respectively connected with a pull-up resistor R17 and R22.
说明: The pull-up resistor acts as a pull-up. Pull-up is to connect a pin of uncertain value to a high level through a resistor, so that the pin is at a high level. This resistor is the pull-up resistor, as shown in R17 and R22 in the above figure. The resistor also acts as a current limiter. The pull-down resistor is the same. The function of adding a pull-up resistor to the pin of the chip is to increase the output level, thereby improving the noise margin of the input signal of the chip and enhancing the anti-interference ability. When the buttons K1 and K2 are disconnected, both EINT8 and EINT19 are in high level state. When the buttons of the buttons K1~K6 are pressed, the corresponding external interrupt line is grounded and is in a low level state. This is mainly to read the port register status corresponding to the external interrupt line, and then you can know whether there is a button pressed.

Registered and unregistered devices

From the device point of view, devices can be divided into devices with registers and devices without registers. A key device is a device that has no registers. The fact that there are no internal registers in the key device does not mean that it has no corresponding external registers. In order to save costs, external registers are often integrated into the processor chip. In this way, the processor can control the functions of external devices through internal registers. So the current processor is no longer a pure processor like before, it is more like a simple computer.

Key Device Related Port Registers

The register associated with key K1 is the port G control register, as shown in the figure below. Taken together, it can be seen that the key K1 is connected to the EINT8 pin, which corresponds to bit 0 of the GPG0 port.
insert image description here
Ports are high-speed storage units (also called registers) with limited capacity, typically 8, 16, and 32 bits. It can be used to store instructions, data and addresses. The operation of the hardware device is generally realized by reading the state of the corresponding register through the software method. The following introduces the G port registers related to the key device. For these contents, you can refer to the S3C2440 chip user manual of Samsung, also called datasheet.
Port G has three controllers, GPGCON, GPGDAT, and GPGUP. The address, read and write requirements of each register of this port are shown in the table below.
insert image description here
1.GPGCON寄存器
GPGCON is the configuration register (GPG Configure). In S3C2440, most pins are multiplexed. A pin can be configured as an input, output, or other function. Here GPGCON is used for which of the following functions are: data input, data output, interrupt and reservation. Every two bits of GPGCON can take values ​​00, 01, 10, and 11 to indicate different functions.
As can be seen from the above table, the address of the GPGCON bus is 0x56000060, which is actually a 4-byte register.
2.GPGDAT寄存器
GPGDAT is the data register. GPGDAT is used to record the state of the pins. Each bit of the register represents a state. When the pin is set as an input by GPGCON, read this register to obtain the state value of the corresponding bit; when the pin is set as an output by GPGCON, write the corresponding bit of this register to make the pin output high or low flat. When a pin is set as an interrupt by GPGCO, this pin will be set as an interrupt signal source.
3.GPGUP寄存器
The GPGUP register is the port pull-up register. The port pull-up registers control the enable or disable of the pull-up registers for each port. When the corresponding bit is 1, it means that the corresponding pin has no internal pull-up resistor; when it is 0, the corresponding pin uses a pull-up resistor. When a pull-up or pull-down resistor is required, and the peripheral circuit does not have a pull-up or pull-down resistor, then an internal pull-up or pull-down resistor can be used instead. The pull-up and pull-down resistors are shown in the figure below.
insert image description here
Generally, when the GPIO pin is empty, that is, when the chip is not connected, its voltage is unstable and is easily affected by noise signals. If the pin is connected to a pull-up resistor, the level will be in a high state; if the pull-up resistor is connected, the pin level will be pulled down. In addition, a pull-up resistor can enhance the drive capability of the I/O port. Since hardware engineers generally design external pull-up or pull-down resistors for circuits, driver developers generally disable internal pull-up or pull-down resistors when writing drivers.
4.各寄存器的设置
The 3 port registers GPGCON, GPGDAT and GPGUP are interrelated. Their setting relationship is shown in the table below.
insert image description here
insert image description here
insert image description here

Example program analysis of button interrupt

Now start to analyze the button device program. The key driver is composed of initialization function, exit function and interrupt processing function.

Button Driver Composition

The relationship between the key driver initialization function, exit function and interrupt processing function is shown in the figure below.
insert image description here

  • When the module is loaded, the initialization function is called s3c2440_buttons_init(). In this function, request_irq()the function will be further called to register the interrupt. request_irq()The function operates on an interrupt descriptor array structure in the kernel irq_desc. The array structure is relatively complex, and its main function is to record the interrupt processing function corresponding to the interrupt number.
  • When an interrupt arrives, it will query the interrupt handler corresponding to the interrupt number in the interrupt descriptor array, and then execute the function. In this instance, the function's function name is isr_button.
  • When the module is unloaded, the exit function will be called s3c2440_buttons_exit(). In this function, free_irq()the interrupt number used to release the device is called. free_irq()The function will also operate the interrupt descriptor array structure irq_descand delete the interrupt handler corresponding to the device.

Initialization function s3c2440_buttons_init()

The initialization function s3c2440_buttons_init()is mainly responsible for the initialization of the module. Module initialization mainly includes setting the interrupt trigger mode, registering the interrupt number, etc. The specific code of this function is as follows:

static int __init s3c2440_buttons_init(void)
{
    
    
	int ret;   //存储返回值
	set_irq_type(K1_IRQ1, IRQ_TYPE_EDGE_FAILLING);  //设置按键k为下降沿触发中断
	
	/*注册中断处理函数*/
	ret = request_irq(K1_IRQ1, isr_button, SA_INTERRUPT, DEVICE_NAME, NULL);
	if(ret)
	{
    
    
		printk("K1_IRQ: could not register interrupt\n");
		return ret;
	}
	printk(DEVICE_NAME"initialized\n");
	return 0;
}

Next, analyze the s3c2440_buttons_init() function line by line

  • Line 4, use set_irq_type()to set the interrupt trigger condition. set_irq_type()The prototype of the function is as follows:
int set_irq_type(unsigned int irq, unsigned int type);

The parameter irqindicates the interrupt number, and the parameter type is used to define the trigger type of the interrupt. The interrupt trigger types include low-level trigger, high-level trigger, falling-edge trigger, rising-edge trigger, rising-edge and falling-edge combined trigger. The interrupt type defined here is IRQ_TYPE_EDGE_FALLING, indicating that the external interrupt is triggered by a falling edge. Interrupt trigger types are defined in <include/linux/irq.h>.

#define IRQ_TYPE_NONE      0x00000000  /*未定义中断类型*/
#define IRQ_TYPE_EDGE_RISING 0x00000001 /*上升沿中断类型*/
#define IRQ_TYPE_EDGE_FALLING 0x00000002 /*下降沿中断类型*/
#define IRQ_TYPE_EDGE_BOTH (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING  ) 
										/*上升沿和下降沿联合触发类型*/
#define IRQ_TYPE_LEVEL_HIGH 0x00000004 /*高电平触发类型*/
#define IRQ_TYPE_LEVEL_LOW  0x00000008 /*低电平触发类型*/
  • 6 lines, used to apply for interrupt for button K1. The parameter K1_IRQ1 is the interrupt number to apply for. The parameter isr_buttonis the interrupt callback function, which is triggered by key 1. The trigger condition is set as falling edge trigger. The falling edge trigger means that in two consecutive clock cycles, the interrupt controller detects the corresponding pin of the port, the first cycle is high level, and the second cycle is low level. As shown below.
    insert image description here

  • Lines 7~11, when the application interrupts an error, print the error message and return. printk()The usage of the function printf()is the same as that of the function, except that the former is used in the driver program, and the latter is used in the user program.

Interrupt handler isr_button()

When the button is pressed, the interrupt is triggered, and the interrupt handler will be triggered. The main function of this function is to judge whether the button K1 is pressed.
Interrupt handlers isr_button()are implemented by functions. The parameters of this function are passed by the system when calling this function. The parameter irqindicates the interrupt number that was triggered. The parameter dev_idis set up for the shared interrupt line, because the key driver does not use the shared interrupt, so the NULL value is passed in here. The parameter regsis a pointer to a register file structure. The register file holds the context of the processor before the processor enters the interrupt code. This information is generally only used during debugging, and is rarely used at other times. So for general drivers, this parameter is usually useless.

static irqreturn_t isr_button(int irq, void *dev_id, struct pt_regs *regs)
{
    
    
	unsigned long GPGDAT;
	GPGDAT=(unsigned long)ioremap(0x56000064, 4); /*映射内核地址*/
	if(irq == K1_IRQ1)
	{
    
    
		if((*(volatile unsigned long *)GPGDAT) & 1 == 0 ) //是否K1仍然被按下
		{
    
    
			printk("K1 is pressed\n");
		}
	}
	return 0;
}
  • Line 3 defines a long integer variable GPGDAT, which is used to store the kernel address. Only the kernel address can be accessed by the driver, and the related concepts of the kernel address will be described later.
  • Line 4 uses ioremapto convert a physical port address on a development board to a kernel address. ioremapThe implementation in the kernel is as follows:
void *ioremap(unsigned long phys_addr, unsigned long size)

The parameter of this function phys_addrindicates the initial I/O port address to be mapped. The parameter sizeindicates the size of the space to be mapped. As can be seen from the above GPGDAT register table, the size belongs to a 32-bit register. So, its parameters are 0x56000064 and 4 bytes respectively.

  • Line 5 judges whether the signal is an interrupt signal sent by pressing button 1.
  • Lines 7~10, when the key K1 is pressed, the message "K1 is pressed" will be printed in the terminal or log file. Line 7 indicates that when the 0th bit of the GPGDAT register is 0 (low level), the button K1 is pressed.

Exit function s3c2440_buttons_exit()

When a module is no longer used, it needs to exit the module. The exit module of the button s3c2440_buttons_exit()is implemented by a function, and its main function is to release the interrupt line.

static void __exit s3c2440_buttons_exit(void)
{
    
    
	free_irq(K1_IRQ, NULL);  /*释放中断线*/
	printk(DEVICE_NAME "exit\n");
}
  • Line 3, release the interrupt line applied by the key K1.
  • 4 lines, print debugging information.

clock mechanism

Some clock mechanisms are often used in Linux drivers, mainly to delay for a period of time. During this time, the hardware device can complete the corresponding work. This section will give a brief introduction to the clock mechanism of Linux.

time measure

An important global variable in the Linux kernel is HZthat this variable represents a value related to clock interrupts. The clock interrupt is generated by the system timing hardware at periodic intervals, and the periodic value is represented by HZ. According to different hardware platforms, the value of HZ is different. This value is generally defined as 1000, as shown in the following code.

#define HZ  1000

Here HZ means that the interrupt occurs 1000 times per second. Whenever a clock interrupt occurs, the value of the core's internal counter will increase by 1. The internal counter is jiffiesrepresented by a variable, which is set to 0 when the system is initialized. Every time the clock arrives, the value of this counter is increased by 1, which means that this variable records the time elapsed since the system was booted. You can use the following macros to
compare the values ​​of variables. The prototypes of these macros are as follows:jiffies

#define time_after(a,b)     \
	(typecheck(unsigned long , a) && \
	typecheck(unsigned long, b) && \
	((long)(b) - (long)(a) < 0))
#define time_before(a,b)   time_after(b,a)
#define time_after_eq(a,b)  \
	(typecheck(unsigned long , a) && \
	typecheck(unsigned long, b) && \
	((long)(a) - (long)(b) >= 0))
#define time_before_eq(a,b)  time_after_eq(b,a)

The macro in line 1 time_aftersimply compares the size of a and b, and returns true if a>b. time_beforeThe macro on line 5 time_afteris implemented by a macro. The macro in line 6 time_after_eqis used to compare the size and equality of a and b, and a>=breturn true if so. time_before_eqThe macro on line 10 time_after_eqis implemented by a macro.

time delay

In the C language, sleep()functions are often used to delay the program for a period of time. This function can achieve millisecond-level delays. In the device driver, many operations on the device also need to be delayed for a period of time, so that the device can complete some specific tasks. In the Linux kernel, there are many delay technologies, and only two important ones are explained here.
1.短时延时
When the device driver needs to wait for the completion of hardware processing, it will actively delay for a period of time. This time is generally tens of milliseconds, or even shorter. For example, when the driver writes data to a certain register of the device, the driver needs to wait for a certain period of time because the register writes data slowly, and then continues to perform the following work.
The Linux kernel provides three functions to complete nanosecond, microsecond and millisecond delays. The prototypes of these three functions are as follows:

static inline void ndelay(unsigned long x)
static inline void udelay(unsigned long usecs)
static inline void msllep(unsigned int msecs)

The implementation of these functions is related to the specific platform, and some platforms cannot realize nanosecond-level waiting at all. In this case, the time to execute a piece of code can only be calculated based on the CPU frequency information, and then simulated by software through a busy wait. This software simulation is similar to the following code:

static inline void ndelay(unsigned long x)
{
    
    
	...  /*由x计算出count的值*/
	while(count)
	{
    
    
		count--; /*忙等待*/
	}
}

In addition to using msleep()functions to achieve millisecond-level delays, there are other functions that are also used to achieve millisecond-level delays. This function will make the waiting process sleep instead of busy waiting. The prototype of the function is as follows:

void msleeep(unsigned int msecs)
unsigned long msleep_interruptible(unsigned int msecs)
static inline void ssleep(unsigned int seconds)

These three functions will not be busy waiting, but put the waiting process into the waiting queue, and wake up the process in the waiting queue when the delay time arrives. Where msleep()、ssleep()functions cannot be interrupted, and msleep_interruptible()functions can be interrupted.
2.长时延时
A long delay means that the driver will delay for a relatively long period of time. To achieve this delay, it is generally to compare the current jiffiesand target jiffiesvalues. Long delays can be implemented using busy waiting. The following code shows an example of a driver delay of 3 seconds:

unsigned long timeout = jiffies + 3*Hz;
while(time_before(jiffies, timeout));

time_beforeThe macro simply compares the size of the two times and returns true if the value of parameter 1 is less than the value of parameter 2.

summary

Most devices use interrupts to drive code execution. For example, the key driver described in this chapter will only trigger the previously registered interrupt handler when the user presses the keyboard. This mechanism has many advantages and can save a lot of CPU time. In addition to interrupts, this chapter also briefly introduces the clock mechanism. The speed of hardware work is generally slow. When operating some registers of the hardware, it generally requires the kernel to delay for a period of time. The busy waiting mechanism can be used for a short time, but for long For time delay, the waiting delay mechanism is used for the number.

Guess you like

Origin blog.csdn.net/m0_56145255/article/details/131665266