Linux kernel is 2.6.32.2
Source code analysis tool source insight
Preface: It is very simple to operate several GPIO ports in a bare metal machine, just configure the control registers and data registers, but it still takes some effort to implement the same function in a Linux system.
The following is the source code of the driver.
#include <……>
#define DEVICE_NAME "leds" //Device name
static unsigned long led_table [] = {
S3C2410_GPB(5),
S3C2410_GPB(6),
S3C2410_GPB(7),
S3C2410_GPB(8),
};
static unsigned int led_cfg_table [] = {
S3C2410_GPIO_OUTPUT,
S3C2410_GPIO_OUTPUT,
S3C2410_GPIO_OUTPUT,
S3C2410_GPIO_OUTPUT,
};
static int sbc2440_leds_ioctl(
struct inode *inode,
struct file *file,
unsigned int cmd,
unsigned long arg)
{
switch(cmd) {
case 0:
case 1:
if (arg > 4) {
return -EINVAL;
}
s3c2410_gpio_setpin(led_table[ arg], !cmd);
return 0;
default:
return -EINVAL;
}
}
static struct file_operations dev_fops = {
.owner = THIS_MODULE,
.ioctl = sbc2440_leds_ioctl,
};
static struct miscdevice misc = { //Miscellaneous device structure
.minor = MISC_DYNAMIC_MINOR, //Minor device number
.name = DEVICE_NAME,
.fops = &dev_fops,
};
static int __init dev_init(void)
{
int ret;
int i;
for (i = 0; i < 4; i++) {
s3c2410_gpio_cfgpin(led_table[i], led_cfg_table[i]);
s3c2410_gpio_setpin(led_table[i], 0);
}
ret = misc_register(&misc); //Miscellaneous device registration
printk (DEVICE_NAME"tinitializedn");
return ret;
}
static void __exit dev_exit(void)
{
misc_deregister(&misc); //Misc device deregistration
}
module_init(dev_init); //Module loading function
module_exit(dev_exit); //Module uninstall function
MODULE_LICENSE("GPL"); //Certificate
MODULE_AUTHOR("FriendlyARM Inc."); //Author
The above lists the source code of the MINI2440led driver with simple comments, and the header file has been omitted. For the above driver, we mainly analyze the overall framework of this driver and some important functions and macro definitions involved in it.
1. Misc equipment
Misc devices are translated into miscellaneous devices or mixed devices. Miscellaneous devices have been specifically introduced in one of my articles. Here, I will explain them in detail based on this driver. Linux device drivers are mainly divided into character devices, block devices, and network devices. However, the above drivers do not belong to the common forms of the above three categories. We call the above drivers miscellaneous devices. What are miscellaneous devices? Linux contains many types of devices, but no matter how they are classified, they can not completely summarize all devices. We classify those that do not belong to the above three forms into a category called miscellaneous devices. Miscellaneous devices share the same major device number (MISC_MAJOR, which is 10), but the minor device numbers are different. All miscellaneous devices form a linked list. When accessing the device, the kernel finds the corresponding device according to this device number, and then calls the various functions registered in its corresponding file_operations structure. For miscellaneous devices, the Linux kernel specifically provides such a structure miscdevice, which is very inclusive. The structure is as follows:
The following files are defined in /linux2.6.32.2/include/linux/Miscdivice.h
struct miscdevice {
int minor;
const char *name;
const struct file_operations *fops;
struct list_head list;
struct device *parent;
struct device *this_device;
const char *nodename;
mode_t mode;
};
The miscdevice registration and deregistration functions provided are as follows.
int misc_register(struct miscdevice * misc);
int misc_deregister(struct miscdevice *misc);
In fact, the essence of miscellaneous devices is still character devices, but the device driver has been encapsulated. The main body of miscellaneous devices is still the implementation of the file_operations structure, so it is not mysterious. But it should be noted that miscellaneous devices are often used in embedded systems. The basic form of their use is shown in this driver program and will not be described again.
2. List of GPIO ports corresponding to LED
static unsigned long led_table [] = {
S3C2410_GPB(5),
S3C2410_GPB(6),
S3C2410_GPB(7),
S3C2410_GPB(8),
};
The LED device driver mainly performs s3c2410_gpio_cfgpin(led_table[i], led_cfg_table[i]); (configuration pin function) on the above ports.
s3c2410_gpio_setpin(led_table[i], 0); (set pin level status)
Operation. Let's first clarify the definitions of these ports.
The following files are defined in /arch/arm/mach-s3c2410/include/mach/gpio-nrs.h
/* S3C2410 GPIO number definitions. */
#define S3C2410_GPA(_nr) (S3C2410_GPIO_A_START + (_nr))
#define S3C2410_GPB(_nr) (S3C2410_GPIO_B_START + (_nr))
#define S3C2410_GPC(_nr) (S3C2410_GPIO_C_START + (_nr))
#define S3C2410_GPD(_nr) (S3C2410_GPIO_D_START + (_nr ))
#define S3C2410_GPE(_nr) (S3C2410_GPIO_E_START + (_nr))
#define S3C2410_GPF(_nr) (S3C2410_GPIO_F_START + (_nr))
#define S3C2410_GPG(_nr) (S3C2410_GPIO_G_START + (_nr))
#define S3C2410_GPH(_nr) (S3C2410_GPIO_H_START + (_nr))
enum s3c_gpio_number {
S3C2410_GPIO_A_START = 0,
S3C2410_GPIO_B_START = S3C2410_GPIO_NEXT(S3C2410_GPIO_A),
S3C2410_GPIO_C_START = S3C2410_GPIO_NEXT(S3C2410_GPIO_B),
S3C2410_GPIO_D_START = S3C2410_GPIO_NEXT(S3C2410_GPIO_C),
S3C2410_GPIO_E_START = S3C2410_GPIO_NEXT(S3C2410_GPIO_D),
S3C2410_GPIO_F_START = S3C2410_GPIO_NEXT(S3C2410_GPIO_E),
S3C2410_GPIO_G_START = S3C2410_GPIO_NEXT(S3C2410_GPIO_F),
S3C2410_GPIO_H_START = S3C2410_GPIO_NEXT(S3C2410_GPIO_G),
};
#define S3C2410_GPIO_NEXT(__gpio)
((__gpio##_START) + (__gpio##_NR) + CONFIG_S3C_GPIO_SPACE + 0)
CONFIG_S3C_GPIO_SPAC is a kernel configuration option, which can be found in .config. My configuration is:
CONFIG_S3C_GPIO_SPACE = 0
Therefore, taking S3C2410_GPB(5) as an example, its macro expansion is:
S3C2410_GPIO_NEXT(S3C2410_GPIO_A) +5 =>
(S3C2410_GPIO_A_START + S3C2410_GPIO_A_NR + CONFIG_S3C_GPIO_SPACE + 0) + 5 =>
Obviously, S3C2410_GPB(5) is the IO offset of the current GPB from the first address of GPA + the number of GPAs + the offset of GPB, that is,
0+32+5=37, same
S3C2410_GPB(0) is equivalent to 32
S3C2410_GPB(5) Equivalent to 37
S3C2410_GPB(6) Equivalent to 38
S3C2410_GPB(7) Equivalent to 39
S3C2410_GPB(8) is equivalent to 40
At this point we should understand that the function of this macro is to number the ports. For GPA, the port number range is 0~31, and the port number range of GPB is 32~63, and so on. Of course, not all numbers here are used. Because the number of ports in each group is different, 32 ports are defined for each group to ensure that each group has enough ports. After getting the port number, the result of dividing it by 32 can determine which group the port belongs to. For example, if the port number is 38, it is divided by 32 to get 1, and it knows that it belongs to the I/O port in GPB. This will be seen in further analysis later.
3. LED corresponding port status list analysis
static unsigned int led_cfg_table [] = {
S3C2410_GPIO_OUTPUT,
S3C2410_GPIO_OUTPUT,
S3C2410_GPIO_OUTPUT,
S3C2410_GPIO_OUTPUT,
};
S3C2410_GPIO_OUTPUT is defined in mach/regs-gpio.h
Here we mainly look at the last two digits, which indicate the status of the port.
00 represents input, 01 represents output, 10 represents function 2, and 1 represents function 3. Please note that GPA does not have an input function.
#define S3C2410_GPIO_LEAVE (0xFFFFFFFF)
#define S3C2410_GPIO_INPUT (0xFFFFFFF0) /* not available on A */
#define S3C2410_GPIO_OUTPUT (0xFFFFFFF1)
#define S3C2410_GPIO_IRQ (0xFFFFFFF2) /* not available for all */
#define S3C2410_GPIO_SFN2 (0xFFFFFFF2) /* bank A => addr/cs/nand */
#define S3C2410_GPIO_SFN3 (0xFFFFFFF3) /* not available on A */
4. s3c2410_gpio_cfgpin(led_table[i], led_cfg_table[i]) analysis
The function source code is defined in linux/arch/arm/plat-s3c24xx/gpio.c
Function prototype:
void s3c2410_gpio_cfgpin(unsigned int pin, unsigned int function)
{
void __iomem *base = S3C24XX_GPIO_BASE(pin);
unsigned long mask;
unsigned long con;
unsigned long flags;
if (pin < S3C2410_GPIO_BANKB) { //Judge whether the I/O port belongs to GPA,
mask = 1 << S3C2410_GPIO_OFFSET(pin);
} else {
mask = 3 << S3C2410_GPIO_OFFSET(pin)*2;
}
switch (function) { //Perform corresponding operations according to the function of the pin to be set
case S3C2410_GPIO_LEAVE:
mask = 0;
function = 0;
break;
case S3C2410_GPIO_INPUT:
case S3C2410_GPIO_OUTPUT:
case S3C2410_GPIO_SFN2:
case S3C2410_GPIO_SFN3:
if (pin < S3C2410_GPIO_BANKB) {
function -= 1;
function &= 1;
function <<= S3C2410_GPIO_OFFSET(pin);
} else {
function &= 3;
function <<= S3C2410_GPIO_OFFSET(pin)*2;
}
}
/* modify the specified register wwith IRQs off */
local_irq_save(flags);
con = __raw_readl(base + 0x00);
con &= ~mask;
con |= function;
__raw_writel(con, base + 0x00);
local_irq_restore(flags);
}
Let's take a look at the main framework first. The main body uses switch(function) to find the corresponding function to be set and perform the corresponding operation. This is probably easy to understand. The following will explain in detail some of the difficult to understand parts.
For void __iomem *base = S3C24XX_GPIO_BASE(pin); first look at its implementation
The following content is defined in /linux-2.6.32.2/arch/arm/mach-s3c2410includemachRegs-gpio.h
Previous article:Mini2440 Norflash driver transplantation process
Next article:How to re-burn supervivi on mini2440
- Popular Resources
- Popular amplifiers
Professor at Beihang University, dedicated to promoting microcontrollers and embedded systems for over 20 years.
- LED chemical incompatibility test to see which chemicals LEDs can be used with
- Application of ARM9 hardware coprocessor on WinCE embedded motherboard
- What are the key points for selecting rotor flowmeter?
- LM317 high power charger circuit
- A brief analysis of Embest's application and development of embedded medical devices
- Single-phase RC protection circuit
- stm32 PVD programmable voltage monitor
- Introduction and measurement of edge trigger and level trigger of 51 single chip microcomputer
- Improved design of Linux system software shell protection technology
- What to do if the ABB robot protection device stops
- Huawei's Strategic Department Director Gai Gang: The cumulative installed base of open source Euler operating system exceeds 10 million sets
- Download from the Internet--ARM Getting Started Notes
- Learn ARM development(22)
- Learn ARM development(21)
- Learn ARM development(20)
- Learn ARM development(19)
- Learn ARM development(14)
- Learn ARM development(15)
- Analysis of the application of several common contact parts in high-voltage connectors of new energy vehicles
- Wiring harness durability test and contact voltage drop test method
- Three ways to write fennel beans
- My own DIY download website, a small website placed on the router, please support
- CSR8670 - What are ANC, CVC, and DSP noise reduction?
- Battery real-time capacity test
- Share a wave of DIY reference designs for the Internet of Things
- Summary of pointer usage in embedded C language
- A colorimeter instrument based on USB interface
- Low-power electronic load for fast load transient testing
- 【ST NUCLEO-G071RB Review】ADC_DAC posting encountered problems
- MSP430 JTAG pin diagram