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

来源:互联网 发布:淘宝怎么凑单 编辑:程序博客网 时间:2024/06/04 18:58

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

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

0 0
原创粉丝点击