概述
在内部,scull使用一个struct scull_dev类型的结构表示每个设备,定义如下:
struct scull_dev{
struct scull_qset *data; /*Pointer to first quantum set*/
int quantum; /*the current quantum size*/
int qset; /*the current array size*/
unsigned long size; /*amount of data stored here*/
unsigned int access_key; /*used by sculluid and scullpriv*/
struct semaphore sem; /*mutual exclusion semaphore*/
struct cdev cdev; /*Char device structure*/
};
scull驱动的设备注册:
struct cdev是内核内部的设备表示类型,在scull中对其进行初始化如下,
static void scull_setup_cdev(struct scull_dev *dev, int index)
{
int err, devno = MKDEV(scull_major, scull_minor+index);
cdev_init(&dev->cdev, &scull_fops);
dev->cdev.owner = THIS MODULE;
dev->cdev.ops = &scull_fops;
err = cdev_add(&dev->cdev, devno, 1);
/*Fail gracefully if need be*/
if(err)
printk(KERN_NOTICE "Error %d adding scull%d", err, index);
}
注册一个字符设备的经典方法是使用:
int register_chrdev(unsigned int major, const char *name, struct file_operation *fops);
int unregister_chrdev(unsigned int major, const char *name);
open方法:
open方法提供给驱动来做任何的初始化来准备后续的操作,在大部分驱动中,open应当进行下面的工作
- 检查设备特定的错误(例如设备没准备好,或者类似的硬件错误)
- 如果它第一次打开,初始化设备
- 如果需要,更新f_op指针
- 分配并填充要放进filp->private_data的任何数据结构
open原型: int (*open)(struct inode *inode, struct file *filp);
int scull_open(struct inode *inode, strcut 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*/
}
release方法:
release方法是open方法的反面,在任何实现方式下,设备方法应当进行下面的任务,
- 释放open分配在filp->private_data中的任何东西
- 在最后的close关闭设备
int scull_release(struct inode *inode, struct file *filp)
{
return 0;
}
读和写:
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 count, loff_t *offp);
filp,文件指针;count,请求传输的数据大小;buff,指向持有被写入数据的缓存或者放入新数据的缓存;offp,指向一个'long offset type'对象,指出用户正在存取的文件位置。
ssize_t scull_read(struct file *filp, char __user *buf, size_t count, loff_t *f_ops)
{
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_ops >= dev->size)
goto out;
if(*f_ops + count > dev->size)
count = dev->size - *f_pos;
/*find listitem, qset index, and offset in the quantum*/
item = (long)*f_ops / itemsize;
rest = (long)*f_ops % 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;
}
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;
}
矢量操作的原型是:
ssize_t (*readv) (struct file *filp, const struct iovec *iov, unsigned long count, loff_t *ppos); ssize_t (*writev) (struct file *filp, const struct iovec *iov, unsigned long count, loff_t *ppos);
最后
以上就是可耐外套为你收集整理的Linux Device Driver: char device 的全部内容,希望文章能够帮你解决Linux Device Driver: char device 所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复