Linux驱动——注册字符类设备

一、分配内存空间

        前面介绍的杂项设备并没有分配内存空间这个过程, 是因为系统自带的代码已经给杂项设备分配好了。

        Linux 中注册字符类设备需要首先申请内存空间, 有一个专门分配小内存空间的函数 kmalloc, 这个函数在头文件“include/linux/slab.h” 中, 如下图所示, 使用命令“vim include/linux/slab.h” 打开头文件。

        如上图所示, 函数 static inline void *kzalloc(size_t size, gfp_t flags)有两个参数,参数 size_t size: 申请的内存大小(最大 128K),代表优先权, 内存不够可以延迟分配。

        和申请内存的函数 kzalloc 对应的是 kfree 释放函数, 如下图所示。

 

        void kfree(const void *)函数只有一个参数, 就是内存的指针, 这个指针由申请内存的函数 kzalloc 返回。

二、注册字符类设备的函数

        注册字符类设备的初始化函数为 cdev_init, 这个函数在头文件“include/linux/cdev.h” 中, 使用命令“vim include/linux/cdev.h” 打开这个头文件如下图所示。

 

        如上图所示红框中的函数“void cdev_init(struct cdev *, const struct file_operations *)” 和结构体“cdev”。cdev_init 函数有两个参数,

        参数 struct cdev *: cdev 字符设备文件结构体

        参数 const struct file_operations *: file_operations 结构体。

        注册字符设备的函数为 cdev_add, 这个函数也是在头文件“include/linux/cdev.h” 中, 如下图所示。

        如上图所示, 和注册驱动的函数 cdev_add 对应的还有卸载驱动的函数 cdev_del。注册驱动的函数 int cdev_add(struct cdev *, dev_t, unsigned);有三个参数:

        参数 struct cdev *: cdev 字符设备文件结构

        参数 dev_t: 设备号 dev,前面已经介绍和使用过了参数 unsigned: 设备范围大小卸载驱动的函数 void cdev_del(struct cdev *)只有一个参数, cdev 字符设备结构体

三、注册字符类设备的例程

/*包含初始化宏定义的头文件,代码中的module_init和module_exit在此文件中*/
#include <linux/init.h>
/*包含初始化加载模块的头文件,代码中的MODULE_LICENSE在此头文件中*/
#include <linux/module.h>
/*定义module_param module_param_array的头文件*/
#include <linux/moduleparam.h>
/*定义module_param module_param_array中perm的头文件*/
#include <linux/stat.h>
/*三个字符设备函数*/
#include <linux/fs.h>
/*MKDEV转换设备号数据类型的宏定义*/
#include <linux/kdev_t.h>
/*定义字符设备的结构体*/
#include <linux/cdev.h>

/*分配内存空间函数头文件*/
#include <linux/slab.h>

#define DEVICE_NAME "ascdev"
#define DEVICE_MINOR_NUM 2
#define DEV_MAJOR 0
#define DEV_MINOR 0
#define REGDEV_SIZE 3000

MODULE_LICENSE("Dual BSD/GPL");
/*声明是开源的,没有内核版本限制*/
MODULE_AUTHOR("iTOPEET_dz");
/*声明作者*/

int numdev_major = DEV_MAJOR;
int numdev_minor = DEV_MINOR;

/*输入主设备号*/
module_param(numdev_major,int,S_IRUSR);
/*输入次设备号*/
module_param(numdev_minor,int,S_IRUSR);

struct reg_dev
{
	char *data;
	unsigned long size;
	
	struct cdev cdev;
};
struct reg_dev *my_devices;

struct file_operations my_fops = {
	.owner = THIS_MODULE,
};


/*设备注册到系统*/
static void reg_init_cdev(struct reg_dev *dev,int index){
	int err;
	int devno = MKDEV(numdev_major,numdev_minor+index);
	
	/*数据初始化*/
	cdev_init(&dev->cdev,&my_fops);
	dev->cdev.owner = THIS_MODULE;
	dev->cdev.ops = &my_fops;
	
	/*注册到系统*/
	err = cdev_add(&dev->cdev,devno,1);
	if(err){
		printk(KERN_EMERG "cdev_add %d is fail! %d\n",index,err);
	}
	else{
		printk(KERN_EMERG "cdev_add %d is success!\n",index);
	}
}

