一. 简介
前面一篇文章简单了解了,新字符设备驱动框架涉及的函数。文章地址如下:
本文来使用 新字符设备驱动框架来实现字符设备的注册与卸载。
二. 新的字符设备驱动框架搭建
1. 创建工程
首先,创建文件夹。这里我的驱动代码都放在 ubuntu系统 /home/wangtian/zhengdian_Linux目录下,进入 /Linux_Drivers 目录下,创建文件夹为 3_newchrled:
wangtian@wangtian-virtual-machine:~/zhengdian_Linux/Linux_Drivers$ mkdir 3_newchrled
将 2_led工程下的 .vscode及其以下文件,拷贝到 3_newchrled 文件夹下(.vscode及其以下文件中配置了 所调用的 Linux内核源码路径):
wangtian@wangtian-virtual-machine:~/zhengdian_Linux/Linux_Drivers/3_newchrled$ cp ../2_led/.vscode/ ./ -rf
将 2_led工程下的 Makefile文件拷贝到 该工程下:
wangtian@wangtian-virtual-machine:~/zhengdian_Linux/Linux_Drivers/3_newchrled$ cp ../2_led/Makefile ./
更改 Makefile文件中涉及编译目标名 obj-m的所指向的名:
obj-m := newchrled.o
通过 vscode打开 3_newchrled 文件夹。在 3_newchrled文件夹下创建 newchrled.c 文件。
可以参考 2_led实验的代码,在 newchrled.c加入 头文件, 寄存器的物理地址,映射后的虚拟地址指针,以及 led的开关灯宏:
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/io.h>
//寄存器的物理地址
#define CCM_CCGR1_BASE (0X020C406C)
#define SW_MUX_GPIO1_IO03_BASE (0X020E0068)
#define SW_PAD_GPIO1_IO03_BASE (0X020E02F4)
#define GPIO1_GDIR_BASE (0X0209C004)
#define GPIO1_DR_BASE (0X0209C000)
//地址映射后寄存器的虚拟地址指针
static void __iomem * IMX6ULL_CCM_CCGR1;
static void __iomem * IMX6ULL_SW_MUX_GPIO1_IO03;
static void __iomem * IMX6ULL_SW_PAD_GPIO01_IO03;
static void __iomem * IMX6ULL_GDIR;
static void __iomem * IMX6ULL_DR;
#define LED_OFF 0 //关闭Led灯
#define LED_ON 1 //打开Led灯
2. 新的字符设备驱动框架搭建
可以参考 Linux内核源码(即 NXP官方提供的)中,新字符设备驱动框架代码,来编写 这里的设备驱动代码。
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/cdev.h>
//寄存器的物理地址
#define CCM_CCGR1_BASE (0X020C406C)
#define SW_MUX_GPIO1_IO03_BASE (0X020E0068)
#define SW_PAD_GPIO1_IO03_BASE (0X020E02F4)
#define GPIO1_GDIR_BASE (0X0209C004)
#define GPIO1_DR_BASE (0X0209C000)
#if 0
//地址映射后寄存器的虚拟地址指针
static void __iomem * IMX6ULL_CCM_CCGR1;
static void __iomem * IMX6ULL_SW_MUX_GPIO1_IO03;
static void __iomem * IMX6ULL_SW_PAD_GPIO01_IO03;
static void __iomem * IMX6ULL_GDIR;
static void __iomem * IMX6ULL_DR;
#endif
#define NEWCHRLED "newchrled" //设备名
#define NEWCHRLED_COUNT 1 //设备个数
#define LED_OFF 0 //关闭Led灯
#define LED_ON 1 //打开Led灯
//Led设备结构体
struct newchrled_dev{
struct cdev led_cdev;
dev_t dev_id; //设备号
int major; //主设备号
int minor; //次设备号
};
//字符设备的函数集
const struct file_operations fops = {
.owner = THIS_MODULE,
};
struct newchrled_dev newchr_led;
/*驱动模块入口函数 */
static int __init newchrled_init(void)
{
int ret = 0;
printk("newchrled_init!\r\n");
newchr_led.major = 0;
/* 注销字符设备 */
//设备号的分配
if(newchr_led.major) //给定主设备号
{
newchr_led.dev_id = MKDEV(newchr_led.major, 0);
ret = register_chrdev_region(newchr_led.dev_id, NEWCHRLED_COUNT, NEWCHRLED);
}
else{ //没有给定主设备号
ret = alloc_chrdev_region(&(newchr_led.dev_id), 0, NEWCHRLED_COUNT, NEWCHRLED);
newchr_led.major = MAJOR(newchr_led.dev_id);
newchr_led.minor = MINOR(newchr_led.dev_id);
}
if (ret < 0) {
printk("register-chrdev failed!\n");
return -1;
}
//初始化设备
newchr_led.led_cdev.owner = THIS_MODULE;
cdev_init(&newchr_led.led_cdev, &fops);
//注册设备
cdev_add(&newchr_led.led_cdev, newchr_led.dev_id, NEWCHRLED_COUNT);
return 0;
}
/*驱动模块出口函数 */
static void __exit newchrled_exit(void)
{
printk("newchrled_eixt!\r\n");
/* 注销字符设备 */
//删除设备
cdev_del(&newchr_led.led_cdev);
//注销设备号
unregister_chrdev_region(newchr_led.dev_id, NEWCHRLED_COUNT);
}
/* 注册与卸载驱动设备 */
module_init(newchrled_init);
module_exit(newchrled_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("LingXueWu");
新的字符设备驱动框架代码已经编写好。
编译驱动代码,可以正常编译生成 驱动文件 newchr_led.ko。下一篇加入 Led灯的函数操作集。