ldd3-3-scull.2设备的读写操作(失败)

/***************************************************************************************************
                                        scull2.h
空间模型:
    scull_dev
    {
        *data----------->scull_qset       /-------------------->scull_qset       /-------...--->NULL
        quantum         {   *next--------/                     {   *next--------/
        qset                **data->*quantum0                      **data->*quantum0
        size            }           *quantum1-->char[4000]     }            *quantum1-->char[4000]
        access_key                  *quantum2-->char[4000]                  *quantum2-->char[4000]
        sem                         *quantum3                               *quantum3
        cdev                        *quantum4-->char[4000]                  *quantum4-->char[4000]
                                    ...                                     ...
    }                               *qua.1000-->char[4000]                  *qua.1000-->char[4000]
                                    量子集          量子
    scull_qset是一个链表结构
        一个指针指向下一个结构。
        一个指针指向一个量子集。
            量子集是n=1000个指针的数组,每个指针指向一块内存,此内存有m=4000字节大。一块内存即是以个量子。

策略:
    写入:  若在指定位置写入数据时
                若该位置还没有量子集,开辟一个量子集空间,即1000个指针的空间,初始化为NULL
                若该位置也没有量子时,开辟一个量子  空间,即4000个字节的空间。有一个量子集指针指向它。
    读出:
            若指定位置的数据不存在,则不会读到数据。
           
本例子实现如下功能:
    1 自动注册设备,添加节点。
    2 只实现一个设备scull0。量子集大小改为100 量子大小改为400
    3 实现了4个操作,借鉴ldd3的例子,去掉中断和信号量的使用,以后涉及
        scull_open()
        scull_release()
        scull_read()
        scull_write()
    4 本例子使用方法:
        1 将所有代码放同一目录下
        2 make
        3 gcc user2.c
        4 insmod sucll2.ko
        5 ./a.out
        6 rmmod sucll2
    5 查看信息 tail -f /var/log/messages
编者:张永辉 2012年10月12日
***************************************************************************************************/
#ifndef _SCULL_H_
#define _SCULL_H_
/***************************************************************************************************
                                    常量定义
***************************************************************************************************/
#ifndef     SCULL_MAJOR
    #define SCULL_MAJOR 0           //dynamic major by default 缺省的主设备号
#endif

#ifndef     SCULL_NR_DEVS
    #define SCULL_NR_DEVS 4         // scull0 1 2 3
#endif

#ifndef     SCULL_QUANTUM           //量子大小,单位字节
    #define SCULL_QUANTUM 400
#endif

#ifndef     SCULL_QSET              //量子集大小
    #define SCULL_QSET    100
#endif
/***************************************************************************************************
                                    数据结构定义
***************************************************************************************************/
struct scull_qset
{
    void **data;
    struct scull_qset *next;
};

struct scull_dev {
    struct scull_qset *data;  // Pointer to first quantum set   指向第一个 scull_qset结构
    int quantum;              // the current quantum size       一个量子的大小,  默认4000
    int qset;                 // the current array size         一个量子集的个数,默认1000
    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          设备
};

//**************************************************************************************************
#endif

 

/***************************************************************************************************
                                            scull2.c
***************************************************************************************************/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>               /* everything...    */
#include <linux/kernel.h>
#include <asm/uaccess.h>            /* copy_*_user      */
#include <linux/cdev.h>             //cdev
#include <linux/types.h>            /* size_t           */
#include <linux/slab.h>             /* kmalloc()        */
#include <linux/device.h>           //class_create

//#include <linux/moduleparam.h>
#include <linux/errno.h>            /* error codes      */
#include <linux/proc_fs.h>
//#include <linux/fcntl.h>          /* O_ACCMODE        */
//#include <linux/seq_file.h>
//#include <asm/system.h>           /* cli(), *_flags   */

#include "scull2.h"
MODULE_LICENSE("Dual BSD/GPL");
/***************************************************************************************************
                            全局变量声明
***************************************************************************************************/
dev_t   scull_num;                              //  设备号
int     scull_major     =   SCULL_MAJOR;        //主设备号   scull.h #define=0
int     scull_minor     =   0;                  //次设备号

int     scull_quantum   =   SCULL_QUANTUM;      //量子大小 = 400
int     scull_qset      =   SCULL_QSET;         //量子集大小 = 100

struct  scull_dev       *scull_devices=NULL;    //在 scull_init_module 分配的设备存储区
static  struct class    *scull_class=NULL;
/***************************************************************************************************
                            函数声明
***************************************************************************************************/
//装载和卸载函数
static void scull_cleanup_module(void);
static int scull_init_module(void);
//以file_operations scull_fops{}为接口的函数
static int     scull_open   (struct inode *node, struct file *filp);
static int     scull_release(struct inode *node, struct file *filp);
static ssize_t scull_read   (struct file  *filp, char __user *buf,        size_t count,loff_t *f_pos);
static ssize_t scull_write  (struct file  *filp, const  char __user *buf, size_t count,loff_t *f_pos);
    //子函数:
    int                 scull_trim  (struct scull_dev *dev);
    struct  scull_qset *scull_follow(struct scull_dev *dev, int n);

