一、如何对设备操作

linux中对设备进行操作是通过文件的方式进行的,包括open、read、write。
对于设备文件,一般称其为设备节点,
节点有一个属性是设备号(主设备号、次设备号),其中主设备号将设备文件与驱动模块对应起来

当我们open一个设备节点时,告诉了kernel要操作的是是主设备号为XX的节点,然后kernel会通过过XX来寻找合适的内存模块,进而调用内存模块中定义的open函数

由于操作节点之前kernel就需要有主设备号的信息,因此主设备号的申请、具有该主设备号的字符设备的添加都需要在驱动模块的初始化函数中执行

二、主设备号的申请

建议采用动态申请的主设备号的方式,linux中有很多设备,每一个设备对应着一个主设备号,动态申请是由内核分配一个没用的主

设备号,
动态申请函数为alloc_chrdev_region,相对应的释放函数为unregister_chrdev_region。
申请完后,可以从/proc/devices中读到分配的主设备号,后面建立设备节点时还需要用到

三、向kernle添加字符设备

上一步向内核申请了主设备号,就可以向kernel中添加字符设备了
kennel中一个字符设备对应了一个结构体cdev,这个结构体中定义了对字符设备的操作方式file_operations(包括open、read、write),这些操作方式也需要在驱动模块中事先定义好。

字符设备结构体cdev的添加步骤:
cdev初始化:cdev_init,该函数将file_operations与cdev对应起来
向kernel添加:cdev_add,该函数将主设备号与cdev结构体对应起来

当对open设备节点时,首先通过节点找到主设备号,然后再kernel中搜索与主设备号相对应的字符设备cdev,然后动过cdev中file_operations结构体定义的open方法(这个open是需要自己实现的)

四、3个重要的结构体

一个是file_operations,这里面主要包含了驱动的主要实现方法
一个是inode,这个是节点的信息,包含了主设备号和cdev结构体
一个是file,当节点首次被打开时,就会在内核中创建一个file结构体,file结构其充当了file_operations中方法的纽带,要不然read和wirte方法怎么知道操作的是那个设备的数据。

file中的自定义内容(驱动需要的数据)一般是在open中定义,然后read和write就可以操作自定义的数据了。

下面是一个简单的实例,可以看到驱动是怎样把自定义的open方法和主设备号对应起来的

#include <linux/module.h>  /*它定义了模块的 API、类型和宏(MODULE_LICENSE、MODULE_AUTHOR等等),所有的内核模块都必须包含这个头文件。*/   

#include <linux/init.h>  

#include <linux/fs.h> //设备号相关函数  

#include <linux/slab.h> //内存分配相关函数  

#include <linux/types.h>  

#include <linux/kdev_t.h>//设备号相关函数  

#include <linux/cdev.h>//字符设备头文件  

#include <linux/module.h>  

  

struct char_dev  

{  

    int size;  

    char *data;  

    struct cdev cdev;//内核中的字符设备  

};  

  

int major = 0;  

int minor = 0;  

struct char_dev char_devices;  

  

int char_open(struct inode *inode, struct file *filep)  

{  

    int Major = 0;  

    Major = MAJOR(inode->i_rdev);  

    printk("open my_char_dev major: %d\n", Major);  

      

    return 0;  

}  

  

struct file_operations char_fops = {  

    .owner = THIS_MODULE,  

    .open = char_open,  

};  

  

  

  

  

static void char_exit(void) //如果init函数中调用了该函数,则不应有 __exit  

{  

    dev_t dev;  

    printk("char device driver exit \n");  

    //释放设备号  

    dev = MKDEV(major, minor);  

    unregister_chrdev_region(dev, 1);  

    printk("release major %d\n", major);  

      

    //释放内存  

    if(char_devices.data){  

        kfree(char_devices.data);  

    }  

      

    //从内核中删除字符设备  

    cdev_del(&(char_devices.cdev));  

}  

  

static int __init char_init(void)//__init一个标记,表明是初始化函数  

{  

    //初始化的代码  

    dev_t dev;  

    int result;   

    printk("char device driver init \n");  

      

    //动态向内核申请设备号      

    result = alloc_chrdev_region(&dev, 0, 1, "my_char_dev");  

    major = MAJOR(dev);  

    minor = MINOR(dev);  

    printk("alloc major %d\n", major);  

    if (result < 0) {  

        printk(KERN_WARNING "my_char_dev: can't get major %d\n", major);  

        return result;  

    }  

      

    //为设备分配一块内存  

    char_devices.size = 100;  

    char_devices.data = (char*)kmalloc(char_devices.size, GFP_KERNEL);  

    if (!char_devices.data) {  

        result = -ENOMEM;  

        goto fail;  //不能直接退出函数,需要释放设备号  

    }  

      

    //向内核中添加字符设备cdev  

    cdev_init(&(char_devices.cdev), &char_fops);  

    char_devices.cdev.owner = THIS_MODULE;  

    char_devices.cdev.ops = &char_fops;  

    result = cdev_add(&(char_devices.cdev), dev, 1);  

    if((result < 0)) {  

        printk(KERN_WARNING "Error %d adding my_char_dev\n", result);  

        goto fail;  

    }  

  

    return 0; //成功  

fail:  

    char_exit();  

    return result;  

}  

  

MODULE_LICENSE("Dual BSD/GPL");  

//当模块被加载时,执行moudle_init函数,该函数会调用初始化函数  

module_init(char_init);  

//模块卸载时,调用,释放资源  

module_exit(char_exit);

