动态分配节点号:
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