Linux视频学习笔记
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-x
0755
普通文件,所有者可读写执行 drwxrwxrwt
1777
目录,设置 Sticky Bit
5. 总结
0644
中的0
表示无特殊权限(SUID/SGID/Sticky Bit 均未设置)。日常使用中,
644
和0644
完全等效,但显式写0
更规范(尤其在编程时)。
5.全局符号表
6.查看ko的依赖
方法 | 适用场景 | 是否需要 root |
---|---|---|
modinfo <模块> | grep depends | 查看未加载模块的声明依赖 | 否 |
lsmod | grep <模块> | 查看已加载模块的实际依赖 | 否 |
modprobe --show-depends <模块> | 模拟加载过程显示依赖 | 是 |
objdump -p <模块>.ko | 分析二进制依赖(高级调试) | 否 |
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_NONEreturn 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");elseprintk(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 32struct 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 32struct 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");