Linux驱动-设备注册

注册字符设备号

chrdev.c

/*
 * @Author:topeet
 * @Description:字符设备自动创建设备节点步骤一创建类,创建设备
 */
#include <linux/init.h>	  //初始化头文件
#include <linux/module.h> //最基本的文件,支持动态添加和卸载模块。
#include <linux/fs.h>	  //包含了文件操作相关struct的定义,例如大名鼎鼎的struct file_operations
#include <linux/kdev_t.h>
#include <linux/cdev.h>		   //对字符设备结构cdev以及一系列的操作函数的定义。//包含了cdev 结构及相关函数的定义。
#define DEVICE_NUMBER 1		   //定义次设备号的个数
#define DEVICE_SNAME "schrdev" //定义静态注册设备的名称
#define DEVICE_ANAME "achrdev" //定义动态注册设备的名称
#define DEVICE_MINOR_NUMBER 0  //定义次设备号的起始地址
#include <linux/device.h>	   //包含了device、class 等结构的定义
#define DEVICE_CLASS_NAME "chrdev_class"
#define DEVICE_NODE_NAME "chrdev_test" //宏定义设备节点的名字
static int major_num, minor_num;	   //定义主设备号和次设备号

struct class *class;				   /* 类 */
struct device *device;				   /* 设备 */
struct cdev cdev;					   //定义一个cdev结构体
module_param(major_num, int, S_IRUSR); //驱动模块传入普通参数major_num
module_param(minor_num, int, S_IRUSR); //驱动模块传入普通参数minor_num
dev_t dev_num;						   /* 设备号 */

/**
 * @description: 打开设备
 * @param {structinode} *inode:传递给驱动的 inode
 * @param {structfile} *file:设备文件,file 结构体有个叫做 private_data 的成员变量,
 *  一般在 open 的时候将 private_data 指向设备结构体。
 * @return: 0 成功;其他 失败 
 */
int chrdev_open(struct inode *inode, struct file *file)
{
	printk("chrdev_open\n");
	return 0;
}
// 设备操作函数结构体
struct file_operations chrdev_ops = {
	.owner = THIS_MODULE,
	.open = chrdev_open};
/**
 * @description: 驱动入口函数
 * @param {*}无
 * @return {*} 0 成功;其他 失败
 */
static int hello_init(void)
{
	int ret; //函数返回值
	if (major_num)
	{
		/*静态注册设备号*/
		printk("major_num = %d\n", major_num); //打印传入进来的主设备号
		printk("minor_num = %d\n", minor_num); //打印传入进来的次设备号

		dev_num = MKDEV(major_num, minor_num);								//MKDEV将主设备号和次设备号合并为一个设备号
		ret = register_chrdev_region(dev_num, DEVICE_NUMBER, DEVICE_SNAME); //注册设备号
		if (ret < 0)
		{
			printk("register_chrdev_region error\n");
		}
		printk("register_chrdev_region ok\n"); //静态注册设备号成功
	}
	else
	{
		/*动态注册设备号*/
		ret = alloc_chrdev_region(&dev_num, DEVICE_MINOR_NUMBER, 1, DEVICE_ANAME);
		if (ret < 0)
		{
			printk("alloc_chrdev_region error\n");
		}
		printk("alloc_chrdev_region ok\n"); //动态注册设备号成功

		major_num = MAJOR(dev_num);			   //将主设备号取出来
		minor_num = MINOR(dev_num);			   //将次设备号取出来
		printk("major_num = %d\n", major_num); //打印传入进来的主设备号
		printk("minor_num = %d\n", minor_num); //打印传入进来的次设备号
	}
	// 初始化 cdev
	cdev.owner = THIS_MODULE;
	cdev_init(&cdev, &chrdev_ops);
	// 向系统注册设备
	cdev_add(&cdev, dev_num, DEVICE_NUMBER);
	// 创建 class 类
	class = class_create(THIS_MODULE, DEVICE_CLASS_NAME);
	// 在 class 类下创建设备
	device = device_create(class, NULL, dev_num, NULL, DEVICE_NODE_NAME);
	return 0;
}
/**
 * @description: 驱动出口函数
 * @param {*}无
 * @return {*}无
 */
