栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 系统运维 > 运维 > Linux

linux驱动学习笔记(一)字符设备驱动

Linux 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

linux驱动学习笔记(一)字符设备驱动

linux字符设备驱动

cdev结构体cdev操作函数分配和释放设备号自动创建设备节点file_operations结构体字符设备驱动模板实例

实例说明 总结

cdev结构体

在include/linux/cdev.h

struct cdev {
	struct kobject kobj; 
	struct module *owner; 
	const struct file_operations *ops; 
	struct list_head list;
	dev_t dev;
	unsigned int count;
};
cdev操作函数
//用于初始化cdev成员,并建立文件操作和cdev之间的联系
void cdev_init(struct cdev *cdev, struct file_operations *fops)
//用于动态申请一个cdev内存
struct cdev *cdev_alloc(void)
//减少使用计数
void cdev_put(struct cdev *p);
//向系统添加一个cdev
int cdev_add(struct cdev *, dev_t, unsigned);
//向系统删除一个cdev
void cdev_del(struct cdev *);
分配和释放设备号

在调用cdev_add之前应该先申请设备号

//用于已知设备号情况
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);
//释放掉已经申请的设备号
void unregister_chrdev_region(dev_t from, unsigned count);
自动创建设备节点

1.创建类和删除类

struct class *class_create (struct module *owner, const char *name);
void class_destroy(struct class *cls);

2.创建设备和删除设备

struct device *device_create(struct class *class,struct device *parent,dev_t devt,void *drvdata,const char *fmt, ...);
void device_destroy(struct class *class, dev_t devt);
file_operations结构体
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 *);
	//函数一般用于询问设备是否可被非阻塞地立即读写。
	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 (*mremap)(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 (*aio_fsync) (struct kiocb *, 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
};
字符设备驱动模板
struct xxx_dev_t {
struct cdev cdev;
...
} xxx_dev;

int xxx_release(struct inode *inode, struct file *filp)
{
	//printk("chrdevbase release!rn");
	return 0;
}
int xxx_open(struct inode *inode, struct file *filp)
{
	//printk("chrdevbase open!rn");
	return 0;
}

ssize_t xxx_write(struct file *filp, const char __user *buf, size_t count,loff_t *f_pos){
	...
	copy_from_user(..., buf, ...);//用户空间缓冲区到内核空间的复制
	...
}

ssize_t xxx_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{
	copy_to_user(buf,...,...);
}

long xxx_ioctl(struct file *filp, unsigned int cmd,unsigned long arg){
	...
	switch (cmd) {
		case XXX_CMD1:
		...
		break;
		case XXX_CMD2:
		...
		break;
		default:
		
		return - ENOTTY;
	}
	return 0;
}
//字符设备驱动文件操作结构体模板
struct file_operations xxx_fops = {
	.owner = THIS_MODULE,
	.read = xxx_read,
	.write = xxx_write,
	.unlocked_ioctl= xxx_ioctl,
	.open = xxx_open;
	.release = xxx_release;
	...
};
static int __init xxx_init(void)
{
	...
	cdev_init(&xxx_dev.cdev, &xxx_fops); 
	xxx_dev.cdev.owner = THIS_MODULE;
	
	if (xxx_major) {
		register_chrdev_region(xxx_dev_no, 1, DEV_NAME);
	} else {
		alloc_chrdev_region(&xxx_dev_no, 0, 1, DEV_NAME);
	}
	ret = cdev_add(&xxx_dev.cdev, xxx_dev_no, 1); 
	...
}

static void __exit xxx_exit(void)
{
unregister_chrdev_region(xxx_dev_no, 1); 
cdev_del(&xxx_dev.cdev); 
...
}
实例
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#define MEM_CLEAR (_IO(0xEF, 0x1)) 
#define MEM_READ (_IOR(0xEF, 0x2)) 
#define MEM_WRITE (_IOW(0xEF, 0x3)) 
#define MEM_READ_WRITE (_IOWR(0xEF, 0x4)) 
#define DEMO_CNT			1		  	
#define DEMO_NAME			"demo"	
#define MEM_BUFFER_SIZE 1000

struct demo_dev{
	dev_t devid;			
	struct cdev cdev;		
	struct class *class;		
	struct class val_class;
	struct device *device;	
	int major;				
	int minor;				
	unsigned char mem[MEM_BUFFER_SIZE];
};
struct demo_dev demo;	

static ssize_t demo_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offset)
{
	unsigned long p = *offset;
	unsigned int count = cnt;
	int ret = 0;
	struct demo_dev *dev = filp->private_data;
	if (p >= MEM_BUFFER_SIZE)
		return 0;
	if (count > MEM_BUFFER_SIZE - p)
		count = MEM_BUFFER_SIZE - p;
	if (copy_to_user(buf, dev->mem + p, count)) {
		ret = -EFAULT;
	} else {
		*offset += count;
		ret = count;
		printk("read %u bytes(s) from %lun",cnt,p);
	}
	return ret;
}
static ssize_t demo_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offset)
{
	unsigned long p = *offset;
	unsigned int count = cnt;
	int ret = 0;
	struct demo_dev *dev = filp->private_data;
	if (p >= MEM_BUFFER_SIZE)
		return 0;
	if (count > MEM_BUFFER_SIZE - p)
		count = MEM_BUFFER_SIZE - p;
	if (copy_from_user(dev->mem + p, buf, count))
		ret = -EFAULT;
	else {
		*offset += count;
		ret = count;
		printk("written %u bytes(s) from %lun", count, p);
	}
	return ret;
}
static loff_t demo_llseek(struct file *filp, loff_t offset, int orig)
{
	loff_t ret = 0;
	switch (orig) {
		case 0: 
			if (offset< 0) {
				ret = -EINVAL;
				break;
			}
			if ((unsigned int)offset > MEM_BUFFER_SIZE) {
				ret = -EINVAL;
				break;
			}
			filp->f_pos = (unsigned int)offset;
			ret = filp->f_pos;
			break;
		case 1: 
			if ((filp->f_pos + offset) > MEM_BUFFER_SIZE) {
				ret = -EINVAL;
				break;
			}
			if ((filp->f_pos + offset) < 0) {
				ret = -EINVAL;
				break;
			}
			filp->f_pos += offset;
			ret = filp->f_pos;
		break;
		default:
			ret = -EINVAL;
			break;
	}
	return ret;
}
static long demo_ioctl(struct file *filp, unsigned int cmd,unsigned long arg)
{
	struct demo_dev *dev = filp->private_data;
	switch (cmd) {
		case MEM_CLEAR:
			memset(dev->mem, 0, MEM_BUFFER_SIZE);
			printk(KERN_INFO "demo is set to zeron");
			break;
		default:
			return -EINVAL;
	}
	return 0;
}
static int demo_open(struct inode *inode, struct file *filp)
{
	struct demo_dev *dev = container_of(inode->i_cdev,struct demo_dev, cdev);
	filp->private_data = dev;
	return 0;
}
static int demo_release(struct inode *inode, struct file *filp)
{
	return 0;
}
static struct file_operations demo_fops = {
	.owner = THIS_MODULE,
	.llseek = demo_llseek,
	.read = demo_read,
	.write = demo_write,
	.unlocked_ioctl = demo_ioctl,
	.open = demo_open,
	.release = demo_release,
};
char* strcpy(char* dest, const char* src) {
	char* tmp = dest;
	while ((*dest++ = *src++) != '0');
	return tmp;
}
static ssize_t std_show(struct device *dev,
		struct device_attribute *attr, char *buf)
{
	return sprintf(buf, "%sn",demo.mem);
}
static ssize_t std_store(struct device *dev,  
	struct device_attribute *attr, const char *buf, size_t len)
{
	strcpy(demo.mem,buf);
	return len;
}
static DEVICE_ATTR_RW(std);