/***************************************************************************************************
                            file_operations 函数实现
***************************************************************************************************/
static int scull_open (struct inode *node, struct file *filp)
{
    struct scull_dev *dev;          //定义scull_dev指针

    printk(KERN_WARNING "scull_open() scull opend \n");

    dev = container_of(node->i_cdev, struct scull_dev, cdev);   //获得scull_dev的地址
    filp->private_data = dev;                                   //以后会用到

    if ( (filp->f_flags & O_ACCMODE) == O_WRONLY)               //为什么要判断?  二次打开?
    {
        printk(KERN_WARNING "    scull_trim()\n");
        scull_trim(dev);            //释放该设备的所有内存空间
    }

    return 0;
}

static int scull_release(struct inode *node, struct file *filp)
{
    printk(KERN_WARNING "scull_rlease() scull rleased \n");
    return 0;
}
//------------------------------read----------------------------------------------------------------
static ssize_t scull_read(struct file *filp, char __user *buf, size_t count,loff_t *f_pos)
{
    struct  scull_dev  *dev = filp->private_data;       //==scull_devices
    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 = 0;                                 //out 处使用的值

    printk(KERN_WARNING "scull_read() is called! \n");

    //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;
    }

    //计算索引和偏移量
    item = (long)*f_pos / itemsize;
    rest = (long)*f_pos % itemsize;
    s_pos = rest / quantum;
    q_pos = rest % quantum;

    dptr = scull_follow(dev, item);                     //查找‘第item个scull_qset的’的位置

    if (dptr == NULL || !dptr->data || ! dptr->data[s_pos])
    {
        goto out;                                       //* don't fill holes */
    }

    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--------------------------------------------------------------------
static 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;       //==scull_devices
    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;                           //out 处使用的值

    printk(KERN_WARNING "scull_write() is writed! \n");

    //if (down_interruptible(&dev->sem))                    //待解释
    //{
    //  return -ERESTARTSYS;
    //}

    item = (long)*f_pos / itemsize;                     //第item个量子集
    rest = (long)*f_pos % itemsize;                     //偏移
    s_pos = rest / quantum;                             //第  s_pos个量子
    q_pos = rest % quantum;                             //偏移q_pos

    dptr = scull_follow(dev, item);                     //查找‘第item个scull_qset的’的位置
    if (dptr == NULL)
    {
        goto out;
    }

    if (!dptr->data)
    {                                                   //分配1000个空间:qest==1000个指针
        dptr->data = (void **)kmalloc(qset *    sizeof(char *), GFP_KERNEL);
        if (!dptr->data)
        {
            goto out;
        }
        memset(dptr->data, 0, qset * sizeof(char *));
    }
    if (dptr->data[s_pos]==NULL)
    {
        dptr->data[s_pos] = kmalloc(quantum, GFP_KERNEL);//分配4000个空间:quantum==4000个char数据空间
        if (!dptr->data[s_pos])
            goto out;
    }

    //写入数据到量子,直到结尾。    为什么不让剩余的数据写到下一个量子?
    if (count > quantum - q_pos)
    {
        count = quantum - q_pos;
    }

    if (copy_from_user(dptr->data[s_pos]+q_pos, buf, count))//拷贝数据到[quantum==4000]
    {
        retval = -EFAULT;
        goto out;
    }

    //文件指针文章更改,以便连续写操作
    *f_pos += count;
    retval = count;

    //更新文件大小
    if (dev->size < *f_pos)
    {
        dev->size = *f_pos;
    }

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

/***************************************************************************************************
                            file_operations 函数实现 需要调用的函数
***************************************************************************************************/
//-------------------------释放该设备的所有内存空间-------------------------------------------------
int scull_trim(struct scull_dev *dev)
{
    struct scull_qset *next, *dptr;
    int qset = dev->qset;

    int i;

    dptr =dev->data;
    //printk(KERN_WARNING   "    scull_trim()  dptr = %d\n",dptr);      //应该=0才正确
    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;
}

//-------------------------返回第n个scull_qset的地址------------------------------------------------
//若第n个不存在则创建至n个 至少共有n+1个scull_qset
struct scull_qset *scull_follow(struct scull_dev *dev, int n)           //n表示链表中有多少个struct scull_qset
{
    struct scull_qset *qs = dev->data;

    printk(KERN_WARNING "  \_scull_follow()\n");
    //首次时,建立第一个scull_qset
    if (!qs)
    {
        qs = dev->data = kmalloc(sizeof(struct scull_qset), GFP_KERNEL);
        if (qs == NULL)
        {
            return NULL;  /* Never mind */
        }
        memset(qs, 0, sizeof(struct scull_qset));
    }

