驱动程序(工作于内核态)
头文件:
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <asm/arch/regs-gpio.h>
#include <asm/hardware.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <asm/arch/regs-gpio.h>
#include <asm/hardware.h>
安装模块(使之成为内核的一部分) insmod (.ko文件)
卸载模块 rmmod
卸载模块 rmmod
模块安装与卸载框架
int __init first_drv_init() //模块加载函数,加载时才调用
{
printk("init\n"); //内核的打印函数
return 0;
}
int __init first_drv_init() //模块加载函数,加载时才调用
{
printk("init\n"); //内核的打印函数
return 0;
}
void __exit first_drv_exit() //模块卸载函数,卸载时才调用
{
printk("exit\n");
}
{
printk("exit\n");
}
模块加载与卸载的修饰: 单独写最后面
module_init(first_drv_init);
module_exit(first_drv_exit);
MODULE_LICENSE("GPL");
module_init(first_drv_init);
module_exit(first_drv_exit);
MODULE_LICENSE("GPL");
makefile基本框架:
KERN_DIR = /work/system/linux-2.6.22.6 //内核目录(编译好的)
all:
make -C $(KERN_DIR) M=`pwd` modules
clean:
make -C $(KERN_DIR) M=`pwd` modules clean
rm -rf modules.order
obj-m += first_drv.o
KERN_DIR = /work/system/linux-2.6.22.6 //内核目录(编译好的)
all:
make -C $(KERN_DIR) M=`pwd` modules
clean:
make -C $(KERN_DIR) M=`pwd` modules clean
rm -rf modules.order
obj-m += first_drv.o
===内核导出符号:
void myprint() //例如某个导出函数
{
printk("myprint!\n");
}
EXPORT_SYMBOL(myprint); //声明为导出符号
之后在写其他模块时可以调用它
void myprint() //例如某个导出函数
{
printk("myprint!\n");
}
EXPORT_SYMBOL(myprint); //声明为导出符号
之后在写其他模块时可以调用它
===模块参数:在用户空间可以修改内核模块中全局变量的值
在模块中申明静态全局变量
static short mpshort=1;
module_param(mpshort,short,S_IRWXU);//模块参数声明宏,S_IRWXU为读写可执行权限
在安装模块时使用模块参数
insmod modparam.ko mpshort=100; //在安装时调用
在安装后会在 /sys/module/生成文件夹modparam里面有子文件夹paramters
存在几个文件都是由模块参数命名的
里面的全局变量还可以进行修改
cd /sys/module/modparam/paramters/
cat mpshort 得到100
如果有写的权限就可以 echo 55 > mpshort (更改)
在模块中申明静态全局变量
static short mpshort=1;
module_param(mpshort,short,S_IRWXU);//模块参数声明宏,S_IRWXU为读写可执行权限
在安装模块时使用模块参数
insmod modparam.ko mpshort=100; //在安装时调用
在安装后会在 /sys/module/生成文件夹modparam里面有子文件夹paramters
存在几个文件都是由模块参数命名的
里面的全局变量还可以进行修改
cd /sys/module/modparam/paramters/
cat mpshort 得到100
如果有写的权限就可以 echo 55 > mpshort (更改)
===内存管理
物理地址:出现在地址总线上的值
虚拟地址:每个进程都有0~4G独立空间
逻辑地址:汇编文件中使用的偏移地址
头文件
#include<linux/fs.h>
#include<linux/slab.h>
#include<linux/vmalloc.h>
物理地址:出现在地址总线上的值
虚拟地址:每个进程都有0~4G独立空间
逻辑地址:汇编文件中使用的偏移地址
头文件
#include<linux/fs.h>
#include<linux/slab.h>
#include<linux/vmalloc.h>
unsigned char *kernelkmalloc=NULL;
unsigned long *kernelpagemem=NULL;
unsigned char *kernelvmalloc=NULL;
在函数中
kernelkmalloc=(unsigned char *)kmalloc(100,GFP_KERNEL)
申请100字节
参二:常用取值
GFP_KERNEL:代表分配内存,分配过程中可能导致睡眠(如果找不到连续空间)
GFP_ATOMIC:分配过程中不会导致睡眠(内核没有找到连续空间时会得到一个错误值)
GFP_DMA:申请到的内存通常情况下位于0~16M之间(0~16M肯定可以用于DMA传输)
__GFP_HIGHMEM:申请高端内存,896M以上的物理地址
if(IS_ERR(kernelkmalloc)) //如果出错
{
printk("kmalloc failed!\n");
ret=PTR_ERR(kernelkmalloc); //获取错误编号
kfree(kernelkmalloc);
return ret;
}
kfree(kernelkmalloc); //释放
kernelpagemem=__get_free_pages(GFP_KERNEL,4); //按页申请
申请2^4页也就是16页
if(IS_ERR(kernelpagemem))
{
ret=PTR_ERR(kernelpagemem); //获取错误编号
return ret;
}
free_pages(kernelpagemem); //释放页
kernelvmalloc=(unsigned char *)vmalloc(1024*1024); //一般申请大空间内存不连续
if(IS_ERR(kernelvmalloc))
{
ret=PTR_ERR(kernelvmalloc); //获取错误编号
return ret;
}
vfree(kernelvmalloc); //释放
注意写驱动需要关心内存泄漏问题
unsigned long *kernelpagemem=NULL;
unsigned char *kernelvmalloc=NULL;
在函数中
kernelkmalloc=(unsigned char *)kmalloc(100,GFP_KERNEL)
申请100字节
参二:常用取值
GFP_KERNEL:代表分配内存,分配过程中可能导致睡眠(如果找不到连续空间)
GFP_ATOMIC:分配过程中不会导致睡眠(内核没有找到连续空间时会得到一个错误值)
GFP_DMA:申请到的内存通常情况下位于0~16M之间(0~16M肯定可以用于DMA传输)
__GFP_HIGHMEM:申请高端内存,896M以上的物理地址
if(IS_ERR(kernelkmalloc)) //如果出错
{
printk("kmalloc failed!\n");
ret=PTR_ERR(kernelkmalloc); //获取错误编号
kfree(kernelkmalloc);
return ret;
}
kfree(kernelkmalloc); //释放
kernelpagemem=__get_free_pages(GFP_KERNEL,4); //按页申请
申请2^4页也就是16页
if(IS_ERR(kernelpagemem))
{
ret=PTR_ERR(kernelpagemem); //获取错误编号
return ret;
}
free_pages(kernelpagemem); //释放页
kernelvmalloc=(unsigned char *)vmalloc(1024*1024); //一般申请大空间内存不连续
if(IS_ERR(kernelvmalloc))
{
ret=PTR_ERR(kernelvmalloc); //获取错误编号
return ret;
}
vfree(kernelvmalloc); //释放
注意写驱动需要关心内存泄漏问题
内核链表
例子:两个数据结构,如果分别写操作函数比较麻烦,内核提供一种对链表的支持
struct student
{
char name[20];
int age;
int sex;
int id;
struct student *next;
}
struct employee
{
char name[20];
int age;
int sex;
int id;
int salary;
struct student *next;
}
内核链表操作(增删改查)代码对于任何数据结构只有一个
头文件
#include<linux/list.h>
struct student
{
char name[20];
int age;
int sex;
int id;
struct list_head list;
}
初始化节点
struct list_head student_list; //创建头节点
struct student *studentp=NULL;
INIT_LIST_HEAD(&student_list); //内核初始化头节点函数
studentp=kmalloc(sizeof(struct student)*10,GFP_KERNEL);
//申请10个空间,因为没有在中断上下文中所以无所谓睡眠
memset(studentp,0,sizeof(struct student)*10); //注意这里的memset和用户空间中不是一套代码
for(i=0;i<10;i++) //初始化
{
sprintf(studentp[i].name,"student%d",i);
...
list_add(&(studentp[i].list),&student_list); //添加
}
/* 链表节点的遍历 */
struct list_head *pos=NULL; //用来保存查找结点地址
struct student *student_tmp=NULL; //临时变量
list_for_each(pos,&student_list)
{
student_tmp=list_entry(pos,struct student,list);
//在已知pos时得到的(某个尾节点地址)得到头节点地址
printk("%s",student_tmp->name);
}
链表析构就只要
kfree(studentp);
还有(不必要) list_del(&(studentp[i].list));
节点删除
list_del
例子:两个数据结构,如果分别写操作函数比较麻烦,内核提供一种对链表的支持
struct student
{
char name[20];
int age;
int sex;
int id;
struct student *next;
}
struct employee
{
char name[20];
int age;
int sex;
int id;
int salary;
struct student *next;
}
内核链表操作(增删改查)代码对于任何数据结构只有一个
头文件
#include<linux/list.h>
struct student
{
char name[20];
int age;
int sex;
int id;
struct list_head list;
}
初始化节点
struct list_head student_list; //创建头节点
struct student *studentp=NULL;
INIT_LIST_HEAD(&student_list); //内核初始化头节点函数
studentp=kmalloc(sizeof(struct student)*10,GFP_KERNEL);
//申请10个空间,因为没有在中断上下文中所以无所谓睡眠
memset(studentp,0,sizeof(struct student)*10); //注意这里的memset和用户空间中不是一套代码
for(i=0;i<10;i++) //初始化
{
sprintf(studentp[i].name,"student%d",i);
...
list_add(&(studentp[i].list),&student_list); //添加
}
/* 链表节点的遍历 */
struct list_head *pos=NULL; //用来保存查找结点地址
struct student *student_tmp=NULL; //临时变量
list_for_each(pos,&student_list)
{
student_tmp=list_entry(pos,struct student,list);
//在已知pos时得到的(某个尾节点地址)得到头节点地址
printk("%s",student_tmp->name);
}
链表析构就只要
kfree(studentp);
还有(不必要) list_del(&(studentp[i].list));
节点删除
list_del
===内核定时器
时钟中断
HZ:常数,决定了时钟中断发生的频率
tick:发生时钟中断的时间间隔=1/HZ
jiffies:核心变数,纪录开机以来经历了多少tick
struct timer_list //重要结构体
头文件
#include <linux/timer.h>
具体用例
MODULE_LICENSE("GPL");
struct timer_list bigdog_timer;
void bigdog_timer_handler(unsigned long data) //固定定义格式,此例中内核每次调用传进100
{
static int bark_count=0;
printk("wangwang!!--%ld\n",data);
if(bark_count<10)
{
bigdog_timer.expires=jiffies+HZ*5;
bigdog_timer.data++;
add_timer(&bigdog_timer);
bark_count++;
}
}
int __init kerneltimer_init(void)
{
//初始化定时器变量
init_timer(&bigdog_timer); //内核提供
bigdog_timer.expires=jiffies+HZ*5;
//从安装后开始计时一定时间后(这里是5s)开始,发生一个超时
bigdog_timer.function=bigdog_timer_handler;//处理函数
bigdog_timer.data=100;//定时时间到了可以传个参数进来也可以事先指定
add_timer(&bigdog_timer); //添加定时器到内核中
return 0;
}
void __exit kerneltimer_exit(void)
{
del_timer(&bigdog_timer); //卸载定时器
}
时钟中断
HZ:常数,决定了时钟中断发生的频率
tick:发生时钟中断的时间间隔=1/HZ
jiffies:核心变数,纪录开机以来经历了多少tick
struct timer_list //重要结构体
头文件
#include <linux/timer.h>
具体用例
MODULE_LICENSE("GPL");
struct timer_list bigdog_timer;
void bigdog_timer_handler(unsigned long data) //固定定义格式,此例中内核每次调用传进100
{
static int bark_count=0;
printk("wangwang!!--%ld\n",data);
if(bark_count<10)
{
bigdog_timer.expires=jiffies+HZ*5;
bigdog_timer.data++;
add_timer(&bigdog_timer);
bark_count++;
}
}
int __init kerneltimer_init(void)
{
//初始化定时器变量
init_timer(&bigdog_timer); //内核提供
bigdog_timer.expires=jiffies+HZ*5;
//从安装后开始计时一定时间后(这里是5s)开始,发生一个超时
bigdog_timer.function=bigdog_timer_handler;//处理函数
bigdog_timer.data=100;//定时时间到了可以传个参数进来也可以事先指定
add_timer(&bigdog_timer); //添加定时器到内核中
return 0;
}
void __exit kerneltimer_exit(void)
{
del_timer(&bigdog_timer); //卸载定时器
}
module_init(kerneltimer_init);
module_exit(kerneltimer_exit);
module_exit(kerneltimer_exit);
===系统调用
UNIX C: open
ANSI C: fopen
linux下的驱动程序是为应用程序提供服务的,驱动程序在内核态执行
应用程序首先使用适当的值填充寄存器:
arch/arm/asm/unistd.h
然后调用特殊的指令,跳转到内核某个固定的位置,根据应用程序填充的
固定的值来找到相应的函数执行。
用户空间编程fd=open("ss.s"...);首先用5(open)->寄存器,然后调用
sys_call_table[5](就是sys_open内核函数)
增加一个新的系统调用
vi arch/arm/kernel/sys_arm.c
//asmlinkage表示当用汇编语言调用函数时传递参数不使用寄存器用栈
asmlinkage int sys_add(int x,int y)
{
printk("add\n");
return x+y;
}
更新头文件
vi arch/arm/include/asm/unistd.h
#define __NR_add (__NR_SYSCALL_BASE+366) //366个系统调用
更新系统调用表
vi arch/arm/kernel/calls.S //不能随便放药严格遵循顺序
CALL(sys_add) //放到365个之后一个
重新编译内核make加载新内核
应用程序系统调用
syscall(366,12,13);//得到sys_add(12,13)
硬件设备分类:
字符设备:顺序读写,不带缓冲
块设备:读写顺序不固定,带有读写缓冲 (sync强制将缓冲区写到硬件设备)
网络设备
UNIX C: open
ANSI C: fopen
linux下的驱动程序是为应用程序提供服务的,驱动程序在内核态执行
应用程序首先使用适当的值填充寄存器:
arch/arm/asm/unistd.h
然后调用特殊的指令,跳转到内核某个固定的位置,根据应用程序填充的
固定的值来找到相应的函数执行。
用户空间编程fd=open("ss.s"...);首先用5(open)->寄存器,然后调用
sys_call_table[5](就是sys_open内核函数)
增加一个新的系统调用
vi arch/arm/kernel/sys_arm.c
//asmlinkage表示当用汇编语言调用函数时传递参数不使用寄存器用栈
asmlinkage int sys_add(int x,int y)
{
printk("add\n");
return x+y;
}
更新头文件
vi arch/arm/include/asm/unistd.h
#define __NR_add (__NR_SYSCALL_BASE+366) //366个系统调用
更新系统调用表
vi arch/arm/kernel/calls.S //不能随便放药严格遵循顺序
CALL(sys_add) //放到365个之后一个
重新编译内核make加载新内核
应用程序系统调用
syscall(366,12,13);//得到sys_add(12,13)
硬件设备分类:
字符设备:顺序读写,不带缓冲
块设备:读写顺序不固定,带有读写缓冲 (sync强制将缓冲区写到硬件设备)
网络设备
字符设备框架
硬件上有一个字符设备,内核就有一个cdev结构与之对应
struct cdev
{
dev_t dev;//设备号
/*
设备号(32bit)=主设备号(高12bit)+次设备号低(20bit)
主设备号:代表一个类型的设备
次设备号:用于区分相同设备中不同个体
如查看串口主、次设备号ls /dev/s3c2410_serial* -l其中204为主,64.为次
主设备号选取:
静态分配
cat /proc/devices //查看设备和主设备号
或者看文件 Documentation/device.txt
动态分配
*/
const struct file_operations *ops;
}
例子
MODULE_LICENSE("GPL");
dev_t dev=0;
int __init cdd_init(void)
{
int ret=0;
//注册设备号
dev=MKDEV(CDD_MAJOR,CDD_MINOR);//前面是主后面是次,生成设备号
ret=register_chrdev_region(dev,10,"cdd_demp");//连续注册10个设备号
if(ret<0) return ret;
return 0;
}
void __exit cdd_exit(void)
{
unregister_chrdev_region(dev,10);//注销设备号
}
module_init(cdd_init);
module_exit(cdd_exit);
硬件上有一个字符设备,内核就有一个cdev结构与之对应
struct cdev
{
dev_t dev;//设备号
/*
设备号(32bit)=主设备号(高12bit)+次设备号低(20bit)
主设备号:代表一个类型的设备
次设备号:用于区分相同设备中不同个体
如查看串口主、次设备号ls /dev/s3c2410_serial* -l其中204为主,64.为次
主设备号选取:
静态分配
cat /proc/devices //查看设备和主设备号
或者看文件 Documentation/device.txt
动态分配
*/
const struct file_operations *ops;
}
例子
MODULE_LICENSE("GPL");
dev_t dev=0;
int __init cdd_init(void)
{
int ret=0;
//注册设备号
dev=MKDEV(CDD_MAJOR,CDD_MINOR);//前面是主后面是次,生成设备号
ret=register_chrdev_region(dev,10,"cdd_demp");//连续注册10个设备号
if(ret<0) return ret;
return 0;
}
void __exit cdd_exit(void)
{
unregister_chrdev_region(dev,10);//注销设备号
}
module_init(cdd_init);
module_exit(cdd_exit);
cdev的操作
头文件
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/fs/h>
cdev_init()//初始化cdev
cdev_add()//向内核注册cdev
定义cdev类型变量
dev_t dev=0;
struct cdev cdd_cdev;
int cdd_open(struct inode *inode,struct file *filp)
{
printk("open\n");
return 0;
}
int cdd_read(struct file *filp,char __user *buf,size_t count,
loff_t *offset)
{
printk("read\n");
return 0;
}
int cdd_write(struct file *filp,const char __user *buf,
size_t count,loff_t *offset)
{
printk("write\n");
return 0;
}
int cdd_ioctl(struct inode *inode,struct file *filp,
unsigned int cmd,unsigned long data)
{
printk("ioctl\n");
return 0;
}
int cdd_release(struct inode *inode,struct file *filp)
{
printk("release\n");
return 0;
}
struct file_operations cdd_fops=
{
.owner=THIS_MODULE,
.open=cdd_open,
.read=cdd_read,
.write=cdd_write,
.ioctl=cdd_ioctl,
.release=cdd_release,
};
u32 cdd_major=0;'
u32 cdd_minor=0;
struct class *dev_class=NULL;
struct device *dev_device=NULL;
int __init cdd_init(void)
{
int ret=0;
//注册设备号
ret=alloc_chrdev_region(&dev,cdd_minior,1,"cdd_demp");//自动获取主设备号
if(ret<0)
{
return ret;
}
cdd_major=MAJOR(dev);//获取主设备号
cdev_init(&cdd_cdev,&cdd_fops); //初始化cdev
ret=cdev_add(&cdd_cdev,dev,1); //向内核中添加cdev
if(ret<0){
unregister_chrdev_region(dev,1);
return ret;
}
//自动创建设备节点文件
dev_class=class_create(THIS_MODULE,"cdd_class");//注册设备类
//多一个/sys/class/cdd_class文件夹
if(IS_ERR(dev_class))
{
ret=PTR_ERR(dev_class);
cdev_del(&cdd_cdev);
unregister_chrdev_region(dev,1);
return ret;
}
dev_device=device_create(dev_class.NULL,dev,NULL,"cdd%d",cdd_minor);
//注册设备,最后一个参数为可变参数
//会多一个/sys/class/cdd_class/cdd0,多一个/dev/cdd0
if(IS_ERR(dev_device))
{
ret=PTR_ERR(dev_device);
class_destroy(dev_class);
cdev_del(&cdd_cdev);
unregister_chrdev_region(dev,1);
return ret;
}
return 0;
}
void __exit cdd_exit(void)
{
//逆序消除影响
device_destroy(dev_class,dev);
class_destroy(dev_class);
cdev_del(&cdd_cdev);
unregister_chrdev_region(dev,1);//注销设备号
}
module_init(cdd_init);
module_exit(cdd_exit);
测试程序
#include<stdio.h>
#include<fcntl.h>
#include<stdlib.h>
int fd=0;
int main()
{
sfd=open("/dev/cdd",O_RDWR);
}
安装设备
手工创建设备节点文件,主设备号经查证得248,次设备号为0
mknod /dev/cdd c 248 0
或者自动创建设备节点文件
设备节点文件是由udev或者mdev机制创建的可以在/sbin里面看到
udev,后台守护进程,可以接收uevent事件
头文件
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/fs/h>
cdev_init()//初始化cdev
cdev_add()//向内核注册cdev
定义cdev类型变量
dev_t dev=0;
struct cdev cdd_cdev;
int cdd_open(struct inode *inode,struct file *filp)
{
printk("open\n");
return 0;
}
int cdd_read(struct file *filp,char __user *buf,size_t count,
loff_t *offset)
{
printk("read\n");
return 0;
}
int cdd_write(struct file *filp,const char __user *buf,
size_t count,loff_t *offset)
{
printk("write\n");
return 0;
}
int cdd_ioctl(struct inode *inode,struct file *filp,
unsigned int cmd,unsigned long data)
{
printk("ioctl\n");
return 0;
}
int cdd_release(struct inode *inode,struct file *filp)
{
printk("release\n");
return 0;
}
struct file_operations cdd_fops=
{
.owner=THIS_MODULE,
.open=cdd_open,
.read=cdd_read,
.write=cdd_write,
.ioctl=cdd_ioctl,
.release=cdd_release,
};
u32 cdd_major=0;'
u32 cdd_minor=0;
struct class *dev_class=NULL;
struct device *dev_device=NULL;
int __init cdd_init(void)
{
int ret=0;
//注册设备号
ret=alloc_chrdev_region(&dev,cdd_minior,1,"cdd_demp");//自动获取主设备号
if(ret<0)
{
return ret;
}
cdd_major=MAJOR(dev);//获取主设备号
cdev_init(&cdd_cdev,&cdd_fops); //初始化cdev
ret=cdev_add(&cdd_cdev,dev,1); //向内核中添加cdev
if(ret<0){
unregister_chrdev_region(dev,1);
return ret;
}
//自动创建设备节点文件
dev_class=class_create(THIS_MODULE,"cdd_class");//注册设备类
//多一个/sys/class/cdd_class文件夹
if(IS_ERR(dev_class))
{
ret=PTR_ERR(dev_class);
cdev_del(&cdd_cdev);
unregister_chrdev_region(dev,1);
return ret;
}
dev_device=device_create(dev_class.NULL,dev,NULL,"cdd%d",cdd_minor);
//注册设备,最后一个参数为可变参数
//会多一个/sys/class/cdd_class/cdd0,多一个/dev/cdd0
if(IS_ERR(dev_device))
{
ret=PTR_ERR(dev_device);
class_destroy(dev_class);
cdev_del(&cdd_cdev);
unregister_chrdev_region(dev,1);
return ret;
}
return 0;
}
void __exit cdd_exit(void)
{
//逆序消除影响
device_destroy(dev_class,dev);
class_destroy(dev_class);
cdev_del(&cdd_cdev);
unregister_chrdev_region(dev,1);//注销设备号
}
module_init(cdd_init);
module_exit(cdd_exit);
测试程序
#include<stdio.h>
#include<fcntl.h>
#include<stdlib.h>
int fd=0;
int main()
{
sfd=open("/dev/cdd",O_RDWR);
}
安装设备
手工创建设备节点文件,主设备号经查证得248,次设备号为0
mknod /dev/cdd c 248 0
或者自动创建设备节点文件
设备节点文件是由udev或者mdev机制创建的可以在/sbin里面看到
udev,后台守护进程,可以接收uevent事件
一个多设备操作的例子:
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <asm/uaccess.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <asm/arch/regs-gpio.h>
#include <asm/hardware.h>
#include <linux/timer.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <asm/uaccess.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <asm/uaccess.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <asm/arch/regs-gpio.h>
#include <asm/hardware.h>
#include <linux/timer.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <asm/uaccess.h>
MODULE_LICENSE("GPL");
struct cdd_cdev
{
struct cdev cdev;
struct class_device *dev_device;
u8 led;
char kbuf[100];
};
{
struct cdev cdev;
struct class_device *dev_device;
u8 led;
char kbuf[100];
};
struct cdd_cdev *cdd_cdevp=NULL; //用来动态申请空间
int cdd_open(struct inode *inode,struct file *filp)
{
struct cdd_cdev *pcdevp=NULL;
printk("open\n");
pcdevp=container_of(inode->i_cdev,struct cdd_cdev ,cdev);
printk("led=%d\n",pcdevp->led);
filp->private_data=pcdevp; //想放什么放什么
return 0;
}
int cdd_read(struct file *filp,char __user *buf,size_t count,loff_t *offset)
{
int ret=0;
struct cdd_cdev *pcdevp=filp->private_data;
printk("read\n");
printk("LED=%d\n",pcdevp->led);
ret=copy_to_user(buf,pcdevp->kbuf,count);//read(fd,buf,10)
return ret;
}
int cdd_write(struct file *filp,const char __user *buf,size_t count,loff_t *offset)
{
int ret=0;
struct cdd_cdev *pcdevp=filp->private_data;
printk("write\n");
ret=copy_from_user(pcdevp->kbuf,buf,count);
return ret;
}
int cdd_ioctl(struct inode *inode,struct file *filp,unsigned int cmd,unsigned long data)
{
printk("ioctl\n");
return 0;
}
int cdd_release(struct inode *inode,struct file *filp)
{
printk("release\n");
return 0;
}
struct file_operations cdd_fops=
{
.owner=THIS_MODULE,
.open=cdd_open,
.read=cdd_read,
.write=cdd_write,
.ioctl=cdd_ioctl,
.release=cdd_release,
};
{
struct cdd_cdev *pcdevp=NULL;
printk("open\n");
pcdevp=container_of(inode->i_cdev,struct cdd_cdev ,cdev);
printk("led=%d\n",pcdevp->led);
filp->private_data=pcdevp; //想放什么放什么
return 0;
}
int cdd_read(struct file *filp,char __user *buf,size_t count,loff_t *offset)
{
int ret=0;
struct cdd_cdev *pcdevp=filp->private_data;
printk("read\n");
printk("LED=%d\n",pcdevp->led);
ret=copy_to_user(buf,pcdevp->kbuf,count);//read(fd,buf,10)
return ret;
}
int cdd_write(struct file *filp,const char __user *buf,size_t count,loff_t *offset)
{
int ret=0;
struct cdd_cdev *pcdevp=filp->private_data;
printk("write\n");
ret=copy_from_user(pcdevp->kbuf,buf,count);
return ret;
}
int cdd_ioctl(struct inode *inode,struct file *filp,unsigned int cmd,unsigned long data)
{
printk("ioctl\n");
return 0;
}
int cdd_release(struct inode *inode,struct file *filp)
{
printk("release\n");
return 0;
}
struct file_operations cdd_fops=
{
.owner=THIS_MODULE,
.open=cdd_open,
.read=cdd_read,
.write=cdd_write,
.ioctl=cdd_ioctl,
.release=cdd_release,
};
#define CDD_COUNT 10
dev_t dev=0;
u32 cdd_major=0;
u32 cdd_minor=0;
struct class *dev_class=NULL;
dev_t dev=0;
u32 cdd_major=0;
u32 cdd_minor=0;
struct class *dev_class=NULL;
int __init cdd_init(void)
{
int ret=0;
int i=0;
ret=alloc_chrdev_region(&dev,cdd_minor,CDD_COUNT,"cdd_demo");
if(ret<0)
{
goto failure_register_chrdev;
}
printk("register succeed %d\n",dev);
cdd_major=MAJOR(dev);
cdd_cdevp=kzalloc(sizeof(struct cdd_cdev)*CDD_COUNT, GFP_KERNEL);//动态分配
if(IS_ERR(cdd_cdevp))
{
goto failure_kzalloc;
}
printk("kzalloc succeed\n");
dev_class=class_create(THIS_MODULE,"cdd_class");
if(IS_ERR(dev_class))
{
ret=PTR_ERR(dev_class);
goto failure_dev_class;
}
printk("class succeed\n");
for(i=0;i<CDD_COUNT;i++)
{
/*初始化cdev*/
cdev_init(&(cdd_cdevp[i].cdev),&cdd_fops);
printk("cdev_init%d succeed\n",i);
/*添加cdev到核*/
cdev_add(&(cdd_cdevp[i].cdev),dev+i,1);
printk("cdev_add%d succeed\n",i);
/*创建节点*/
cdd_cdevp[i].dev_device=class_device_create(dev_class,NULL,dev+i,NULL,"cdd%d",i);
printk("device_create%d succeed\n",i);
cdd_cdevp[i].led=i;
}
return 0;
failure_dev_class:
kfree(cdd_cdevp);
failure_kzalloc:
unregister_chrdev_region(dev,CDD_COUNT);
failure_register_chrdev:
return ret;
}
{
int ret=0;
int i=0;
ret=alloc_chrdev_region(&dev,cdd_minor,CDD_COUNT,"cdd_demo");
if(ret<0)
{
goto failure_register_chrdev;
}
printk("register succeed %d\n",dev);
cdd_major=MAJOR(dev);
cdd_cdevp=kzalloc(sizeof(struct cdd_cdev)*CDD_COUNT, GFP_KERNEL);//动态分配
if(IS_ERR(cdd_cdevp))
{
goto failure_kzalloc;
}
printk("kzalloc succeed\n");
dev_class=class_create(THIS_MODULE,"cdd_class");
if(IS_ERR(dev_class))
{
ret=PTR_ERR(dev_class);
goto failure_dev_class;
}
printk("class succeed\n");
for(i=0;i<CDD_COUNT;i++)
{
/*初始化cdev*/
cdev_init(&(cdd_cdevp[i].cdev),&cdd_fops);
printk("cdev_init%d succeed\n",i);
/*添加cdev到核*/
cdev_add(&(cdd_cdevp[i].cdev),dev+i,1);
printk("cdev_add%d succeed\n",i);
/*创建节点*/
cdd_cdevp[i].dev_device=class_device_create(dev_class,NULL,dev+i,NULL,"cdd%d",i);
printk("device_create%d succeed\n",i);
cdd_cdevp[i].led=i;
}
return 0;
failure_dev_class:
kfree(cdd_cdevp);
failure_kzalloc:
unregister_chrdev_region(dev,CDD_COUNT);
failure_register_chrdev:
return ret;
}
void __exit cdd_exit(void)
{
int i=0;
for(i=0;i<CDD_COUNT;i++)
{
class_device_unregister(cdd_cdevp[i].dev_device);
cdev_del(&(cdd_cdevp[i].cdev));
}
class_destroy(dev_class);
kfree(cdd_cdevp);
unregister_chrdev_region(dev,CDD_COUNT);
}
module_init(cdd_init);
module_exit(cdd_exit);
{
int i=0;
for(i=0;i<CDD_COUNT;i++)
{
class_device_unregister(cdd_cdevp[i].dev_device);
cdev_del(&(cdd_cdevp[i].cdev));
}
class_destroy(dev_class);
kfree(cdd_cdevp);
unregister_chrdev_region(dev,CDD_COUNT);
}
module_init(cdd_init);
module_exit(cdd_exit);
进一步完整驱动程序使之更像GNU C
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <asm/uaccess.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <asm/arch/regs-gpio.h>
#include <asm/hardware.h>
#include <linux/timer.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <asm/uaccess.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <asm/uaccess.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <asm/arch/regs-gpio.h>
#include <asm/hardware.h>
#include <linux/timer.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <asm/uaccess.h>
MODULE_LICENSE("GPL");
#define BUF_SIZE 100
struct cdd_cdev
{
struct cdev cdev;
struct class_device *dev_device;
u8 led;
char kbuf[BUF_SIZE];
u32 data_len;
};
{
struct cdev cdev;
struct class_device *dev_device;
u8 led;
char kbuf[BUF_SIZE];
u32 data_len;
};
struct cdd_cdev *cdd_cdevp=NULL; //用来动态申请空间
int cdd_open(struct inode *inode,struct file *filp)
{
struct cdd_cdev *pcdevp=NULL;
printk("open\n");
pcdevp=container_of(inode->i_cdev,struct cdd_cdev ,cdev);
printk("led=%d\n",pcdevp->led);
filp->private_data=pcdevp; //想放什么放什么
return 0;
}
int cdd_read(struct file *filp,char __user *buf,size_t count,loff_t *offset)
{
int ret=0;
u32 pos=*offset; //从哪个空间开始读
u32 cnt=count; //读取字节数
struct cdd_cdev *pcdevp=filp->private_data;
//printk("read\n");
//printk("LED=%d\n",pcdevp->led);
if(cnt>(pcdevp->data_len-pos))
{
cnt=pcdevp->data_len-pos;
}
ret=copy_to_user(buf,pcdevp->kbuf+pos,cnt);//read(fd,buf,10)
*offset+=cnt;
return ret;
}
int cdd_write(struct file *filp,const char __user *buf,size_t count,loff_t *offset)
{
int ret=0;
struct cdd_cdev *pcdevp=filp->private_data;
u32 pos=*offset;
u32 cnt=count;
//printk("write\n");
if(cnt>(BUF_SIZE-pos))
{
cnt=BUF_SIZE-pos;
}
ret=copy_from_user(pcdevp->kbuf+pos,buf,cnt);
*offset+=cnt;
if(*offset>pcdevp->data_len)
{
pcdevp->data_len=*offset;
}
return ret;
}
int cdd_ioctl(struct inode *inode,struct file *filp,unsigned int cmd,unsigned long data)
{
//printk("ioctl\n");
return 0;
}
int cdd_release(struct inode *inode,struct file *filp)
{
//printk("release\n");
return 0;
}
loff_t cdd_llseek(struct file *filp, loff_t offset, int whence)
{
struct cdd_cdev *pcdevp=filp->private_data;
loff_t newpos=0;
switch(whence)
{
case SEEK_SET:
newpos=offset;
break;
case SEEK_CUR:
newpos=filp->f_pos+offset;
break;
case SEEK_END:
newpos=pcdevp->data_len+offset;
break;
default:
return -EINVAL;
}
if(newpos<0||newpos>=BUF_SIZE)
{
return -EINVAL;
}
filp->f_pos=newpos;
return newpos;
}
{
struct cdd_cdev *pcdevp=NULL;
printk("open\n");
pcdevp=container_of(inode->i_cdev,struct cdd_cdev ,cdev);
printk("led=%d\n",pcdevp->led);
filp->private_data=pcdevp; //想放什么放什么
return 0;
}
int cdd_read(struct file *filp,char __user *buf,size_t count,loff_t *offset)
{
int ret=0;
u32 pos=*offset; //从哪个空间开始读
u32 cnt=count; //读取字节数
struct cdd_cdev *pcdevp=filp->private_data;
//printk("read\n");
//printk("LED=%d\n",pcdevp->led);
if(cnt>(pcdevp->data_len-pos))
{
cnt=pcdevp->data_len-pos;
}
ret=copy_to_user(buf,pcdevp->kbuf+pos,cnt);//read(fd,buf,10)
*offset+=cnt;
return ret;
}
int cdd_write(struct file *filp,const char __user *buf,size_t count,loff_t *offset)
{
int ret=0;
struct cdd_cdev *pcdevp=filp->private_data;
u32 pos=*offset;
u32 cnt=count;
//printk("write\n");
if(cnt>(BUF_SIZE-pos))
{
cnt=BUF_SIZE-pos;
}
ret=copy_from_user(pcdevp->kbuf+pos,buf,cnt);
*offset+=cnt;
if(*offset>pcdevp->data_len)
{
pcdevp->data_len=*offset;
}
return ret;
}
int cdd_ioctl(struct inode *inode,struct file *filp,unsigned int cmd,unsigned long data)
{
//printk("ioctl\n");
return 0;
}
int cdd_release(struct inode *inode,struct file *filp)
{
//printk("release\n");
return 0;
}
loff_t cdd_llseek(struct file *filp, loff_t offset, int whence)
{
struct cdd_cdev *pcdevp=filp->private_data;
loff_t newpos=0;
switch(whence)
{
case SEEK_SET:
newpos=offset;
break;
case SEEK_CUR:
newpos=filp->f_pos+offset;
break;
case SEEK_END:
newpos=pcdevp->data_len+offset;
break;
default:
return -EINVAL;
}
if(newpos<0||newpos>=BUF_SIZE)
{
return -EINVAL;
}
filp->f_pos=newpos;
return newpos;
}
struct file_operations cdd_fops=
{
.owner=THIS_MODULE,
.open=cdd_open,
.read=cdd_read,
.write=cdd_write,
.ioctl=cdd_ioctl,
.release=cdd_release,
.llseek=cdd_llseek,
};
{
.owner=THIS_MODULE,
.open=cdd_open,
.read=cdd_read,
.write=cdd_write,
.ioctl=cdd_ioctl,
.release=cdd_release,
.llseek=cdd_llseek,
};
#define CDD_COUNT 10
dev_t dev=0;
u32 cdd_major=0;
u32 cdd_minor=0;
struct class *dev_class=NULL;
dev_t dev=0;
u32 cdd_major=0;
u32 cdd_minor=0;
struct class *dev_class=NULL;
int __init cdd_init(void)
{
int ret=0;
int i=0;
ret=alloc_chrdev_region(&dev,cdd_minor,CDD_COUNT,"cdd_demo");
if(ret<0)
{
goto failure_register_chrdev;
}
//printk("register succeed %d\n",dev);
cdd_major=MAJOR(dev);
cdd_cdevp=kzalloc(sizeof(struct cdd_cdev)*CDD_COUNT, GFP_KERNEL);//动态分配
if(IS_ERR(cdd_cdevp))
{
goto failure_kzalloc;
}
//printk("kzalloc succeed\n");
dev_class=class_create(THIS_MODULE,"cdd_class");
if(IS_ERR(dev_class))
{
ret=PTR_ERR(dev_class);
goto failure_dev_class;
}
//printk("class succeed\n");
for(i=0;i<CDD_COUNT;i++)
{
/*初始化cdev*/
cdev_init(&(cdd_cdevp[i].cdev),&cdd_fops);
//printk("cdev_init%d succeed\n",i);
/*添加cdev到核*/
cdev_add(&(cdd_cdevp[i].cdev),dev+i,1);
//printk("cdev_add%d succeed\n",i);
/*创建节点*/
cdd_cdevp[i].dev_device=class_device_create(dev_class,NULL,dev+i,NULL,"cdd%d",i);
//printk("device_create%d succeed\n",i);
cdd_cdevp[i].led=i;
}
return 0;
failure_dev_class:
kfree(cdd_cdevp);
failure_kzalloc:
unregister_chrdev_region(dev,CDD_COUNT);
failure_register_chrdev:
return ret;
}
{
int ret=0;
int i=0;
ret=alloc_chrdev_region(&dev,cdd_minor,CDD_COUNT,"cdd_demo");
if(ret<0)
{
goto failure_register_chrdev;
}
//printk("register succeed %d\n",dev);
cdd_major=MAJOR(dev);
cdd_cdevp=kzalloc(sizeof(struct cdd_cdev)*CDD_COUNT, GFP_KERNEL);//动态分配
if(IS_ERR(cdd_cdevp))
{
goto failure_kzalloc;
}
//printk("kzalloc succeed\n");
dev_class=class_create(THIS_MODULE,"cdd_class");
if(IS_ERR(dev_class))
{
ret=PTR_ERR(dev_class);
goto failure_dev_class;
}
//printk("class succeed\n");
for(i=0;i<CDD_COUNT;i++)
{
/*初始化cdev*/
cdev_init(&(cdd_cdevp[i].cdev),&cdd_fops);
//printk("cdev_init%d succeed\n",i);
/*添加cdev到核*/
cdev_add(&(cdd_cdevp[i].cdev),dev+i,1);
//printk("cdev_add%d succeed\n",i);
/*创建节点*/
cdd_cdevp[i].dev_device=class_device_create(dev_class,NULL,dev+i,NULL,"cdd%d",i);
//printk("device_create%d succeed\n",i);
cdd_cdevp[i].led=i;
}
return 0;
failure_dev_class:
kfree(cdd_cdevp);
failure_kzalloc:
unregister_chrdev_region(dev,CDD_COUNT);
failure_register_chrdev:
return ret;
}
void __exit cdd_exit(void)
{
int i=0;
for(i=0;i<CDD_COUNT;i++)
{
class_device_unregister(cdd_cdevp[i].dev_device);
cdev_del(&(cdd_cdevp[i].cdev));
}
class_destroy(dev_class);
kfree(cdd_cdevp);
unregister_chrdev_region(dev,CDD_COUNT);
}
module_init(cdd_init);
module_exit(cdd_exit);
{
int i=0;
for(i=0;i<CDD_COUNT;i++)
{
class_device_unregister(cdd_cdevp[i].dev_device);
cdev_del(&(cdd_cdevp[i].cdev));
}
class_destroy(dev_class);
kfree(cdd_cdevp);
unregister_chrdev_region(dev,CDD_COUNT);
}
module_init(cdd_init);
module_exit(cdd_exit);
如果是控制led灯
在用户空间有两种方式控制led亮灭
1: /dev/led0 /dev/led1
fd0=open("/dev/led0",O_RDWR);
ioctl(fd0,1,0); //亮,第三个参数废弃ioctl(fd,cmd,data)
ioctl(fd0,0,0); //灭
在用户空间有两种方式控制led亮灭
1: /dev/led0 /dev/led1
fd0=open("/dev/led0",O_RDWR);
ioctl(fd0,1,0); //亮,第三个参数废弃ioctl(fd,cmd,data)
ioctl(fd0,0,0); //灭
===中断
内核中找到gpio对应的中断编号函数
gpio_to_irq()
内核中找到gpio对应的中断编号函数
gpio_to_irq()