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

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

先把用到的函数写在前面:

// 注册字符设备 
// 该函数还存在一定的缺点,会一次性注册整个主设备号下的所有次设备号,之后我们会通过其他方式解决这个问题
int register_chrdev(unsigned int major, const char *name,const struct file_operations *fops);
// 取消注册字符设备
void unregister_chrdev(unsigned int major, const char *name);

// file_operations中经常需要实现的成员如下
struct file_operations
{
	struct module *owner;
	int (*open)(strcut inode *,struct file *);
	int (*release)(struct inode *,struct file *);
	ssize_t (*write)(struct file *,const char __user *,size_t ,loff_t *);
	ssize_t (*read)(struct file *,char __user *,size_t,loff_t *);
	...
}
// 创建类
#define class_create(owner, name)		\
({						\
	static struct lock_class_key __key;	\
	__class_create(owner, name, &__key);	\
})
// 销毁类
void class_destroy(struct class *cls);

// 创建设备
struct device *device_create(struct class *cls, struct device *parent,dev_t devt, void *drvdata,const char *fmt, ...);
// 销毁设备
void device_destroy(struct class *cls, dev_t devt);

函数参数说明:

// 注册字符设备
// 该函数还存在一定的缺点,会一次性注册整个主设备号下的所有次设备号,之后我们会通过其他方式解决这个问题
int register_chrdev(unsigned int major, const char *name,const struct file_operations *fops);

major:主设备号,给0默认系统分配最大的且未占用的主设备号(设备号为 dev_t 类型,32位,前12位为主设备号,后20位为次设备号,主设备号1-254,次设备号0-255 可以用MKDEV(major,minor)将主设备号和次设备号组 合成设备号)
name:字符设备的名字
fops:file_operations结构体

// 创建类
class_create(owner, name);

owner:THIS_MODULE
name:该类的名字,该函数会是/sys/class目录下生成name这个名字的文件夹

// 创建设备
struct device *device_create(struct class *cls, struct device *parent,dev_t devt, void *drvdata,const char *fmt, ...);

cls:class_create函数创建的类
parent:父对象
devt:设备号
drvdata:私有数据
fmt:从该参数起用法与printf相同,该参数为设备的名称,会出现在/dev目录下,并且会在/sys/class 对应文件夹里创建该设备名的文件夹。

标题标题总结的字符设备驱动步骤:
  1. 首先第一步是MODULE_LICENSE(“GPL”);
  2. 模块的入口出口 module_init(入口函数) module_exit(出口函数)
  3. 然后写对应的入口出口函数
  4. 写file_operations结构体
  5. 完成结构体中函数接口
  6. 入口函数中注册字符设备(register_chrdev(…)),创建类class_create(…) 创建设备device_create(…),紧接着在出口函数中销毁刚才创建的这些内容,先入后出的原则。先销毁设备(device_destroy(…)),接着销毁类(class_destroy(…)),最后取消字符设备的注册(unregister_chrdev(…))。

下面是示例代码,内容还有部分未完善,之后的内容会慢慢改进。

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

#define DEV_NUM			10

static int test_major;
static struct class *test_class;

static int test_open(struct inode *pnode,struct file *pfile)
{
	return 0;
}

static int test_release(struct inode *pnode,struct file *pfile)
{
	return 0;
}

static ssize_t test_write(struct file *pfile,const char __user *pbuf,size_t count,loff_t *poffset)
{
	return count;
}

static ssize_t test_read(struct file *pfile,char __user *pbuf,size_t count,loff_t *poffset)
{
	return count;
}

static struct file_operations fops=
{
	.open = test_open,
	.release = test_release,
	.write = test_write,
	.read = test_read
}

static int __init test_init(void)
{
	test_major = register_chrdev(0,"test_dev",&fops);
	test_class = class_create(THIS_MODULE,"test_class");
	for(int i=0;i<DEV_NUM;i++)
		device_create(test_class,NULL,MKDEV(test_major,i),NULL,"test_dev%d",i);
	return 0;
}

static void __exit test_exit(void)
{
	for(int i=0;i<DEV_NUM;i++)
		device_destroy(test_class,MKDEV(test_major,i));
	class_destroy(test_class);
	unregister_chrdev(test_major,"test_dev");
}

module_init(test_init);
module_exit(test_exit);

MODULE_LICENSE("GPL");

Makefile文件的内容:

#内核的路径
KERNEL_DIR := 						#your kernel path
#模块源文件路径
CURRENT_DIR := $(shell pwd)
#将驱动编译成模块 obj-y 是将驱动编译进内核
obj-m := test.o
#支持c99标准
ccflags-y := -std=gnu99 -Wno-declaration-after-statement

# -C 切换到内核路径
# M=“”指定模块文件的位置
# ARCH指定架构    CROSS_COMPILE指定交叉编译器
# modules 告诉内核Makefile我们要编译的是模块
kernel_modules:
	$(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
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值