static int scdev_init(void)
{
	int ret = 0,i;
	dev_t num_dev;
	
	
	printk(KERN_EMERG "numdev_major is %d!\n",numdev_major);
	printk(KERN_EMERG "numdev_minor is %d!\n",numdev_minor);
	
	if(numdev_major){
		num_dev = MKDEV(numdev_major,numdev_minor);
		ret = register_chrdev_region(num_dev,DEVICE_MINOR_NUM,DEVICE_NAME);
	}
	else{
		/*动态注册设备号*/
		ret = alloc_chrdev_region(&num_dev,numdev_minor,DEVICE_MINOR_NUM,DEVICE_NAME);
		/*获得主设备号*/
		numdev_major = MAJOR(num_dev);
		printk(KERN_EMERG "adev_region req %d !\n",numdev_major);
	}
	if(ret<0){
		printk(KERN_EMERG "register_chrdev_region req %d is failed!\n",numdev_major);		
	}
	
	my_devices = kmalloc(DEVICE_MINOR_NUM * sizeof(struct reg_dev),GFP_KERNEL);
	if(!my_devices){
		ret = -ENOMEM;
		goto fail;
	}
	memset(my_devices,0,DEVICE_MINOR_NUM * sizeof(struct reg_dev));
	
	/*设备初始化*/
	for(i=0;i<DEVICE_MINOR_NUM;i++){
		my_devices[i].data = kmalloc(REGDEV_SIZE,GFP_KERNEL);
		memset(my_devices[i].data,0,REGDEV_SIZE);
		/*设备注册到系统*/
		reg_init_cdev(&my_devices[i],i);
	}
	
		
	printk(KERN_EMERG "scdev_init!\n");
	/*打印信息,KERN_EMERG表示紧急信息*/
	return 0;

fail:
	/*注销设备号*/
	unregister_chrdev_region(MKDEV(numdev_major,numdev_minor),DEVICE_MINOR_NUM);
	printk(KERN_EMERG "kmalloc is fail!\n");
	
	return ret;
}

static void scdev_exit(void)
{
	int i;
	printk(KERN_EMERG "scdev_exit!\n");
	
	/*除去字符设备*/
	for(i=0;i<DEVICE_MINOR_NUM;i++){
		cdev_del(&(my_devices[i].cdev));
	}
		
	unregister_chrdev_region(MKDEV(numdev_major,numdev_minor),DEVICE_MINOR_NUM);
}


