Linux驱动入门篇(三):基本的字符设备模块(2)

  上一节中介绍了设备号的申请和释放,这一节开始了解字符设备的相关操作。

  首先定位到<linux/cdev.h>文件,查看内核提供给字符设备的接口。

cdev结构

struct cdev {
	struct kobject kobj;	//内嵌的kobject对象
	struct module *owner;	//此结构所属模块
	const struct file_operations *ops;	//文件操作结构
	struct list_head list;	//通用双向链表
	dev_t dev;		//设备号
	unsigned int count;
};

  owner成员一般初始化为 THIS_MODULE,THIS_MODULE 是一个指向当前模块的 struct module结构指针,也就是指向当前模块。

字符设备的函数接口

void cdev_init(struct cdev *, const struct file_operations *);
struct cdev *cdev_alloc(void);
int cdev_add(struct cdev *, dev_t, unsigned);
void cdev_del(struct cdev *);

  以上是<linux/cdev.h>提供的部分函数接口。接下来一个一个地解决掉它们。

  首先是 cdev_init 函数,先看源码。

void cdev_init(struct cdev *cdev, const struct file_operations *fops)
{
	memset(cdev, 0, sizeof *cdev);
	INIT_LIST_HEAD(&cdev->list);
	kobject_init(&cdev->kobj, &ktype_cdev_default);
	cdev->ops = fops;
}

  此函数的功能是初始化一个 cdev 结构。参数 cdev 即为要进行初始化的结构,参数 fops则是此设备的 file_operations(具体作用留到后面)。

  可以看到 cdev_init 的主要作用就是初始化 cdev 结构,把 fops 指针连接到 cdev结构。做好此设备被添加到系统的准备。

  接下来看 cdev_alloc 函数。

struct cdev *cdev_alloc(void)
{
	struct cdev *p = kzalloc(sizeof(struct cdev), GFP_KERNEL);
	if (p) {
		INIT_LIST_HEAD(&p->list);
		kobject_init(&p->kobj, &ktype_cdev_dynamic);
	}
	return p;
}

  此函数为 cdev 结构申请了一块内存,并返回它的地址,失败时返回NULL。

  下一个函数是 cdev_add,它将一个字符设备添加到系统。

int cdev_add(struct cdev *p, dev_t dev, unsigned count)
{
	int error;

	p->dev = dev;
	p->count = count;

	error = kobj_map(cdev_map, dev, count, NULL,
			 exact_match, exact_lock, p);
	if (error)
		return error;

	kobject_get(p->kobj.parent);

	return 0;
}

  参数 p 是设备的 struct cdev指针,dev 是首个设备号,count 是连续的次设备号的数量。函数通过把指针 p 添加到系统,来描述设备的添加,使设备立即生效。若添加失败,则返回负数的错误码。

  最后一个函数是 cdev_del,它的功能是从系统移除一个 cdev 结构。

void cdev_del(struct cdev *p)
{
	cdev_unmap(p->dev, p->count);
	kobject_put(&p->kobj);
}

  此函数从系统中移除指针 p,有可能会释放 p 指向的结构。

字符设备的注册和注销

  还记得上一节中未实现的 mycdev_setup 函数和 mycdev_del 函数吗?现在我们已经可以实现它们啦。

#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/module.h>

dev_t devno;    //设备号

static struct class *my_class;
static struct cdev my_cdev;
static struct file_operations my_fops;

static int __init mycdev_init(void)
{
        int ret;
        ret = alloc_chrdev_region(&devno, 0, 1, "mycdev");
        if(ret != 0){
                printk(KERN_NOTEICE "Alloc device number failed.");
                return -1;
        }

	//开始实现cdev_setup()
	cdev_init(&my_cdev, &my_fops);
	my_cdev.owner = THIS_MODULE;

	ret = cdev_add(&my_cdev, devno, 1);
	if(ret < 0){
		printk(KERN_NOTEICE "Add cdev failed.");
                return -2;
	}
	//cdev_setup()结束

        my_class = class_create(THIS_MODULE, "mycdev");
        device_create(my_class, NULL, devno, NULL, "mycdev");

        return 0;
}

static void mycdev_exit(void)
{
	//mycdev_del()实现
	cdev_del(&my_cdev);
	//mycdev_del()结束

        device_destroy(my_class, devno);
        class_destroy(my_class);

        unregister_chrdev_region(devno);
}

module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE(“Dual BSD/GPL”);

  现在我们就完成了一个基本的字符设备模块,它实现了设备号的申请与注销,设备文件的创建与销毁以及字符设备的初始化、注册与注销。

  但是,这还不够。我们的目的是使用字符设备,至少需要读或者写此设备。如何让字符设备模块提供读写功能呢?这就和 struct file_operations 结构有关了,留待下一节详细叙述。

原文地址:https://www.cnblogs.com/salvare/p/8395066.html

时间: 2024-10-08 22:53:18

Linux驱动入门篇(三):基本的字符设备模块(2)的相关文章

Linux驱动开发之 三 (那些必须要了解的硬件知识 之 存储器篇)