KDIR=/usr/src/linux-headers-$(shell uname -r)

PWD=$(shell pwd)

obj-m = CharDevice.o

all:
$(MAKE) -C $(KDIR) M=$(PWD)

注:驱动insmod后,通过/proc/devices查看主设备号,然后通过mknod在/dev下创建设备节点,注意保持主设备好的一致,当前的节点只支持open方法,可以在demsg中进行验证。

[ARM-Linux开发] 主设备号--驱动模块与设备节点联系的纽带的更多相关文章

  1. linux设备管理之主设备号与次设备号

    主设备号和次设备号 一个字符设备或者块设备都有一个主设备号和次设备号.主设备号和次设备号统称为设备号.主设备号用来表示一个特定的驱动程序.次设备号用来表示使用该驱动程序的其他设备.(主设备号和控制这类 ...

  2. Linux:主设备号和次设备号

    http://www.linuxidc.com/Linux/2011-03/33863.htm     Linux的设备管理是和文件系统紧密结合的,各种设备都以文件的形式存放在/dev目录下,称为设备 ...

  3. Linux chroot 并使用之前系统设备节点

    /********************************************************************************* * Linux chroot 并使 ...

  4. 在Ubuntu上建立Arm Linux 开发环境

    我使用的是友善2410的板子,以前都是用Fedora,现在家里的电脑被我转为Linux专用的了,装的是Ubuntu.但是嵌入式还是要玩的,在装载过程中也遇到一些小麻烦.在此记录一下,一来自己比较健忘, ...

  5. linux驱动之设备号与创建设备节点

    设备号: 1.自己主动分配 major = register_chrdev(0,"first_drv",&first_sdv_fops);//注冊 注冊设备时给设备号写0, ...

  6. 成功移植SQLite3到ARM Linux开发板

    SQLite,是一款轻型的数据库,是遵守ACID的关联式数据库管理系统,它的设计目标是嵌入式的,而且目前已经在很多嵌入式产品中使用了它,它占用资源非常的低,在嵌入式设备中,可能只需要几百K的内存就够了 ...

  7. minigui移植到arm linux开发板上无法执行

    要保证目录下有该文件 /etc/MiniGUI.cfg 复制过程使用cp –af 强制复制

  8. 嵌入式Linux学习笔记(三) 字符型设备驱动--LED的驱动开发

    在成功构建了一个能够运行在开发板平台的系统后,下一步就要正式开始应用的开发(这里前提是有一定的C语言基础,对ARM体系的软/硬件,这部分有疑问可能要参考其它教程),根据需求仔细分解任务,可以发现包含的 ...

  9. 【Linux开发】linux设备驱动归纳总结(三):1.字符型设备之设备申请

    linux设备驱动归纳总结(三):1.字符型设备之设备申请 操作系统:Ubunru 10.04 实验平台:S3C2440 + linux2.6.29内核 注:在今后驱动程序的学习中经常需要查看内核源代 ...

随机推荐

  1. hiveserver2和zookeeper的HA搭建(转)

    最近公司新项目申请资源,虚拟机资源打开时候使用source login.sh的脚本来进行登录注册,好奇心驱使下看了看里面的shell脚本,使用到了hiveserver2的zookeeper连接,百度一 ...

  2. java中使用final关键字修饰一个变量时,是引用不能变,还是引用的对象不能变?

    java中使用final关键字修饰一个变量时,是引用不能变,还是引用的对象不能变? 是引用对象的地址值不能变,引用变量所指向的对象的内容是可以改变. final变量永远指向这个对象,是一个常量指针,而 ...

  3. QT工程中添加资源(简单明了)

    1. 在工程文件下右击添加新文件 2. 在QT目录下选择QT Resource File 3. 填写资源名称 4. 点击完成就可以看到自己建立的资源了 5. 点击右键添加现有文件,找到自己要添加的资源 ...

  4. form设计批量赋值

    data: { list:[], cards: { cardname: "", cardtitle: "", cardtel: "", ca ...

  5. luogu_4317: 花神的数论题

    花神的数论题 题意描述: 设\(sum(i)\)表示\(i\)的二进制数中\(1\)的个数. 给定一个整数\(N\),求\(\prod_{i=1}^Nsum(i)\). 输入描述: 输入包含一个正整数 ...

  6. 4.学习springmvc的响应数据和结果视图

    一.返回值分类: 字符串:返回值为需要展示的视图 void:返回为默认的页面(方法名.jsp) ModleAndView:存储对象并返回设置的页面 1.字符串: jsp: <a href=&qu ...

  7. 1.Http讲解

    1.什么是HTTP HTTP(超文本传输协议)是一个简单的请求-响应协议,它通常运行在TCP上. 文本:html,字符串,.... 超文本:图片,音乐,视频,定位,地图... 80端口 HTTPS:安 ...

  8. 洛谷P4735题解

    若想要深入学习可持久化0-1Trie树,传送门. Description: 给定数列 \(\{a_n\}\) ,支持两种操作: 在数列尾添加一个数 \(x\) ,数列长度变成 \(n+1\) ; 给定 ...

  9. K8s的存储卷使用总结

    K8s的存储卷: 它有四种存储卷: 1. emptyDir: 空目录,这种存储卷会随着Pod的删除而被清空,它一般作为缓存目录使用,或临时目录, 当做缓存目录时,通常会将一块内存空间映射到该目录上,让 ...

  10. shell for 循环演示

    test.sh #!/bin/bash for skill in Ada Coffe Action Java; do echo "I am good at ${skill}Script&qu ...