您的位置:首页 > 运维架构 > Linux

armlinux字符设备驱动启动简析

2011-05-26 08:54 405 查看
armlinux字符设备驱动启动简析

ARM-LINUX字符设备驱动启动简析

对驱动的启动过程有点模糊,就翻了翻书,看了看代码,简略的总结如下,有不对的地方希望大家指出。

1)insmod xxx.ko 即加载驱动,大体来说可以分为3小快。

1.注册设备号,没主设备号便动态生产一个设备号。其主要是调用该函数

__register_chrdev_region(MAJOR(n), MINOR(n),next - n, name); char_dev.c
将字符设备相关的信息写入struct char_device_struct结构体中,下面列出其代码
static struct char_device_struct { ///char_dev.cstruct char_device_struct *next;unsigned int major;unsigned int baseminor;int minorct;char name[64];struct cdev *cdev; /* will die */} *chrdevs[CHRDEV_MAJOR_HASH_SIZE];
该结构体 *chrdevs[CHRDEV_MAJOR_HASH_SIZE]将所有注册的设备号链接成一个链表,不同设备通过major来进行区分,子设备通过baseminor和minorct来进行区分。如此便完成了设备号的注册。

2.为设备结构分配空间,同时初始化设备的基本参数信息。包括初始化设备的基本参数信息,以及初始化cdev字符设备结构的信息。这个是初始化的重点,具体是调用

void cdev_init(struct cdev *, const struct file_operations *);/// char_dev.c
该函数完成的主要工作是将 file_operation *fops赋值给cdev->ops,

struct file_operation *fops包含了对字符设备进行操作的基本功能函数。下面列出

struct cdev结构和字符设备需要用到的几个struct file_operation结构项


struct cdev { //cdev.h
struct kobject kobj;
struct module *owner;
const struct file_operations *ops;
struct list_head list;
dev_t dev;
unsigned int count;
};


struct file_operations { //fs.h
struct module *owner;
loff_t (*llseek) (struct file *, loff_t, int);
ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
int (*open) (struct inode *, struct file *);
int (*release) (struct inode *, struct file *);
}
当接口函数OPEN打开该设备时,会通过反解析到这个结构里的open 来完成OPEN操作。

3.接下来的这一步是最关键的一步。它将设备模块信息传递给内核。具体是调用
int cdev_add(struct cdev *, dev_t, unsigned);
该函数的最主要的步骤是调用了

int kobj_map(struct kobj_map *domain, dev_t dev, unsigned long range,
struct module *module, kobj_probe_t *probe,int (*lock)(dev_t, void *), void *data) //drivers/base/map.c

该函数通过将该设备的信息添加到*cdev_map结构中,实现了设备挂载到内核。
static struct kobj_map *cdev_map存储了所有挂载到内核的设备。下面列出它的结构

struct kobj_map { //drivers/base/map.c
struct probe {
struct probe *next;
dev_t dev;
unsigned long range;
struct module *owner;
kobj_probe_t *get;
int (*lock)(dev_t, void *);
void *data;
} *probes[255];
struct mutex *lock;
};
该结构内置一个probe结构。*probes[255]将所有的字符设备链接成一个链表方便内核对其进行反解析,以找到设备操作函数。

2)open("/dev/xxx",flags)

该函数实现了内核对上述过程的反解析,从而找到设备,并调用设备的open函数,给设备操作分配初始资源,并返回文件句柄。方便其他操作接口函数的调用。

由于OPEN过程比较复杂,下面只对其进行简要分析。

sys_open -> do_sys_open -> do_filp_open -> nameidata_to_filp ->__dentry_open

可见,OPEN过程,会先创建一个struct file *filp结构,该结构代表一个打开的文件。我们会将设备的信息都赋给它,在操作设备时,我们可通过它识别所要操作的设备和所要操作的函数。下面列出其对我们有用的结构代码。

struct file { //fs.hconst struct file_operations *f_op;
unsigned int f_flags;
fmode_t f_mode;
loff_t f_pos;
struct fown_struct f_owner;
void *private_data;
}

可见OPEN的过程,其实主要是给struct file *filp赋与必要的值,方便之后对设备的操作。当然其中还有对设备的查找,主要是通过__dentry_open()中调用
static int chrdev_open(struct inode *inode, struct file *filp) // char_dev.c
而该函数中又会调用

struct kobject *kobj_lookup(struct kobj_map *domain, dev_t dev, int *index) //drivers/base/map.c

查找相应的设备信息,也就是第一部分说的那些,然后会调用设备的OPEN函数,最后返回文件句柄。

由此,接口函数OPEN的工作便完成了。

3).ssize_t read(int fd,void *buf,size_t count)或

ssize_t write(int fd,void *buf,size_t count)或
off_t lseek(int fildes,off_t offset,int whence)或

int ioctl(int fd,unsigned long int cmd)

这些接口函数通过文件句柄值,方便的调用设备的操作函数,这个调用很简单就不说了。

4).close(int fd)
即关闭打开的文件struct file *filp,释放该结构。
5).rmmod xxx
1.释放设备信息占用的内存。

2.调用 void cdev_del(struct cdev *p)
static struct kobj_map *cdev_map结构中清除设备的信息,即从内核中清除设备信息。
由此,字符设备的启动过程就解析完了,可能有些错误,希望您提出,共同学习


内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: