字符设备驱动框架梳理

1、常用头文件包含

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/cdev.h>
#include <linux/device.h>

2、驱动初始化函数和驱动卸载函数及注册

//初始化和卸载
​
static int __init newchrled_init(void)
​
static void __exit newchrled_exit(void)
​
//注册
​
module_init(newchrled_init);
module_exit(newchrled_exit);
​
// 版权信息
MODULE_LICENSE("GPL");
MODULE_AUTHOR("zgf");

2.1驱动设备结构体以及实例化

// LED设备结构体
struct newchrled_dev
{
    struct cdev cdev;       // 字符设备
    struct class *class;    // 类
    struct device *device;  // 设备
    dev_t devid;            // 设备号
    int major;              // 主设备号
    int minor;              // 次设备号
​
};
struct newchrled_dev newchrled; // led设备

2.2驱动初始化函数和驱动卸载函数

2.2.1地址映射(可选) vir_addr = ioremap(psy_addr,byte_size)

物理地址宏定义格式:#define CCM_CCGR1_BASE (0x020c406c)

虚拟地址定义格式:static void __iomem *IMX6U_CCM_CCGR1;

地址反映射 iounmap(vir_addr)

2.2.2设备初始化

如:寄存器操作,时钟,复用,电气属性,输入输出模式,初始高低电平

对于连续bit的操作可采用读改写方式。使用readl和writel访问虚拟地址映射空间

val = readl(IMX6U_CCM_CCGR1); // 读
val &= ~(3 << 26);            // 改
val |= (3 << 26);
writel(val, IMX6U_CCM_CCGR1); // 写

2.2.3分配设备号(分给定主设备号和不给定主设备号)(带返回值)
if(newchrled.major)  // 给定主设备号
{
    newchrled.devid = MKDEV(newchrled.major, 0);
    ret = register_chrdev_region(newchrled.devid, NEWCHRLED_COUNT, NEWCHRLED_NAME);
}
else                // 未给定主设备号
{
        ret = alloc_chrdev_region(&newchrled.devid,0,NEWCHRLED_COUNT,NEWCHRLED_NAME);
        newchrled.major = MAJOR(newchrled.devid);
        newchrled.minor = MINOR(newchrled.devid);
}
释放设备号 unregister_chrdev_region(newchrled.devid, NEWCHRLED_COUNT);

2.2.4 字符设备结构体cdev

表示一个字符设备 struct cdev cdev; // 字符设备

1、字符设备结构体初始化

cdev_init(&newchrled.cdev, &newchrled_fops);

1.1添加file_operations结构体

static const struct file_operations newchrled_fops =
{
    .owner   = THIS_MODULE,
    .open    = newchrled_open,
    .release = newchrled_release,
    .write   = newchrled_write,
};

常用的操作函数原型

static int test_open(struct inode *inode, struct file *filp)
​
//open函数中可添加文件私有数据,如:
filp->private_data = &newchrled;
​
​
​
static int test_release(struct inode *inode, struct file *filp)
​
static ssize_t test_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
// 添加私有数据后,可以在其他操作中间接操作设备,更加安全。如:
 struct newchrled_dev *dev = (struct newchrled_dev*)filp->private_data;
​
​
static ssize_t test_write(struct file *filp, const char __user *buf, size_t count, loff_t *ppos)

2、将字符设备添加到linux系统(带返回值)

ret = cdev_add(&newchrled.cdev, newchrled.devid, NEWCHRLED_COUNT); 

3、卸载驱动时,从内核中删除字符设备

 cdev_del(&newchrled.cdev);

驱动卸载顺序:

删除字符设备---->释放设备号---->摧毁设备----->摧毁类(暂用)

使用与注册顺序相反的卸载顺序试了一下也能用,但不排除运气成分:

摧毁设备----->摧毁类---->删除字符设备---->释放设备号

2.2.5 自动创建设备节点

1、类创建(带返回值)

 newchrled.class = class_create(THIS_MODULE,NEWCHRLED_NAME);

异常处理方法

if(IS_ERR(newchrled.class))
    {
        printk("fail class_creat!\r\n");
        ret = PTR_ERR(newchrled.class);
        goto fail_class;
    }

2、设备创建(带返回值)

newchrled.device = device_create(newchrled.class,NULL,newchrled.devid,NULL,NEWCHRLED_NAME);

3、设备摧毁

 device_destroy(newchrled.class,newchrled.devid);

4、类摧毁

 class_destroy(newchrled.class);

对于带返回值的注册函数,中间要穿插异常处理方法,

异常点1,异常点2.......异常点n

处理n, 处理n-1...........处理1

3、测试函数

1、常用的头文件包含

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>

2、int main(int argc, char *argv[])

argc:参数个数

*argv[] 参数

argv[]参数是字符串形式,需要通过atoi函数转换成int形式,用于判断

测试函数中的

open,close,read,write函数与内核中的.read,.release,.read,.write函数对应,即用户间接调用内核程序

open一个文件会得到文件描述符fd,以后的read,write,close操作均对这个操作符执行表示操作这个文件

fd为负数,-1是不正常的状态。

read,write,close均带有返回值,为负数时不正常的状况,可以输出提示信息,更好定位错误位置。

  • 5
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值