static struct attribute *led_class_attrs[] = {
	&dev_attr_std.attr,
	NULL,
};

static const struct attribute_group led_group = {
	.attrs = led_class_attrs,
};

static const struct attribute_group *led_groups[] = {
	&led_group,
	NULL,
};
static int __init demo_init(void)
{
	
	if (demo.major) {		
		demo.devid = MKDEV(demo.major, 0);
		register_chrdev_region(demo.devid, DEMO_CNT, DEMO_NAME);
	} else {						
		alloc_chrdev_region(&demo.devid, 0, DEMO_CNT, DEMO_NAME);	
		demo.major = MAJOR(demo.devid);	
		demo.minor = MINOR(demo.devid);	
	}
	printk("major=%d,minor=%drn",demo.major, demo.minor);	
	
	
	demo.cdev.owner = THIS_MODULE;
	cdev_init(&demo.cdev, &demo_fops);
	
	
	cdev_add(&demo.cdev, demo.devid, DEMO_CNT);

	
	demo.val_class.owner = THIS_MODULE;
	demo.val_class.name = DEMO_NAME;
	demo.val_class.class_groups = led_groups;
	class_register(&demo.val_class);
	
	demo.device = device_create(&demo.val_class, NULL, demo.devid, NULL, DEMO_NAME);
	if (IS_ERR(demo.device)) {
		return PTR_ERR(demo.device);
	}
	
	return 0;
}
static void __exit demo_exit(void)
{
	
	cdev_del(&demo.cdev);
	unregister_chrdev_region(demo.devid, DEMO_CNT); 

	device_destroy(&demo.val_class, demo.devid);
	class_unregister(&demo.val_class);
	//class_destroy(demo.class);
}
//将上面两个函数指定为驱动的入口和出口函数 
module_init(demo_init);
module_exit(demo_exit);

//LICENSE和作者信息
MODULE_LICENSE("GPL");
MODULE_AUTHOR("bin");

container_of() 的作用是通过结构体成员的指针找到对应结构体的指针,这个技巧在Linux内核编程中十分常用。
在struct demo_dev *dev = container_of(inode->i_cdev,struct demo_dev, cdev); 语句
中, 传给container_of() 的第1个参数是结构体成员的指针, 第2个参数为整个结构体的类型, 第3个参数
为传入的第1个参数即结构体成员的类型, container_of() 返回值为整个结构体的指针。

实例说明

本实例以两方式进行和应用层通信 分别是file_operations , sysfs
编译加载到内核中后,可以在/dev节点下找到demo这个文件,或者在/sys/class/demo中找到std文件 可对应进行读写操作

总结

字符设备驱动主要工作就是初始化,添加删除结构体,申请释放设备号,编写file_operations 的操作函数等。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/740317.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号