linux字符设备注册

头文件:

#include <linux/module.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/mm.h>
#include <linux/sched.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <linux/slab.h>
#include <asm/io.h>
#include <asm/system.h>
#include <asm/uaccess.h>

 

相关内容

2.6内核中使用cdev结构描述一个字符设备。

struct cdev {
	struct kobject kobj;
	struct module *owner;
	const struct file_operations *ops;
	struct list_head list;
	dev_t dev;
	unsigned int count;
};

从dev_t获取主次设备号

MAJOR(dev_t dev)
MINOR(dev_t dev)

#define MINORBITS 20
#define MINORMASK ((1U << MINORBITS) - 1)

#define MAJOR(dev) ((unsigned int) ((dev) >> MINORBITS))
#define MINOR(dev) ((unsigned int) ((dev) & MINORMASK))
由主设备号和此设备号生成设备号dev_t

MKDEV(int major,int minor)

#define MKDEV(ma,mi) (((ma) << MINORBITS) | (mi))

申请设备号

int register_chrdev_region(dev_t from,unsigned count,const char *name);
int alloc_chrdev_region(dev_t *dev,unsigned baseminor,unsigned count,const char *name);

2.6内核操作cdev的一组函数

void cdev_init(struct cdev *,struct file_operations *);
struct cdev *cdev_alloc(void);
void cdev_put(struct cdev *p);
int cdev_add(struct cdev *,dev_t,unsigned);
void cdev_del(struct cdev *);


struct file_operations {
	struct module *owner;//拥有该模块的指针,一般赋值为THIS_MODULES 
	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 (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);//初始化一个异步的读操作 
	ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);//初始化一个异步的写操作
	int (*readdir) (struct file *, void *, filldir_t);//仅用于读取目录,对于设备文件,该字段为NULL 
	unsigned int (*poll) (struct file *, struct poll_table_struct *); //轮训函数,判断目前是否可以以非阻塞方式读/写 
	int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long); 
	long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
	//不适用BLK,的文件系统,用该函数代替ioctl 
	long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
	//64为系统的32位ioctl将用此函数指针代替 
	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 *, struct dentry *, int datasync);//刷新待处理的数据 
	int (*aio_fsync) (struct kiocb *, int datasync);//异步 fsync
	int (*fasync) (int, struct file *, int);//通知FASYNC标志发生变化 
	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);
	//通常为NULL 
	int (*check_flags)(int);
	//允许模块检查传递给fcntl(F_SETEL...)调用的标志 
	int (*dir_notify)(struct file *filp, unsigned long arg);
	//对文件系统有效,驱动程序不必实现 
	int (*flock) (struct file *, int, struct file_lock *);
	ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);
	//VFS调用,将管道数据粘接到文件
	ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);
	//VFS调用,将文件数据粘接到管道
	int (*setlease)(struct file *, long, struct file_lock **);
};


void cdev_init(struct cdev *cdev, const struct file_operations *fops)
{
	memset(cdev, 0, sizeof *cdev);
	INIT_LIST_HEAD(&cdev->list);
	kobject_init(&cdev->kobj, &ktype_cdev_default);
	cdev->ops = fops;
}

struct cdev *cdev_alloc(void)
{
	struct cdev *p = kzalloc(sizeof(struct cdev), GFP_KERNEL);
	if (p) {
		INIT_LIST_HEAD(&p->list);
		kobject_init(&p->kobj, &ktype_cdev_dynamic);
	}
	return p;
}

int cdev_add(struct cdev *p, dev_t dev, unsigned count)
{
	p->dev = dev;
	p->count = count;
	return kobj_map(cdev_map, dev, count, NULL, exact_match, exact_lock, p);
}

void cdev_del(struct cdev *p)//调用该函数后需调用unregister_chrdev_region() 释放申请到的设备号 
{
	cdev_unmap(p->dev, p->count);
	kobject_put(&p->kobj);
}
void unregister_chrdev_region(dev_t from,unsigned count); 

字符驱动模块加载与卸载函数模板

/*设备结构体*/
struct xxx_dev_t{
	struct cdev cdev;
	...
} xxx_dev;
//驱动模块加载函数
static int __init xxx_init(void)
{
	...
	cdev_init(&xxx_dev.cdev,&xxx_fops); // 初始化 
	xxx_dev.owmer = THIS_MODULE;
	if(xxx_major){						// 申请设备号 
		register_chdev_region(xxx_dev_no,num,DEV_NAME);
	}else{
		alloc_chrdev_region(&xxx_dev_no,base,num,DEV_NAME);
	}  
	ret = cdev_add(&xxx_dev.cdev,xxx_dev_no,num); // 注册设备号 
}  
//模块卸载函数
static void __exit xxx_exit(void)
{
	unregister_chrdev_region(xxx_dev_no,num);
	cdev_del(&xxx_dev.cdev);
	... 
} 
字符设备驱动读,写,IO控制模板

ssize_t xxx_read(struct file *filp,char __user *buf,szie_t count,loff_t *f_pos)
{
	...
	copy_to_user(buf,...,...); 
	...
} 
ssize_t xxx_write(struct file *filp,const char __user *buf,szie_t count,loff_t *f_pos)
{
	...
	copy_from_user(...,buf,...); 
	...
} 
/*内核空间与用户空间不能直接访问,续借助一下两个函数,返回值为不能被赋值的字节数*/ 
unsigned long copy_from_user(void *to, const void __user *from, unsigned long n)
unsigned long copy_to_user( void __user *to, const void *from, unsigned long n)
//复制的是char、int、long可使用put_user()get_user()
int xxx_ioctl(struct inode *inode,struct file *filep,unsigned int cmd,unsigned long arg)
{
	命令有效性检测
	依据命令类型,检测参数空间是否可以访问
	switch(cmd){
	case XXX_CMD1:
		...
		break;
	case XXX_CMD1:
		...
		break; 
	default://不支持的命令 
		return -ENOTTY;	
	} 
	return 0; 
}  
文件操作函数模板
struct file_operations xxx_fops = {
	.owner = THIS_MODULE,
	.read = xxx_read,
	.write = xxx_write,
	.iotcl = xxx_ioctl,
	...
};
驱动中同时包含两个以上的设备在xxxopen中通过container_of通过结构体成员的指针找到对应结构体的指针

#define container_of(ptr, type, member) ({			\
        const typeof( ((type *)0)->member ) *__mptr = (ptr);	\
        (type *)( (char *)__mptr - offsetof(type,member) );})

当然也可以通过inode结构体中的i_rdev成员获取其次设备号













评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值