module_init(scdev_init);
/*初始化函数*/
module_exit(scdev_exit);
/*卸载函数*/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
资源大于15MB分2次上传。 清晰度一般。加到11章 第12,13章没有。 第1章 嵌入式系统基础. 1.1 嵌入式系统简介 1.1.1 嵌入式系统定义 1.1.2 嵌入式系统与PC 1.1.3 嵌入式系统的特点 1.2 嵌入式系统的发展 1.2.1 嵌入式系统现状 1.2.2 嵌入式系统发展趋势 1.3 嵌入式操作系统与实时操作系统 1.3.1 Linux 1.3.2 uC/OS 1.3.3 Windows CE 1.3.4 VxWorks 1.3.5 Palm OS 1.3.6 QNX 1.4 嵌入式系统选型 第2章 基于ARM9处理器的硬件开发平台 2.1 ARM处理器简介 2.1.1 ARM公司简介 2.1.2 ARM微处理器核 .2.2 ARM9微处理器简介 2.2.1 与ARM7处理器的比较 2.2.2 三星S3C2410X处理器详解 2.3 FS2410开发平台 第3章 创建嵌入式系统开发环境 3.1 嵌入式Linux的开发环境 3.2 Cygwin 3.3 虚拟机 3.4 交叉编译的预备知识 3.4.1 Make命令和Makefile文件 3.4.2 binutils工具包 3.4.3 gcc编译器 3.4.4 Glibc库 3.4.5 GDB 3.5 交叉编译 3.5.1 创建编译环境 3.5.2 编译binutils 3.5.3 编译bootstrap_gcc 3.5.4 编译Glibc 3.5.5 编译完整的gcc 3.5.6 编译GDB 3.5.7 成果 3.5.8 其他交叉编译方法 3.6 通过二进制软件包创建交叉编译环境 3.7 开发套件 第4章 调试嵌入式系统程序 4.1 嵌入式系统调试方法 4.1.1 实时在线仿真 4.1.2 模拟调试 4.1.3 软件调试 4.1.4 BDM/JTAG调试 4.2 ARM仿真器 4.2.1 techorICE ARM仿真器 4.2.2 ARM仿真器工作原理 4.2.3 ARM仿真器的系统功能层次 4.2.4 使用仿真器和ADS Debugger调试ARM开发板 4.3 JTAG接口 4.3.1 JTAG引脚定义 4.3.2 通过JTAG烧写Flash 4.3.3 烧写Flash技术内幕 第5章 Bootloader 5.1 嵌入式系统的引导代码 5.1.1 初识Bootloader 5.1.2 Bootloader的启动流程 5.2 Bootloader之vivi 5.2.1 vivi简介 5.2.2 vivi的配置与编译 5.2.3 vivi代码导读 5.3 Bootloader之U-Boot 5.3.1 U-Boot代码结构分析 5.3.2 编译U-Boot代码 5.3.3 U-Boot代码导读 5.3.4 U-Boot命令 5.4 FS2410的Bootloader 第6章 Linux系统在ARM平台的移植 6.1 移植的概念 6.2 Linux内核结构 6.3 Linux-2.4内核向ARM平台的移植 6.3.1 根目录 6.3.2 arch目录 6.3.3 arch/arm/boot目录 6.3.4 arch/arm/def-configs目录 6.3.5 arch/arm/kernel目录 6.3.6 arch/arm/mm目录 6.3.7 arch/arm/mach-s3c2410目录 6.4 Linux-2.6内核向ARM平台的移植 6.4.1 定义平台和编译器 6.4.2 arch/arm/mach-s3c2410/devs.c 6.4.3 arch/arm/mach-s3c2410/mach-fs2410.c 6.4.4 串口输出 6.5 编译Linux内核 6.5.1 代码成熟等级选项 6.5.2 通用的一些选项 6.5.3 和模块相关的选项 6.5.4 和块相关的选项 6.5.5 和系统型相关的选项 6.5.6 和总线相关的选项 6.5.7 和内核特性相关的选项 6.5.8 和系统启动相关的选项 6.5.9 和浮点运算相关的选项 6.5.10 用户空间使用的二进制文件格式的选项 6.5.11 和电源管理相关的选项 6.5.12 和网络协议相关的选项 6.5.13 和设备驱动程序相关的选项 6.5.14 和文件系统相关的选项 6.5.15 和程序性能分析相关的选项 6.5.16 和内核调试相关的选项 6.5.17 和安全相关的选项 6.5.18 和加密算法相关的选项 6.5.19 库选项 6.5.20 保存内核配置 第7章 Linux设备驱动程序开发 7.1 设备驱动概述 7.1.1 设备驱动和文件系统的关系 7.1.2 设备型分 7.1.3 内核空间和用户空间.. 7.2 设备驱动基础 7.2.1 设备驱动中关键数据结构 7.2.2 字符设备驱动开发 第8章 网络设备驱动程序开发 8.1 网络设备驱动程序简介 8.1.1 device数据结构 8.1.2 sk_buff数据结构 8.1.3 内核的驱动程序接口 8.2 以太网控制器CS8900A 8.2.1 特性 8.2.2 工作原理 8.2.3 电路连接 8.2.4 引脚 8.2.5 操作模式 8.3 网络设备驱动程序实例 8.3.1 初始化函数 8.3.2 打开函数 8.3.3 关闭函数 8.3.4 发送函数 8.3.5 接收函数 8.3.6 中断处理函数 第9章 USB驱动程序开发 9.1 USB驱动程序简介 9.1.1 USB背景知识 9.1.2 Linux内核对USB规范的支持 9.1.3 OHCI简介 9.2 Linux下USB系统文件结点 9.3 USB主机驱动结构 9.3.1 USB数据传输时序 9.3.2 USB设备连接/断开时序 9.4 主要数据结构及接口函数 9.4.1 数据传输管道 9.4.2 统一的USB数据传输块 9.4.3 USBD数据描述 9.4.4 USBD与HCD驱动程序接口 9.4.5 USBD层的设备管理 9.4.6 设备驱动与USBD接口 9.5 USBD文件系统接口 9.5.1 设备驱动程序访问 9.5.2 设备拓扑访问 9.5.3 设备信息访问 9.6 设备驱动与文件系统接口 9.7 USB HUB驱动程序 9.7.1 HUB驱动初始化 9.7.2 HUB Probe相关函数 9.8 OHCI HCD实现 9.8.1 OHCI驱动初始化 9.8.2 与USBD连接 9.8.3 OHCI根HUB 9.9 扫描仪设备驱动程序 9.9.1 USBD接口 9.9.2 文件系统接口 9.10 USB主机驱动在S3C2410X平台的实现 9.10.1 USB主机控制器简介 9.10.2 驱动程序的移植 第10章 图形用户接口 10.1 嵌入式系统中的GUI简介 10.1.1 MicroWindows 10.1.2 MiniGUI 10.1.3 Qt/Embedded 10.2 MiniGUI编程 10.2.1 MiniGUI移植 10.2.2 MiniGUI编程 10.3 初识Qt/Embedded 10.3.1 Qt介绍 10.3.2 系统要求 10.3.3 Qt的架构 10.4 Qt/Embedded嵌入式图形开发基础 10.4.1 建立Qt/Embedded 开发环境 10.4.2 认识Qt/Embedded开发环境 10.4.3 窗体 10.4.4 对话框 10.4.5 外形与感觉 10.4.6 国际化 10.5 Qt/Embedded实战演练 10.5.1 安装Qt/Embedded工具开发包 10.5.2 交叉编译Qt/Embedded库 10.5.3 Hello,World 10.5.4 发布Qt/Embedded程序到目标板 10.5.5 添加一个Qt/Embedded应用到QPE 第11章 Java虚拟机的移植 11.1 Java虚拟机概述 11.1.1 Java虚拟机的概念 11.1.2 J2ME 11.1.3 KVM 11.2 Java虚拟机的移植 11.2.1 获得源码 11.2.2 编译环境的建立 11.2.3 JDK的安装 11.2.4 KVM的移植及编译 11.2.5 KVM的测试 11.3 其他可选的虚拟机 11.4 性能优化
Linux字符设备驱动Linux内核中的一种设备驱动型,用于与字符设备进行交互。字符设备是一种按字符流进行输入输出的设备,如串口、终端、打印机等。 在Linux内核中,字符设备驱动通过注册字符设备文件来与用户空间进行通信。它提供了一组操作函数,用于处理读写、打开关闭等操作。 对于字符设备驱动的剖析可以从以下几个方面来理解: 1. 设备注册字符设备驱动需要通过调用`register_chrdev`函数来注册设备号,并通过`cdev_add`函数向内核注册字符设备驱动。 2. 设备文件操作:字符设备驱动需要实现一些操作函数,如`open`、`read`、`write`、`release`等。这些函数在用户空间通过系统调用来触发,驱动会根据不同的操作进行相应的处理。 3. 设备文件结构:在内核中,每个字符设备都有一个对应的`struct file_operations`结构体,用于存储设备文件操作函数的地址。驱动需要将这个结构体与设备文件关联起来。 4. 中断处理:对于一些特殊的字符设备,如串口接收数据触发中断时,驱动需要实现中断处理函数,并通过注册中断处理程序来响应中断。 5. 设备驱动的通信:字符设备驱动通过读写设备寄存器、共享内存等方式与设备进行通信。驱动需要通过合适的方式与设备进行数据的交互。 总之,字符设备驱动Linux内核中与字符设备交互的重要组成部分。剖析字符设备驱动可以帮助理解驱动注册设备文件操作、中断处理等工作原理,从而更好地了解Linux内核的设备驱动机制。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

琪琪猫不会嵌入式

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值