设备驱动之一 - 基于第三章的组织结构改变的scull驱动


       LDD3到第三章的组织结构改变的scull驱动

scull.h文件---驱动头文件

/**********************************************
 * Author: lewiyon@hotmail.com
 * File name: scull.h
 * Description: define scull
 * Date: 2012-07-4
 *********************************************/
#ifndef __SCULL_H
#define __SCULL_H

#include <linux/semaphore.h>
#include <linux/cdev.h>

#ifndef SCULL_MAJOR
#define SCULL_MAJOR     0
#endif

#ifndef SCULL_NR_DEVS
#define SCULL_NR_DEVS   1
#endif

#ifndef SCULL_QUANTUM
#define SCULL_QUANTUM   4096
#endif

#ifndef SCULL_QSET
#define SCULL_QSET      4096
#endif

/*
 * parameters of module
 */
extern int scull_major;
extern int scull_quantum;
extern int scull_qset;

struct scull_qset {
    void **data;
    struct scull_qset *next;
};

/*
 * define scull_dev struct
 */
struct scull_dev {
    struct scull_qset *data; /* point first child set */
    int quantum;             /* size of current set   */
    int qset;                /* size of array         */
    unsigned long size;      /* save total quantity   */
    unsigned int access_key; /* used by scullid and scullpriv */
    struct semaphore sem;    /* mutex                 */
    struct cdev cdev;        /* character device      */
};

extern int scull_trim(struct scull_dev *dev);

#endif

 

main.c -- 驱动模块实现源码

/**********************************************
 * Author: lewiyon@hotmail.com
 * File name: scullmod.c 
 * Description: initialize and release function for scull
 * Date: 2012-07-4
 *********************************************/

#include <linux/init.h>         /* module        */
#include <linux/module.h>       /* module        */
#include <linux/moduleparam.h>  /* module        */
#include <linux/errno.h>        /* error codes   */
#include <linux/kernel.h>       /* printk        */
#include <linux/slab.h>         /* kmalloc kfree */
#include <linux/types.h>        /* dev_t         */

/* local head files */
#include "scull.h"
#include "file.h"

/* default parameters of module */
int scull_major = SCULL_MAJOR;
int scull_minor = 0;
int scull_nr_devs = SCULL_NR_DEVS;
int scull_quantum = SCULL_QUANTUM;
int scull_qset = SCULL_QSET;

/* input parameters of module */
module_param(scull_major, int, S_IRUGO);
module_param(scull_minor, int, S_IRUGO);
module_param(scull_nr_devs, int, S_IRUGO);
module_param(scull_quantum, int, S_IRUGO);
module_param(scull_qset, int, S_IRUGO);

struct scull_dev *scull_devices;

/*
 * scull_trim -  遍历链表,并释放所有找到的量子和量子集
 * @dev: scull设备
 */
int scull_trim(struct scull_dev *dev)
{
    int i, qset;
    struct scull_qset *next, *dptr;
    
    qset = dev->qset;
    for (dptr = dev->data; dptr; dptr = next) {
        if (dptr->data) {
            for (i = 0; i < qset; i++)
                kfree(dptr->data[i]);
            kfree(dptr->data);
            dptr->data = NULL;
        }
        next = dptr->next;
        kfree(dptr);
    }
    dev->size = 0;
    dev->quantum = scull_quantum;
    dev->qset = scull_qset;
    dev->data = NULL;

    return 0;
}

static void scull_setup_cdev(struct scull_dev *dev, int index)
{
    int err;
    int devno;

    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);
    if (err) 
        printk(KERN_NOTICE "Error %d adding scull%d", err, index);
    
}

/*
 * initialze scull module 
 */
void scull_cleanup_module(void)
{
    int i;
    dev_t devno;

    devno = MKDEV(scull_major, scull_minor);
    /* delete each entry */
    if (scull_devices) {
        for (i = 0; i < scull_nr_devs; i++) {
            scull_trim(scull_devices + i);
            cdev_del(&scull_devices[i].cdev);
        }
        kfree(scull_devices);
    }

    /* unregister */
    unregister_chrdev_region(devno, scull_nr_devs);
    printk(KERN_WARNING "scull: Bye!\n");
}


/*
 * initialze scull module 
 */
