ldd3 chapter3 字符驱动简单记录

动态分配节点号:

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


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值