以调用led为例,编写驱动程序+应用程序+makefile。
总体功能:test程序作为应用程序,通过fd = open(argv[1], O_RDWR)与驱动文件相连,这里的open以及后续的write,close均由驱动程序leddrv.c重写提供。leddrv.c中p_led_opr->init(minor)表示的哪个灯,灯的状态两参数,由board_demo.c重写提供。board_demo.c根据各个板的灯接口不同进行特别的设置。
编写驱动程序
① 确定主设备号,也可以让内核分配
② 定义自己的 file_operations 结构体
③ 实现对应的 drv_open/drv_read/drv_write 等函数,填入 file_operations 结构体
④ 把 file_operations 结构体告诉内核:register_chrdev
⑤ 谁来注册驱动程序啊?得有一个入口函数:安装驱动程序时,就会去调用这个入口函数
⑥ 有入口函数就应该有出口函数:卸载驱动程序时,出口函数调用 unregister_chrdev
⑦ 其他完善:提供设备信息,自动创建设备节点:class_create, device_create
leddrv.c
1、程序功能:制造驱动drv,中间调用到board_demo.c的内容,led_opr.h受到board_demo.c的调用,其中含有board_demo.c中的一些函数,结构体的声明
2、程序执行顺序:
module_init(led_init);进入到:static int __init led_init(void)
在tatic struct file_operations led_drv定义各项行为,可选择性进行
owner = THIS_MODULE,
.open = led_drv_open,
.read = led_drv_read,
.write = led_drv_write,
.release = led_drv_close,
module_exit(led_exit);进入到:tatic void __exit led_exit(void)
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>
#include "led_opr.h"
#define LED_NUM 2
/* 1. 确定主设备号 */
static int major = 0;
static struct class *led_class;
struct led_operations *p_led_opr;//指针来调用单板相关代码
#define MIN(a, b) (a < b ? a : b)
/* 3. 实现对应的open/read/write等函数,填入file_operations结构体 */
//函数内传入的参数在file_operations 结构体内定义,如ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
static ssize_t led_drv_read (struct file *file, char __user *buf, size_t size, loff_t *offset)
{
printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
return 0;
}
/* write(fd, &val, 1); */
static ssize_t led_drv_write (struct file *file, const char __user *buf, size_t size, loff_t *offset)
{
int err;
char status;
struct inode *inode = file_inode(file);//file_node()返回指定文件(file)的inode编号
int minor = iminor(inode);//使用iminor获取它的次设备号
printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
err = copy_from_user(&status, buf, 1);//从buf拷贝1个字节到status的地址中,表示灯的状态
/* 根据次设备号和status控制LED */
p_led_opr->ctl(minor, status);
return 1;
}
static int led_drv_open (struct inode *node, struct file *file)
{
int minor = iminor(node);//用iminor函数得到次设备号
printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
/* 根据次设备号初始化LED */
p_led_opr->init(minor);//在board_demo.c中重写
return 0;
}
static int led_drv_close (struct inode *node, struct file *file)
{
printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
return 0;
}
/* 2. 定义自己的file_operations结构体 */
static struct file_operations led_drv = {
.owner = THIS_MODULE,
.open = led_drv_open,
.read = led_drv_read,
.write = led_drv_write,
.release = led_drv_close,
};
/* 4. 把file_operations结构体告诉内核:注册驱动程序 */
/* 5. 谁来注册驱动程序啊?得有一个入口函数:安装驱动程序时,就会去调用这个入口函数 */
//led_init功能