Linux驱动学习(4-字符设备-自动创建字符设备并读写)

上一节我们主要讲解了手动创建字符设备节点并访问,但是感觉太过于麻烦,因此,我们这一节主要讲解如何自动创建字符设备并进行读写操作,以及讲解编写字符设备驱动的框架
不过在此之前,我们先不上代码,先了解一下基本的概念。

1、设备号dev_t

设备号为设备驱动模块程序在Linux系统中唯一识别号。其为32bits的无符号整数, 一个设备号分成主设备号和次设备号两部分:(Linux2.6版本的设备号)
主号12bits 次号20bits
一般情况下主设备号为一种设备类型,次设备号为这类设备的具体一个设备。
MKDEV(ma,mi);将ma主设备号和次设备号合成一个32bit的完整设备号;
MAJOR(dev);从一个设备号中提取出主设备号;
MINOR(dev);从一个设备号中提取次设备号;

2、struct cdev字符设备控制块

字符设备控制块struct cdev是字符设备的核心,内核就是通过对其的挂接和卸载完成字符设备工作。
这里写图片描述

3、字符设备函数操作集struct file_operations

这里写图片描述
利用struct file_operations声明实体,然后对相应的函数给出实现体,最后将该实体赋给cdev中ops的成员。

4、代码展示

看了些理论感觉很无聊,我们还是先上点代码吧
模块代码
cdevAPI.c

#include "include/kmodlue.h"

//协议声明
MODULE_LICENSE("GPL");

//声明一个节点名作为模块输入参数
//其将在/dev目录下被创建。
static char *DeviceNodeName = "ss";
module_param(DeviceNodeName, charp, 0600);
MODULE_PARM_DESC(DeviceNodeName, "The default value is \"ss\"");



dev_t           dev;                //存放设备号,因该理解为主设备号
struct cdev     cdev;               //字符设备控制块
int             dsn;                //次设备号起始值
int             acount_of_devices;      //声明次设备号数量,即具体设备数量
                                        //To define a pointer of device class 定义一个设备类型指针
struct class    *D_class;
                                            //To define a pointer of device 定义一个设备
struct device   *D_device;
                                                //Show it in(显示在) /proc/devices file
#define DeviceName          "Chr_dev"
                                                    //Show it in(显示在) /sys/class/ dir.
#define DeviceNameClass     "Chr_dev_class"

#define MEM_SIZE 20                 //声明内核内存空间大小
unsigned char *mem_datas;               //内核内存空间指针


//API的打开程序接口
int cdev_open(struct inode *i, struct file *filp)
{
                                    //将ChrDevp指针赋值给filp->private_data,也就是今后,
                                    //函数struct file *参数中的filp->private_data也指向mem_datas所指向的空间。
    filp->private_data = mem_datas;
    printk(KERN_INFO "The device was openned by the AP!\n");
    return 0;
}

//API的关闭程序接口
int cdev_close(struct inode *i, struct file *filp)
{
    filp->private_data = NULL;
    printk(KERN_INFO "The device was closed by the AP!\n");
    return 0;
}





//定义AP程序操作接口
struct file_operations  cdev_fops = {
    .owner = THIS_MODULE,
    .open = cdev_open,
    .release = cdev_close,
};



//mount fuction 挂载函数
static int __init chr_dev_init(void)
{
    int rst = 0;
    dsn = 3;
    acount_of_devices = 1;
    //动态申请设备号
    rst = alloc_chrdev_region(&dev, dsn, acount_of_devices, DeviceName);
    if(rst < 0)
    {
        rst = -1;
        printk(KERN_INFO "Fail:alloc_chrdev_region()\n");
        goto Exit_1;

    }
    //To init the cdev blk 初始化字符控制快
    cdev_init(&cdev,&cdev_fops);
    cdev.owner = THIS_MODULE;
    //添加设备快
    rst = cdev_add(&cdev, dev, dsn+1);
    if(rst < 0)
    {
        rst = -2;
        printk(KERN_INFO "Fail:cdev_add()\n");
        goto Exit_2;
    }
    //创建设备类型
    D_class = class_create(THIS_MODULE, DeviceNameClass);
    if(IS_ERR(D_class))
    {
        rst = -3;
        printk(KERN_INFO "Fail:class_create()\n");
        goto Exit_3;
    }
    //创建设备节点
    D_device = device_create(D_class, NULL, dev, NULL, DeviceNodeName);
    if(IS_ERR(D_device))
    {
        rst = -4;
        printk(KERN_INFO "Fail:device_create()\n");
        goto Exit_4;
    }

    //创建内核数据空间
    mem_datas = kmalloc(MEM_SIZE, GFP_KERNEL);
    if(IS_ERR(mem_datas))
    {
        rst = -5;
        printk(KERN_INFO "Fail:kmalloc() for ChrDevp->mem_datas\n");
        goto Exit_5;
    }
    //Inited is success! and exit. 初始化成功
    //rst = 0;
    printk(KERN_INFO "The device module was mounted by the kernel!,the device number maj:%d and min: %d\n",
            MAJOR(dev), MINOR(dev));
    goto Exit_1;

Exit_5:
    //销毁设备节点
    device_destroy(D_class, dev);

Exit_4:
    //销毁设备类型
    class_destroy(D_class);

Exit_3:
    //删除设备块
    cdev_del(&cdev);

Exit_2:
    //注销设备号
    unregister_chrdev_region(dev, acount_of_devices);


Exit_1://程序退出,初始化结束
    return rst;
}

