查询方式的按键驱动程序框架详解:
上层代码为drv.c,以下是详细代码
static struct class *button_class;
static int major = 0;//先定义主设备号1(用来寻找驱动程序)
static struct button_operations *p_button_opr;//在.h文件后定义b_opr结构体后,在.c文件中定义结构体的指针全局变量,由底层代码提供的
static int button_open (struct inode *node, struct file *file)//打开函数
{
int minor = iminor(node);//根据搜索栏查看别人的用法,使用inode节点即可查询到次设备号,查询minor(次设备)用法事 :点击搜索-》内核所在的目录—》drivers-》char
p_button_opr->init(minor);//调用底层的operation结构体的初始化函数初始化测设备号; 要定义底层opr结构体,在其button_drv.h中定义
return 0;
};
static ssize_t button_read (struct file *file, char __user *buf, size_t size, loff_t *off)//这里的user是空的宏,表示用户态的buf,在内核,在驱动程序时不可以使用它
{
unsigned int minor = iminor(file_inode(file));//根据搜索栏查看别人的用法,使用inode节点即可查询到次设备号
char level;
int err;
level=p_button_opr->read(minor);//调用底层的operation结构体的读函数读取设备号的引脚
err = copy_to_user(buf,&level,1);//因为用户不可直接访问buf,所以用此函数可传给用户
return 1;//表示读了一个字节
};
static struct file_operations button_fops = {
.open = button_open,
.read = button_read,
};//定义file_operations 已实现open read write等函数
void register_button_operations(struct button_operations *opr)//有底层代码提供struct button_operations这个结构体
{
int i;
p_button_opr = opr;
for(i=0;i< opr->count;i++)
{
device_create(button_class, NULL, MKDEV(major, i), NULL, "100ask_button%d",i);//这个函数用来在虚拟文件系统里构建一些信息可以自动构建设备节点
//在button_class中创建设备节点,其中主设备号为major,次设备号为i,名字为100ask_button 0 ,1,2
}
}
void unregister_button_operations(void)//有底层代码提供struct button_operations这个结构体,撤销设备节点
{
int i;
for(i=0;i < p_button_opr->count;i++)
{
device_destroy(button_class, MKDEV(major, i));//这个函数用来在虚拟文件系统里构建一些信息可以撤销设备节点,其中主设备号为major,次设备号为i,
}
}
EXPORT_SYMBOL(register_button_operations);
EXPORT_SYMBOL(unregister_button_operations);//定义的函数给另外驱动程序使用,需要用export将其符号导出来
int button_init(void)//3初始化button函数,用来在内核注册file_operations结构体,入口函数
{
major = register_chrdev(0, "100ask_button", &button_fops);//注册函数中参数0代表内核自己分配的主设备号,“”中代表名字,&file_operations的结构体名字
button_class = class_create(THIS_MODULE, "100ask_button");//7最后一步 提供设备信息,自动创建设备节点,创建class_create和device_create
if (IS_ERR(button_class))
return -1;
return 0;
}
void button_exit(void)//4出口函数,用来注销file_operations
{
class_destroy(button_class);
unregister_chrdev(major, "100ask_button");
}
module_init(button_init);
module_exit(button_exit);
MODULE_LICENSE("GPL");
下层代码为board_xxx.c
static void board_xxx_button_init_gpio (int which)
{
printk("%s %s %d, init gpio for button %d\n", __FILE__, __FUNCTION__, __LINE__, which);
}
static int board_xxx_button_read_gpio (int which)
{
printk("%s %s %d, read gpio for button %d\n", __FILE__, __FUNCTION__, __LINE__, which);
return 1; //打印一句话外 还返回引脚的电平
}
static struct button_operations my_buttons_ops ={
.count = 2, //2个按键
.init = board_xxx_button_init_gpio,//初始化引脚函数
.read = board_xxx_button_read_gpio,//读引脚函数
};
int board_xxx_button_init(void)//看驱动程序先看入口函数
{
register_button_operations(&my_buttons_ops);//它注册了一个结构体
return 0;
}
void board_xxx_button_exit(void)//出口函数注销这个结构体
{
unregister_button_operations();
}
module_init(board_xxx_button_init);
module_exit(board_xxx_button_exit);
MODULE_LICENSE("GPL");
button_drv.h文件如下:
#ifndef _BUTTON_DRV_H//防止头文件被重复包含
#define _BUTTON_DEV_H
struct button_operations{
int count;//有多个按键
void (*init) (int which);//初始化哪一个按键,不需要有返回值
int (*read) (int which);//读取哪一个按键
};
//这两个函数是给驱动使用的,需要在.h文件中定义
void register_button_operations(struct button_operations *opr);//有底层代码提供struct button_operations这个结构体
void unregister_button_operations(void);//有底层代码提供struct button_operations这个结构体,撤销设备节点
#endif
button_test.c文件如下:
```c
int main(int argc, char **argv)
{
int fd;
char val;
if(argc!=2)
{
printf("Usage :%s <dev>\n",argv[0]);
return -1;
}
fd = open (argv[1],O_RDWR);
if(fd == -1)
{
printf("can not open file %s\n",argv[1]);
return -1;
}
read(fd,&val,1);
printf("get button : %d\n",val);
close(fd);
return 0;
}
makefile文件如下:
KERN_DIR = /home/john/100ask_imx6ull-sdk/Linux-4.9.88//makefile文件的KERN_DIR一定要为内核所在的目录,因为驱动程序所用到的头文件是不同的,不同的单板对应的头文件是不同地
all:
make -C $(KERN_DIR) M=`pwd` modules
$(CROSS_COMPILE)gcc -o button_test button_test.c
clean:
make -C $(KERN_DIR) M=`pwd` modules clean
rm -rf modules.order`在这里插入代码片`
rm -f ledtest
obj-m += button_drv.o //将button_drv.o编译成button_drv.ko
obj-m += board_xxx.o //将board_xxx.o编译成button_drv.ko
以上是今天按键驱动框架的学习,良好的习惯,收益终身,从今天开始也记录下嵌入式linux的学习记录吧,啧啧啧。么么,哈哈哈!