字符设备驱动

一。字符设备程序思路

字符设备是以不固定长度与系统进行数据传输。字符设备发出请求时,IO读写就发生了。而块设备需要一块内存缓冲区。

1.1 设备号分配

  初始化(init): 首先进行设备号的分配,完成对设备主设备号的分配。其中涉及到结构体有dev_t, cdev。 dev_t中存储有主设备号和次设备号,可通过MAJOR(dev_t dev)和MINOR(det_t dev)获取int类型的设备号。通过MKDEV(int major,int minor)可转换成dev_t类型。而cdev类型是

struct cdev {
    struct kobject kobj;                    // 内嵌的kobject对象 
    struct module *owner;                   // 所属模块
    const struct file_operations *ops;      // 文件操作结构体
    struct list_head list;                  //linux内核所维护的链表指针
    dev_t dev;                              //设备号
    unsigned int count;                     //设备数目
};

可将cdev结构体嵌入自己的设备结构体中。(但是我没有这么做,我用了my_device用于存储自己的设备信息)。

  设备分配可采用静态分配和动态分配两种方法。静态分配register_chrdev_region(dev_t first, unsigned int count, char *name),第一个参数提供了分配设备号的起始值,第二个参数为连续设备号的个数(我使用了两个),第三个参数为设备名。成功分配返回值为0。动态分配alloc_chrdev_region(dev_t *dev, unsigned int firstminor, unsigned int count c,char *name)。设备是否使用动态分配,通过一个宏进行初始化,再通判断char_driver_major的值判断是否进行动态分配。传入dev_t的major值为0才可以进行动态分配。

通过查看/proc/devices可以得到新分配的主设备号

动态分配结果:

1.2初始化cdev

  两种方法: 第一种通过cdev_alloc(),再将cdev->ops = &myfops;

  第二种:void cdev_init(struct cdev *cdev, cdev file_operation *fops)

  直接将cdev中的ops赋值为文件操作结构体的地址。

  而file_operation结构体的变量,在创建时就进行了初始化,内含多个函数如open,read,write等。根据书本的要求进行了定义,具体操作。

  “结构体 file_operations中存储着内核模块中执行这项操作的函数的地址” “struct inode一个是代表文件,struct file一个是代表打开的文件”

  其实我还没有弄懂文件操作结构体的意义,以及如何传入变量的,之后深入研究再另记。

1.3 注册设备到系统中

  通过cdev_add(struct cdev *dev, dev_t num, unsigned int count);

2.设备操作实现(待完成)

 

3.内存分配

   首先分配自己定义设备结构体的空间,并用memset将当前内存区全部设置为0。再通过指针将my_device1[i].size也就是储存设备大小信息的字段,赋值为CHAR_DRIVER_SIZE,储存单个设备的储存信息大小(4096)。再将data区初始化为0,以待之后读取输出使用。在write函数中,通过copy_from_user(void *to, const void__user *from, unsigned long count)会将buf内的内容写出给设备的data区。

4.注销设备

   cdev_del

   unrigister_chrdev_region用法同上。

 

二。驱动代码

  

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h> //file structure,
#include <linux/cdev.h>//static allo. and register
#include <linux/device.h> //unknown
#include <asm/uaccess.h> 
#include <linux/slab.h>//kmalloc 
MODULE_AUTHOR("zzg");


#ifndef CHAR_DRIVER_MAJOR
#define CHAR_DRIVER_MAJOR 0
#endif

#ifndef CHAR_DRIVER_SIZE
#define CHAR_DRIVER_SIZE 4096
#endif

#ifndef DEV_NUM 
#define DEV_NUM 2
#endif

struct my_device{
    char *data;
    unsigned long size;

};

int char_driver_major=CHAR_DRIVER_MAJOR;//this value can be given from console,by using function MODULE_PARM.
struct my_device *my_device1;
struct cdev cdev;

/*char_driver_open*/
int char_driver_open(struct inode*inode, struct file *filp){
    struct my_device *dev;
    unsigned int minor_number=iminor(inode);
    if(minor_number>=DEV_NUM) return -ENODEV;    
    dev=my_device1;
    filp->private_data=dev;
    return 0;
}

/*char_driver_release*/
int char_driver_release(struct inode *inode, struct file * filp){
    return 0;

}


/*char_driver_read
    read function ----fread. this function tells kernel how many datas should be read from user space, and than update the position in file.
*/

static ssize_t char_driver_read(struct file *filp, char __user *buf, size_t size, loff_t *ppos){
    unsigned long p= *ppos;
    unsigned int count =size;
    int ret=0;
    struct my_device *dev=filp->private_data;

    if(p>=CHAR_DRIVER_SIZE)
    return 0;

    if(count >CHAR_DRIVER_SIZE -p)
    count=CHAR_DRIVER_SIZE-p;

    if(copy_to_user(buf,(void*)(dev->data+p),count))
    {
    ret=-EFAULT;

    }
    else{
        *ppos +=count;
        ret=count;
    
    printk(KERN_INFO "read %d bytes(s) from %d\n",count,p);
    }
    return ret;
}

