led_drv.c文件如下:
#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/*① 确定主设备号,也可以让内核分配*/
static int major = 0;static struct class *led_class;struct led_operations *p_led_opr;
#define MIN(a , b)(a < b ? a : b)
/*③ 实现对应的 drv_open/drv_read/drv_write 等函数,填入 file_operations 结构体*/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;
};//app调用此函数时采用write(fd, &val, 1) 取值val的一字节写入fd
static ssize_t led_drv_write (struct file *file, const char __user *buf, size_t size, loff_t *offset)
{ int err;char status;unsigned int minor = iminor(file_inode(file));//根据file结构体来得到次设备号printk("%s %s line %d\n",__FILE__,__FUNCTION__,__LINE__);err = copy_from_user(&status, buf, 1);//buf里的数据copy到statusp_led_opr->ctl(minor,status);//根据status的值操作led//根据次设备号和status控制ledreturn 1;};
static int led_drv_open(struct inode *node, struct file *file)
{int minor = iminor(node);printk("%s %s line %d\n",__FILE__,__FUNCTION__,__LINE__);//根据次设备号初始化ledp_led_opr->init(minor);return 0;
};
static int led_drv_close (struct inode *node, struct file *file)
{printk("%s %s line %d\n",__FILE__,__FUNCTION__,__LINE__);return 0;
}
;
/*② 定义自己的 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,};//④ 把 file_operations 结构体告诉内核:register_chrdev、static int __init led_init(void)
{int err;int i;printk("%s %s line %d\n",__FILE__,__FUNCTION__,__LINE__);major = register_chrdev(0, "led", &led_drv);led_class = class_create(THIS_MODULE, "led");//⑦ 其他完善:提供设备信息,自动创建设备节点:class_create, device_create*/err = PTR_ERR(led_class);if (IS_ERR(led_class)){printk("%s %s line %d\n",__FILE__,__FUNCTION__,__LINE__);unregister_chrdev(major,"led");return -1;}for(i=0;i<LED_NUM;i++)device_create(led_class, NULL, MKDEV(major, i), NULL, "led%d",i); p_led_opr = get_board_led_opr();return 0;};//⑤ 谁来注册驱动程序啊?得有一个入口函数:安装驱动程序时,就会去调用这个入口函数
//⑥ 有入口函数就应该有出口函数:卸载驱动程序时,出口函数调用 unregister_chrdev
static void __exit led_exit(void)
{int i;for(i = 0; i < LED_NUM; i++)device_destroy(led_class, MKDEV(major, i));printk("%s %s line %d\n",__FILE__,__FUNCTION__,__LINE__);device_destroy(led_class, MKDEV(major,0));class_destroy(led_class);unregister_chrdev(major,"led");
};module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");
为了使同一个驱动程序可以操作不同的led,采用分层的思想,以下是led_opr.h文件
struct led_operations{int (*init)(int which);//初始化led,选择哪一个ledint (*ctl)(int which, char status);//控制led,选择哪一个led 亮1 灭0
};struct led_operations *get_board_led_opr(void);//调用此函数是为了让leddrv.c访问led_operation结构体
为了使此驱动程序使用于其他板子,以下是board_demo文件:
#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"static int board_demo_led_init(int which)//初始化led,which哪一个led
{printk("%s %s line %d,led %d\n",__FILE__,__FUNCTION__,__LINE__,which);return 0; }static int board_demo_led_ctl(int which,char status)//初始化led,which哪一个led
{printk("%s %s line %d, led %d, %s\n", __FILE__, __FUNCTION__, __LINE__, which, status ? "on" : "off");return 0; }static struct led_operations board_demo_led_opr = {.init = board_demo_led_init,.ctl = board_demo_led_ctl,
};struct led_operations *get_board_led_opr(void)
{return &board_demo_led_opr;}
makefile文件如下:
KERN_DIR = /home/john/100ask_imx6ull-sdk/Linux-4.9.88all:make -C $(KERN_DIR) M=`pwd` modules $(CROSS_COMPILE)gcc -o ledtest ledtest.c clean:make -C $(KERN_DIR) M=`pwd` modules cleanrm -rf modules.orderrm -f ledtest# 参考内核源码drivers/char/ipmi/Makefile
# 要想把a.c, b.c编译成ab.ko, 可以这样指定:
# ab-y := a.o b.o
# obj-m += ab.o# leddrv.c board_demo.c 编译成 100ask.ko
100ask_led-y := leddrv.o board_demo.o
obj-m += 100ask_led.o