static void hello_exit(void)
{
	//注销设备号
	unregister_chrdev_region(MKDEV(major_num, minor_num), DEVICE_NUMBER);
	//删除设备
	cdev_del(&cdev);
	//注销设备
	device_destroy(class, dev_num);
	//删除类
	class_destroy(class);
	printk("gooodbye! \n");
}
// 将上面两个函数指定为驱动的入口和出口函数
module_init(hello_init);
module_exit(hello_exit);
//  LICENSE 和作者信息
MODULE_LICENSE("GPL");
MODULE_AUTHOR("topeet");

Makefile

obj-m += chrdev.o  #先写生成的中间文件的名字是什么,-m的意思是把我们的驱动编译成模块
KDIR:=/home/topeet/driver/imx6ull/linux-imx-rel_imx_4.1.15_2.1.0_ga/
PWD?=$(shell pwd)   #获取当前目录的变量
all:
	make -C $(KDIR) M=$(PWD) modules  #make会进入内核源码的路径,然后把当前路径下的代码编译成模块

应用

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>


int main(int argc,char *argv[])
{
	int fd;
	char buf[64] = {0};
	fd = open("/dev/chrdev_test",O_RDWR);
	if(fd < 0)
	{
		perror("open error \n");
		return fd;
	}
	read(fd,buf,sizeof(buf));
	close(fd);
	return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
LINUX设备驱动第三版_ 前言 第一章 设备驱动程序简介 设备驱动程序的作用 内核功能划分 设备和模块的分类 安全问题 版本编号 许可证条款 加入内核开发社团 本书概要 第二章 构造和运行模块 设置测试系统 Hello World模块 核心模块与应用程序的对比 编译和装载 内核符号表 预备知识 初始化和关闭 模块参数 在用户空间编写驱动程序 快速参考 第三章 字符设备驱动程序 scull的设计 主设备号和次设备号 一些重要的数据结构 字符设备注册 open和release scull的内存使用 read和write 试试新设备 快速参考 第四章 调试技术 内核中的调试支持 通过打印调试 通过查询调试 通过监视调试 调试系统故障 调试器和相关工具 第五章 并发和竞态 scull的缺陷 并发及其管理 信号量和互斥体 completion 自旋锁 锁陷阱 除了锁之外的办法 快速参考 第六章 高级字符驱动程序操作 ioctl 阻塞型I/O poll和select 异步通知 定位设备 设备文件的访问控制 快速参考 第七章 时间、延迟及延缓操作 度量时间差 获取当前时间 延迟执行 内核定时器 tasklet 工作队列 快速参考 第八章 分配内存 kmalloc函数的内幕 后备高速缓存 get_free_page和相关函数 vmalloc及其辅助函数 per-CPU变量 获取大的缓冲区 快速参考 第九章 与硬件通信 I/O端口和I/O内存 使用I/O端口 I/O端口示例 使用I/O内存 快速参考 第十章 中断处理 准备并口 安装中断处理例程 实现中断处理例程 顶半部和底半部 中断共享 中断驱动的I/O 快速参考 第十一章 内核的数据类型 使用标准C语言类型 为数据项分配确定的空间大小 接口特定的类型 其他有关移植性的问题 链表 快速参考 第十二章 PCI驱动程序 PCI接口 ISA回顾 PC/104和PC/104+ 其他的PC总线 SBus NuBus 外部总线 快速参考 第十三章 USB驱动程序 USB设备基础 USB和Sysfs USB urb 编写USB驱动程序 不使用urb的USB传输 快速参考 第十四章 Linux设备模型 kobject、kset和子系统 低层sysfs操作 热插拔事件的产生 总线、设备驱动程序 类 各环节的整合 热插拔 处理固件 快速索引 第十五章 内存映射和DMA Linux的内存管理 mmap设备操作 执行直接I/O访问 直接内存访问 快速参考 第十六章 块设备驱动程序 注册设备操作 请求处理 其他一些细节 快速参考 第十七章 网络驱动程序 snull设计 连接到内核 net_device结构细节 打开和关闭 数据包传输 数据包的接收 中断处理例程 不使用接收中断 链路状态的改变 套接字缓冲区 MAC 地址解析 定制 ioctl 命令 统计信息 组播 其他知识点详解 快速参考 第十八章 TTY驱动程序 小型TTY驱动程序 tty_driver函数指针 TTY线路设置 ioctls proc和sysfs对TTY设备的处理 tty_driver结构详解 tty_operations结构详解 tty_struct结构详解 快速参考 参考书目 9112405-1_o.jpg (85.53 KB, 下载次数: 50)

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值