《linux设备驱动程序》第3章总结

第3章字符驱动
1  Scull的设计
书上没看懂  就搜了一下

Scull:scull(simple character utility for loading localities,"区域装载的简单字符工具")


2    主次编号

2.1 每个驱动都有主次编号,由编号来控制设备驱动


主编号标识相连的驱动,

次编号由内核分配的指针来决定引用哪个设备

主次编号都在<linux/types.h>里

2.2 如果想获得一个dev_t的主次编号使用:

# <linux/kdev_t.h>

MAJOR(dev_t dev)

MINOR(dev_t dev)

相反转换一个dev_t 使用:

     MKDEV(int major,int minor)
2.3 分配和释放设备
知道设备编号使用:

#<linux/fs.h>
int register_chrdev_region(dev_t first,unsigned int count,char *name);
不知道设备编号,内核动态分配一个主编号,使用下面函数,也是常用到的

int alloc_chrdev_region(dev_t *dev,unsigned int firstminor,unsigned int count,char *name)
当你不使用设备编号时  要释放掉

void unregister_chrdev_region(dev_t first,unsigned int count)

3一些重要的数据结构
注册设备编号只是第1步,还包括file_operations,file,inode
3.1 file_operations
是一个字符驱动如何建立连接的结构,定义在<linux/fs.h>里,是1个函数指针集合,结构中每个成员必须指向驱动中的函数
3.2 file(文件结构)
struct file 定义于<linux/fs.h>,是设备驱动中第二个最重要的数据结构,是内核结构,不出现在用户程序中
struct file中一些成员:
mode_t f_mode;文件模式是可读或者可写
loff_t f_pos;当前读写位置
unsigned int f_flags;文件标志
struct file_operations *f_op;内核安排指针为它的open而用
void *private_data;open系统调用这个指针为null
struct dentry *f_debtry;关联到文件的目录入口(dentry)结构
3.3 inode 结构
inode 只有2个成员对编写驱动代码有用:
dev_t  i_rdev;
代表设备文件节点,包含实际的设备编号
struct cdev *i_cdev;
struct cdev是内核内部结构,代表字符设备


4字符设备注册
4.1 分配和初始化这些结构

#<linux/cdev.h>
struct cdev *my_cdev = cdev_alloc();
my_cdev -> ops = &my_fops
4.2 如果嵌入设备特定的结构,使用

void cdev_init(struct cdev *cdev,struct file_operations *fops);
4.3 建立之后告诉内核,调用:

int cdev_add(struct cdev *dev,dev_t num,unsigned int count);
4.4 去除一个字符设备
void cdev_del(struct cdev *dev);

5 open和release
5.1 open方法在初始化之后准备后续的操作,包括
检查设备特定的错误
如果它第一次打开,初始化设备
如果需要,更新f_op指针
分配并填充要放进filp->private_data的任何数据结构
5.2 Open方法原型:

int (*open)(struct inode *inode,struct file *file);
int scull_open(struct inode *inode, struct file *filp)
{
	struct scull_dev *dev; /* device information */
	dev = container_of(inode->i_cdev, struct scull_dev, cdev);
	filp->private_data = dev; /* for other methods */
	/* now trim to 0 the length of the device if open was write-only */
	if ( (filp->f_flags & O_ACCMODE) == O_WRONLY)
	{
		scull_trim(dev); /* ignore errors */
	}
	return 0; /* success */
}


5.3 release方法和open相反,相当于关闭设备

int scull_release(struct inode *inode, struct file *filp)
{
return 0;
}

6 scull的内存使用


7读和写
7.1 读和写的原型

ssize_t read(struct file *filp, char __user *buff, size_t count, loff_t *offp);
ssize_t write(struct file *filp, const char __user *buff, size_t c	ount, loff_t *offp);
7.2给read的参数方法:


ssize_t scull_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
{
	struct scull_dev *dev = filp->private_data;
	struct scull_qset *dptr; /* the first listitem */
	int quantum = dev->quantum, qset = dev->qset;
	int itemsize = quantum * qset; /* how many bytes in the listitem */
	int item, s_pos, q_pos, rest;
	ssize_t retval = 0;

	if (down_interruptible(&dev->sem)
		return -ERESTARTSYS;
	if (*f_pos >= dev->size)
		goto out;
	if (*f_pos + count > dev->size)
		count = dev->size - *f_pos;
		/* find listitem, qset index, and offset in the quantum */
	item = (long)*f_pos / itemsize;
	rest = (long)*f_pos % itemsize;
	s_pos = rest / quantum;
	q_pos = rest % quantum;
	/* follow the list up to the right position (defined elsewhere) */
	dptr = scull_follow(dev, item);
	if (dptr == NULL || !dptr->data || ! dptr->data[s_pos])
		goto out; /* don't fill holes */
	/* read only up to the end of this quantum */
	if (count > quantum - q_pos)
		count = quantum - q_pos;
	if (copy_to_user(buf, dptr->data[s_pos] + q_pos, count))
	{
		retval = -EFAULT;
		goto out;
	}
	*f_pos += count;
	retval = count;
	out:
	up(&dev->sem);
	return retval;
}



7.3 write方法

ssize_t scull_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos)
{
	struct scull_dev *dev = filp->private_data;
	struct scull_qset *dptr;
	int quantum = dev->quantum, qset = dev->qset;
	int itemsize = quantum * qset;
	int item, s_pos, q_pos, rest;
	ssize_t retval = -ENOMEM; /* value used in "goto out" statements */
	if (down_interruptible(&dev->sem))
	return -ERESTARTSYS;
	/* find listitem, qset index and offset in the quantum */
	item = (long)*f_pos / itemsize;
	rest = (long)*f_pos % itemsize;
	s_pos = rest / quantum;
	q_pos = rest % quantum;
	/* follow the list up to the right position */
	dptr = scull_follow(dev, item);
	if (dptr == NULL)
		goto out;
	if (!dptr->data)
	{
		dptr->data = kmalloc(qset * sizeof(char *), GFP_KERNEL);
		if (!dptr->data)
			goto out;
		memset(dptr->data, 0, qset * sizeof(char *));
	}
	if (!dptr->data[s_pos])
	{
		dptr->data[s_pos] = kmalloc(quantum, GFP_KERNEL);
		if (!dptr->data[s_pos])
			goto out;
	}
	/* write only up to the end of this quantum */
	if (count > quantum - q_pos)
	count = quantum - q_pos;
	if (copy_from_user(dptr->data[s_pos]+q_pos, buf, count))
	{
		retval = -EFAULT;
		goto out;
	}
	*f_pos += count;
	retval = count;
	/* update the size */
	if (dev->size < *f_pos)
	dev->size = *f_pos;
	out:
	up(&dev->sem);
	return retval;
}
8使用新设备
一旦你装备好刚刚描述的 4 个方法, 驱动可以编译并测试了

9具体代码可以参考博客:

http://blog.csdn.net/yusiguyuan/article/details/10960519
http://mm1010114626.blog.163.com/blog/static/1640922492011510114536456/



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值