Linux设备驱动程序学习(1)-字符设备驱动程序

Linux设备驱动程序学习(1)
-字符设备驱动程序

一、分配设备号

   1、 对字符设备的访问是通过文件系统内的设备名称进行的(/dev/ttyS0)。在内核中,

#include <linux/types.h>   dev_t  用来保存设备编号——包括主设备号和次设备号。

由dev_t获得主次设备号:

                         MAJOR(dev_t dev);

                         MINOR(dev_t dev);

由主次设备号获得dev_t

                        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、动态分配设备编号

        以《ldd3》作者的观点,分配主设备号的最佳方式是:默认采用动态分配,同时保留在加载甚至是编译时指定主设备号的余地。示例驱动scull采用了如下方式:

 

二、与设备驱动相关的三个重要数据结构

1、struct file_operation

它用来建立设备编号与设备操作之间的连接,scull设备驱动程序的此结构初始化为如下形式:

2、struct file     文件描述符

    file 结构与用户空间的FILE没有任何关联,FILE在C库中定义且不会出现在内核代码中,而struct file是一个内核结构,它不会出现在用户程序中。

3、struct inode

    内核用inode结构在内部表示文件,因此它和file结构不同,后者表示打开的文件描述符。对单个文件,可能会有许多个表示打开的文件描述符的file结构,但它们都指向单个inode结构。

dev_t   i_rdev; 包含真正的设备编号,unsigned int imajor(struct inode *inode); unsigned int iminor(struct inode *inode);

struct cdev *i_cdev;

 

三、注册字符设备

    内核内部使用struct cdev结构来表示字符设备。在内核调用设备的操作之前,必须分配并注册一个或者多个上述结构。在 <linux/cdev.h>中定义了这个结构及其相关操作。

    1、分配cdev结构

        struct cdev *my_cdev = cdev_alloc();

        my_cdev->ops = &my_fops;

    2、初始化分配到了cdev结构

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

        my_cedv->owner  = THIS_MODULE;

    3、告诉内核该结构的信息

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

    4、从系统中删除一个字符设备

        voi d cdev_del(struct cdev *dev);

    scull的设备注册如下:

四、设备的相关操作实现 ——open()      release()        read()          write()      

open方法提供给驱动程序以初始化的能力,为以后的操作作准备。应完成的工作如下:

(1)检查设备特定的错误(如设备未就绪或硬件问题);

(2)如果设备是首次打开,则对其进行初始化;

(3)如有必要,更新f_op指针;

(4)分配并填写置于filp->private_data里的数据结构。

而根据scull的实际情况,他的open函数只要完成第四步(将初始化过的struct scull_dev dev的指针传递到filp->private_data里,以备后用)就好了,所以open函数很简单。但是其中用到了定义在<linux/kernel.h>中的container_of宏,源码如下:

#define container_of(ptr, type, member) ({            /
    const typeof( ((type *)0)->member ) *__mptr = (ptr);    /
    (type *)( (char *)__mptr - offsetof(type,member) );})

其实从源码可以看出,其作用就是:通过指针ptr,获得包含ptr所指向数据(是member结构体)的type结构体的指针。即是用指针得到另外一个指针。

release方法提供释放内存,关闭设备的功能。应完成的工作如下:

(1)释放由open分配的、保存在file->private_data中的所有内容;

(2)在最后一次关闭操作时关闭设备。

由于前面定义了scull是一个全局且持久的内存区,所以他的release什么都不做。


read和write

 read和write方法的主要作用就是实现内核与用户空间之间的数据拷贝。因为Linux的内核空间和用户空间隔离的,所以要实现数据拷贝就必须使用在<asm/uaccess.h>中定义的

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,
                             unsigned long count);

而值得一提的是以上两个函数和

#define __copy_from_user(to,from,n)    (memcpy(to, (void __force *)from, n), 0)
#define __copy_to_user(to,from,n)    (memcpy((void __force *)to, from, n), 0)

之间的关系:通过源码可知,前者调用后者,但前者在调用前对用户空间指针进行了检查。

五、scull的内存使用模型

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值