3074 views|7 replies

6027

Posts

6

Resources
The OP
 

"Atomic Linux Driver Development" Basic Reading 2: Linux LED Driver Development [Copy link]

After understanding the general process and basic introduction of character device driver development, let's carry out a specific character device driver development.

Just like our bare metal development or STM32 development based on the ARM core, we develop from the easy to the difficult. For example, when we operate the LED, in STM32, we actually initialize the registers and then perform specific register operations to turn the LED on and off. The LED driver written based on Linux must comply with the Linux driver framework.

The corresponding LED control port in ATK-CLMP135B is PI3:

When we are doing bare metal development or controlling stm32, we usually don't pay much attention to the location of the physical space. But at this time, we have to pay attention to one problem when doing Linux development. Address mapping, we must first understand the main functions of the memory management unit mmu. One is the mapping of virtual space and physical space, which is the specific function completed by the driver. The user layer does not need to consider the specific physical address of the device, but the specific address of the device is a direct operation of the peripherals, realizing the mapping of the user layer and the physical layer. The second function is memory protection, setting the access rights of the memory, and setting the buffer characteristics of the virtual storage space.

Due to the limitation of address mapping, there is usually a situation where there are many virtual addresses to one physical address. The problem of virtual address being larger than the physical address range is handled by the processor. When the kernel starts, the memory management unit will be initialized and the memory mapping will be set. All future CPU accesses are virtual addresses. At this time, we can use the ioremap function to obtain the virtual address corresponding to the specified physical address space. The actual operation of the application layer is also through the operation of the virtual address space. When uninstalling the driver, we also need to use the iounmap function to release the previous mapping.

The LED driver is actually the high and low level control of the IO port. Here, the IO input and output are not the gpio pins we talked about when we studied microcontrollers. There are two concepts involved here, IO port and IO memory. When an external register or memory is mapped to the IO space, it is called a port, and when a register or memory is mapped to the memory space, it is called memory. After the physical address of the register is mapped to the virtual address through the ioremap function, these addresses can be directly accessed through pointers. The Linux kernel recommends using the operation function to read and write the mapped memory.

Next, write the LED program:

head File:

#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>

Macro Definition

#define LED_MAJOR		200		/* 主设备号 */
#define LED_NAME		"led" 	/* 设备名字 */

#define LEDOFF 	0				/* 关灯 */
#define LEDON 	1				/* 开灯 */
 
/* 寄存器物理地址 */
#define PERIPH_BASE     		     	(0x40000000)
#define MPU_AHB4_PERIPH_BASE			(PERIPH_BASE + 0x10000000)
#define RCC_BASE        		    	(MPU_AHB4_PERIPH_BASE + 0x0000)	
#define RCC_MP_AHB4ENSETR				(RCC_BASE + 0XA28)
#define GPIOI_BASE						(MPU_AHB4_PERIPH_BASE + 0xA000)	
#define GPIOI_MODER      			    (GPIOI_BASE + 0x0000)	
#define GPIOI_OTYPER      			    (GPIOI_BASE + 0x0004)	
#define GPIOI_OSPEEDR      			    (GPIOI_BASE + 0x0008)	
#define GPIOI_PUPDR      			    (GPIOI_BASE + 0x000C)	
#define GPIOI_BSRR      			    (GPIOI_BASE + 0x0018)


/* 映射后的寄存器虚拟地址指针 */
static void __iomem *MPU_AHB4_PERIPH_RCC_PI;
static void __iomem *GPIOI_MODER_PI;
static void __iomem *GPIOI_OTYPER_PI;
static void __iomem *GPIOI_OSPEEDR_PI;
static void __iomem *GPIOI_PUPDR_PI;
static void __iomem *GPIOI_BSRR_PI;

function

void led_switch(u8 sta)
{
	u32 val = 0;
	if(sta == LEDON) {
		val = readl(GPIOI_BSRR_PI);
		val |= (1 << 19);	
		writel(val, GPIOI_BSRR_PI);
	}else if(sta == LEDOFF) {
		val = readl(GPIOI_BSRR_PI);
		val|= (1 << 3);	
		writel(val, GPIOI_BSRR_PI);
	}	
}

