linux内核开发第4讲:编写最简单的字符设备驱动

文章介绍了Linux内核驱动的基本结构,包括helloDev.c中的入口函数hello_init和hello_exit,以及设备号的生成、注册和设备操作函数的定义。通过register_chrdev_region将设备注册到内核,并使用cdev和file_operations结构体定义字符设备及其操作。
摘要由CSDN通过智能技术生成

1.驱动文件

helloDev.c
Makefile
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/wait.h>
#include <linux/poll.h>
#include <linux/sched.h>
#include <linux/slab.h>

#define BUFFER_MAX (10)
#define OK (0)
#define ERROR (-1)

struct cdev *gDev = NULL;
struct file_operations *gFile = NULL;
dev_t devNum;
unsigned int subDevNum = 1;
int reg_major= 232;
int reg_minor = 0;
char *buffer = NULL;
int flag = 0;

int hello_open(struct inode *p, struct file *f) {
    printk(KERN_EMERG "hello_open\r\n");
    return 0;
}

ssize_t hello_write(struct file *f, const char __user *u, size_t s, loff_t *l) {
    printk(KERN_EMERG "hello_write\r\n");
    return 0;
}

ssize_t hello_read(struct file *f, char __user *u, size_t s, loff_t *l) {
    printk(KERN_EMERG "hello_read\r\n");
    return 0;
}

int hello_init(void) {
    devNum = MKDEV(reg_major, reg_minor);
    if (OK == register_chrdev_region(devNum, subDevNum, "helloworld")) {
        printk(KERN_EMERG "register_chrdev_region ok\n");
    }
    else {
        printk(KERN_EMERG "register_chrdev_region error\n");
        return ERROR;
    }
    printk(KERN_EMERG "hello driver init\n");
    gDev = kzalloc(sizeof(struct cdev), GFP_KERNEL);
    gFile = kzalloc(sizeof(struct file_operations), GFP_KERNEL);
    gFile->open = hello_open;
    gFile->read = hello_read;
    gFile->write = hello_write;
    gFile->owner = THIS_MODULE;
    cdev_init(gDev, gFile);
    cdev_add(gDev, devNum, 3);
    return 0;
}

void __exit hello_exit(void) {
    cdev_del(gDev);
    unregister_chrdev_region(devNum, subDevNum);
    return;
}

module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");

2.helloDev.c解析

C语言程序的入口是main函数,而驱动的入口函数是什么呢?
module_init声明了驱动的入口函数是hello_init
当执行insmod指令的时候就会执行这个入口函数
module_exit声明了驱动的删除函数hello_exit
MODULE_LICENSE用于说明版权

devNum = MKDEV(reg_major, reg_major);
根据主设备号reg_major,从设备号号reg_major,MKDEV函数生成一个设备号,MKDEV规则是:
设备号用于唯一标识一个设备,主设备号用于标识一类设备,
#define MINORBITS	20
#define MINORMASK	((1U << MINORBITS) - 1)

#define MAJOR(dev)	((unsigned int) ((dev) >> MINORBITS))
#define MINOR(dev)	((unsigned int) ((dev) & MINORMASK))
#define MKDEV(ma,mi)	(((ma) << MINORBITS) | (mi))
register_chrdev_region(devNum, subDevNum, "helloworld")
把上面的设备号注册到内核中,subDevNum标识设备数量
注册之后,这个设备号,别人就不可以再使用了
/**
 * @from: 设备号,一般在调用之前就得通过一个主设备号和次设备号得到一个设备号
 *  比如from = MKDEV(major, 0); 0为次设备号的起始值
 * @count: 次设备个数  以major为主设备号,以0~0+count的次设备号
 * @name: 设备名称.
 *
 * Return value is zero on success, a negative error code on failure.
 */
int register_chrdev_region(dev_t from, unsigned count, const char *name)
gDev = kzalloc(sizeof(struct cdev), GFP_KERNEL);
用于申请一个结构体cdec,cdev表示一个字符设备

gFile = kzalloc(sizeof(struct file_operation), GFP_KERNEL);
用于申请一个file_operation结构体,file表示一个文件,file_operation表示对这个文件的操作
在内核中,设备也是文件
struct file_operations {
	struct module *owner;
	loff_t (*llseek) (struct file *, loff_t, int);
	ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
	ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
	ssize_t (*read_iter) (struct kiocb *, struct iov_iter *);
	ssize_t (*write_iter) (struct kiocb *, struct iov_iter *);
	int (*iterate) (struct file *, struct dir_context *);
	int (*iterate_shared) (struct file *, struct dir_context *);
	unsigned int (*poll) (struct file *, struct poll_table_struct *);
	long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
	long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
	int (*mmap) (struct file *, struct vm_area_struct *);
	int (*open) (struct inode *, struct file *);
	int (*flush) (struct file *, fl_owner_t id);
	int (*release) (struct inode *, struct file *);
	int (*fsync) (struct file *, loff_t, loff_t, int datasync);
	int (*fasync) (int, struct file *, int);
	int (*lock) (struct file *, int, struct file_lock *);
	ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
	unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
	int (*check_flags)(int);
	int (*flock) (struct file *, int, struct file_lock *);
	ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);
	ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);
	int (*setlease)(struct file *, long, struct file_lock **, void **);
	long (*fallocate)(struct file *file, int mode, loff_t offset,
			  loff_t len);
	void (*show_fdinfo)(struct seq_file *m, struct file *f);
#ifndef CONFIG_MMU
	unsigned (*mmap_capabilities)(struct file *);
#endif
	ssize_t (*copy_file_range)(struct file *, loff_t, struct file *,
			loff_t, size_t, unsigned int);
	int (*clone_file_range)(struct file *, loff_t, struct file *, loff_t,
			u64);
	ssize_t (*dedupe_file_range)(struct file *, u64, u64, struct file *,
			u64);
};
cdev_init(gDev, gFile);
gDev表示设备,gFile表示文件操作,建立了字符设备和文件操作之间的关系

cdev_add(gDev, devNum, 1);
建立了字符设备gDev和设备号devNum之间的联系
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值