新版无设备树字符设备驱动
根据学习《正点原子linux驱动开发指南》整理,为了方便,直观,快速编写字符设备驱动程序而构建此基本框架。如果有侵权,联系本人删除。
新版无设备树字符设备驱动较旧版多了动态申请设备号和自动创建设备节点的功能。
此为无设备树下的字符设备驱动,虽然现在普遍用的是有设备树的字符设备程序,但不易新手理解,所以本文分析无设备树下字符设备驱动的框架方便理解字符设备驱动的结构。
基本框架如下,*号之间为要填的内容。下文通过示例代码简要描述基于基本框架驱动程序的编写步骤,每一步都是基于前一步的扩充。
/*引入头文件*/
**************************************************
/*各种头文件如*/
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
**************************************************
/*宏定义*/
**************************************************
/* 设备号个数 */
/* 设备名字 */
/* 宏定义寄存器物理地址 */
/*用于实现用户功能的宏定义*/
**************************************************
/*全局变量,指针定义*/
**************************************************
/* 寄存器映射指针 */
/* 保存设备状态的全局变量 */
/* 实现用户功能的全局变量 */
**************************************************
/* 定义结构体 */
**************************************************
/* 定义cdev设备结构体 */ 必须
/* 定义实现用户功能的结构体 */
**************************************************
/* 创建一个cdev设备结构体 */
**************************************************
struct newchrled_dev newchrled; /* led设备 */必须
**************************************************
/*用户自定义函数*/
**************************************************
/* 定义自定义函数1 */
/* 定义自定义函数2 */
**************************************************
/*定义设备的操作函数*/
**************************************************
open/*打开设备*/
read/*读取数据*/
write/*写入数据*/
release/*关闭/释放设备*/
**************************************************
/* 定义字符设备操作结构体,并用设备的操作函数填充结构体*/
**************************************************
static struct file_operations newchrled_fops={
.owner = THIS_MODULE,
.open = lopen,
.read = read,
.write = write,
.release = release ,
}
**************************************************
/* 定义驱动入口函数 */
**************************************************
/* 初始化,该部分可在驱动入口函数任意位置,也可在open操作函数中 */
/* 1、寄存器地址映射 */
/* 2.配置gpio等寄存器值 */
/* 注册字符设备驱动 */
/* 1、分配设备号 */
/* 2、初始化cdev */
/* 3、添加一个cdev */
/* 4、创建类 */
/* 5、创建设备 */
**************************************************
/* 定义驱动出口函数 */
**************************************************
/*取消映射 */
/*注销字符设备 */
/*1.删除cdev */
/*2.释放设备号*/
/*3.删除设备*/
/*4.删除类*/
**************************************************
/* 定义驱动加载和卸载 */
**************************************************
module_init(led_init);/* 驱动加载 */
module_exit(led_exit);/* 驱动卸载 */
MODULE_LICENSE("test") //添加模块 LICENSE信息
MODULE_AUTHOR("kk") //添加模块作者信息
**************************************************
第一步引入头文件,创建字符设备驱动的加载与卸载
1.引入头文件。
2.创建字符设备驱动的加载与卸载。
3.添加模块信息。
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
/* 驱动入口函数 */
static int __init xxx_init(void)
{
/* 入口函数具体内容 */
return 0;
}
/* 驱动出口函数 */
static void __exit xxx_exit(void)
{
/* 出口函数具体内容 */
}
module_init(xxx_init); //注册模块加载函数
module_exit(xxx_exit); //注册模块卸载函数
MODULE_LICENSE("test") //添加模块 LICENSE信息
MODULE_AUTHOR("kk") //添加模块作者信息
第二步设置字符设备注册与注销中的分配和释放设备号
1.定义设备号个数和名字。
2.创建cdev字符设备结构体。
3.驱动入口函数中调用分配设备号函数
4.驱动出口函数中调用释放设备号函数。
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#define NEWCHRLED_CNT 1 /* 设备号个数 */
#define NEWCHRLED_NAME "newchrled" /* 名字 */
/* 创建一个newchrled设备结构体 */
struct newchrled_dev
{
dev_t devid; /* 设备号 */
struct cdev cdev; /* cdev */
struct class *class; /* 类 */
struct device *device; /* 设备 */
int major; /* 主设备号 */
int minor; /* 次设备号 */
};
struct newchrled_dev newchrled; /* led设备 */
/* 驱动入口函数 */
static int __init xxx_init(void)
{
/* 注册字符设备驱动 */
/* 1、分配设备号 */
if (newchrled.major){ /* 定义了设备号 */
newchrled.devid = MKDEV(newchrled.major, 0);
register_chrdev_region(newchrled.devid, NEWCHRLED_CNT, NEWCHRLED_NAME);
}
else { /* 没有定义设备号 */
alloc_chrdev_region(&newchrled.devid, 0, NEWCHRLED_CNT, NEWCHRLED_NAME); /* 申请设备号 */
newchrled.major = MAJOR(newchrled.devid); /* 获取主设备号 */
newchrled.minor = MINOR(newchrled.devid); /* 获取次设备号 */
}
return 0;
}
/* 驱动出口函数 */
static void __exit led_exit(void)
{
/* 注销字符设备 */
unregister_chrdev_region(newchrled.devid, NEWCHRLED_CNT);/*释放设备号*/
}
module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("test") //添加模块 LICENSE信息
MODULE_AUTHOR("kk") //添加模块作者信息
第三步设置字符设备注册与注销中的cdev的注册和注销
1.定义一个字符设备操作结构体。
2.驱动入口函数中初始化cdev。
3.驱动入口函数中添加cdev。
4.驱动出口函数中删除cdev。
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#define NEWCHRLED_CNT 1 /* 设备号个数 */
#define NEWCHRLED_NAME "newchrled" /* 名字 */
/* 创建一个newchrled设备结构体 */
struct newchrled_dev
{
dev_t devid; /* 设备号 */
struct cdev cdev; /* cdev */
struct class *class; /* 类 */
struct device *device; /* 设备 */
int major; /* 主设备号 */
int minor; /* 次设备号 */
};
struct newchrled_dev newchrled; /* led设备 */
/* 定义一个字符设备操作结构体,用于满足注册字符设备函数的输入参数的要求 后面按要求填充结构体*/
static struct file_operations newchrled_fops;
/* 驱动入口函数 */
static int __init xxx_init(void)
{
/* 注册字符设备驱动 */
/* 1、创建设备号 */
if (newchrled.major){ /* 定义了设备号 */
newchrled.devid = MKDEV(newchrled.major, 0);
register_chrdev_region(newchrled.devid, NEWCHRLED_CNT, NEWCHRLED_NAME);
}
else { /* 没有定义设备号 */
alloc_chrdev_region(&newchrled.devid, 0, NEWCHRLED_CNT, NEWCHRLED_NAME); /* 申请设备号 */
newchrled.major = MAJOR(newchrled.devid); /* 获取主设备号 */
newchrled.minor = MINOR(newchrled.devid); /* 获取次设备号 */
}
/* 2、初始化cdev */
newchrled.cdev.owner = THIS_MODULE; /*THIS_MODULE为默认参数*/
cdev_init(&newchrled.cdev, &newchrled_fops); /*newchrled_fops为操作结构体*/
/* 3、添加一个cdev */
cdev_add(&newchrled.cdev, newchrled.devid, NEWCHRLED_CNT);/*newchrled.devid 设备号由上面函数获取,NEWCHRLED_CNT为设备数量*/
return 0;
}
/* 驱动出口函数 */
static void __exit led_exit(void)
{
/* 注销字符设备 */
cdev_del(&newchrled.cdev);/* 删除cdev */
unregister_chrdev_region(newchrled.devid, NEWCHRLED_CNT);/*删除设备号*/
}
module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("test") //添加模块 LICENSE信息
MODULE_AUTHOR("kk") //添加模块作者信息
第四步设置字符设备注册与注销中的自动创建设备节点功能(创建类和设备)
1.驱动入口函数中创建类。
2.驱动入口函数中创建设备。
3.驱动出口函数中删除类。
4.驱动出口函数中删除设备。
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#define NEWCHRLED_CNT 1 /* 设备号个数 */
#define NEWCHRLED_NAME "newchrled" /* 名字 */
/* 创建一个newchrled设备结构体 */
struct newchrled_dev
{
dev_t devid; /* 设备号 */
struct cdev cdev; /* cdev */
struct class *class; /* 类 */
struct device *device; /* 设备 */
int major; /* 主设备号 */
int minor; /* 次设备号 */
};
struct newchrled_dev newchrled; /* led设备 */
/* 定义一个字符设备操作结构体,用于满足注册字符设备函数的输入参数的要求 后面按要求填充结构体*/
static struct file_operations newchrled_fops;
/* 驱动入口函数 */
static int __init xxx_init(void)
{
/* 注册字符设备驱动 */
/* 1、创建设备号 */
if (newchrled.major){ /* 定义了设备号 */
newchrled.devid = MKDEV(newchrled.major, 0);
register_chrdev_region(newchrled.devid, NEWCHRLED_CNT, NEWCHRLED_NAME);
}
else { /* 没有定义设备号 */
alloc_chrdev_region(&newchrled.devid, 0, NEWCHRLED_CNT, NEWCHRLED_NAME); /* 申请设备号 */
newchrled.major = MAJOR(newchrled.devid); /* 获取主设备号 */
newchrled.minor = MINOR(newchrled.devid); /* 获取次设备号 */
}
/* 2、初始化cdev */
newchrled.cdev.owner = THIS_MODULE; /*THIS_MODULE为默认参数*/
cdev_init(&newchrled.cdev, &newchrled_fops); /*newchrled_fops为操作结构体*/
/* 3、添加一个cdev */
cdev_add(&newchrled.cdev, newchrled.devid, NEWCHRLED_CNT);/*newchrled.devid 设备号由上面函数获取,NEWCHRLED_CNT为设备数量*/
/* 4、创建类 */
newchrled.class = class_create(THIS_MODULE, NEWCHRLED_NAME);
if (IS_ERR(newchrled.class)) {/*类创建出错处理*/
return PTR_ERR(newchrled.class);
}
/* 5、创建设备 */
newchrled.device = device_create(newchrled.class, NULL, newchrled.devid, NULL, NEWCHRLED_NAME);
if (IS_ERR(newchrled.device)) { /*设备创建出错处理*
return PTR_ERR(newchrled.device);
}
return 0;
}
/* 驱动出口函数 */
static void __exit led_exit(void)
{
/* 注销字符设备 */
cdev_del(&newchrled.cdev);/* 删除cdev */
unregister_chrdev_region(newchrled.devid, NEWCHRLED_CNT);/*删除设备号*/
device_destroy(newchrled.class, newchrled.devid);/*删除设备*/
class_destroy(newchrled.class);/*删除类*/
}
module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("test") //添加模块 LICENSE信息
MODULE_AUTHOR("kk") //添加模块作者信息
第五步加入用户操作函数框架打开,读取,写入,关闭的功能
1.定义打开操作函数。
2.定义读取操作函数。
3.定义写入操作函数。
4.定义关闭操作函数。
5.将操作函数填充至设备操作结构体中。
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#define NEWCHRLED_CNT 1 /* 设备号个数 */
#define NEWCHRLED_NAME "newchrled" /* 名字 */
/* 创建一个newchrled设备结构体 */
struct newchrled_dev
{
dev_t devid; /* 设备号 */
struct cdev cdev; /* cdev */
struct class *class; /* 类 */
struct device *device; /* 设备 */
int major; /* 主设备号 */
int minor; /* 次设备号 */
};
struct newchrled_dev newchrled; /* led设备 */
/*用户操作函数*/
/*打开设备*/
static int led_open(struct inode *inode, struct file *filp)
{
filp->private_data = &newchrled; /* 设置私有数据 */
return 0;
}
/*读取数据*/
static ssize_t led_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{
return 0;
}
/*写入数据*/
static ssize_t led_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
return 0;
}
/*关闭/释放设备*/
static int led_release(struct inode *inode, struct file *filp)
{
return 0;
}
/* 定义一个字符设备操作结构体,用于满足注册字符设备函数的输入参数的要求 按要求填充结构体*/
static struct file_operations newchrled_fops={
.owner = THIS_MODULE,
.open = led_open,
.read = led_read,
.write = led_write,
.release = led_release,
}
/* 驱动入口函数 */
static int __init xxx_init(void)
{
/* 注册字符设备驱动 */
/* 1、创建设备号 */
if (newchrled.major){ /* 定义了设备号 */
newchrled.devid = MKDEV(newchrled.major, 0);
register_chrdev_region(newchrled.devid, NEWCHRLED_CNT, NEWCHRLED_NAME);
}
else { /* 没有定义设备号 */
alloc_chrdev_region(&newchrled.devid, 0, NEWCHRLED_CNT, NEWCHRLED_NAME); /* 申请设备号 */
newchrled.major = MAJOR(newchrled.devid); /* 获取主设备号 */
newchrled.minor = MINOR(newchrled.devid); /* 获取次设备号 */
}
/* 2、初始化cdev */
newchrled.cdev.owner = THIS_MODULE; /*THIS_MODULE为默认参数*/
cdev_init(&newchrled.cdev, &newchrled_fops); /*newchrled_fops为操作结构体*/
/* 3、添加一个cdev */
cdev_add(&newchrled.cdev, newchrled.devid, NEWCHRLED_CNT);/*newchrled.devid 设备号由上面函数获取,NEWCHRLED_CNT为设备数量*/
/* 4、创建类 */
newchrled.class = class_create(THIS_MODULE, NEWCHRLED_NAME);
if (IS_ERR(newchrled.class)) {/*类创建出错处理*/
return PTR_ERR(newchrled.class);
}
/* 5、创建设备 */
newchrled.device = device_create(newchrled.class, NULL, newchrled.devid, NULL, NEWCHRLED_NAME);
if (IS_ERR(newchrled.device)) { /*设备创建出错处理*
return PTR_ERR(newchrled.device);
}
return 0;
}
/* 驱动出口函数 */
static void __exit led_exit(void)
{
/* 注销字符设备 */
cdev_del(&newchrled.cdev);/* 删除cdev */
unregister_chrdev_region(newchrled.devid, NEWCHRLED_CNT);/*删除设备号*/
device_destroy(newchrled.class, newchrled.devid);/*删除设备*/
class_destroy(newchrled.class);/*删除类*/
}
module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("test") //添加模块 LICENSE信息
MODULE_AUTHOR("kk") //添加模块作者信息
第六步引入寄存器地址信息并完善用户操作函数打开,读取,写入,关闭的功能
1.宏定义寄存器地址(开发板数据手册中的设备地址),定义寄存器地址映射指针。
2.在字符设备注册和注销中加入初始化寄存器地址映射(将设备地址映射到操作系统中的虚拟地址)。
3.用下面函数实现对寄存器的配置。
用writel(二进制值, 要写入的寄存器地址)函数写入寄存器一个值。
用readl(要读取的寄存器地址)函数读取寄存器的值。
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#define NEWCHRLED_CNT 1 /* 设备号个数 */
#define NEWCHRLED_NAME "newchrled" /* 名字 */
/* 宏定义寄存器物理地址 */
#define CCM_CCGR1_BASE (0X020C406C)
#define SW_MUX_GPIO1_IO03_BASE (0X020E0068)
#define SW_PAD_GPIO1_IO03_BASE (0X020E02F4)
#define GPIO1_DR_BASE (0X0209C000)
#define GPIO1_GDIR_BASE (0X0209C004)
/* 映射后的寄存器虚拟地址指针 */
static void __iomem *IMX6U_CCM_CCGR1;
static void __iomem *SW_MUX_GPIO1_IO03;
static void __iomem *SW_PAD_GPIO1_IO03;
static void __iomem *GPIO1_DR;
static void __iomem *GPIO1_GDIR;
/* 创建一个newchrled设备结构体 */
struct newchrled_dev
{
dev_t devid; /* 设备号 */
struct cdev cdev; /* cdev */
struct class *class; /* 类 */
struct device *device; /* 设备 */
int major; /* 主设备号 */
int minor; /* 次设备号 */
};
struct newchrled_dev newchrled; /* led设备 */
/*用户操作函数*/
/*打开设备*/
static int led_open(struct inode *inode, struct file *filp)
{
filp->private_data = &newchrled; /* 设置私有数据 */
return 0;
}
/*读取数据*/
static ssize_t led_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{
return 0;
}
/*写入数据*/
static ssize_t led_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
return 0;
}
/*关闭/释放设备*/
static int led_release(struct inode *inode, struct file *filp)
{
return 0;
}
/* 定义一个字符设备操作结构体,用于满足注册字符设备函数的输入参数的要求 后面按要求填充结构体*/
static struct file_operations newchrled_fops={
.owner = THIS_MODULE,
.open = led_open,
.read = led_read,
.write = led_write,
.release = led_release,
}
/* 驱动入口函数 */
static int __init xxx_init(void)
{
/* 初始化LED */
/* 1、寄存器地址映射 */
IMX6U_CCM_CCGR1 = ioremap(CCM_CCGR1_BASE, 4);
SW_MUX_GPIO1_IO03 = ioremap(SW_MUX_GPIO1_IO03_BASE, 4);
SW_PAD_GPIO1_IO03 = ioremap(SW_PAD_GPIO1_IO03_BASE, 4);
GPIO1_DR = ioremap(GPIO1_DR_BASE, 4);
GPIO1_GDIR = ioremap(GPIO1_GDIR_BASE, 4);
/* 注册字符设备驱动 */
/* 1、创建设备号 */
if (newchrled.major){ /* 定义了设备号 */
newchrled.devid = MKDEV(newchrled.major, 0);
register_chrdev_region(newchrled.devid, NEWCHRLED_CNT, NEWCHRLED_NAME);
}
else { /* 没有定义设备号 */
alloc_chrdev_region(&newchrled.devid, 0, NEWCHRLED_CNT, NEWCHRLED_NAME); /* 申请设备号 */
newchrled.major = MAJOR(newchrled.devid); /* 获取主设备号 */
newchrled.minor = MINOR(newchrled.devid); /* 获取次设备号 */
}
/* 2、初始化cdev */
newchrled.cdev.owner = THIS_MODULE; /*THIS_MODULE为默认参数*/
cdev_init(&newchrled.cdev, &newchrled_fops); /*newchrled_fops为操作结构体*/
/* 3、添加一个cdev */
cdev_add(&newchrled.cdev, newchrled.devid, NEWCHRLED_CNT);/*newchrled.devid 设备号由上面函数获取,NEWCHRLED_CNT为设备数量*/
/* 4、创建类 */
newchrled.class = class_create(THIS_MODULE, NEWCHRLED_NAME);
if (IS_ERR(newchrled.class)) {/*类创建出错处理*/
return PTR_ERR(newchrled.class);
}
/* 5、创建设备 */
newchrled.device = device_create(newchrled.class, NULL, newchrled.devid, NULL, NEWCHRLED_NAME);
if (IS_ERR(newchrled.device)) { /*设备创建出错处理*/
return PTR_ERR(newchrled.device);
}
return 0;
}
/* 驱动出口函数 */
static void __exit led_exit(void)
{
/* 取消映射 */
iounmap(IMX6U_CCM_CCGR1);
iounmap(SW_MUX_GPIO1_IO03);
iounmap(SW_PAD_GPIO1_IO03);
iounmap(GPIO1_DR);
iounmap(GPIO1_GDIR);
/* 注销字符设备 */
cdev_del(&newchrled.cdev);/* 删除cdev */
unregister_chrdev_region(newchrled.devid, NEWCHRLED_CNT);/*删除设备号*/
device_destroy(newchrled.class, newchrled.devid);/*删除设备*/
class_destroy(newchrled.class);/*删除类*/
}
module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("test") //添加模块 LICENSE信息
MODULE_AUTHOR("kk") //添加模块作者信息
第七步初始化gpio配置,自定义用户函数从而实现要实现的功能
1.在注册设备中加入gpio的初始化配置。
2.定义用户宏定义,全局变量,用户自定义函数。
3.将自定义用户函数加入到打开,读取,写入,关闭的操作函数中。
在read操作函数中用copy_to_user(void user *to, const void *from, unsigned long n)函数来完成内核空间的数据(from)到用户空间的数据(to)的复制。
在write操作函数中用copy_from_user(const void *writebuf, voiduser *buf, unsigned long cnt)将用户空间的数据(buf)复制到 writebuf这个内核空间的数据中。
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#define NEWCHRLED_CNT 1 /* 设备号个数 */
#define NEWCHRLED_NAME "newchrled" /* 名字 */
#define LEDOFF 0 /* 关灯 */
#define LEDON 1 /* 开灯 */
/* 宏定义寄存器物理地址 */
#define CCM_CCGR1_BASE (0X020C406C)
#define SW_MUX_GPIO1_IO03_BASE (0X020E0068)
#define SW_PAD_GPIO1_IO03_BASE (0X020E02F4)
#define GPIO1_DR_BASE (0X0209C000)
#define GPIO1_GDIR_BASE (0X0209C004)
/* 映射后的寄存器虚拟地址指针 */
static void __iomem *IMX6U_CCM_CCGR1;
static void __iomem *SW_MUX_GPIO1_IO03;
static void __iomem *SW_PAD_GPIO1_IO03;
static void __iomem *GPIO1_DR;
static void __iomem *GPIO1_GDIR;
/* 创建一个newchrled设备结构体 */
struct newchrled_dev
{
dev_t devid; /* 设备号 */
struct cdev cdev; /* cdev */
struct class *class; /* 类 */
struct device *device; /* 设备 */
int major; /* 主设备号 */
int minor; /* 次设备号 */
};
struct newchrled_dev newchrled; /* led设备 */
/*用户自定义函数*/
void led_switch(u8 sta)
{
u32 val = 0;
if(sta == LEDON) {
val = readl(GPIO1_DR);
val &= ~(1 << 3);
writel(val, GPIO1_DR);
}else if(sta == LEDOFF) {
val = readl(GPIO1_DR);
val|= (1 << 3);
writel(val, GPIO1_DR);
}
}
/*用户操作函数*/
/*打开设备*/
static int led_open(struct inode *inode, struct file *filp)
{
filp->private_data = &newchrled; /* 设置私有数据 */
return 0;
}
/*读取数据*/
static ssize_t led_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{
int retvalue;
unsigned char databuf[1];
databuf[1]=0;
retvalue = copy_to_user(buf, databuf, cnt);
return 0;
}
/*写入数据*/
static ssize_t led_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
int retvalue;
unsigned char databuf[1];
unsigned char ledstat;
retvalue = copy_from_user(databuf, buf, cnt);
if(retvalue < 0) {
printk("kernel write failed!\r\n");
return -EFAULT; 119 }
ledstat = databuf[0]; /* 获取状态值 */
if(ledstat == LEDON) {
led_switch(LEDON); /* 打开LED灯 */
} else if(ledstat == LEDOFF) {
led_switch(LEDOFF); /* 关闭LED灯 */
}
return 0;
}
/*关闭/释放设备*/
static int led_release(struct inode *inode, struct file *filp)
{
return 0;
}
/* 定义一个字符设备操作结构体,用于满足注册字符设备函数的输入参数的要求 后面按要求填充结构体*/
static struct file_operations newchrled_fops={
.owner = THIS_MODULE,
.open = led_open,
.read = led_read,
.write = led_write,
.release = led_release,
}
/* 驱动入口函数 */
static int __init xxx_init(void)
{
/* 初始化LED */
/* 1、寄存器地址映射 */
IMX6U_CCM_CCGR1 = ioremap(CCM_CCGR1_BASE, 4);
SW_MUX_GPIO1_IO03 = ioremap(SW_MUX_GPIO1_IO03_BASE, 4);
SW_PAD_GPIO1_IO03 = ioremap(SW_PAD_GPIO1_IO03_BASE, 4);
GPIO1_DR = ioremap(GPIO1_DR_BASE, 4);
GPIO1_GDIR = ioremap(GPIO1_GDIR_BASE, 4);
/* 2、使能GPIO1时钟 */
val = readl(IMX6U_CCM_CCGR1);
val &= ~(3 << 26); /* 清楚以前的设置 */
val |= (3 << 26); /* 设置新值 */
writel(val, IMX6U_CCM_CCGR1);
/* 3、设置GPIO1_IO03的复用功能,将其复用为
* GPIO1_IO03,最后设置IO属性。
*/
writel(5, SW_MUX_GPIO1_IO03);
/* 寄存器SW_PAD_GPIO1_IO03设置IO属性 */
writel(0x10B0, SW_PAD_GPIO1_IO03);
/* 4、设置GPIO1_IO03为输出功能 */
val = readl(GPIO1_GDIR);
val &= ~(1 << 3); /* 清除以前的设置 */
val |= (1 << 3); /* 设置为输出 */
writel(val, GPIO1_GDIR);
/* 5、默认关闭LED */
val = readl(GPIO1_DR);
val |= (1 << 3);
writel(val, GPIO1_DR);
/* 注册字符设备驱动 */
/* 1、创建设备号 */
if (newchrled.major){ /* 定义了设备号 */
newchrled.devid = MKDEV(newchrled.major, 0);
register_chrdev_region(newchrled.devid, NEWCHRLED_CNT, NEWCHRLED_NAME);
}
else { /* 没有定义设备号 */
alloc_chrdev_region(&newchrled.devid, 0, NEWCHRLED_CNT, NEWCHRLED_NAME); /* 申请设备号 */
newchrled.major = MAJOR(newchrled.devid); /* 获取主设备号 */
newchrled.minor = MINOR(newchrled.devid); /* 获取次设备号 */
}
/* 2、初始化cdev */
newchrled.cdev.owner = THIS_MODULE; /*THIS_MODULE为默认参数*/
cdev_init(&newchrled.cdev, &newchrled_fops); /*newchrled_fops为操作结构体*/
/* 3、添加一个cdev */
cdev_add(&newchrled.cdev, newchrled.devid, NEWCHRLED_CNT);/*newchrled.devid 设备号由上面函数获取,NEWCHRLED_CNT为设备数量*/
/* 4、创建类 */
newchrled.class = class_create(THIS_MODULE, NEWCHRLED_NAME);
if (IS_ERR(newchrled.class)) {/*类创建出错处理*/
return PTR_ERR(newchrled.class);
}
/* 5、创建设备 */
newchrled.device = device_create(newchrled.class, NULL, newchrled.devid, NULL, NEWCHRLED_NAME);
if (IS_ERR(newchrled.device)) { /*设备创建出错处理*/
return PTR_ERR(newchrled.device);
}
return 0;
}
/* 驱动出口函数 */
static void __exit led_exit(void)
{
/* 取消映射 */
iounmap(IMX6U_CCM_CCGR1);
iounmap(SW_MUX_GPIO1_IO03);
iounmap(SW_PAD_GPIO1_IO03);
iounmap(GPIO1_DR);
iounmap(GPIO1_GDIR);
/* 注销字符设备 */
cdev_del(&newchrled.cdev);/* 删除cdev */
unregister_chrdev_region(newchrled.devid, NEWCHRLED_CNT);/*删除设备号*/
device_destroy(newchrled.class, newchrled.devid);/*删除设备*/
class_destroy(newchrled.class);/*删除类*/
}
module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("test") //添加模块 LICENSE信息
MODULE_AUTHOR("kk") //添加模块作者信息
下一步工作指引
到此完成驱动程序的编写
下一步编译生成驱动程序文件
再一步加载驱动程序给linux内核
再下一步是应用程序的编写
在应用程序中的设置:
1.open该驱动程序文件。
2.准备好要发送的数据或要接受的数据存放的位置。
3.调用内核的read或write函数并填入上面准备好的内容。
4.收到返回的参数并执行下一步操作。
应用程序在调用内核的read或write函数后
内核将调用驱动程序的read或write操作函数实现对硬件设备寄存器的自定义操作。
驱动程序通过copy_to_user或copy_from_user实现用户空间的数据和系统空间的数据的通信。
从而返回给应用程序相关数据。