int scull_init_module(void)
{
    int i, res;
    dev_t dev = 0;
    
    if (scull_major) {
        dev = MKDEV(scull_major, scull_minor);
        res = register_chrdev_region(dev, scull_nr_devs, "scull");
    } else {
        res = alloc_chrdev_region(&dev, scull_minor, 
                scull_nr_devs, "scull");
        scull_major = MAJOR(dev);
    }
    if (res < 0) {
        printk(KERN_WARNING "scull: can't get major %d\n", scull_major);
        return res;
    }

    /* 
     * allocate the device struct cache
     */
    scull_devices = kmalloc(scull_nr_devs * sizeof(struct scull_dev), 
            GFP_KERNEL);
    if (NULL == scull_devices) {
        res = -ENOMEM;
        printk(KERN_WARNING "scull: NOMEM for scull!\n");
        goto fail;
    }
    memset(scull_devices, 0, scull_nr_devs * sizeof(struct scull_dev));
    /* initialize each device */
    for (i = 0; i < scull_nr_devs; i++) {
        scull_devices[i].quantum = scull_quantum;
        scull_devices[i].qset = scull_qset;
        sema_init(&scull_devices[i].sem, 1);
        scull_setup_cdev(&scull_devices[i], i);
    }
    printk(KERN_INFO "scull: OK!\n");
    return 0;
fail:
    scull_cleanup_module();
    return res;
}

module_init(scull_init_module);
module_exit(scull_cleanup_module);

MODULE_AUTHOR("lewiyon@hotmail.com");
MODULE_LICENSE("GPL");

file.h  -- 文件操作函数头文件

/**********************************************
 * Author: lewiyon@hotmail.com
 * File name: file.h
 * Description: file header 
 * Date: 2012-07-4
 *********************************************/
#ifndef __SCULL_FILE_H
#define __SCULL_FILE_H

extern const struct file_operations scull_fops;

#endif

file.c -- 文件操作函数源码

/**********************************************
 * Author: lewiyon@hotmail.com
 * File name: file.c
 * Description: realize cull file ops
 * Date: 2012-07-04
 *********************************************/

#include <linux/module.h>       /* THIS_MODULE          */
#include <linux/kernel.h>       /* printk & container   */
#include <linux/uaccess.h>      /* cp_to/from_user      */
#include <linux/types.h>        /* size_t               */
#include <linux/fs.h>           /* inode st.            */

#include "scull.h" 

/* 
 * scull_follow - 在指定的scull_dev中查找指定的量子集
 * @dev: scull_dev设备结构体指针
 * @n: 量子集在scull_dev中的位置
 * 
 * return: 
 *        @对应量子集结构指针 - 成功;
 *        @NULL - 失败;
 */
struct scull_qset *scull_follow(struct scull_dev *dev, int n)
{
    struct scull_qset *qs;
    
    qs = dev->data;
    if (!qs)
    {
        qs = dev->data = kmalloc(sizeof(struct scull_qset), GFP_KERNEL);
        if (qs == NULL) 
        {
            printk(KERN_WARNING "scull_follow_if_fail\n");
            return NULL;
        }
        memset(qs, 0, sizeof(struct scull_qset));
    }
    
    /* 创建其他的量子集 */
    while (n--) 
    {
        if (!qs->next)
        {
            qs->next = kmalloc(sizeof(struct scull_qset), GFP_KERNEL);
            if (NULL == qs->next)
            {
                printk(KERN_WARNING "scull_follow_n_%d\n", n);
                return NULL;
            }
            memset(qs->next, 0, sizeof(struct scull_qset));
        }
        qs = qs->next;
    }
    
    return qs;
}

/* 
 * scull_read - 从scull_dev中的文件读取数据
 */
ssize_t scull_read(struct file *filp, char __user *buf, 
        size_t count, loff_t *f_pos)
{
    struct scull_dev *dev;
    struct scull_qset *dptr;
    int quantum, qset, itemsize;
    int item, rest, s_pos, q_pos;
    ssize_t retval;
    
    dev = filp->private_data;
    quantum = dev->quantum;
    qset = dev->qset;
    itemsize = quantum * qset;
    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;
    
    /* 查找listitem中量子集的索引以及量子偏移位 */
    item = (long)*f_pos / itemsize;    
    rest = (long)*f_pos % itemsize;
    s_pos = rest / quantum;
    q_pos = rest % quantum;
    
    /* 在dev中查找量子集item */
    dptr = scull_follow(dev, item);
    if (NULL == dptr || !dptr->data || !dptr->data[s_pos])
        goto out;

    /* 只读取到当前量子的结尾 */        
    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;
}

/* 
 * scull_write - 往scull_dev中的文件写入数据
 */
