字符设备驱动程序框架

1, 设备号的内部表示形式

       类型:dev_t 32=12(主设备号) + 20(次设备号)

相关宏:<linux/kdev_t.h>

       MAJOR(dev_t dev)

       MINOR(dev_t dev)

       MKDEV(int major, int minor);

2. 分配和释放设备号

       相关函数:

       //静态分配设备号

       int register_chrdev_region(dev_t first, unsigned int count,

 

                           char *name);

       //动态分配设备号

       int alloc_chrdev_region(dev_t *dev, unsigned int firstminor,

 

                        unsigned int count, char *name);

 

       void unregister_chrdev_region(dev_t first, unsigned int count);

3.获取设备号的通常写法

       if (scull_major) {

 

             dev = MKDEV(scull_major, scull_minor);

 

             result = register_chrdev_region(dev, scull_nr_devs, "scull");

 

       } else {

 

            result = alloc_chrdev_region(&dev, scull_minor, scull_nr_devs,

 

            "scull");

 

            scull_major = MAJOR(dev);

 

       }

 

       if (result < 0) {

 

            printk(KERN_WARNING "scull: can't get major %d\n", scull_major);

 

            return result;

 

       }

 

4.一些重要的数据结构

       struct file_operations

       {

              //用于防止一个正在使用的模块被卸载,通常值为THIS_MODULE

              struct module *owner;

              //seek

              loff_t (*llseek) (struct file *, loff_t, int);

              //read

              ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);

              ssize_t (*aio_read)(struct kiocb *, char __user *, size_t, loff_t);

              //write

              ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);

              ssize_t (*aio_write)(struct kiocb *, const char __user *, size_t, loff_t *);

              //readdir

              int (*readdir) (struct file *, void *, filldir_t);

              //poll

              unsigned int (*poll) (struct file *, struct poll_table_struct *);

              //ioctl

              int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);

              //mmap

              int (*mmap) (struct file *, struct vm_area_struct *);

              //open

              int (*open) (struct inode *, struct file *);

              //flush

              int (*flush) (struct file *);

              //release

              int (*release) (struct inode *, struct file *);

              //fsync

              int (*fsync) (struct file *, struct dentry *, int);

              int (*aio_fsync)(struct kiocb *, int);

              int (*fasync) (int, struct file *, int);

              //lock

              int (*lock) (struct file *, int, struct file_lock *);

              //readv writev

              ssize_t (*readv) (struct file *, const struct iovec *, unsigned long, loff_t *);

 

              ssize_t (*writev) (struct file *, const struct iovec *, unsigned long, loff_t *);

 

              //sendfile

              ssize_t (*sendfile)(struct file *, loff_t *, size_t, read_actor_t, void *);

              //sendpage

              ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *,

 

  int);

 

              //get_unmapped_area

              unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned

  long, unsigned long, unsigned long);

 

              //check_flags

              int (*check_flags)(int)

              //dir_notify

              int (*dir_notify)(struct file *, unsigned long);

              ...

       };

 

       //代表打开的文件

       struct file

       {

              //读写权限

              mode_t f_mode;

              //文件读写位置

              loff_t f_pos;

              //文件标志(O_RDONLY, O_NONBLOCK, O_SYNC)

              unsigned int f_flags;

              //文件操作

              struct file_operations *f_op;

              //设备文件的私有数据

              void *private_data;

              //与文件相关的目录,

              struct dentry *f_dentry;

              ...

       };

 

       struct inode

       {

              //对于设备文件来说,此域表示真实的设备号

              dev_t i_rdev;

              //当引结点指向一个字符设备时,代表内核内部结构的字符设备

              struct cdev *i_cdev;

       };

       //I结点中获取次设备号

       unsigned int iminor(struct inode *inode);

       // I结点中获取主设备号

       unsigned int imajor(struct inode *inode);

 

5. 注册字符设备

       头文件:<linux/cdev.h>

      

       cdev 结构体的定义:

       struct cdev

       {

              struct kobject kobj;        /*内嵌的kobject对象*/

              struct module *owner;   /*所属模块*/

              struct file_operations *ops; /*相关的文件操作*/

              struct list_head list;

              dev_t dev;      /*设备模块*/

              unsigned int count;

       };

分配和初始化字符设备相关结构

       方法1(将cdev作为单独的一个结构):

       struct cdev *my_cdev = cdev_alloc( ); //分配设备空间

 

       my_cdev->ops = &my_fops;

 

       //设备初始化

       void cdev_init(struct cdev *cdev, struct file_operations *fops);

       //添加设备

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

       //删除设备

       void cdev_del(struct cdev *dev);

       方法2(将cdev作为自定义设备结构的一个成员):

       struct mydev

       {

              …

              struct cdev;

       }

       设备分配初始化以及删除操作类似

 

PS: 2.6内核以前的字符设备注册方法

int register_chrdev(unsigned int major, const char *name, struct file_operations *fops);

 

int unregister_chrdev(unsigned int major, const char *name);

 

6. 字符设备驱动程序模板

字符设备驱动模块加载和卸载函数模板

//设备结构体

struct xxx_dev_t

{

    struct cdev cdev;

   

};

//设备驱动模块加载函数

static int __init xxx_init(void)

{

   

    //初始化cdev

    cdev_init(&xxx_dev.cdev, &xxx_fops);

    //获取字符设备号

    if(xxx_major)

    {

       register_chrdev_region(xxx_dev_no, 1, DEV_NAME);

    }

    else

    {

       alloc_chrdev_region(&xxx_dev_no, 0, 1, DEV_NAME);

    }

    //注册设备

    ret = cdev_add(&xxx_dev.cdev, xxx_dev_no, 1);

}

//设备驱动模块卸载函数

static void __exit xxx_exit(void)

{

    //释放占用的设备号

    unregister_chrdev_region(xxx_dev_no, 1);

    //注销设备

cdev_del(&xxx_dev.cdev);

}

字符设备常用I/O操作函数模板

//读设备

ssize_t xxx_read(struct file *filep, char __user *buf, size_t count, loff_t *f_pos)

{

   

    copy_to_user(buf, …, …);

   

}

//写设备

ssize_t xxx_write(struct file *filep, const char __user *buf, size_t count, loff_t *f_pos)

{

   

    copy_from_user(…, buf, …);

   

}

//ioctl函数

int xxx_ioctl(struct inode *inode, struct file *filep, unsigned int cmd, unsigned long arg)

{

   

    switch(cmd)

    {

       case XXX_CMD1:

          

           break;

       case XXX_CMD2:

          

           break;

       default:

           //不能支持的命令

           return –ENOTTY;

    }

    return 0;

}

 

用户空间与内核空间的数据传输

//内核空间到用户空间数据的复制

unsigned long copy_to_user(void __user *to, const void *from,                         

unsigned long count);

 

 

//用户空间到内核空间的复制

unsigned long copy_from_user(void *to, const void __user *from,

                              unsinged long count);

 

上述函数均返回不能被复制的字节数,因此,如果完全复制成功,返回值为0.

如果要复制的内在是简单类型,如char, int,long等,则可以使用简单的put_user()get_user()函数。

如:

int val;

get_user(val, (int*)arg);

put_user(val, (int*)arg);

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值