    //遍历整个链表,如果scull_qset为空,进行初始化
    while (n--)
    {
        if (!qs->next)                                                  //遍历n-1次,从scull_dev传下来的scull_qset开始
        {
            qs->next = kmalloc(sizeof(struct scull_qset), GFP_KERNEL);
            if (qs->next == NULL)
            {
                return NULL;  /* Never mind */
            }
            memset(qs->next,0,  sizeof(struct scull_qset));
        }
        qs = qs->next;
        continue;
    }
    return qs;
}

//**************************************************************************************************

//初始化设备的file_operations结构体,必须的。  此结构
struct file_operations scull_fops =
{
    .open   =   scull_open,
    .read   =   scull_read,
    .write  =   scull_write,
    .release =  scull_release,
};

/***************************************************************************************************
                            注册和注销模块
***************************************************************************************************/
static int scull_init_module(void)
{
    int result;
    printk(KERN_WARNING "scull2 initing ... \n");

    //------------------注册设备号:scull2----------------------------------------------------------
    result = alloc_chrdev_region(&scull_num, scull_minor, 1,"scull2");
    if(result < 0)
    {
        printk(KERN_WARNING "alloc_chrdev_region error \n");
        return result;
    }
    scull_major = MAJOR(scull_num);
    printk(KERN_WARNING "scull_num = %d \n",scull_num);
    printk(KERN_WARNING "scull_major = %d \n",scull_major);

    //分配存储空间给scull_devices, 也给dev分配了空间。
    scull_devices =  (struct scull_dev*)kmalloc( 1*sizeof(struct scull_dev), GFP_KERNEL);
    if (!scull_devices)
    {
        printk(KERN_WARNING "kmalloc scull_devices error");
        result = -ENOMEM;
        goto err1;
    }
    memset(scull_devices, 0, 1 * sizeof(struct scull_dev));     //初始化空间

    //------------------初始化cdev并添加到系统------------------------------------------------------
    cdev_init(&scull_devices->cdev, &scull_fops);
    scull_devices->cdev.owner = THIS_MODULE;

    result = cdev_add(&scull_devices->cdev, scull_num, 1);
    if (result)
    {
        printk(KERN_NOTICE "cdev_add error! \n");
        goto err2;
    }
    //-------------------创建设备节点---------------------------------------------------------------
    scull_class = class_create(THIS_MODULE, "scull_class2");//在/sys目录下建立了一个scull_class2的目录
    if (IS_ERR(scull_class))
    {
        printk(KERN_INFO "gvar_class2 create error\n");
        goto err3;
        //class_destroy(gvar_class);
    }

    class_device_create(scull_class,NULL,scull_num,NULL,"scull" "%d", MINOR(scull_num));
    if(0)
    {   printk(KERN_INFO "device_create error\n");
        goto err4;
        //class_device_destroy()
    }

    printk(KERN_INFO "create device suess !\n");
    return 0;
    //------------------失败处理:所有操作将回退----------------------------------------------------
    err4:
        class_destroy(scull_class);
    err3:
        cdev_del(&scull_devices->cdev);
    err2:
        kfree(scull_devices);
    err1:
        unregister_chrdev_region(scull_num,1);
    return result;
}

static void scull_cleanup_module(void)
{
    class_device_destroy(scull_class,scull_num);
    if(scull_class != NULL)
    {
        class_device_destroy(scull_class, scull_num);
        class_destroy(scull_class);
    }

    if(&scull_devices->cdev != NULL)
    {
        cdev_del(&scull_devices->cdev);
    }

    unregister_chrdev_region(scull_num,1);
    printk(KERN_WARNING "scull goodbye! \n");
}

module_init(scull_init_module);
module_exit(scull_cleanup_module);

 

 

/***************************************************************************************************
                                    用户使用函数
user2.c
编者:张永辉 2012年10月12日10
***************************************************************************************************/
#include <stdio.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/types.h>

int main(void)
{
    int fd, num = 1,ret;

    fd = open("/dev/scull0", O_RDWR, S_IRUSR | S_IWUSR);
    printf("fd = %d \n",fd);

    if (fd != -1 )
    {
        num = 654;
        //ret = write(fd, &num, sizeof(int));           //失败,内核报错
        printf("write num = %d\n",num);
       
        //rewinddir(fd);
        ret = read(fd, &num, sizeof(int));              //失败,返回0,num的值为改变
        printf("read ret = %d\n",ret);
        printf("read num = %d\n",num);
        close(fd);
    }else
    {
        printf("scull0 Device open failure !\n");
    }
    return 0;
}

 

 

obj-m := scull2.o                                    #模块编译的目标必须以obj-m 这样的形式指出
KERNELDIR ?= /lib/modules/$(shell uname -r)/build       #模块的编译必须指定内核源代码的路径

PWD := $(shell pwd)
modules:
 $(MAKE) -C $(KERNELDIR) M=$(PWD) modules            #本行开头必须使用 Tab 键
modules_install:
 $(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install

clearn:
 rm -rf *.o *.ko *mod.o [mM]odule* .*.cmd .tmp_versions


 

 

 


 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值