void led_unmap(void)
{
	/* 取消映射 */
	iounmap(MPU_AHB4_PERIPH_RCC_PI);
	iounmap(GPIOI_MODER_PI);
	iounmap(GPIOI_OTYPER_PI);
	iounmap(GPIOI_OSPEEDR_PI);
	iounmap(GPIOI_PUPDR_PI);
	iounmap(GPIOI_BSRR_PI);
}

static int led_open(struct inode *inode, struct file *filp)
{
	return 0;
}

static ssize_t led_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{
	return 0;
}

static ssize_t led_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
	int retvalue;
	unsigned char databuf[1];
	unsigned char ledstat;

	retvalue = copy_from_user(databuf, buf, cnt);
	if(retvalue < 0) {
		printk("kernel write failed!\r\n");
		return -EFAULT;
	}

	ledstat = databuf[0];		/* 获取状态值 */

	if(ledstat == LEDON) {	
		led_switch(LEDON);		/* 打开LED灯 */
	} else if(ledstat == LEDOFF) {
		led_switch(LEDOFF);		/* 关闭LED灯 */
	}
	return 0;
}

static int led_release(struct inode *inode, struct file *filp)
{
	return 0;
}

static struct file_operations led_fops = {
	.owner = THIS_MODULE,
	.open = led_open,
	.read = led_read,
	.write = led_write,
	.release = 	led_release,
};

static int __init led_init(void)
{
	int retvalue = 0;
	u32 val = 0;

	/* 初始化LED */
	/* 1、寄存器地址映射 */
  	MPU_AHB4_PERIPH_RCC_PI = ioremap(RCC_MP_AHB4ENSETR, 4);
	GPIOI_MODER_PI = ioremap(GPIOI_MODER, 4);
  	GPIOI_OTYPER_PI = ioremap(GPIOI_OTYPER, 4);
	GPIOI_OSPEEDR_PI = ioremap(GPIOI_OSPEEDR, 4);
	GPIOI_PUPDR_PI = ioremap(GPIOI_PUPDR, 4);
	GPIOI_BSRR_PI = ioremap(GPIOI_BSRR, 4);

	/* 2、使能PI时钟 */
	val = readl(MPU_AHB4_PERIPH_RCC_PI);
	val &= ~(0X1 << 8);	/* 清除以前的设置 */
	val |= (0X1 << 8);	/* 设置新值 */
	writel(val, MPU_AHB4_PERIPH_RCC_PI);

	/* 3、设置PI3通用的输出模式。*/
	val = readl(GPIOI_MODER_PI);
	val &= ~(0X3 << 3);	/* bit0:1清零 */
	val |= (0X1 << 3);	/* bit0:1设置01 */
	writel(val, GPIOI_MODER_PI);

	/* 3、设置PI3为推挽模式。*/
	val = readl(GPIOI_OTYPER_PI);
	val &= ~(0X1 << 3);	/* bit0清零,设置为上拉*/
	writel(val, GPIOI_OTYPER_PI);

	/* 4、设置PI3为高速。*/
	val = readl(GPIOI_OSPEEDR_PI);
	val &= ~(0X3 << 3); /* bit0:1 清零 */
	val |= (0x2 << 3); /* bit0:1 设置为10*/
	writel(val, GPIOI_OSPEEDR_PI);

	/* 5、设置PI3为上拉。*/
	val = readl(GPIOI_PUPDR_PI);
	val &= ~(0X3 << 3); /* bit0:1 清零*/
	val |= (0x1 << 3); /*bit0:1 设置为01*/
	writel(val,GPIOI_PUPDR_PI);

	/* 6、默认关闭LED */
	val = readl(GPIOI_BSRR_PI);
	val |= (0x1 << 3);	
	writel(val, GPIOI_BSRR_PI);

	/* 6、注册字符设备驱动 */
	retvalue = register_chrdev(LED_MAJOR, LED_NAME, &led_fops);
	if(retvalue < 0) {
		printk("register chrdev failed!\r\n");
		goto fail_map;
	}
	return 0;
	
fail_map:
	led_unmap();
	return -EIO;
}

