大型linux驱动项目框架基础
文章目录
声明:尝试阶段,欢迎有志之士提出建议,或者共同完善
联系方式:qq:1614811057
博客目前状态:持续更新中
1.主入口
makefile
arch ?= x86
modname ?= hello_drv
ifeq ($(arch),arm)
KERNELDIR := /home/psd/code_space/100ask_imx6ull-sdk/Linux-4.9.88
else
KERNELDIR := /lib/modules/$(shell uname -r)/build/
endif
PWD := $(shell pwd)
# ccflags-y += -I$(PWD)/../irq_sr501
ccflags-y += -I/home/psd/code_space/100ask_imx6ull-sdk/my_project_space/project_test/irq/irq_sr501
all:
make -C $(KERNELDIR) M=$(PWD) modules
clean:
make -C $(KERNELDIR) M=$(PWD) clean
obj-m += $(modname).o
obj-m += irq_main.o
obj-m += irq_sr501.o
main.c
#include "irq_main.h"
module_t mod_table[] = {
{ irq_sr501_init, irq_sr501_exit }, // 定义函数指针数组,用于存储模块初始化和退出函数的地址
};
sub_module_t kmd_module_table[] = {
{
.name = "irq_sr501",
.code_num = 0,
.sub_component = &mod_table[0]
},
};
static int irq_main_init(void)
{
int ret = 0;
int i = 0;
// 对模块进行初始化,遍历子模块数组,并依次调用其中存储的函数指针
for (i = 0; i < ARRAY_SIZE(kmd_module_table); i++) {
if (kmd_module_table[i].sub_component->init != NULL) {
ret = (int)kmd_module_table[i].sub_component->init();
printk("sucess to initialize module: %s %d %d\n", kmd_module_table[i].name, kmd_module_table[i].code_num, i);
if (ret != 0) {
// 如果初始化函数返回错误,则打印错误信息并返回
printk(KERN_ERR "Failed to initialize module: %s %d %d\n", kmd_module_table[i].name, kmd_module_table[i].code_num, i);
return ret;
}
}
}
return ret;
}
static void irq_main_exit(void)
{
int i = 0;
// 对模块进行退出操作,逆序遍历子模块数组,并依次调用其中存储的函数指针
for (i = ARRAY_SIZE(kmd_module_table) - 1; i >= 0; i--) {
if (kmd_module_table[i].sub_component->exit != NULL) {
kmd_module_table[i].sub_component->exit();
printk("sucess to initialize module: %s %d %d\n", kmd_module_table[i].name, kmd_module_table[i].code_num, i);
}
}
}
module_init(irq_main_init); // 设置模块初始化函数
module_exit(irq_main_exit); // 设置模块退出函数
MODULE_LICENSE("GPL"); // 设置模块许可证
main.h
#ifndef __IRQ_MAIN_H__
#define __IRQ_MAIN_H__
#include "irq_sr501.h"
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/fcntl.h>
#include <linux/fs.h>
#include <linux/gfp.h>
#include <linux/gpio/consumer.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/kernel.h>
#include <linux/kmod.h>
#include <linux/major.h>
#include <linux/module.h>
#include <linux/of_gpio.h>
#include <linux/of_irq.h>
#include <linux/platform_device.h>
#include <linux/poll.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/slab.h>
#include <linux/stat.h>
#include <linux/timer.h>
#include <linux/tty.h>
typedef struct kmd_module {
int (*init)(void);
void (*exit)(void);
} module_t;
typedef struct kmd_sub_module {
char* name;
int code_num;
module_t* sub_component;
} sub_module_t;
static int irq_main_init(void);
static void irq_main_exit(void);
#endif
2.驱动模块
2.1温度中断传感器sr501
路径:
/home/psd/code_space/100ask_imx6ull-sdk/my_project_space/project_test/irq/irq_sr501
.c
#include "irq_sr501.h"
static int __init irq_sr501_init(void)
{
printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
return 0;
}
static void __exit irq_sr501_exit(void)
{
printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
}
.h
#ifndef __IRQ_SR501_H__
#define __IRQ_SR501_H__
#include <linux/module.h>
#include <linux/poll.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 <linux/gpio/consumer.h>
#include <linux/platform_device.h>
#include <linux/of_gpio.h>
#include <linux/of_irq.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/slab.h>
#include <linux/fcntl.h>
#include <linux/timer.h>
static int __init irq_sr501_init(void);
static void __exit irq_sr501_exit(void);
#endif
目前状态:
无法加载sr_501文件
从下面的实验已经可以找到问题所在了,但是这一块不急,先在同一目录下多集成几个模块之后再扩大规模
3.最新进展:
同目录下的不同模块集成已完成,
正在完善主入口文件里面的基础配套操作
3.1文件目录结构介绍
3.1.1目录:
3.1.2文件:
led_ctrl.h:
#ifndef __LED_CTRL_H__
#define __LED_CTRL_H__
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/uaccess.h>
#define LED_NAME "led"
#define PATH_DTS_LED "/leds/led0"
#define KMD_ERR(str) \
printk("%s %s line: %d %s \n", __FILE__, __FUNCTION__, __LINE__, str);
typedef struct led_ctrl {
char *dev_name;
struct device_node *led_node;
int led_num;
} led_ctrl_t;
void test(void);
int led_ctrl_init(led_ctrl_t * led);
int led_ctrl_exit(led_ctrl_t * led);
#endif
led_ctrl.c:
#include"led_ctrl.h"
// led_ctrl_t led =
// {
// .dev_name = LED_NAME,
// };
void test(void)
{
KMD_ERR("led_ctrl_init 测试");
}
int led_ctrl_init(led_ctrl_t * led)
{
// led->led_node = of_find_node_by_path(PATH_DTS_LED);
KMD_ERR("led_ctrl_init 测试");
return 0;
}
int led_ctrl_exit(led_ctrl_t * led)
{
KMD_ERR("led_ctrl_exit 测试");
return 0;
}
irq_base.h:
#ifndef __IRQ_BASE_H__
#define __IRQ_BASE_H__
#include <linux/module.h>
#include <linux/poll.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 <linux/gpio/consumer.h>
#include <linux/platform_device.h>
#include <linux/of_gpio.h>
#include <linux/of_irq.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/slab.h>
#include <linux/fcntl.h>
#include <linux/timer.h>
#define KMD_ERR(str) \
printk("%s %s line: %d %s \n", __FILE__, __FUNCTION__, __LINE__, str);
typedef struct irq_base
{
unsigned int major;
const char *name;
struct class *class;
struct device * device;
} irq_base_t;
// 字符设备初始化
static int irq_cdev_init(void);
#endif
irq_base.c
#include "irq_base.h"
#include "irq_sr501.h"
#define IRQ_BASE_NAME "IRQ_BASE"
static irq_base_t irq_base = {
.name = IRQ_BASE_NAME,
};
static int irq_base_open(struct inode *inode, struct file *file)
{
int ret = 0;
printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
return -ret;
}
static ssize_t irq_base_read(struct file *file, char __user *buf,
size_t count, loff_t *ppos)
{
int ret = 0;
printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
return ret;
}
static ssize_t irq_base_write(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
int ret = 0;
printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
return ret;
}
static int irq_base_release(struct inode *inode, struct file *file)
{
printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
return 0;
}
static const struct file_operations irq_base_fops = {
.owner = THIS_MODULE,
.open = irq_base_open,
.read = irq_base_read,
.write = irq_base_write,
.release = irq_base_release,
};
static int irq_cdev_init(void)
{
// 在/proc/devices文件中添加对应的字符设备信息和主设备号
irq_base.major = register_chrdev(0, irq_base.name, &irq_base_fops);
if (irq_base.major < 0)
{
KMD_ERR("irq_base.major get ERR");
goto ERR_IO;
}
// 在/sys/class/目录下添加了一个IRQ_BASE的目录,
irq_base.class = class_create(THIS_MODULE, irq_base.name);
if(IS_ERR(irq_base.class))
{
KMD_ERR("irq_base.class get ERR");
return PTR_ERR(irq_base.class);
goto class_ERR;
}
irq_base.device = device_create(irq_base.class,NULL, MKDEV(irq_base.major,0), NULL, "%s", irq_base.name);
if(IS_ERR(irq_base.device))
{
KMD_ERR("irq_base.device get ERR");
return PTR_ERR(irq_base.device);
goto device_ERR;
}
return 0;
device_ERR:
class_destroy(irq_base.class);
class_ERR:
unregister_chrdev(irq_base.major, irq_base.name);
ERR_IO:
return -EIO; //没有这样的设备或地址
}
/* 在入口函数 */
static int __init irq_base_init(void)
{
int ret = 0;
// printk(KERN_ERR "irq_sr501_init 测试\n");
if(irq_sr501_init())
{
printk(KERN_ERR "irq_sr501_init ERR\n");
return -EINVAL;
}
if((ret = irq_cdev_init()) != 0)
{
KMD_ERR("irq_cdev_init ERR");
return ret;
}
printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
return 0;
}
/* 有入口函数就应该有出口函数:卸载驱动程序时,就会去调用这个出口函数
*/
static void __exit irq_base_exit(void)
{
device_destroy(irq_base.class, MKDEV(irq_base.major, 0));
class_destroy(irq_base.class);
unregister_chrdev(irq_base.major, irq_base.name);
irq_sr501_exit();
printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
}
module_init(irq_base_init);
module_exit(irq_base_exit);
MODULE_DESCRIPTION("irq_base_driver");
MODULE_LICENSE("GPL");
irq_sr501.h
#ifndef __IRQ_SR501_H__
#define __IRQ_SR501_H__
#include <linux/module.h>
#include <linux/poll.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 <linux/gpio/consumer.h>
#include <linux/platform_device.h>
#include <linux/of_gpio.h>
#include <linux/of_irq.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/slab.h>
#include <linux/fcntl.h>
#include <linux/timer.h>
int __init irq_sr501_init(void);
void __exit irq_sr501_exit(void);
#endif
irq_sr501.c
#include "irq_sr501.h"
/* 在入口函数 */
int __init irq_sr501_init(void)
{
printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
return 0;
}
/* 有入口函数就应该有出口函数:卸载驱动程序时,就会去调用这个出口函数
*/
void __exit irq_sr501_exit(void)
{
printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
}
MODULE_DESCRIPTION("irq_sr501_driver");
MODULE_LICENSE("GPL");
makefile
arch ?= arm
modname ?= irq_base
PWD := $(shell pwd)
TARGET := IRQ_MAIN
INCLUDE_DIR := other_module
CFLAGS_DIR := $(PWD)/$(INCLUDE_DIR)
# 定义额外的编译选项,EXTRA_CFLAGS 变量用于指定额外的编译选项,可以通过它向编译器传递自定义的选项
EXTRA_CFLAGS += -I$(CFLAGS_DIR)
ifeq ($(arch),arm)
KERNELDIR := /home/psd/code_space/100ask_imx6ull-sdk/Linux-4.9.88
else
KERNELDIR := /lib/modules/$(shell uname -r)/build/
endif
$(TARGET)-objs := ./other_module/led_ctrl.o irq_sr501.o irq_base.o #依赖的中间文件
obj-m += $(TARGET).o #生成最终TARGET.ko文件
# $(CFLAGS) INCDIR=$(PWD)/$(INCLUDE_DIR)
all:
make -C $(KERNELDIR) M=$(PWD) EXTRA_CFLAGS='$(EXTRA_CFLAGS)' modules
@echo "Copying module files to ~/nfs_rootfs/"
@if [ -d ~/nfs_rootfs ]; then \
if [ -e ~/nfs_rootfs/*.ko -o -e ~/nfs_rootfs/*.sh ]; then \
rm -f ~/nfs_rootfs/*.ko ~/nfs_rootfs/*.sh; \
fi; \
if [ -e ./*.ko ]; then \
cp -f ./*.ko ~/nfs_rootfs/; \
fi; \
if [ -e ./*.sh ]; then \
cp -f ./*.sh ~/nfs_rootfs/; \
fi; \
fi
clean:
make -C $(KERNELDIR) M=$(PWD) clean
#obj-m是模块化编译会生成.ko文件,obj-y则是编译进内核不会生成.ko文件
#-objs代表需要编译的依赖中间.o文件
op_mod.sh
lsmod
dmesg -c
rmmod *.ko
lsmod
dmesg
# 删除完之后打印下
insmod *.ko
dmesg
cat /proc/devices
ls /sys/class
ls /dev
3.2调试进展
已经可以调用其他目录下驱动模块代码