ssize_t scull_write(struct file *filp, const char __user *buf, 
        size_t count, loff_t *f_pos)
{
    struct scull_dev *dev;
    struct scull_qset *dptr;
    int quantum , qset;
    int itemsize, item, s_pos, q_pos, rest;
    ssize_t retval;    
    
    dev = filp->private_data;
    quantum = dev->quantum;
    qset = dev->qset;
    itemsize = quantum * qset;
    retval = -ENOMEM;
    
    if (down_interruptible(&dev->sem))
        return -ERESTARTSYS;
    
    /* 查找listitem中量子集的索引以及量子偏移位 */
    item = (long)*f_pos / itemsize;    
    rest = (long)*f_pos % itemsize;
    s_pos = rest / quantum;
    q_pos = rest % quantum;
    
    /* 在dev中查找量子集item */
    dptr = scull_follow(dev, item);
    if (dptr == NULL)
    {
        printk(KERN_WARNING "scull_follow_fail\n");
        goto out;
    }
    if (!dptr->data)
    {
        dptr->data = kmalloc(qset * sizeof(char *), GFP_KERNEL);
        if (!dptr->data)
        {
            printk(KERN_WARNING "km_dptr->data_fail\n");
            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])
        {
            printk(KERN_WARNING "km_dptr->data[s_pos]_fail\n");
            goto out;
        }
    }
    
    /* 只写到当前量子末尾 */
    if (count > quantum - q_pos)
        count = quantum - q_pos;
    
    if (copy_from_user(dptr->data[s_pos] + q_pos, buf, count))
    {
        retval = -EFAULT;
        printk(KERN_WARNING "copy_fail\n");
        goto out;
    }
    *f_pos += count;
    retval = count;
    
    /* 更新scull_dev数据大小记录 */
    if (dev->size < *f_pos)
        dev->size = *f_pos;

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

/* 
 * scull_llseek - 往scull_dev中的文件写入数据
 */
loff_t scull_llseek(struct file *filp, loff_t off, int whence)
{
    struct scull_dev *dev;
    loff_t newpos;
    
    dev = filp->private_data;
    switch(whence) 
    {
        case SEEK_SET:
            newpos = off;
            break;
        case SEEK_CUR:
            newpos = filp->f_pos + off;
            break;
        case SEEK_END:
            newpos = dev->size + off;
            break;
        default:
            return -EINVAL;
    }
    if (newpos < 0) 
        return -EINVAL;
    filp->f_pos = newpos;
    return newpos;
}

/* 
 * scull_open - 打开scull_dev中的文件
 */
int scull_open(struct inode *inode, struct file *filp)
{
    struct scull_dev *dev;
    
    dev = container_of(inode->i_cdev, struct scull_dev, cdev); 
    filp->private_data = dev;
    if (O_WRONLY == (filp->f_flags & O_ACCMODE))
    { 
        if (down_interruptible(&dev->sem))
            return -ERESTARTSYS;
        
        scull_trim(dev); 
        up(&dev->sem);
    }

    return 0;
}

/* 
 * scull_release - 关闭scull_dev中的文件
 */
int scull_release(struct inode *inode, struct file *filp)
{
    return 0;
}

const 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,
};

makefile文件

obj-m += scull.o 
scull-objs := main.o file.o 
KERNELBUILD := /lib/modules/$(shell uname -r)/build
default:
	$(MAKE) -C $(KERNELBUILD) M=$(shell pwd) modules
clean:
	-rm -rf *.o .*.cmd *.ko* *.mod.c .tmp_versions
	-rm -rf  modules.order Module.symvers

测试文件:

#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include  <errno.h>


int main()
{
    int fp0;
    char Buf[4096];
    
    /* 初始化Buf */
    strcpy(Buf,"Scull is char dev!");
    printf("BUF: %s\n",Buf);
    
    /* 打开设备文件 */
    fp0 = open("/dev/scull0", O_RDWR);
    if (fp0 < 0)
    {
        printf("Open scull Error!\n");
        return -1;
    }
    
    /* 写入设备 */
    strcpy(Buf,"Scull write!");
    write(fp0, Buf, sizeof(Buf));
    /* 重新定位文件位置 */
    lseek(fp0,0,SEEK_SET);
    
    /* 清除Buf */
    strcpy(Buf,"Buf is NULL!");
    
    /* 读出设备 */
    read(fp0, Buf, sizeof(Buf));
    /* 检测结果 */
    printf("BUF: %s success\n",Buf);
    
    return 0;    

}


参考文献:(部分代码摘自下列网站)

      http://www.cnblogs.com/adolph-suyu/archive/2011/12/04/2275990.html

posted on 2012-07-11 19:45  YoungerChina  阅读(360)  评论(0编辑  收藏  举报

导航