//umount fuction 卸载函数
static void __exit chr_dev_exit(void)
{
    //释放内核内存空间
    kfree(mem_datas);
    //销毁设备节点
    device_destroy(D_class, dev);
    //销毁设备类型
    class_destroy(D_class);
    //删除设备块
    cdev_del(&cdev);
    //注销设备号
    unregister_chrdev_region(dev, acount_of_devices);
    printk(KERN_INFO "The device module was umounted by the kernel!\n");
}

//封装模块挂载函数
module_init(chr_dev_init);
//封装模块卸载函数
module_exit(chr_dev_exit);

应用程序代码
testap.c

#include<stdio.h>
#include<stdlib.h>
#include <fcntl.h>
int main(int c, char **v)
{
    int fp = open("/dev/ss",O_RDWR);
    if(fp < 0)
    {
        printf("Error,device no exists!\n");    
        return -1;
    }
    printf("Openning success!\n");

    sleep(3);

    printf("Closing success!\n");
    close(fp);
    return 0;
}

Makefile
这里写图片描述

include目录里面的文件
include/cmd.h

#ifndef __CMD_H__
#define __CMD_H__

#define MAGIC   0x34

#define OPEN_LED        1
#define CLOSE_LED       2

#define IO_OPEN_LED     _IO(MAGIC, OPEN_LED)
#define IO_CLOSE_LED    _IO(MAGIC, CLOSE_LED)


#endif

include/kmodule.h

#ifndef __KMODULE_H__
#define __KMODULE_H__
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/ioctl.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/types.h>
#include <linux/slab.h>
#include <asm/uaccess.h>
#endif

由于之前装载、卸载模块已经说明很多次,因此,这里不再进行说明,如果有不明白的地方,请参考以前的教程。

5、函数简要说明

一、动态分配设备号函数:
int alloc_register_region(dev_t *dev, unsigned basemonor, unsigned count, const char *name);
返回值:0表示成功,非0表示失败。
输入参数:
dev,存放获得的设备号,指针方式传入;
baseminor,第一个次设备号,;
count,总共次设备号数量;
name,被分配的设备或驱动名称。

二、创建设备类型目录及相应配置函数:
struct class *class_create(owner, name);
返回值:为struct class指针,可用IS_ERR()函数来判断返回是否成功。
输入参数:
owner ,一般使用THIS_MODULE参数;
name,设备类型名称。
会在/sys/class中创建name参数的目录及一些文件。

三、自动创建设备节点函数:
struct device *device_create(struct class *class, struct device *parent, dev_t devt, void *drvdata, const char *fmt, …);
返回值:struct device *指针,可用IS_ERR()函数来判断返回是否成功。
class,为用class_create()得到的指针;
parent,指向父级设备,一般可以为NULL;
devt,设备号;
drvdata,为回调函数,或携带数据,一般为NULL;
fmt,为设备类型名称。
自动会在/dev目录下创建fmt设备节点。

四、删除设备节点函数:
void device_destroy(struct class *class, dev_t devt);
返回参数:空。
输入参数:
class,要删除设备节点所属的设备类型;
devt,要删除的设备号。

五、删除设备类型函数:
void class_destroy(struct class *cls);
返回值:空。
输入参数:
cls,要删除的设备类型。

六、开发字符设备并读写的框架流程

初始化
1、动态申请设备号 alloc_register_region
2、初始化字符设备块 cdev_init
3、向内核添加设备块 cdev_add
4、创建设备类型 class_create
5、创建设备节点 device_create
6、创建内核数据空间 kmalloc

卸载
1、释放内核内存空间 kfree
2、销毁设备节点 device_destroy
3、销毁设备类型 class_destroy
4、注销设备号 unregister_chrdev_region

我们在insmod模块的时候,模块初始化会自动创建设备节点。通过设备节点,应用程序就可以进行正常的数据操作了。read write open close等等

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值