/*char_driver_weite
*/

static ssize_t char_driver_write(struct file *filp, char __user *buf, size_t size, loff_t *ppos){
    unsigned long p= *ppos;
    unsigned int count =size;
    int ret=0;
    struct my_device *dev=filp->private_data;

    if(p>=CHAR_DRIVER_SIZE)
        return 0;

    if(count > CHAR_DRIVER_SIZE-p)
        count=CHAR_DRIVER_SIZE-p;

    if(copy_from_user(dev->data+p,buf,count))
    {
        ret=-EFAULT;

    }
    else{
        *ppos +=count;
        ret=count;
    
        printk(KERN_INFO "written %d bytes(s) from %d\n",count,p);
    }
    return ret;
}

/*llseek:change the current read/write position in file*/
static loff_t char_driver_llseek(struct file *filp,loff_t offset, int whence){
    loff_t newpos;
    switch(whence){
        case 0:
            newpos=offset;
            break;
        case 1:
            newpos=filp->f_pos+offset;
            break;
        case 2:
            newpos=CHAR_DRIVER_SIZE+offset;   //what to doSIZE??
            break;
        defualt:
        return -EINVAL;
    }
    if(newpos<0)    return -EINVAL;
    filp->f_pos = newpos;
    return newpos;

}

//file_operation initialization(function pointer).the specific realization of these functions will be directly used from the given funtions.
struct file_operations char_driver_fops ={
    .owner = THIS_MODULE,
    .llseek=char_driver_llseek,
    .read=char_driver_read,
    .write=char_driver_write,
//    .ioctl=char_driver_ioctl,
    .open=char_driver_open,
    .release=char_driver_release,    

};

//step1:firstly the Char_device must be registered.
static int char_driver_init(void){
    //initialization
    int result,i;
    dev_t dev_test=MKDEV(char_driver_major,0);//transfer device in dev_t structure
    if(char_driver_major){
        result=register_chrdev(dev_test,2,"dev_test");//2 is the sequential device number.should not be too large, because the number could be overlapped with the next major number from other device.
      }
    else{
        result=alloc_chrdev_region(&dev_test,0,2,"dev_test");
        char_driver_major=MAJOR(dev_test);
    }
    if(char_driver_major){
    printk(KERN_INFO "device_number %d alreader allocated\n", char_driver_major);
    }
// result value should be examined if any erro existed, but we assume the dynamic number is already allocated.

//step2:initiate cdev by using cdev.init(struct cdev *cdev, struct file_operations *fops). there will be a connection between cden and file operation
    cdev_init(&cdev,&char_driver_fops);
    cdev.owner=THIS_MODULE;
    cdev.ops=&char_driver_fops;


//step3 cdev_add tell the kernel the information about the structure
    cdev_add(&cdev,MKDEV(char_driver_major,0), DEV_NUM );

//step4: memory allocation for device
    my_device1=kmalloc(DEV_NUM * sizeof(struct my_device), GFP_KERNEL);
    if (!my_device1){
        result= -ENOMEM;
        goto fail_malloc;
    
    }
    memset(my_device1,0,sizeof(struct my_device));

//allocate for device
    for (i=0; i<DEV_NUM;i++){
        my_device1[i].size=CHAR_DRIVER_SIZE;
        my_device1[i].data=kmalloc(CHAR_DRIVER_SIZE,GFP_KERNEL);
        memset(my_device1[i].data,0,CHAR_DRIVER_SIZE);
    
    }
    return 0;
    
    fail_malloc:
    unregister_chrdev_region(MKDEV(char_driver_major,0),2);

    return result;
}


static void char_driver_exit(void){


    cdev_del(&cdev);
    kfree(my_device1);
    unregister_chrdev_region(MKDEV(char_driver_major,0),2);
}

MODULE_LICENSE("GPL");

module_init(char_driver_init);
module_exit(char_driver_exit);

 

3. 驱动测试

  借助网上代码http://blog.chinaunix.net/uid-11829250-id-337300.html 进行测试。

  首先通过在/dev下创建设备 mknod dev_test c 250 0

  gcc -o mem memdevapp.c 编译测试文件。

 4.总结及问题

  对于字符设备驱动有了初步认识,但对于读取过程,包括初始化函数的调用过程,还需要深入了解。file_operation的函数实现,是设计的重点,因此还需更进一步进行学习。

  主要问题:1.多个设备在分配设备号时,查看多个分配的结果

  2.初始化函数调用的参数,是如何一个传递路径

  3.内存分配问题

 

posted @ 2017-10-22 21:37  zz刚  阅读(187)  评论(0编辑  收藏  举报