概述
第3章字符驱动
1 Scull的设计
书上没看懂 就搜了一下
Scull:scull(simple character utility for loading localities,"区域装载的简单字符工具")
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方法
8使用新设备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; }
一旦你装备好刚刚描述的 4 个方法, 驱动可以编译并测试了9具体代码可以参考博客:
http://blog.csdn.net/yusiguyuan/article/details/10960519
http://mm1010114626.blog.163.com/blog/static/1640922492011510114536456/
最后
以上就是彪壮火为你收集整理的《linux设备驱动程序》第3章总结的全部内容,希望文章能够帮你解决《linux设备驱动程序》第3章总结所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复