您的位置:首页 > 其它

ldd3 chapter3 字符驱动简单记录

2016-06-19 17:43 369 查看
动态分配节点号:

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

删除节点号:

void unregister_chrdev_region(dev_t from, unsigned count)


动态分配cdev:

struct cdev *cdev_alloc(void)
初始化cdev,并绑定file_operations:

void cdev_init(struct cdev *cdev, const struct file_operations *fops)

将cdev和节点号绑定:

int cdev_add(struct cdev *, dev_t, unsigned);


删除cdev:

void cdev_del(struct cdev *p)

代码:

#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/list.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <asm/uaccess.h>
#include <linux/semaphore.h>

loff_t scull_llseek(struct file *filp, loff_t f_ops, int flag);

ssize_t scull_read(struct file *filp, char __user * buf, size_t count, loff_t *f_ops);

ssize_t scull_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_ops);

int scull_ioctl(struct inode *p_inode, struct file *filp, unsigned int cmd, unsigned long args);

int scull_open(struct inode *p_inode, struct file *filp);

int scull_release(struct inode *p_inode, struct file *filp);

struct file_operations scull_fops =
{
.owner = THIS_MODULE,
.llseek = scull_llseek,
.read = scull_read,
.write = scull_write,
.ioctl = scull_ioctl,
.open = scull_open,
.release = scull_release,
};

struct scull_qset
{
struct list_head qset_node;
void **data;
};

struct scull_dev
{
struct list_head qset_head;
int quantum;
int qset;
unsigned long size;

dev_t dev_t;
struct cdev cdev;
struct semaphore sem;
};

struct scull_dev g_scull_dev;

//根据index 返回p_qset结构,如果没有就创建一个
struct scull_qset *scull_follow(struct scull_dev* dev, int index)
{
int i = 0;
struct list_head * p_qset_node;
struct scull_qset *p_qset;

list_for_each(p_qset_node, &dev->qset_head)
{
if((i++) == index)
{
return container_of(p_qset_node, struct scull_qset, qset_node);
}
}

p_qset = (struct scull_qset *)kmalloc(sizeof(struct scull_qset), GFP_KERNEL);
if (p_qset == NULL)
return NULL;
memset(p_qset, 0, sizeof(struct scull_qset));

p_qset->data = kmalloc(dev->qset * sizeof(char *), GFP_KERNEL);
if (p_qset->data == NULL)
{
kfree(p_qset);
return NULL;
}
memset(p_qset->data, 0, dev->qset * sizeof(char *));

list_add_tail(&p_qset->qset_node, &dev->qset_head);

return p_qset;
}

int scull_trim(struct scull_dev *dev)
{
struct list_head *p_qset_node = NULL;
struct scull_qset *p_qset;
int i;

while(!list_empty(&dev->qset_head))
{
p_qset_node = dev->qset_head.next;
p_qset = container_of(p_qset_node, struct scull_qset, qset_node);
for (i = 0; i < dev->qset; i++)
{
kfree(p_qset->data[i]);
}

list_del(p_qset_node);
kfree(p_qset);
}

dev->size = 0;

return 0;
}

loff_t scull_llseek(struct file *filp, loff_t f_ops, int flag)
{
printk(KERN_ERR"llseek\n");
return 0;
}

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 *p_qset;

int qset_size = dev->quantum * dev->qset;
int qset_index,qset_rest;
int quan_index,quan_rest;
ssize_t retval = 0;

if (down_interruptible(&dev->sem))
return -ERESTARTSYS;

qset_index = (long)*f_ops / qset_size;
qset_rest = (long)*f_ops % qset_size;

quan_index = qset_rest / dev->quantum;
quan_rest = qset_rest % dev->quantum;

if (*f_ops > dev->size)
goto out;

if ((*f_ops + count) > dev->size)
{
count = dev->size - *f_ops;
}

p_qset = scull_follow(dev, qset_index);

if (p_qset == NULL || p_qset->data == NULL || p_qset->data[quan_index] == NULL)
{
goto out;
}

if (count > (dev->quantum - quan_rest))
{
count = dev->quantum - quan_rest;
}

