1.Image zImage uImage 

Linux视频学习笔记_#学习

tip:内核源码需要先进行比编译后才能对内核模块进行编译;(只有编译内核后才会生成autoconf.h文件该文件是给到驱动访问的一些宏定义)

Linux视频学习笔记_#学习_02

2.宏内核与微内核

Linux视频学习笔记_#学习_03

Linux视频学习笔记_#学习_04

3.makefile

Linux视频学习笔记_#include_05

Linux视频学习笔记_#笔记_06

Linux视频学习笔记_#include_07

Linux视频学习笔记_设备号_08

tip:obj-m 的一个全局变量(是针对整个内核来说不只是当前的文件中),且可以理解为这是一个全局数组里面是包含很多数据,所以用一般用"+="(添加一个新元素)

4.内核模块传参

Linux视频学习笔记_#笔记_09

Linux视频学习笔记_#笔记_10

Linux视频学习笔记_#笔记_11

Linux视频学习笔记_#include_12

权限字段解析 (-rw-r--r--)

在 ls -l 的输出中,权限字段通常由 10 个字符组成,例如:

text

-rw-r--r--

分解如下:

  1. 第1个字符:文件类型
  • -:普通文件
  • d:目录
  • l:符号链接
  • 其他字符可能表示设备文件、套接字等。
  1. 后续9个字符:权限分组(每3个字符一组)
  • 前3位:所有者(user)权限
  • 中间3位:所属组(group)权限
  • 最后3位:其他用户(others)权限

每组权限的字符含义:

  • r:读权限(read)
  • w:写权限(write)
  • x:执行权限(execute)
  • -:无对应权限

示例解释

在你的输出中:

text

-rw-r--r--

表示:

  • 这是一个普通文件-)。
  • 所有者读写权限(rw-)。
  • 所属组和其他用户只有权限(r--)。

