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

原创 2016年05月31日 16:47:13

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

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等等

linux设备驱动第三篇:写一个简单的字符设备驱动

在linux设备驱动第一篇:设备驱动程序简介中简单介绍了字符驱动,本篇简单介绍如何写一个简单的字符设备驱动。本篇借鉴LDD中的源码,实现一个与硬件设备无关的字符设备驱动,仅仅操作从内核中分配的一些内存...
  • HAOMCU
  • HAOMCU
  • 2015年03月28日 19:05
  • 21307

浅谈linux字符设备注册

Linux中有两种字符设备注册的方法:这里所提到的函数在文件:fs/char_dev.c中定义,在头文件include/linux/cdev.h中声明。一、             老方法:如果你深入...

深入浅出linux之字符设备和input设备

Linux把设备分为了字符设备和块设备(网络设备除外),这几乎是个常识了。对做驱动的linux程序员来说,驱动要么是字符设备驱动,要么是块设备驱动。那么什么是字符设备和块设备?本节我们探索一下字符设备...

Linux设备驱动之字符设备(一)

Linux中设备驱动的分类从上图可以看到Linux系统将各异的设备分为三大类:字符设备,块设备和网络设备。内核针对每一类设备都提供了对应驱动模型架构,包括基本的内核设施和文件系统接口。字符设备:在传送...

Linux 字符设备驱动结构(二)—— 自动创建设备节点

上一篇我们介绍到创建设备文件的方法,利用cat /proc/devices查看申请到的设备名,设备号。 第一种是使用mknod手工创建:mknod filename type major minor ...

《Linux驱动》创建字符类设备节点

/*包含初始化宏定义的头文件,代码中的module_init和module_exit在此文件中*/ #include /*包含初始化加载模块的头文件,代码中的MODULE_LICENSE在此头文件中...

linux驱动学习记录(一)-字符设备框架

该系列是我在学习宋宝华老师的《Linux设备驱动开发详解》以及结合其他网上教程所做记录。 1.     Linux设备   在Linux操作系统下的设备通常分为三类: 字符设备、块设备和网络设备。...

Linux驱动学习字符设备

   在内核中,dev_t类型(在中定义)用来持有设备编号--主次部分都包括。对于2.6.0内核,dev_t是32位的量,12位用作主编号,20位用作次编号。为获得一个dev_t的主或次编号使...

[Linux驱动]字符设备驱动学习笔记(三)———高级

一,ioctl使用实例

[Linux驱动]字符设备驱动学习笔记(二)———实例

一,注册字符设备 #define GLOBALMEM_MAJOR 256 #define GLOBALMEM_SIZE 0X1000 //4k static int char_major=GLOBA...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Linux驱动学习(4-字符设备-自动创建字符设备并读写)
举报原因:
原因补充:

(最多只允许输入30个字)