Linux驱动开发之 三 (那些必须要了解的硬件知识 之 存储器篇) 本文重点学习存储器相关的基本知识,网络上对RAM,ROM,FLASH等有非常详细的介绍,老谢将这些知识点摘抄整理并加以注释如下.这个整理的过程也是加深记忆的过程. 1.什么是内存 在计算机的组成结构中,有一个很重要的部分,就是存储器.存储器是用来存储程序和数据的部件,对于计算机来说,有了存储器,才有记忆功能,才能保证正常工作.存储器的种类很多,按其用途可分为主存储器和辅助存储器,主存储器又称内存储器(简称内存),辅助存储器又称

【SSRS】入门篇(三) -- 为报表定义数据集

原文:[SSRS]入门篇(三) -- 为报表定义数据集 通过前两篇文件 [SSRS]入门篇(一) -- 创建SSRS项目 和 [SSRS]入门篇(二) -- 建立数据源 后, 我们建立了一个SSRS项目,并取得数据源,那么接下来做的就是知道报表要显示什么数据了,这一步可以通过建立数据集来实现. 1.解决方案资源管理器 ->右键选择共享数据集 ->添加新数据集: 2.在共享数据集属性窗口输入数据集名称:AdventureWorksDataset:数据源选择之前建立的:AdventureWorks

8.17_Linux之bash shell脚本编程入门篇(三)之循环以及函数function的使用

bash shell脚本编程入门篇(三)之循环 什么是循环执行? 将某代码段重复运行多次 重复运行多少次: 循环次数事先已知 循环次数事先未知 有进入条件和退出条件 相关命令:for, while, until,selet, for命令的使用 作用: 依次将列表中的元素赋值给"变量名"; 每次赋值后即执行一次循环体; 直到列表中的元素耗尽,循环结束 命令格式: for 变量名 in 列表; do 循环体(正常执行的执行命令) 语句1 语句2 语句3 ... done 列表生成方式: (

linux驱动开发(三) 字符设备驱动框架

还是老规矩先上代码 demo.c #include <linux/init.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/cdev.h> #include <linux/fs.h> int demo_major = 250; int demo_minor = 0; int demo_count = 1; struct cdev cdev; int de

linux驱动开发(三) 字符设备驱动框架(自动创建设备节点)

代码如下 #include <linux/init.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/cdev.h> #include <linux/fs.h> #include <linux/device.h> int demo_major = 250; int demo_minor = 0; int demo_count = 1; stru

Linux嵌入式驱动学习之路(二十一)字符设备驱动程序总结和块设备驱动程序的引入

字符设备驱动程序 应用程序是调用C库中的open read write等函数.而为了操作硬件,所以引入了驱动模块. 构建一个简单的驱动,有一下步骤. 1. 创建file_operations 2. 申请设备号 3. 注册字符设备驱动, 4. 驱动入口 5. 驱动出口 检查数据是否到来的方式: 1. 查询方式 2. 休眠唤醒方式 如果设备出现异常而无法唤醒时,则将永远处于休眠状态. 3. poll机制 如果没有被唤醒,则在一定时间内可自己唤醒. 4. 异步通知(信号) 而以上的几种方式通用性不高,

0915-----Linux设备驱动 学习笔记----------一个简单的字符设备驱动程序

0.前言 研究生生活一切都在步入正轨,我也开始了新的学习,因为实在不想搞存储,所以就决定跟师兄学习设备驱动,看了两星期书,终于有点头绪了,开始记录吧! 1.准备工作 a)查看内核版本 uname -r b)安装内核源码树(http://www.cnblogs.com/Jezze/archive/2011/12/23/2299871.html) 在www.linux.org上下载源码编译,这里是.xz格式,需要安装解压工具,xz-utils: 解压方法示例:xz -d linux-3.1-rc4.

linux驱动开发学习三:异步操作

前面的队列以及锁都是基于阻塞是的操作.要实现同步,还可以通过信号也就是异步的方式来进行.例如在往文件的写入字符后,发送一个信号.捕捉到信号后执行动作.这样就不会造成阻塞,之前的阻塞性IO和POLL,是调用函数进去检查,条件不满足是造成阻塞. 应用层启动异步通知机制就三个步骤: 1 调用signal函数,让指定的信号SIGIO与处理函数sig_handle对应 2 指定一个进程作为文件的”属主(filp-owner)”, 这样内核才知道信号要发给哪个进程 3 在设备文件中添加FASYNC标志,驱动

linux 驱动入门4

不吃苦中苦,难为人上人.努力,给老婆孩子提供个良好的生活居住环境. http://www.cnblogs.com/nan-jing/articles/5806399.html 上文提到了如何创建proc节点.但是我经常看到有操作/sys节点的情况. 莫非应用可以操作sys节点? 问问了别人可以了.哎.别人只能给你说个大概.甚至只能和你说可以操作.为什么,如何操作.别人只能让你看代码.没别的招,想挣钱不? 想.想就看代码吧. 老规矩,贴上全部代码.看看如何创建sys节点 #include<linu