1. 权限的两种表示形式

  • 符号模式-rw-r--r--
  • 分解:
  • -:普通文件
  • rw-:所有者(user)有读(r)、写(w)权限
  • r--:所属组(group)和其他用户(others)只有读(r)权限
  • 数字模式0644
  • 分解:
  • 0:特殊权限位(无特殊权限时通常省略,写作 644
  • 6:所有者权限(rw-,即 4+2+0=6
  • 4:所属组权限(r--,即 4+0+0=4
  • 4:其他用户权限(r--,即 4+0+0=4

2. 开头的 0 的含义

数字模式开头的 0 表示特殊权限位(占3位),具体包括:

  • SUID(Set User ID)4
  • 文件执行时以所有者身份运行(如 /usr/bin/passwd)。
  • SGID(Set Group ID)2
  • 文件执行时以所属组身份运行,或目录中新建文件继承父目录组。
  • Sticky Bit1
  • 目录中的文件仅所有者可删除(如 /tmp)。

若特殊权限未设置(如普通文件),则用 0 表示。

  • 例如:
  • 4755:SUID 设置为 4,权限为 755
  • 1777:Sticky Bit 设置为 1,权限为 777

3. 为什么 0644 和 644 通常等价?

  • 当特殊权限位为 0 时,可以省略(如 chmod 644 file)。
  • 显式写成 0644 是为了强调无特殊权限(常见于脚本或编程中)。

4. 示例对比

符号模式数字模式解释-rw-r--r--0644普通文件,所有者可读写,其他只读-rwxr-xr-x0755普通文件,所有者可读写执行drwxrwxrwt1777目录,设置 Sticky Bit

5. 总结

  • 0644 中的 0 表示无特殊权限(SUID/SGID/Sticky Bit 均未设置)。
  • 日常使用中,644 和 0644 完全等效,但显式写 0 更规范(尤其在编程时)。

5.全局符号表

Linux视频学习笔记_linux_13

Linux视频学习笔记_#笔记_14

Linux视频学习笔记_#笔记_15

6.查看ko的依赖

方法

适用场景

是否需要 root

modinfo <模块> | grep depends

查看未加载模块的声明依赖


lsmod | grep <模块>

查看已加载模块的实际依赖


modprobe --show-depends <模块>

模拟加载过程显示依赖


objdump -p <模块>.ko

分析二进制依赖(高级调试)


7.多个源文件组合成内核模块

Linux视频学习笔记_#include_16

Linux视频学习笔记_#学习_17

8.字符设备

Linux视频学习笔记_设备号_18

Linux视频学习笔记_linux_19

Linux视频学习笔记_#笔记_20

8.1设备号注销申请
静态注册

设备号:一个32位的数字,31~20:主设备号 19~0:次设备号

设备号文件相关规定见(kernel/Documention/devices.txt)不能随便申请一个设备号

Linux视频学习笔记_#笔记_21

Linux视频学习笔记_#学习_22

Linux视频学习笔记_#学习_23

Linux视频学习笔记_linux_24

Linux视频学习笔记_#include_25

动态注册

Linux视频学习笔记_#include_26

Linux视频学习笔记_#学习_27

Linux视频学习笔记_linux_28

8.2字符设备结构体_cdev

Linux视频学习笔记_#学习_29

Linux视频学习笔记_#学习_30

Linux视频学习笔记_#笔记_31

Linux视频学习笔记_#笔记_32

添加字符设备
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>


static dev_t led_num;
static struct cdev led_cdev;


int myled_open(struct inode *inode,struct file *file)
{
	printk(KERN_INFO"myled_open\n"); 
	return 0;
}
int myled_relaese(struct inode *inode,struct file *file)
{
	printk(KERN_INFO"myled_relaese\n"); 
	return 0;
}

static struct file_operations myled_fpos =
{
	.owner  =  THIS_MODULE,
	.open 	=  myled_open,
	.relaese =  myled_relaese,
};

static int __init myled_init(void)
{
	int rt;
	
	/*1.动态申请设备号*/
	rt = alloc_chrdev_region(&led_num,0,1,"my_led");
	if(rt < 0)
	{
		printk(KERN_ERR"register_chrdev_region fail\n");
		goto err_register_chrdev_region;
	}
	
	/*2.打印主次设备号*/
	printk(KERN_INFO"majro=%d minor=%d\n",MAJOR(led_num),NINOR(led_num));
	
	/*3.初始化字符设备的结构体,将led_cdev中的ops成员指向myled_fpos*/
	cdev_init(&led_cdev,&myled_fpos);
	
	/*4.将字符设备添加到内核*/
	rt = cdev_add(&led_cdev,led_num,1);
	if(rt < 0)
	{
		printk(KERN_ERR"cdev_add fail\n");
		goto err_cdev_add;
	}
	
	printk(KERN_INFO"myled_init\n"); 
	
	return 0;
	
err_cdev_add:
	/*注销设备号*/
	unregister_chrdev_region(led_num,1);

err_register_chrdev_region:
	return rt;
}

static void __exit myled_exit(void)
{
	/*内核中删除字符设备*/
	cdev_del(&led_cdev);
	/*注销设备号*/
	unregister_chrdev_region(led_num,1);
	printk(KERN_INFO"myled_exit\n"); 
	
}

module_init(myled_init);
module_exit(myled_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("A simple Linux char driver example");
获取主次设备号手动创建手动创建设备文件

Linux视频学习笔记_#include_33

Linux视频学习笔记_设备号_34

编写应用程序测试

Linux视频学习笔记_设备号_35

8.3自动创建设备文件

Linux视频学习笔记_linux_36

class&device

Linux视频学习笔记_linux_37

#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/device.h>


static dev_t led_num;
static struct cdev led_cdev;

static struct class 	*led_class;
static struct device 	*led_device;

static char msg[100] = {0};


int myled_open(struct inode *inode,struct file *file)
{
	printk(KERN_INFO"myled_open\n"); 
	return 0;
}
int myled_relaese(struct inode *inode,struct file *file)
{
	printk(KERN_INFO"myled_relaese\n"); 
	return 0;
}

static ssize_t myled_read(struct file *file, char __user *buf, size_t len, loff_t *offset) {
    int ret = copy_to_user(buf, msg, len);
    if (ret) {
        printk(KERN_ALERT "Failed to send data to user\n");
        return -EFAULT;
    }
    printk(KERN_INFO "Sent %zu bytes to user\n", len);
    return len;
}

static ssize_t myled_write(struct file *file, const char __user *buf, size_t len, loff_t *offset) {
    if (len > sizeof(msg)) {
        printk(KERN_ALERT "Data too large\n");
        return -EINVAL;
    }
    if (copy_from_user(msg, buf, len)) {  /*返回写入失败的个数*/
        printk(KERN_ALERT "Failed to receive data from user\n");
        return -EFAULT;
    }
    printk(KERN_INFO "Received %zu bytes from user: %s\n", len, msg);
    return len;
}

static struct file_operations myled_fpos =
{
	.owner  =  THIS_MODULE,
	.open 	=  myled_open,
	.relaese =  myled_relaese,
	.write	= 	myled_write,
	.read 	=	myled_read, 
};

static int __init myled_init(void)
{
	int rt;
	
	/*1.动态申请设备号*/
	rt = alloc_chrdev_region(&led_num,0,1,"my_led");
	if(rt < 0)
	{
		printk(KERN_ERR"register_chrdev_region fail\n");
		goto err_register_chrdev_region;
	}
	
	/*2.打印主次设备号*/
	printk(KERN_INFO"majro=%d minor=%d\n",MAJOR(led_num),NINOR(led_num));
	
	/*3.初始化字符设备的结构体,将led_cdev中的ops成员指向myled_fpos*/
	cdev_init(&led_cdev,&myled_fpos);
	
	/*4.将字符设备添加到内核*/
	rt = cdev_add(&led_cdev,led_num,1);
	if(rt < 0)
	{
		printk(KERN_ERR"cdev_add fail\n");
		goto err_cdev_add;
	}
	
	/*5.创建设备类*/
	led_class = class_creat(THIS_MODULE,"myled");
	if(IS_ERR(led_class))
	{
		printk(KERN_ERR"class_creat fail\n");
		goto err_class_creat;
	}
	
	/*6.添加设备信息*/ /*(class:创建device是属于哪个类 parent:默认为NULL devt:设备号,设备号必须正确,因为这个函数会在/dev目录下帮我们自动创建文件设备 drvdata:私有数据 fmt:设备名字创建成功可在/dev目录看见该名字  )*/
	led_device = device_create(led_class,NULL,led_num,NULL,"myled");
	if(IS_ERR(led_device))
	{
		printk(KERN_ERR"device_create fail\n");
		goto err_device_create;
	}
	

	printk(KERN_INFO"myled_init\n"); 
	
	return 0;

err_device_create:
	/*销毁设备类*/
	class_destroy(led_class);

err_class_creat:
	/*内核中删除字符设备*/
	cdev_del(&led_cdev);
	
err_cdev_add:
	/*注销设备号*/
	unregister_chrdev_region(led_num,1);

err_register_chrdev_region:
	return rt;
}

static void __exit myled_exit(void)
{
	
	/*从类中销毁设备信息*/
	device_destroy(led_class,led_num);
	/*销毁设备类*/
    class_destroy(led_class);
	/*内核中删除字符设备*/
	cdev_del(&led_cdev);
	/*注销设备号*/
	unregister_chrdev_region(led_num,1);
	printk(KERN_INFO"myled_exit\n"); 
	
}

module_init(myled_init);
module_exit(myled_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("A simple Linux char driver example");

Linux视频学习笔记_linux_38

9:GPIO标准接口函数

单个gpio

对于gpio的操作统一接口函数 实现跨平台,具体的几个接口函数使用见code

#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/device.h>
#include <linux/gpio.h>
#include <mach/platform.h>


static dev_t led_num;
static struct cdev led_cdev;

static struct class 	*led_class;
static struct device 	*led_device;

static char msg[100] = {0};


int myled_open(struct inode *inode,struct file *file)
{
	printk(KERN_INFO"myled_open\n"); 
	return 0;
}
int myled_relaese(struct inode *inode,struct file *file)
{
	printk(KERN_INFO"myled_relaese\n"); 
	return 0;
}

static ssize_t myled_read(struct file *file, char __user *buf, size_t len, loff_t *offset) {
    int ret = copy_to_user(buf, msg, len);
    if (ret) {
        printk(KERN_ALERT "Failed to send data to user\n");
        return -EFAULT;
    }
    printk(KERN_INFO "Sent %zu bytes to user\n", len);
    return len;
}

static ssize_t myled_write(struct file *file, const char __user *buf, size_t len, loff_t *offset) {
    
	char kbuf[2]={0};
	
	if (len > sizeof(kbuf)) {
        printk(KERN_ALERT "Data too large\n");
        return -EINVAL;
    }
    if (copy_from_user(kbuf, buf, len)) {  /*返回写入失败的个数*/
        printk(KERN_ALERT "Failed to receive data from user\n");
        return -EFAULT;
    }
    printk(KERN_INFO "Received %zu bytes from user: %s\n", len, kbuf);
	//kbuf[0]:指定哪个led
	//kbuf[1]:1-亮 0-灭
	
	if(kbuf[0] == 7)
	{
		gpio_set_value(PAD_GPIO_E+13,!kbuf[1]);
	}
	
	
    return len;
}

static struct file_operations myled_fpos =
{
	.owner  =  THIS_MODULE,
	.open 	=  myled_open,
	.relaese =  myled_relaese,
	.write	= 	myled_write,
	.read 	=	myled_read, 
};

static int __init myled_init(void)
{
	int rt;
	
	/*1.动态申请设备号*/
	rt = alloc_chrdev_region(&led_num,0,1,"my_led");
	if(rt < 0)
	{
		printk(KERN_ERR"register_chrdev_region fail\n");
		goto err_register_chrdev_region;
	}
	
	/*2.打印主次设备号*/
	printk(KERN_INFO"majro=%d minor=%d\n",MAJOR(led_num),NINOR(led_num));
	
	/*3.初始化字符设备的结构体,将led_cdev中的ops成员指向myled_fpos*/
	cdev_init(&led_cdev,&myled_fpos);
	
	/*4.将字符设备添加到内核*/
	rt = cdev_add(&led_cdev,led_num,1);
	if(rt < 0)
	{
		printk(KERN_ERR"cdev_add fail\n");
		goto err_cdev_add;
	}
	
	/*5.创建设备类*/
	led_class = class_creat(THIS_MODULE,"myled");
	if(IS_ERR(led_class))
	{
		printk(KERN_ERR"class_creat fail\n");
		goto err_class_creat;
	}
	
	/*6.添加设备信息*/ /*(class:创建device是属于哪个类 parent:默认为NULL devt:设备号,设备号必须正确,因为这个函数会在/dev目录下帮我们自动创建文件设备 drvdata:私有数据 fmt:设备名字创建成功可在/dev目录看见该名字  )*/
	led_device = device_create(led_class,NULL,led_num,NULL,"myled");
	if(IS_ERR(led_device))
	{
		printk(KERN_ERR"device_create fail\n");
		goto err_device_create;
	}
	
	/*7.添加一个GPIO*/
	rt = gpio_request(PAD_GPIO_E+13,"gpioe13");
	if(rt < 0)
	{
		printk(KERN_ERR"gpio_request fail\n");
		goto err_gpio_request;
	}
	
	/*8.初始化gpio*/
	rt = gpio_direction_output(PAD_GPIO_E+13,1);
	if(rt < 0)
	{
		printk(KERN_ERR"direction_output fail\n");
		goto err_direction_output;
	}

	printk(KERN_INFO"myled_init\n"); 
	
	return 0;
	

err_direction_output:
	/*释放gpio*/
	gpio_free(PAD_GPIO_E+13);
	
err_gpio_request:
	/*从类中销毁设备信息*/
	device_destroy(led_class,led_num);

err_device_create:
	/*销毁设备类*/
	class_destroy(led_class);

err_class_creat:
	/*内核中删除字符设备*/
	cdev_del(&led_cdev);
	
err_cdev_add:
	/*注销设备号*/
	unregister_chrdev_region(led_num,1);

err_register_chrdev_region:
	return rt;
}

static void __exit myled_exit(void)
{
	/*释放gpio*/
	gpio_free(PAD_GPIO_E+13);
	/*从类中销毁设备信息*/
	device_destroy(led_class,led_num);
	/*销毁设备类*/
    class_destroy(led_class);
	/*内核中删除字符设备*/
	cdev_del(&led_cdev);
	/*注销设备号*/
	unregister_chrdev_region(led_num,1);
	printk(KERN_INFO"myled_exit\n"); 
	
}

module_init(myled_init);
module_exit(myled_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("A simple Linux char driver example");
批量申请释放gpio

Linux视频学习笔记_linux_39

Linux视频学习笔记_#include_40

Linux视频学习笔记_linux_41

10:延时

Linux视频学习笔记_#笔记_42

睡眠延时

Linux视频学习笔记_#笔记_43

忙等待延时

Linux视频学习笔记_#笔记_44

11:miscdevices

作用

比如led、key、src_04等等这些字符设备统一归类为misc,用统一的主设备号,而不是每一个占用一个主设备号;

混杂设备主设备号为10;

Tip:输入子系统(如触摸屏)主设备号为13、fb0()主设备号29;

Linux视频学习笔记_设备号_45

code

Linux视频学习笔记_设备号_46

#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/device.h>
#include <linux/gpio.h>
#include <mach/platform.h>
#include <mach/miscdevice.h>

static char msg[100] = {0};


int myled_open(struct inode *inode,struct file *file)
{
	printk(KERN_INFO"myled_open\n"); 
	return 0;
}
int myled_relaese(struct inode *inode,struct file *file)
{
	printk(KERN_INFO"myled_relaese\n"); 
	return 0;
}

static ssize_t myled_read(struct file *file, char __user *buf, size_t len, loff_t *offset) {
    int ret = copy_to_user(buf, msg, len);
    if (ret) {
        printk(KERN_ALERT "Failed to send data to user\n");
        return -EFAULT;
    }
    printk(KERN_INFO "Sent %zu bytes to user\n", len);
    return len;
}

static ssize_t myled_write(struct file *file, const char __user *buf, size_t len, loff_t *offset) {
    
	char kbuf[2]={0};
	
	if (len > sizeof(kbuf)) {
        printk(KERN_ALERT "Data too large\n");
        return -EINVAL;
    }
    if (copy_from_user(kbuf, buf, len)) {  /*返回写入失败的个数*/
        printk(KERN_ALERT "Failed to receive data from user\n");
        return -EFAULT;
    }
    printk(KERN_INFO "Received %zu bytes from user: %s\n", len, kbuf);
	//kbuf[0]:指定哪个led
	//kbuf[1]:1-亮 0-灭
	
	if(kbuf[0] == 7)
	{
		gpio_set_value(PAD_GPIO_E+13,!kbuf[1]);
	}
	
	
    return len;
}

static struct file_operations myled_fpos =
{
	.owner  =  THIS_MODULE,
	.open 	=  myled_open,
	.relaese =  myled_relaese,
	.write	= 	myled_write,
	.read 	=	myled_read, 
};

static struct miscdevice myled_misc = {
	.minor	=	MISC_DYNAMIC_MINO,
	.name	=	"my_led",
	.fops	=	&myled_fpos,
	
};

static int __init myled_init(void)
{
	int rt;
	
	/*miscdevice设备的注册*/
	rt = misc_register(&myled_misc);
	if(rt < 0)
	{
		printk(KERN_ERR"misc_register fail\n");
		goto err_misc_register;
	}
	
	
	/*.添加一个GPIO*/
	rt = gpio_request(PAD_GPIO_E+13,"gpioe13");
	if(rt < 0)
	{
		printk(KERN_ERR"gpio_request fail\n");
		goto err_gpio_request;
	}
	
	/*8.初始化gpio*/
	rt = gpio_direction_output(PAD_GPIO_E+13,1);
	if(rt < 0)
	{
		printk(KERN_ERR"direction_output fail\n");
		goto err_direction_output;
	}

	printk(KERN_INFO"myled_init\n"); 
	
	return 0;
	
err_direction_output:
	/*释放gpio*/
	gpio_free(PAD_GPIO_E+13);
	
err_gpio_request:
	/*注销miscdevice设备*/
	misc_deregister(&myled_misc);
	
err_misc_register:
	return rt;
}

static void __exit myled_exit(void)
{
	/*注销miscdevice设备*/
	misc_deregister(&myled_misc);
	
	/*释放gpio*/
	gpio_free(PAD_GPIO_E+13);
	printk(KERN_INFO"myled_exit\n"); 
	
}

module_init(myled_init);
module_exit(myled_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("A simple Linux char driver example");

Linux视频学习笔记_#学习_47

12:IOCTL

作用

Linux视频学习笔记_设备号_48

Linux视频学习笔记_#include_49

Linux视频学习笔记_linux_50

IOCTL的cmd也是一个32位的数字:

Linux视频学习笔记_#include_51

Linux视频学习笔记_#学习_52

Linux视频学习笔记_#include_53

关于'V'的解释

Linux视频学习笔记_linux_54

Linux视频学习笔记_#学习_55

code
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/device.h>
#include <linux/gpio.h>
#include <mach/platform.h>
#include <mach/miscdevice.h>
#include <linux/ioctl.h>


#define MY_IOCTL_MAGIC 'L'
#define CMD_LED_ON 	_IOW(MY_IOCTL_MAGIC, 0, unsigned long)
#define CMD_LED_OFF _IOW(MY_IOCTL_MAGIC, 1, unsigned long)
#define CMD_BUF_W 	_IOW(MY_IOCTL_MAGIC, 2, unsigned char[4])
#define CMD_BUF_R 	_IOR(MY_IOCTL_MAGIC, 3, unsigned char[4])


int myled_open(struct inode *inode,struct file *file)
{
	printk(KERN_INFO"myled_open\n"); 
	return 0;
}
int myled_relaese(struct inode *inode,struct file *file)
{
	printk(KERN_INFO"myled_relaese\n"); 
	return 0;
}
long myled_unlocked_ioctl(struct file *, unsigned int cmd, unsigned long args)
{
	void __user *argp = (void __user *)args;
	unsigned char buf[4] = {'1','2','3','4'};
	
	if(_IOC_TYPE(cmd) != MY_IOCTL_MAGIC)
		return -ENOIOCTLCMD;
	switch(cmd)
	{
		case CMD_LED_ON:
		{
			gpio_set_value(args,0);
		}break;
		
		case CMD_LED_OFF:
		{
			gpio_set_value(args,1);
		}break;
		
		case CMD_BUF_W:
		{
			copy_from_user(buf,argp,sizeof(buf)); //_IOC_SIZE(cmd) = sizeof(buf)
		}break;
		
		case CMD_BUF_R:
		{
			copy_to_user(argp,buf,sizeof(buf));
		}break;
		
		default:
			retun -ENOIOCTLCMD;
	}
	
	retun 0;
}

static struct file_operations myled_fpos =
{
	.owner  	=  THIS_MODULE,
	.open 		=  myled_open,
	.relaese 	=  myled_relaese,
	.unlocked_ioctl		=  myled_unlocked_ioctl

};

static struct miscdevice myled_misc = {
	.minor	=	MISC_DYNAMIC_MINO,
	.name	=	"my_led",
	.fops	=	&myled_fpos,
	
};



static int __init myled_init(void)
{
	int rt;
	
	/*miscdevice设备的注册*/
	rt = misc_register(&myled_misc);
	if(rt < 0)
	{
		printk(KERN_ERR"misc_register fail\n");
		goto err_misc_register;
	}
	
	
	/*.添加一个GPIO*/
	rt = gpio_request(PAD_GPIO_E+13,"gpioe13");
	if(rt < 0)
	{
		printk(KERN_ERR"gpio_request fail\n");
		goto err_gpio_request;
	}
	
	/*8.初始化gpio*/
	rt = gpio_direction_output(PAD_GPIO_E+13,1);
	if(rt < 0)
	{
		printk(KERN_ERR"direction_output fail\n");
		goto err_direction_output;
	}

	printk(KERN_INFO"myled_init\n"); 
	
	return 0;
	
err_direction_output:
	/*释放gpio*/
	gpio_free(PAD_GPIO_E+13);
	
err_gpio_request:
	/*注销miscdevice设备*/
	misc_deregister(&myled_misc);
	
err_misc_register:
	return rt;
}

static void __exit myled_exit(void)
{
	/*注销miscdevice设备*/
	misc_deregister(&myled_misc);
	
	/*释放gpio*/
	gpio_free(PAD_GPIO_E+13);
	printk(KERN_INFO"myled_exit\n"); 
	
}

module_init(myled_init);
module_exit(myled_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("A simple Linux char driver example");
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>

#define MY_IOCTL_MAGIC 'L'
#define CMD_LED_ON 	_IOW(MY_IOCTL_MAGIC, 0, unsigned long)
#define CMD_LED_OFF _IOW(MY_IOCTL_MAGIC, 1, unsigned long)
#define CMD_BUF_W 	_IOW(MY_IOCTL_MAGIC, 2, unsigned char[4])
#define CMD_BUF_R 	_IOR(MY_IOCTL_MAGIC, 3, unsigned char[4])

int main(int agrc,char **argv)
{
	unsigned char buf[4] = {'1','2','3','4'};
	int fd_led = open("/dev/myled",O_RDWR);
	
	
	
	if(fd_led < 0)
	{
		perror("open /dev/myled");
		retun -1;
	}
	
	ioctl(fd_led,CMD_BUF_W,buf);
	sleep(1);
	ioctl(fd_led,CMD_BUF_R,buf);
	printf("value:%d\n",value);
	
	while(1)
	{
		ioctl(fd_led,CMD_LED_ON,1);
		sleep(1);
		ioctl(fd_led,CMD_LED_OFF,1);
	}
}

传递结构体

Linux视频学习笔记_#学习_56

Linux视频学习笔记_#include_57

Linux视频学习笔记_#include_58

13:内核裁剪与配置

概念

Linux视频学习笔记_设备号_59

将驱动编译进内核

Linux视频学习笔记_#include_60

Kconfig

Linux视频学习笔记_linux_61

Linux视频学习笔记_#笔记_62

Linux视频学习笔记_设备号_63

Linux视频学习笔记_#笔记_64

uImage的固化

Linux视频学习笔记_#include_65

Linux视频学习笔记_#学习_66

14:中断

Linux视频学习笔记_#include_67

ps:禁止硬中断嵌套(没有了smt32中的中断抢占优先级因为CPU速度足够快了,同时会屏蔽所有其他中断)

Linux视频学习笔记_linux_68

单个按键中断
#include <linux/init.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/interrup.h>
#include <linux/gpio.h>

#define MY_IRQ_NUMBER gpio_to_irq(PAD_GPIO_A+28);

static unsigned int data = 100;
static irqreturn_t my_interrupt_handler(int irq, void *dev_id)
{
    // 打印中断发生的消息
	unsigned int b = *(unsigned int *)dev_id;
    printk(KERN_INFO "My IRQ: Interrupt occurred! (IRQ: %d)\n  data:%d", irq ,b);
    
    // 返回 IRQ_HANDLED 表示中断已被处理
    // 如果有多个设备共享同一 IRQ,且不确定是否是本设备触发,可返回 IRQ_NONE
    return IRQ_HANDLED;
}


static int __init my_key_init(void)
{
	 result = request_irq(
        MY_IRQ_NUMBER,              // 中断号
        my_interrupt_handler,       // 中断处理函数
        IRQF_SHARED,                // 标志位,IRQF_SHARED 表示允许共享中断
                                    // 其他常见标志: IRQF_TRIGGER_RISING (上升沿触发)
        "gpioa_28_irq",            // /proc/interrupts 中显示的设备名称
        &data                        // dev_id, 用于共享中断时区分设备,这里用 NULL
    );

    if (result) {
        // request_irq 失败,返回非0值
        printk(KERN_ERR "My IRQ: Failed to register IRQ %d, error %d\n", MY_IRQ_NUMBER, result);
		goto err_request_irq;
        
    }

	printk(KERN_INFO"my_key_init\n"); 
	return 0;
err_request_irq:
	return result;

	
}

static void __exit my_key_exit(void)
{
	
	free_irq(MY_IRQ_NUMBER, &data);
	printk(KERN_INFO"my_key_exit\n"); 
	
}

module_init(my_key_init);
module_exit(my_key_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("A simple Linux char driver example");

Linux视频学习笔记_#笔记_69

多个按键驱动
#include <linux/init.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/interrup.h>
#include <linux/gpio.h>

#define MY_IRQ_NUMBER gpio_to_irq(PAD_GPIO_A+28);


struct key_data {
    int 			irq_n;
	irq_handler_t 	fun;
    unsigned long 	irq_f;
    char 			*name;
	void 			*dev;
};

static irqreturn_t my_interrupt_handler(int irq, void *dev_id);

tatic struct key_data keys[NUM_KEYS] = {
    {18,my_interrupt_handler,IRQF_TRIGGER_RISING,"Key18",NULL},
	{19,my_interrupt_handler,IRQF_TRIGGER_RISING,"Key19",NULL},
	{20,my_interrupt_handler,IRQF_TRIGGER_RISING,"Key20",NULL},
};

static irqreturn_t my_interrupt_handler(int irq, void *dev_id)
{
	int i;
	for (i = 0; i < ARRAY_SIZE(keys); i++)
	{
		if(irq == keys[i].irq_n)
		{
			printk(KERN_INFO "My %s IRQ: Interrupt occurred! (IRQ: %d)\n",keys[i].name,irq);
		}
	}
    return IRQ_HANDLED;
}
static int __init my_key_init(void)
{
	int result;
	int i;

	for (i = 0; i < ARRAY_SIZE(keys); i++)
	{
		result = request_irq(keys[i].irq_n,keys[i].fun,keys[i].irq_f,keys[i].name,keys[i].dev);
		if (result) 
		{
			// request_irq 失败,返回非0值
			printk(KERN_ERR "My IRQ: Failed to register IRQ %d, error %d\n", MY_IRQ_NUMBER, result);
			goto err_request_irq;
		}
	}

	printk(KERN_INFO"my_key_init\n"); 
	return 0;
err_request_irq:
	while(i--)
		free_irq(keys[i].irq_n, keys[i].dev);
	return result;
		
	
}

static void __exit my_key_exit(void)
{
	int i = ARRAY_SIZE(keys);
	while(i--)
		free_irq(keys[i].irq_n, keys[i].dev);
	printk(KERN_INFO"my_key_exit\n"); 
	
}

module_init(my_key_init);
module_exit(my_key_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("A simple Linux char driver example");
中断服务程序与原子上下文

Linux视频学习笔记_#笔记_70

中断&等待队列头

Linux视频学习笔记_设备号_71

#include <linux/init.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/interrup.h>
#include <linux/gpio.h>
#include <linux/sched.h>
#include <mach/miscdevice.h>

#define MY_IRQ_NUMBER gpio_to_irq(PAD_GPIO_A+28);


static wait_queue_head_t button_waitq;
static int key_press_flag = 0;
static unsigned int data = 100;


static irqreturn_t my_interrupt_handler(int irq, void *dev_id)
{
	unsigned int b = *(unsigned int *)dev_id;
	wake_up_interruptible(&button_waitq); //唤醒等待队列头
	key_press_flag = 1;
	
    printk(KERN_INFO "My IRQ: Interrupt occurred! (IRQ: %d)\n  data:%d", irq ,b);
    
    return IRQ_HANDLED;
}

int my_key_open(struct inode *inode,struct file *file)
{
	printk(KERN_INFO"my_key_open\n"); 
	return 0;
}
int my_key_relaese(struct inode *inode,struct file *file)
{
	printk(KERN_INFO"my_key_relaese\n"); 
	return 0;
}

static ssize_t my_key_read(struct file *file, char __user *buf, size_t len, loff_t *offset) {
	
    char key_val;
	
	wait_event_interruptible(&button_waitq,&key_press_flag);//让进程在 wait_queue_head_t 上睡眠。等待key_press_flag为真
	key_press_flag = 0;
	key_val |= gpio_get_value(PAD_GPIO_A+28) ? 0 : 1;

	copy_to_user(buf, &key_val, sizeof key_val);
	
	return len;
}

static struct file_operations my_key_fpos =
{
	.owner  =  THIS_MODULE,
	.open 	=  my_key_open,
	.relaese =  my_key_relaese,
	.read 	=	my_key_read, 
};

static struct miscdevice my_key_misc = {
	.minor	=	MISC_DYNAMIC_MINO,
	.name	=	"my_key",
	.fops	=	&my_key_fpos,
	
};

static int __init my_key_init(void)
{
	int result;
	
	/*miscdevice设备的注册*/
	result = misc_register(&my_key_misc);
	if(result < 0)
	{
		printk(KERN_ERR"misc_register fail\n");
		goto err_misc_register;
	}
	
	result = request_irq(
        MY_IRQ_NUMBER,              // 中断号
        my_interrupt_handler,       // 中断处理函数
        IRQF_SHARED,                // 标志位,IRQF_SHARED 表示允许共享中断
                                    // 其他常见标志: IRQF_TRIGGER_RISING (上升沿触发)
        "gpioa_28_irq",            // /proc/interrupts 中显示的设备名称
        &data                        // dev_id, 用于共享中断时区分设备,这里用 NULL
    );

    if (result) {
        // request_irq 失败,返回非0值
        printk(KERN_ERR "My IRQ: Failed to register IRQ %d, error %d\n", MY_IRQ_NUMBER, result);
		goto err_request_irq;
        
    }
	
	//初始化等待队列头
    init_waitqueue_head(&button_waitq); // <<< 关键:初始化等待队列头 >>>
	
	printk(KERN_INFO"my_key_init\n"); 
	return 0;
err_request_irq:
	return result;
err_misc_register:
	return result;
	
}

static void __exit my_key_exit(void)
{
	
	free_irq(MY_IRQ_NUMBER, &data);
	/*注销miscdevice设备*/
	misc_deregister(&myled_misc);
	printk(KERN_INFO"my_key_exit\n"); 
	
}

module_init(my_key_init);
module_exit(my_key_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("A simple Linux char driver example");



/****************************************/
int main(int agrc,char **argv)
{
	int key_val;
	int fd_key = open("/dev/my_key",O_RDWR);
	
	if(fd_led < 0)
	{
		perror("open /dev/my_key");
		retun -1;
	}
	
	while(1)
	{
		read(fd_key,&key_val,sizeof key_val);
	}
}
中断下半部

Linux视频学习笔记_#学习_72

Linux视频学习笔记_#笔记_73

软中断

中断处理->软中断->小任务   

进程的 只有工作队列中允许睡眠阻塞(eg:按键消抖)

软中断实际基本不使用

Linux视频学习笔记_#include_74

Linux视频学习笔记_#include_75

Linux视频学习笔记_#学习_76

小任务

小任务(小任务运行在中断的上下文、会优先于运行在进程上下文的工作队列,小任务只能使用忙等待延时工作队列则都可以,所有中断处理完后还会来处理tasklet,中断下半部是能被其他人中断打断的)

Linux视频学习笔记_#include_77

#include <linux/init.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/interrup.h>
#include <linux/gpio.h>

#define MY_IRQ_NUMBER gpio_to_irq(PAD_GPIO_A+28);


void mytasklet_handler(unsigned long)
{
	mdealy(20);
	printk(KERN_INFO "My IRQ: Interrupt occurred! (IRQ: %d)\n  data:%d", irq ,b);
    
}

DECLARE_TASKLET(mytasklet,mytasklet_handler,0);

static irqreturn_t my_interrupt_handler(int irq, void *dev_id)
{
    // 打印中断发生的消息
	unsigned int b = *(unsigned int *)dev_id;
    //登记任务    
    tasklet_schedule(&mytasklet);
    return IRQ_HANDLED;
}


static int __init my_key_init(void)
{
	 result = request_irq(
        MY_IRQ_NUMBER,              // 中断号
        my_interrupt_handler,       // 中断处理函数
        IRQF_SHARED,                // 标志位,IRQF_SHARED 表示允许共享中断
                                    // 其他常见标志: IRQF_TRIGGER_RISING (上升沿触发)
        "gpioa_28_irq",            // /proc/interrupts 中显示的设备名称
        &data                        // dev_id, 用于共享中断时区分设备,这里用 NULL
    );

    if (result) {
        // request_irq 失败,返回非0值
        printk(KERN_ERR "My IRQ: Failed to register IRQ %d, error %d\n", MY_IRQ_NUMBER, result);
		goto err_request_irq;
        
    }

	printk(KERN_INFO"my_key_init\n"); 
	return 0;
err_request_irq:
	return result;

	
}

static void __exit my_key_exit(void)
{
	
	free_irq(MY_IRQ_NUMBER, &data);
	printk(KERN_INFO"my_key_exit\n"); 
	
}

module_init(my_key_init);
module_exit(my_key_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("A simple Linux char driver example");
工作队列

Linux视频学习笔记_#笔记_78

Linux视频学习笔记_#笔记_79

#include <linux/init.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/interrup.h>
#include <linux/gpio.h>

#define MY_IRQ_NUMBER gpio_to_irq(PAD_GPIO_A+28);

static struct work_struct my_work;
void mywork_handler(struct work_struct *work)
{
	msleep(20);
	printk(KERN_INFO "My IRQ: Interrupt occurred! (IRQ: %d)\n  data:%d", irq ,b);
    
}
static irqreturn_t my_interrupt_handler(int irq, void *dev_id)
{
    schedule_work(&my_work);
    return IRQ_HANDLED;
}

static int __init my_key_init(void)
{
	 result = request_irq(
        MY_IRQ_NUMBER,              // 中断号
        my_interrupt_handler,       // 中断处理函数
        IRQF_SHARED,                // 标志位,IRQF_SHARED 表示允许共享中断
                                    // 其他常见标志: IRQF_TRIGGER_RISING (上升沿触发)
        "gpioa_28_irq",            // /proc/interrupts 中显示的设备名称
        &data                        // dev_id, 用于共享中断时区分设备,这里用 NULL
    );
	
	INIT_WORK(&my_work,mywork_handler);
	
    if (result) {
        // request_irq 失败,返回非0值
        printk(KERN_ERR "My IRQ: Failed to register IRQ %d, error %d\n", MY_IRQ_NUMBER, result);
		goto err_request_irq;
        
    }

	printk(KERN_INFO"my_key_init\n"); 
	return 0;
err_request_irq:
	return result;

	
}

static void __exit my_key_exit(void)
{
	
	free_irq(MY_IRQ_NUMBER, &data);
	printk(KERN_INFO"my_key_exit\n"); 
	cancel_work_sync(&my_work);
	
}

module_init(my_key_init);
module_exit(my_key_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("A simple Linux char driver example");
延迟工作队列

Linux视频学习笔记_#笔记_80

可以实现不需要直接用msleep的延迟方式

#include <linux/init.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/interrup.h>
#include <linux/gpio.h>

#define MY_IRQ_NUMBER gpio_to_irq(PAD_GPIO_A+28);

static struct delay_work my_work;
void mywork_handler(struct work_struct *work)
{
	//msleep(20);
	printk(KERN_INFO "My IRQ: Interrupt occurred! (IRQ: %d)\n  data:%d", irq ,b);
    
}
static irqreturn_t my_interrupt_handler(int irq, void *dev_id)
{
    schedule_delayed_work(&my_work,	HZ*0.2);
    return IRQ_HANDLED;
}

static int __init my_key_init(void)
{
	 result = request_irq(
        MY_IRQ_NUMBER,              // 中断号
        my_interrupt_handler,       // 中断处理函数
        IRQF_SHARED,                // 标志位,IRQF_SHARED 表示允许共享中断
                                    // 其他常见标志: IRQF_TRIGGER_RISING (上升沿触发)
        "gpioa_28_irq",            // /proc/interrupts 中显示的设备名称
        &data                        // dev_id, 用于共享中断时区分设备,这里用 NULL
    );
	
	INIT_DELAYED_WORK(&my_work,mywork_handler);
	
    if (result) {
        // request_irq 失败,返回非0值
        printk(KERN_ERR "My IRQ: Failed to register IRQ %d, error %d\n", MY_IRQ_NUMBER, result);
		goto err_request_irq;
        
    }

	printk(KERN_INFO"my_key_init\n"); 
	return 0;
err_request_irq:
	return result;

	
}

static void __exit my_key_exit(void)
{
	
	free_irq(MY_IRQ_NUMBER, &data);
	printk(KERN_INFO"my_key_exit\n"); 
	cancel_delayed_work(&my_work);
	
}

module_init(my_key_init);
module_exit(my_key_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("A simple Linux char driver example");
container_of

获得指定成员结构体的首地址这里获取到的是gi[0]的地址然后再强转为结构体类型就可以访问它内部的所有成员了

Linux视频学习笔记_linux_81

Linux视频学习笔记_linux_82

Linux视频学习笔记_#笔记_83

15:内核动态定时器

基础概念
内核时钟

Linux视频学习笔记_设备号_84

Linux视频学习笔记_#include_85

HZ

Linux视频学习笔记_#学习_86

Linux视频学习笔记_#include_87

Linux视频学习笔记_#include_88

jiffies

Linux视频学习笔记_linux_89

Linux视频学习笔记_#笔记_90

Linux视频学习笔记_设备号_91

 

Linux视频学习笔记_设备号_92

动态定时器

Linux视频学习笔记_设备号_93

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/timer.h>     // 包含 timer_list 和相关函数
#include <linux/jiffies.h>   // 包含 HZ 定义和 jiffies

// 定义我们的定时器结构体
static struct timer_list my_timer;

// 定时器回调函数
// 注意:参数是 timer_list 结构体的指针,使用 from_timer() 宏来获取数据
static void my_timer_callback(struct timer_list *t)
{
    // 使用 from_timer 宏从 timer_list 结构体中提取数据
    // 这里我们没有额外数据,所以只是演示用法
    // int *data = from_timer(data, t, fieldname); // 如果你有嵌入 timer_list 的结构体

    printk(KERN_INFO "My dynamic timer fired! Jiffies: %lu\n", jiffies);

    // 为了创建周期性定时器,我们需要在回调函数中重新启动定时器
    // 将定时器的到期时间设置为当前 jiffies + 2 秒(以 jiffies 为单位)
    mod_timer(&my_timer, jiffies + msecs_to_jiffies(2000));
}

// 模块初始化函数
static int __init timer_init(void)
{
    printk(KERN_INFO "Dynamic timer example module loaded.\n");

    // 初始化定时器
    // timer_setup() 是现代内核推荐的方式
    // 第三个参数 '0' 表示没有特殊标志
    timer_setup(&my_timer, my_timer_callback, 0);

    // 设置定时器的首次到期时间
    // HZ 是每秒的 jiffies 数,2 * HZ 表示 2 秒后到期
    my_timer.expires = jiffies + msecs_to_jiffies(2000);

    // 将定时器添加到内核的定时器列表中
    add_timer(&my_timer);

    printk(KERN_INFO "Timer started, will fire every 2 seconds.\n");
    return 0;
}

// 模块退出函数
static void __exit timer_exit(void)
{
    // 在删除模块前,必须确保定时器已停止
    // del_timer() 会停止定时器并等待任何正在运行的回调完成(如果在不同CPU上)
    // 如果定时器未激活,此调用是安全的
    if (del_timer(&my_timer))
        printk(KERN_INFO "Timer was active and has been deleted.\n");
    else
        printk(KERN_INFO "Timer was not active, deleted anyway.\n");

    printk(KERN_INFO "Dynamic timer example module unloaded.\n");
}

// 注册模块的初始化和退出函数
module_init(timer_init);
module_exit(timer_exit);

// 模块信息
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("A simple example of using a dynamic kernel timer.");
MODULE_VERSION("0.1");

Linux视频学习笔记_#笔记_94

Linux视频学习笔记_设备号_95

16:内存分配_kmalloc

概述

Linux视频学习笔记_#include_96

Linux视频学习笔记_#include_97

Linux视频学习笔记_#笔记_98

Linux视频学习笔记_#笔记_99

Linux视频学习笔记_设备号_100

Linux视频学习笔记_linux_101

Linux视频学习笔记_#include_102

kmalloc/kfree

kmalloc最大可分配128KB大小内存

Linux视频学习笔记_linux_103

Linux视频学习笔记_#笔记_104

Linux视频学习笔记_#笔记_105

Linux视频学习笔记_#学习_106

kmalloc 虚拟地址与物理地址偏移一般为 0x8 7个0

Linux视频学习笔记_设备号_107

Linux视频学习笔记_#include_108

get_free_page

Linux视频学习笔记_设备号_109

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/gfp.h>     // 包含 GFP_KERNEL 等标志
#include <linux/mm.h>      // 包含 PAGE_SIZE 宏

// 全局变量,存储分配的内存地址
static unsigned long page_addr = 0; // 用于存储 get_free_pages 返回的地址

// 模块初始化函数
static int __init mem_init(void)
{
    printk(KERN_INFO "get_free_pages example module loaded.\n");

    // 使用 get_free_pages() 申请 1 页内存
    // 参数说明:
    //   gfp_mask: GFP_KERNEL - 表示在进程上下文中进行睡眠等待内存
    //   order: 0 - 表示申请 2^0 = 1 页内存
    page_addr = __get_free_pages(GFP_KERNEL, 0);

    // 检查分配是否成功
    if (!page_addr) {
        printk(KERN_ERR "Failed to allocate a page of memory!\n");
        return -ENOMEM; // 内存不足
    }

    printk(KERN_INFO "Successfully allocated 1 page (order 0) at virtual address: %p, physical address: %pa\n", 
           (void *)page_addr, &page_addr);

    // 演示:向分配的内存写入数据
    // 注意:page_addr 是内核虚拟地址,可以直接访问
    char *ptr = (char *)page_addr;
    snprintf(ptr, PAGE_SIZE, "Hello from kernel page! Allocated at %lu jiffies.\n", jiffies);
    // 确保数据写入内存
    // barrier(); // 通常不需要,但如果是多处理器同步可能需要内存屏障

    // 打印写入的内容
    printk(KERN_INFO "Data in page: %s", ptr);

    return 0; // 成功加载
}

// 模块退出函数
static void __exit mem_exit(void)
{
    // 如果之前成功分配了内存,则释放它
    if (page_addr) {
        // 使用 free_pages() 释放内存
        // 参数:地址和 order (必须与分配时相同)
        free_pages(page_addr, 0);
        printk(KERN_INFO "Freed the allocated page of memory.\n");
        page_addr = 0; // 清空指针,避免悬空指针
    } else {
        printk(KERN_WARNING "No page was allocated to free.\n");
    }

    printk(KERN_INFO "get_free_pages example module unloaded.\n");
}

// 注册模块的初始化和退出函数
module_init(mem_init);
module_exit(mem_exit);

// 模块信息
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("A simple example of using get_free_pages() to allocate kernel memory.");
MODULE_VERSION("0.1");

Linux视频学习笔记_#学习_110

vmalloc

vmalloc不能用在中断它会睡眠、也不能用在DMA他分配的物理地址不连续,效率低因为需要不断创建页表维护映射、优势在于可以分配大块空间比如100MB大小

Linux视频学习笔记_linux_111

Linux视频学习笔记_#学习_112

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/vmalloc.h>  // 包含 vmalloc 和 vfree 声明
#include <linux/string.h>   // 包含 memset, memcpy 等

// 定义要分配的内存大小,例如 1MB
#define ALLOC_SIZE (1024 * 1024) // 1 MiB

// 全局变量,存储分配的内存地址
static void *vmalloc_ptr = NULL;

// 模块初始化函数
static int __init vmalloc_init(void)
{
    printk(KERN_INFO "vmalloc example module loaded.\n");

    // 使用 vmalloc() 分配指定大小的内存
    // 参数: size - 要分配的字节数
    vmalloc_ptr = vmalloc(ALLOC_SIZE);

    // 检查分配是否成功
    if (!vmalloc_ptr) {
        printk(KERN_ERR "vmalloc failed to allocate %d bytes!\n", ALLOC_SIZE);
        return -ENOMEM; // 内存不足
    }

    printk(KERN_INFO "Successfully allocated %d bytes using vmalloc() at virtual address: %p\n", 
           ALLOC_SIZE, vmalloc_ptr);

    // 演示:向分配的内存写入数据
    // 注意:vmalloc_ptr 是内核虚拟地址,可以直接访问
    memset(vmalloc_ptr, 0, ALLOC_SIZE); // 清零内存
    snprintf(vmalloc_ptr, ALLOC_SIZE, "Hello from vmalloc! Allocated %d bytes at %lu jiffies.\n", 
             ALLOC_SIZE, jiffies);

    // 打印写入的内容
    printk(KERN_INFO "Data in vmalloc'd memory: %s", (char *)vmalloc_ptr);

    return 0; // 成功加载
}

// 模块退出函数
static void __exit vmalloc_exit(void)
{
    // 如果之前成功分配了内存,则释放它
    if (vmalloc_ptr) {
        // 使用 vfree() 释放由 vmalloc() 分配的内存
        vfree(vmalloc_ptr);
        printk(KERN_INFO "Freed the vmalloc'd memory block.\n");
        vmalloc_ptr = NULL; // 清空指针,避免悬空指针
    } else {
        printk(KERN_WARNING "No vmalloc'd memory was allocated to free.\n");
    }

    printk(KERN_INFO "vmalloc example module unloaded.\n");
}

// 注册模块的初始化和退出函数
module_init(vmalloc_init);
module_exit(vmalloc_exit);

// 模块信息
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("A simple example of using vmalloc() to allocate kernel memory.");
MODULE_VERSION("0.1");
总结

Linux视频学习笔记_#笔记_113

Linux视频学习笔记_#学习_114

17:输入子系统

 

Linux视频学习笔记_#include_115

Linux视频学习笔记_#笔记_116

Linux视频学习笔记_#include_117

Linux视频学习笔记_设备号_118

#include <linux/init.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/interrup.h>
#include <linux/gpio.h>
#include <linux/input.h>

#define MY_IRQ_NUMBER gpio_to_irq(PAD_GPIO_A+28);

static struct input_dev *button_dev;

static irqreturn_t my_interrupt_handler(int irq, void *dev_id)
{
    /*向内核汇报按键状态*/
	input_report_key(button_dev, KEY_ENTER, !gpio_get_value(PAD_GPIO_A+28)); // 取反,因为低电平表示按下!gpio_get_value(PAD_GPIO_A+28) 会赋值给key_dev.value
	/*汇报结束*/
	input_sync(button_dev);
    return IRQ_HANDLED;
}

static int __init my_key_init(void)
{
	int error;
	result = request_irq(
        MY_IRQ_NUMBER,              // 中断号
        my_interrupt_handler,       // 中断处理函数
        IRQF_TRIGGER_RISING|IRQF_TRIGGER_FALLING,// 标志位,IRQF_SHARED 表示允许共享中断
                                    // 其他常见标志: IRQF_TRIGGER_RISING (上升沿触发)
        "gpioa_28_irq",            // /proc/interrupts 中显示的设备名称
        NULL                        // dev_id, 用于共享中断时区分设备,这里用 NULL
    );
	
    if (result) {
        // request_irq 失败,返回非0值
        printk(KERN_ERR "My IRQ: Failed to register IRQ %d, error %d\n", MY_IRQ_NUMBER, result);
		goto err_request_irq;
        
    }

	
	// --- 1. 分配输入设备 内存空间 ---
    button_dev = input_allocate_device();
    if (!button_dev) {
        printk(KERN_ERR "Failed to allocate input device\n");
        error = -ENOMEM;
        goto free_irq;
    }

    // --- 2. 设置输入设备能力 (事件类型 编码支持哪些按键)---
    set_bit(EV_KEY, button_dev->evbit);       // 支持按键事件
    set_bit(KEY_ENTER, button_dev->keybit);    // 支持 ENTER 键
	set_bit(KEY_UP, button_dev->keybit);    
	set_bit(KEY_DOWN, button_dev->keybit);    
	set_bit(KEY_LEFT, button_dev->keybit);   

    button_dev->name = "mykey_input";      // 设备名称
    button_dev->phys = "gpio-keys/input0";    // 物理路径 (惯例)
    button_dev->id.bustype = 0x0000;        // 总线类型
    button_dev->id.vendor = 0x1688;
    button_dev->id.product = 0x6666;
    button_dev->id.version = 0x1001;

    // --- 3. 注册输入设备 ---
    error = input_register_device(button_dev);
    if (error) {
        printk(KERN_ERR "gpio_key_irq: Failed to register input device: %d\n", error);
        goto free_input_dev;
    }


	printk(KERN_INFO"my_key_init\n"); 
	return 0;

free_input_dev:
	input_free_device(button_dev);
free_irq:
	free_irq(MY_IRQ_NUMBER, NULL);
err_request_irq:
	return result;

	
}

static void __exit my_key_exit(void)
{
	
	input_unregister_device(button_dev);
	input_free_device(button_dev);
	free_irq(MY_IRQ_NUMBER,NULL);
	printk(KERN_INFO"my_key_exit\n"); 
	
}

module_init(my_key_init);
module_exit(my_key_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("A simple Linux char driver example");


/**************************************************/
 int main(int argc,char **argv)
 {
	struct input_event key_dev;
	
	int fd_key = open("/dev/input/event5",O_RDWE);
	 
	if(fd_key < 0)
	{
		return -1;
	}
	
	while(1)
	{
		read(fd_key,&key_dev,sizeof(key_dev));
		
		if(key_dev.type == EV_KEY)
		{
			if(key_dev.code == KEY_ENTER)
			{
				printf("KEY_ENTER:%d",key_dev.value);
			}
		}
	}
	 
 }

Linux视频学习笔记_设备号_119

 整体逻辑:注册一个I2C设备=》probe函数中注册输入设备配置输入事件和类型=》注册工作队列=》注册中断=》触发中断h唤醒工作队列=》工作队列中获取坐标位置上报

Linux视频学习笔记_#学习_120

18:platform设备驱动模型

Linux视频学习笔记_设备号_121

Linux视频学习笔记_#include_122

Linux视频学习笔记_#笔记_123

Linux视频学习笔记_#笔记_124

Linux视频学习笔记_#include_125

led_driver
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/device.h>
#include <linux/gpio.h>
#include <mach/platform.h>
#include <mach/platform_device.h>
#include <mach/miscdevice.h>
#include <linux/ioctl.h>

#define MY_IOCTL_MAGIC 'L'
#define CMD_LED_ON 	_IOW(MY_IOCTL_MAGIC, 0, unsigned long)
#define CMD_LED_OFF _IOW(MY_IOCTL_MAGIC, 1, unsigned long)

static unsigned int gpio_num;
static const char *gpio_name;

int myled_open(struct inode *inode,struct file *file)
{
	printk(KERN_INFO"myled_open\n"); 
	return 0;
}
int myled_relaese(struct inode *inode,struct file *file)
{
	printk(KERN_INFO"myled_relaese\n"); 
	return 0;
}
long myled_unlocked_ioctl(struct file *, unsigned int cmd, unsigned long args)
{

	if(_IOC_TYPE(cmd) != MY_IOCTL_MAGIC)
		return -ENOIOCTLCMD;
	switch(cmd)
	{
		case CMD_LED_ON:
		{
			gpio_set_value(gpio_num,0);
		}break;
		
		case CMD_LED_OFF:
		{
			gpio_set_value(gpio_num,1);
		}break;
		default:
			retun -ENOIOCTLCMD;
	}
	
	retun 0;
}

static struct file_operations myled_fpos =
{
	.owner  	=  THIS_MODULE,
	.open 		=  myled_open,
	.relaese 	=  myled_relaese,
	.unlocked_ioctl		=  myled_unlocked_ioctl

};

static struct miscdevice myled_misc = {
	.minor	=	MISC_DYNAMIC_MINO,
	.name	=	"my_led",
	.fops	=	&myled_fpos,
	
};

static int __devinit myled_probe(struct platform_device * pdev)
{
	int rt;
	struct resource *res;
	
	/*miscdevice设备的注册*/
	rt = misc_register(&myled_misc);
	if(rt < 0)
	{
		printk(KERN_ERR"misc_register fail\n");
		goto err_misc_register;
	}
	
	/*获取平台设备传递的资源*/
	res = platform_get_resource(pdev,IORESOURCE_IO,0);
	gpio_num = res->start;
	gpio_name = res->name;
	
	/*.添加一个GPIO*/
	rt = gpio_request(gpio_num,gpio_name);
	if(rt < 0)
	{
		printk(KERN_ERR"gpio_request fail\n");
		goto err_gpio_request;
	}
	
	/*8.初始化gpio*/
	rt = gpio_direction_output(gpio_num,1);
	if(rt < 0)
	{
		printk(KERN_ERR"direction_output fail\n");
		goto err_direction_output;
	}

	printk(KERN_INFO"myled_init\n"); 
	
	return 0;
	
err_direction_output:
	/*释放gpio*/
	gpio_free(gpio_num);
err_gpio_request:
	/*注销miscdevice设备*/
	misc_deregister(&myled_misc);
err_misc_register:
	return rt;
}

static int __devexit myled_remove(struct platform_device * pdev)
{
	/*注销miscdevice设备*/
	misc_deregister(&myled_misc);
	/*释放gpio*/
	gpio_free(gpio_num);
	printk(KERN_INFO"myled_exit\n");
	
	return 0;
}

static struct platform_driver led_plat_driver = {
	.probe 	=	myled_probe,
	.remove	=	myled_remove,
	.driver =	{
		.name = 	"myled",
		.owner	=	THIS_MODULE,
	},
}

static int __init myled_init(void)
{
	return platform_driver_register(&led_plat_driver);
}

static void __exit myled_exit(void)
{
	return platform_driver_unregister(&led_plat_driver);
}

module_init(myled_init);
module_exit(myled_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("A simple Linux char driver example");
led_device
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/device.h>
#include <linux/gpio.h>
#include <mach/platform.h>
#include <mach/platform_device.h>

void myled_release (struct device *dev)
{
	return;
}

static struct resource led_resource[] = {
	[0] = {
		.start	= 	PAD_GPIO_E+13,
		.end	=	PAD_GPIO_E+13,
		.flags	=	IORESOURCE_IO,
		.name	=	"gpioe_13",
	},
};

static struct platform_device led_plat_device = {
	.name			=	"myled",
	.id				=	-1,
	.num_resources	=	ARRAY_SIZE(led_resource),
	.resource		=	led_resource,
	.device			=	{
		.platform_data 	= 	NULL,
		.release		=	myled_release,
	}
}

static int __init myled_init(void)
{
	int rt;
	rt = platform_device_register(&led_plat_device);
	if( rt < 0)
	{
		goto err_platform_device_register;
	}
	return 0;
	printk(KERN_INFO"my_key_init\n"); 
err_platform_device_register:
	return rt;
}

static void __exit myled_exit(void)
{
	platform_device_unregister(&led_plat_device);
	printk(KERN_INFO"myled_exit\n"); 
}

module_init(myled_init);
module_exit(myled_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("A simple Linux char driver example");
led_driver_platform_data
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/device.h>
#include <linux/gpio.h>
#include <mach/platform.h>
#include <mach/platform_device.h>
#include <mach/miscdevice.h>
#include <linux/ioctl.h>

#define MY_IOCTL_MAGIC 'L'
#define CMD_LED_ON 	_IOW(MY_IOCTL_MAGIC, 0, unsigned long)
#define CMD_LED_OFF _IOW(MY_IOCTL_MAGIC, 1, unsigned long)

#define CMD_LED_ALL_ON 	_IO(MY_IOCTL_MAGIC, 0)
#define CMD_LED_ALL_OFF _IO(MY_IOCTL_MAGIC, 1)

struct gpio_t
{
#define GPIO_MAX_NUMBER 32
	struct gpio io[GPIO_MAX_NUMBER];
	unsigned int num;
};

static struct gpio_t *leds_gpios_p = NULL;

int myled_open(struct inode *inode,struct file *file)
{
	printk(KERN_INFO"myled_open\n"); 
	return 0;
}
int myled_relaese(struct inode *inode,struct file *file)
{
	printk(KERN_INFO"myled_relaese\n"); 
	return 0;
}
long myled_unlocked_ioctl(struct file *, unsigned int cmd, unsigned long args)
{
	int n = args-7;
	
	if(_IOC_TYPE(cmd) != MY_IOCTL_MAGIC)
		return -ENOIOCTLCMD;
	switch(cmd)
	{
		case CMD_LED_ON:
		{
			gpio_set_value(leds_gpios_p->io[n].gpio,0);
		}break;
		
		case CMD_LED_OFF:
		{
			gpio_set_value(leds_gpios_p->io[n].gpio,1);
		}break;
		default:
			retun -ENOIOCTLCMD;
	}
	
	retun 0;
}

static struct file_operations myled_fpos =
{
	.owner  	=  THIS_MODULE,
	.open 		=  myled_open,
	.relaese 	=  myled_relaese,
	.unlocked_ioctl		=  myled_unlocked_ioctl

};

static struct miscdevice myled_misc = {
	.minor	=	MISC_DYNAMIC_MINO,
	.name	=	"my_led",
	.fops	=	&myled_fpos,
	
};

static int __devinit myled_probe(struct platform_device * pdev)
{
	int rt;
	struct resource *res;
	
	/*miscdevice设备的注册*/
	rt = misc_register(&myled_misc);
	if(rt < 0)
	{
		printk(KERN_ERR"misc_register fail\n");
		goto err_misc_register;
	}
	
	/*获取平台设备传递的资源*/
	leds_gpios_p = (struct gpio_t *)pdev->dev.platform_data;

	rt = gpio_request_array(leds_gpios_p->io,leds_gpios_p->num);
	if(rt < 0)
	{
		printk(KERN_ERR"gpio_request fail\n");
		goto err_gpio_request_array;
	}
	
	printk(KERN_INFO"myled_init\n"); 
	
	return 0;
	
err_gpio_request_array:
	/*注销miscdevice设备*/
	misc_deregister(&myled_misc);
err_misc_register:
	return rt;
}

static int __devexit myled_remove(struct platform_device * pdev)
{
	/*注销miscdevice设备*/
	misc_deregister(&myled_misc);
	/*释放gpio*/
	gpio_free_array(leds_gpios_p->io,leds_gpios_p->num);
	printk(KERN_INFO"myled_exit\n");
	
	return 0;
}

static struct platform_driver led_plat_driver = {
	.probe 	=	myled_probe,
	.remove	=	myled_remove,
	.driver =	{
		.name = 	"myled",
		.owner	=	THIS_MODULE,
	},
}

static int __init myled_init(void)
{
	return platform_driver_register(&led_plat_driver);
}

static void __exit myled_exit(void)
{
	return platform_driver_unregister(&led_plat_driver);
}

module_init(myled_init);
module_exit(myled_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("A simple Linux char driver example");
led_device_platform_data
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/device.h>
#include <linux/gpio.h>
#include <mach/platform.h>
#include <mach/platform_device.h>

struct gpio_t
{
#define GPIO_MAX_NUMBER 32
	struct gpio io[GPIO_MAX_NUMBER];
	unsigned int num;
};

void myled_release (struct device *dev)
{
	return;
}

static struct gpio_t leds_gpios=
{
	.io[0]={PAD_GPIO_E+13,GPIO_OUT_INIT_HIGH,"LED_1"},
	.io[1]={PAD_GPIO_E+14,GPIO_OUT_INIT_HIGH,"LED_2"},
	.io[2]={PAD_GPIO_E+15,GPIO_OUT_INIT_HIGH,"LED_3"},
	.io[3]={PAD_GPIO_E+16,GPIO_OUT_INIT_HIGH,"LED_4"},
	.num = 4,
}

static struct platform_device led_plat_device = {
	.name			=	"myled",
	.id				=	-1,
	.device			=	{
		.platform_data 	= 	&leds_gpios,
		.release		=	myled_release,
	}
}

static int __init myled_init(void)
{
	int rt;
	rt = platform_device_register(&led_plat_device);
	if( rt < 0)
	{
		goto err_platform_device_register;
	}
	return 0;
	printk(KERN_INFO"my_key_init\n"); 
err_platform_device_register:
	return rt;
}

static void __exit myled_exit(void)
{
	platform_device_unregister(&led_plat_device);
	printk(KERN_INFO"myled_exit\n"); 
}

module_init(myled_init);
module_exit(myled_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("A simple Linux char driver example");

Linux视频学习笔记_#include_126

19:电源管理

Linux视频学习笔记_设备号_127

20:设备树

Linux视频学习笔记_#include_128

解析文件

Linux视频学习笔记_设备号_129

【搞linux的旺仔】:linux是如何实现platform驱动匹配设备树并执行probe安装驱动的?详细调用流程来了!喜欢就点赞关注吧!_哔哩哔哩_bilibili

Linux视频学习笔记_#include_130

设备树的基本语法

Linux视频学习笔记_#include_131

Linux视频学习笔记_设备号_132

Linux视频学习笔记_#学习_133

Linux视频学习笔记_#笔记_134

Linux视频学习笔记_设备号_135

Linux视频学习笔记_linux_136

Linux视频学习笔记_设备号_137

Linux视频学习笔记_#学习_138

Linux视频学习笔记_设备号_139

&只能引用标签

Linux视频学习笔记_设备号_140

Linux视频学习笔记_#笔记_141

Linux视频学习笔记_linux_142

/dts-v1/;
/{
    model = "This is my device tree";
    #address-cells = <1>;
    #size-cells    = <1>;
    chose{
        bootargs = "root=/dev/nfs rw nfsroot=192.168.1.1 console=ttyS0,115200";
    };

    cpu1:cpu@1{
        device_type = "cpu";
        compatible = "arm","arm,armv8";
        reg = <0x0 0x1>;
    };

    aliases{
        led1=&led;
        led2=&gpio2;
        led3="/node2/gpio@22020102";
        led4="/gpio@22020101";
    };

    node1{
        node1{
            pinnum = <0 1 2 3 4>;
        };
    };
    node2{
        #address-cells = <1>; //他控制的是子节点所以这里也要写外面那个不起作用
        #size-cells    = <1>;
        gpio2:gpio@22020102{
            reg = <0x20220101 0x40>;
        };
    };
    led:gpio@22020101{
        compatible = "led";
        reg = <0x20220101 0x40>;
        status="okay";
    };

};
描述中断资源
interrupts = <GIC_SPI 33 IRQ_TYPE_LEVEL_HIGH>;

Linux视频学习笔记_#学习_143

Linux视频学习笔记_#笔记_144

其中:

  • GIC_SPI:表示这是一个 SPI 类型中断(共享外设中断),由 GIC 管理
  • 33:硬件中断号(SPI 编号)
  • IRQ_TYPE_LEVEL_HIGH:触发类型(高电平触发)
  • interrupt-parent = <&gic>:指定中断父节点为 GIC 控制器
  • interrupt-controller:表是这个节点是中断控制器
  • gipo-controller:表是这个节点是GPIO控制器
  • #interrupt-cells :用于表征后面引用这个节点可以配置几个值
/dts-v1/;
/{
    model = "This is my device tree";
    ft5x06@38{
        compatible="edt,edt-ft5206";
        interrupt-parent=<&gpio0>;  //用哪个中断控制器,里面包含中断资源
        interrupts=<13 1>; //这里的13是GPIO引脚号而不是中断号资源33
    };
};


/*****************/

//平台总线中描述一个用于触摸芯片的中断引脚
static struct resource my_device_resources[] = {
    [0] = {
        .start = 13,
        .end   = 13,
        .flags = IORESOURCE_IRQ,
    }
};

/*****************/
时钟中断资源

Linux视频学习笔记_#include_145

Linux视频学习笔记_#笔记_146

Linux视频学习笔记_#笔记_147

Linux视频学习笔记_linux_148

设备树的编译

先编译内核然后内核会生成dtc编译器用这个编译器可以编译dts成dtb

 在kernel 中 make dtbs 是一键编译内核中dts目录下所有的dts

Linux视频学习笔记_linux_149

Linux视频学习笔记_设备号_150

Linux视频学习笔记_#笔记_151

设备树的传递

通过bootloader传递给内核

misc:

1)编辑器试图

Linux视频学习笔记_#include_152

2)modeinfo

Linux视频学习笔记_#学习_153

3)printk

Linux视频学习笔记_#笔记_154

Linux视频学习笔记_设备号_155

Linux视频学习笔记_#学习_156

Linux视频学习笔记_设备号_157

Linux视频学习笔记_linux_158

4)管道

Linux视频学习笔记_设备号_159