if (copy_to_user(buf, p_qset->data[quan_index] + quan_rest, count))
{
retval = -EFAULT;
goto out;
}

*f_ops += count;
retval = count;

out:
up(&dev->sem);
return retval;
}

ssize_t scull_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_ops)
{
struct scull_dev *dev = filp->private_data;
struct scull_qset *p_qset = NULL;

int qset_size = dev->quantum * dev->qset;
int qset_index,qset_rest;
int quan_index,quan_rest;
ssize_t retval = -ENOMEM;

if(down_interruptible(&dev->sem))
return -ERESTARTSYS;

qset_index = (long)*f_ops / qset_size;
qset_rest  = (long)*f_ops % qset_size;

quan_index = qset_rest / dev->quantum;
quan_rest = qset_rest % dev->quantum;

p_qset = scull_follow(dev, qset_index);
if (p_qset == NULL)
goto out;

if (p_qset->data[quan_index] == NULL)
{
p_qset->data[quan_index] = kmalloc(dev->quantum, GFP_KERNEL);
if (p_qset->data[quan_index] == NULL)
goto out;
}

if (count > (dev->quantum - quan_rest))
{
count = dev->quantum - quan_rest;
}

if (copy_from_user(p_qset->data[quan_index] + quan_rest, buf, count))
{
retval = -EFAULT;
goto out;
}

*f_ops += count;
retval = count;

if (dev->size < *f_ops)
dev->size = *f_ops;

out:
up(&dev->sem);
return retval;
}

int scull_ioctl(struct inode *p_inode, struct file *filp, unsigned int cmd, unsigned long args)
{
printk(KERN_ERR"ioctl\n");
return 0;
}

int scull_open(struct inode *p_inode, struct file *filp)
{
struct scull_dev *dev = NULL;
dev = container_of(p_inode->i_cdev, struct scull_dev, cdev);

filp->private_data = dev;
//如果以append模式打开,则文件指针指向末尾
if (filp->f_flags & O_APPEND)
{
filp->f_pos = p_inode->i_size;
}
//如果以trunc模式打开,则将dev内容清除
if (filp->f_flags & O_TRUNC)
{
scull_trim(dev);
p_inode->i_size = dev->size;
}

printk(KERN_ERR"file size = %ld\n",dev->size);
return 0;
}

int scull_release(struct inode *p_inode, struct file *filp)
{
struct scull_dev *dev = filp->private_data;

if(down_interruptible(&dev->sem))
return -ERESTARTSYS;
//更新文件大小
p_inode->i_size = dev->size;

up(&dev->sem);

return 0;
}

static int scull_init(void)
{
int result = 0;
//分配节点号
if ((result = alloc_chrdev_region(&g_scull_dev.dev_t, 0, 4, "scull")) < 0)
{
printk(KERN_ERR"alloc_chrdev_region error!\n");
return result;
}
//初始化cdev结构,并绑定file_operations和节点号
cdev_init(&g_scull_dev.cdev, &scull_fops);
cdev_add(&g_scull_dev.cdev, g_scull_dev.dev_t, 4);
printk(KERN_ERR"major = %d,minor = %d\n",MAJOR(g_scull_dev.dev_t),MINOR(g_scull_dev.dev_t));

//初始化设备结构体
g_scull_dev.qset = 4096;
g_scull_dev.quantum = 4096;
INIT_LIST_HEAD(&g_scull_dev.qset_head);
sema_init(&g_scull_dev.sem, 1);

return 0;
}

static void scull_exit(void)
{
cdev_del(&g_scull_dev.cdev);
unregister_chrdev_region(g_scull_dev.dev_t, 1);
scull_trim(&g_scull_dev);
return;
}

module_init(scull_init);
module_exit(scull_exit);

MODULE_LICENSE("Dual BSD/GPL");


Makfile:

obj-m += scull.o
KDIR := /home/linux-2.6.32.2
PWD = $(shell pwd)
all:
make -C $(KDIR) M=$(PWD) modules

clean:
rm -rf *.o


测试:
[root@FriendlyARM /nfs]# insmod scull.ko

major = 253,minor = 0

[root@FriendlyARM /nfs]# mknod /dev/scull0 c 253 0

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  字符设备