1.Image zImage uImage

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

2.宏内核与微内核


3.makefile




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




权限字段解析 (
-rw-r--r--)在
ls -l的输出中,权限字段通常由 10 个字符组成,例如:text
-rw-r--r--
分解如下:
- 第1个字符:文件类型
-:普通文件d:目录l:符号链接- 其他字符可能表示设备文件、套接字等。
- 后续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 Bit:
1
- 目录中的文件仅所有者可删除(如
/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 Bit5. 总结
0644中的0表示无特殊权限(SUID/SGID/Sticky Bit 均未设置)。- 日常使用中,
644和0644完全等效,但显式写0更规范(尤其在编程时)。
5.全局符号表



6.查看ko的依赖
方法 | 适用场景 | 是否需要 root |
| 查看未加载模块的声明依赖 | 否 |
| 查看已加载模块的实际依赖 | 否 |
| 模拟加载过程显示依赖 | 是 |
| 分析二进制依赖(高级调试) | 否 |
7.多个源文件组合成内核模块


8.字符设备



8.1设备号注销申请
静态注册
设备号:一个32位的数字,31~20:主设备号 19~0:次设备号
设备号文件相关规定见(kernel/Documention/devices.txt)不能随便申请一个设备号





动态注册



8.2字符设备结构体_cdev




添加字符设备
#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");获取主次设备号手动创建手动创建设备文件


编写应用程序测试

8.3自动创建设备文件

class&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>
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");
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



10:延时

睡眠延时

忙等待延时

11:miscdevices
作用
比如led、key、src_04等等这些字符设备统一归类为misc,用统一的主设备号,而不是每一个占用一个主设备号;
混杂设备主设备号为10;
Tip:输入子系统(如触摸屏)主设备号为13、fb0()主设备号29;

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>
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");
12:IOCTL
作用



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



关于'V'的解释


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);
}
}传递结构体



13:内核裁剪与配置
概念

将驱动编译进内核

Kconfig




uImage的固化


14:中断

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

单个按键中断
#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");
多个按键驱动
#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");中断服务程序与原子上下文

中断&等待队列头

#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);
}
}中断下半部


软中断
中断处理->软中断->小任务
进程的 只有工作队列中允许睡眠阻塞(eg:按键消抖)
软中断实际基本不使用



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

#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");工作队列


#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");延迟工作队列

可以实现不需要直接用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]的地址然后再强转为结构体类型就可以访问它内部的所有成员了



15:内核动态定时器
基础概念
内核时钟


HZ



jiffies




动态定时器

#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");

16:内存分配_kmalloc
概述







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




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


get_free_page

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


#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");总结


17:输入子系统




#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);
}
}
}
}
整体逻辑:注册一个I2C设备=》probe函数中注册输入设备配置输入事件和类型=》注册工作队列=》注册中断=》触发中断h唤醒工作队列=》工作队列中获取坐标位置上报

18:platform设备驱动模型





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");
19:电源管理

20:设备树

解析文件

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

设备树的基本语法









&只能引用标签



/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>;

其中:
-
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,
}
};
/*****************/时钟中断资源




设备树的编译
先编译内核然后内核会生成dtc编译器用这个编译器可以编译dts成dtb
在kernel 中 make dtbs 是一键编译内核中dts目录下所有的dts



设备树的传递
通过bootloader传递给内核
misc:
1)编辑器试图

2)modeinfo

3)printk





4)管道

