static void __exit led_exit(void)
{
	/* 取消映射 */
	led_unmap();

	/* 注销字符设备驱动 */
	unregister_chrdev(LED_MAJOR, LED_NAME);
}

module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("ALIENTEK");
MODULE_INFO(intree, "Y");

Is it very similar to bare metal driver development? The main functions are LED initialization, LED operation, device registration and deregistration, device opening and releasing, etc. The main difference is that Linux driver development must be carried out under its development framework.

This post is from Embedded System

Latest reply

Thank you for recommending this book. I hope you can provide a comprehensive introduction to this book.   Details Published on 2024-3-18 08:55
Personal signature

在爱好的道路上不断前进,在生活的迷雾中播撒光引


3

Posts

0

Resources
2
 

Thanks for sharing, thank you!

This post is from Embedded System
 
 

6065

Posts

4

Resources
3
 

What do you think of this book? Please give us a detailed introduction.

For example, which boards are targeted, what Linux version numbers are there, and what examples are there.

This post is from Embedded System

Comments

The example data of Zhengdian Atom is quite complete. This book uses the i.MX6U board, but I only have the STM32MP135 board.  Details Published on 2024-3-10 10:43
The example data of Zhengdian Atom is quite complete. This book uses the i.MX6U board, but I only have the STM32MP135 board.  Details Published on 2024-3-10 10:42
 
 
 

6027

Posts

6

Resources
4
 
damiaa posted on 2024-3-10 10:36 What do you think of this book? Please give a detailed introduction. For example, which boards are targeted, the Linux version number, and what examples are there.

The example data of Zhengdian Atom is quite complete. This book uses the i.MX6U board, but I only have the STM32MP135 board.

This post is from Embedded System
 
Personal signature

在爱好的道路上不断前进,在生活的迷雾中播撒光引

 
 

6027

Posts

6

Resources
5
 
damiaa posted on 2024-3-10 10:36 What do you think of this book? Please give a detailed introduction. For example, which boards are targeted, the Linux version number, and what examples are there.

There are many examples in the book, and the book is quite thick. It is estimated that it is good to understand the basic knowledge once within the specified time, and then try them one by one in the subsequent practical application.

This post is from Embedded System

Comments

It would be better if it was from ST. There are still some differences between these companies.  Details Published on 2024-3-10 10:46
 
Personal signature

在爱好的道路上不断前进,在生活的迷雾中播撒光引

 
 

6065

Posts

4

Resources
6
 
Qintianqintian0303 posted on 2024-3-10 10:43 There are a lot of examples in the book, and the book is quite thick. It is estimated that it is good to understand the basic knowledge once within the specified time. The subsequent practical application...

It would be better if it was from ST. There are still some differences between these companies.

This post is from Embedded System

Comments

It is true that each company is different. I originally planned to learn by practicing, but I found the structure too difficult. It is impossible to master it completely in two months, so I decided to understand the basics first and advance slowly.  Details Published on 2024-3-10 11:00
 
 
 

6027

Posts

6

Resources
7
 
damiaa posted on 2024-3-10 10:46 It would be better if it was from ST. Each of these is still a little different.

It is true that each company is different. I originally planned to learn by practicing, but I found the structure too difficult. It is impossible to master it completely in two months, so I decided to understand the basics first and advance slowly.

This post is from Embedded System
 
Personal signature

在爱好的道路上不断前进,在生活的迷雾中播撒光引

 
 

726

Posts

4

Resources
8
 

Thank you for recommending this book. I hope you can provide a comprehensive introduction to this book.

This post is from Embedded System
 
 
 

Just looking around
Find a datasheet?

EEWorld Datasheet Technical Support

EEWorld
subscription
account

EEWorld
service
account

Automotive
development
circle

Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号
快速回复 返回顶部 Return list