学习笔记二:新Linux驱动之字符设备驱动

新字符驱动设备

首先看看分配设备号的函数(动态分配和静态分配):

int register_chrdev_region(dev_t, unsigned, const char *);
// 静态分配:通过已知未使用的主设备号像内核注册设备号
// 缺点:设备号可能重复导致注册失败
// 参数1:申请的设备号
// 参数2:申请设备号个数(主设备号不变,次设备号增加)
// 参数3:设备名(体现在/proc/devices)
// 返回值: 成功返回0,失败返回负数

int alloc_chrdev_region(dev_t *, unsigned, unsigned, const char *);
// 动态分配设备号
// 参数1:系统分配的设备号将保存在该变量中
// 参数2:起始次设备号
// 参数3:申请的设备号个数
// 参数4:设备名(体现在/proc/devices)
// 返回值: 成功返回0,失败返回负数

void unregister_chrdev_region(dev_t, unsigned);
// 注销申请的设备号
// 参数1:需要注销的设备号
// 参数2:注销设备号的个数
// 返回值:无

向内核注册字符设备

void cdev_init(struct cdev *, const struct file_operations *);
// 先通过这个函数初始化字符设备结构体cdev并指定了fops
// 参数1:需要初始化的字符设备结构体
// 参数2:文件操作集合
// 返回值:无

int cdev_add(struct cdev *, dev_t, unsigned);
// 将该字符设备注册进内核
// 参数1:需要注册的字符设备结构体
// 参数2:设备号
// 参数3:需要注册的设备个数
// 返回值:成功为0,失败为负数

void cdev_del(struct cdev *);
// 删除字符设备结构体
// 参数1:需要删除的字符设备结构体
// 返回值:无

总结一下新字符设备驱动程序的流程:

  1. 静态或者动态的申请设备号
  2. 初始化并注册字符设备结构体cdev到内核
  3. 在/sys/class目录下创建类
  4. 自动生成设备节点
  5. 其他操作

代码如下:

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

#define DEV_NUM				(1)
#define DEV_NAME			"test_dev"
#define TEST_DESCRIPTION	"test description"

static struct test
{
	dev_t dev;
	int major;
	int minor;
	struct cdev cdev;
	struct class *pclass;
}test;

static struct file_operations fops = 
{
	.owner = THIS_MODULE,
};

static int __init test_init(void)
{
	int ret = -1;
	test.major = 0;
	test.minor = 0;
	test.dev = MKDEV(test.major,test.minor);
	if(test.major)
		ret = register_chrdev_region(test.dev,DEV_NUM,DEV_NAME);
	else
	{
		ret = alloc_chrdev_region(&test.dev,0,DEV_NUM,DEV_NAME);
		test.major = MAJOR(test.dev);
		test.minor = MINOR(test.dev);
	}
	if(ret < 0)
		goto fail_get_chrdev;

	cdev_init(&test.cdev,&fops);
	
	test.cdev.owner = THIS_MODULE;

	ret = cdev_add(&test.cdev,test.dev,DEV_NUM);
	if(ret < 0 )
		goto fail_cdev_add;

	test.pclass = class_create(THIS_MODULE,"test_class");
	if(IS_ERR(test.pclass))
	{
		ret = PTR_ERR(test.pclass);
		goto fail_class_create;
	}

	for(int i =0;i<DEV_NUM;i++)
	{
		struct device *temp_dev = device_create(test.pclass,NULL,MKDEV(test.major,i),NULL,"test_dev%d",i);
		ret = PTR_ERR_OR_ZERO(temp_dev);
		if(ret < 0)
			goto fail_device_create;
	}

	return 0;

fail_device_create:
	for(int i=0;i<DEV_NUM;i++)
		device_destroy(test.pclass,MKDEV(test.major,i));
fail_class_create:
	class_destroy(test.pclass);
fail_cdev_add:
	cdev_del(&test.cdev);
fail_get_chrdev:
	unregister_chrdev_region(test.dev,DEV_NUM);
	return -1;
}

static void __exit test_exit(void)
{
	for(int i=0;i<DEV_NUM;i++)
		device_destroy(test.pclass,MKDEV(test.major,i));
	class_destroy(test.pclass);
	cdev_del(&test.cdev);
	unregister_chrdev_region(test.dev,DEV_NUM);
}

module_init(test_init);
module_exit(test_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("ChenGuo");
MODULE_DESCRIPTION(TEST_DESCRIPTION);

Makefile文件:

KERNEL_DIR := /home/chen/alientek/imx6ull/kernel/linux-imx-rel_imx_4.1.15_2.1.0_ga_alientek
CURRENT_DIR := $(shell pwd)

obj-m := test.o
ccflags-y := -std=gnu99 -Wno-declaration-after-statement

kernel_module:
	$(MAKE) -C $(KERNEL_DIR) M=$(CURRENT_DIR) ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- modules

.PHONY:clean

clean:
	$(MAKE) -C $(KERNEL_DIR) M=$(CURRENT_DIR) ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- clean
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值