android 驱动有关的类和宏

来源:互联网 发布:电脑软件删不掉怎么办 编辑:程序博客网 时间:2024/05/16 05:35

1 container_of

   container_of在Linux Kernel中的应用非常广泛,它用于获得某结构中某成员的入口地址.

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

     关于typeof,这是gcc的C语言扩展保留字,用于声明变量类型.
const typeof( ((type *)0->member ) *__mptr = (ptr);意思是声明一个与member同一个类型的指针常量 *__mptr,并初始化为ptr.

2 offsetof

     关于offsetof见stddef.h中:
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
TYPE是某struct的类型 0是一个假想TYPE类型struct,MEMBER是该struct中的一个成员. 由于该struct的基地址为0, MEMBER的地址就是该成员相对与struct头地址的偏移量.
(type *)( (char *)__mptr - offsetof(type,member) );意思是__mptr的地址减去member在该struct中的偏移量得到的地址, 再转换成type型指针. 该指针就是member的入口地址了.

3 struct inode

   *索引节点对象由inode结构体表示,定义文件在linux/fs.h中

内核使用inode结构在内部表示文件。
inode一般作为file_operations结构中函数的参数传递过来。例如,open()函数将传递一个inode指针进来,表示目前打开的文件结点。需要注意的是,inode的成员已经被系统赋予了合适的值,驱动程序只需要使用该结点中的信息,而不用更改。
Open()函数为:    int (*open) (struct inode *, struct file *);

inode结构中包含大量的有关文件的信息。这里,只对编写驱动程序有用的字段进行介绍,对于该结构更多的信息,可以参看内核源码。

dev_t i_rdev,表示设备文件对应的设备号。

struct list_head i_devices,该成员使设备文件连接到对应的cdev结构,从而对应到自己的驱动程序。

struct cdev *i_cdev,该成员也指向cdev设备。

除了从dev_t得到主设备号和次设备号外,这里还可以使用imajor()和iminor()函数从i_rdev中得到主设备号和次设备号。


imajor()函数在内部调用MAJOR宏,如下代码所示。

    static inline unsigned imajor(const struct inode *inode)  
    {  
        return MAJOR(inode->i_rdev);        /*从inode->i_rdev中提取主设备号*/  
    }

同样,iminor()函数在内部调用MINOR宏,如下代码所示。

    static inline unsigned iminor(const struct inode *inode)  
    {  
        return MINOR(inode->i_rdev); ;      /*从inode->i_rdev中提取次设备号*/  
    }

参考

3 struct file_operations


struct file_operations {
    struct module *owner;//指向拥有这个模块的指针,该成员用来在它的操作还在是使用的时候不允许卸载该模块。
//通常情况下简单初始化为THIS_MODULE。
    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 *);//该操作用来发送数据给设备。
    ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t); //该操作用来初始化一个异步的读操作。
    ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);//该操作用来初始化一个异步的写操作。
    int (*readdir) (struct file *, void *, filldir_t);//该操作用来读取目录。
    unsigned int (*poll) (struct file *, struct poll_table_struct *);//该操作用来查询一个或者多个文件描述符的读或写是会否堵塞。
    int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);//该操作用来提供发出设备特定命令的方法。
    long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
    long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
    int (*mmap) (struct file *, struct vm_area_struct *);//该操作用来请求将设备内存映射到进程的地址空间。
    int (*open) (struct inode *, struct file *);//该操作用来打开设备文件,也是对设备进行的第一个操作。
    int (*flush) (struct file *, fl_owner_t id);
    int (*release) (struct inode *, struct file *);//该操作用来释放文件结构。可以为空。
    int (*fsync) (struct file *, struct dentry *, int datasync);
    int (*aio_fsync) (struct kiocb *, int datasync);
    int (*fasync) (int, struct file *, int);
    int (*lock) (struct file *, int, struct file_lock *);
    ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
    unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
    int (*check_flags)(int);
    int (*dir_notify)(struct file *filp, unsigned long arg);
    int (*flock) (struct file *, int, struct file_lock *);
    ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);
    ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);
    int (*setlease)(struct file *, long, struct file_lock **);
    int (*fsetattr)(struct file *, struct iattr *);
};



4 struct module

结构体struct module在内核中代表一个内核模块,通过insmod(实际执行init_module系统调用)把自己编写的内核模块插入内核时,模块便与一个 struct module结构体相关联,并成为内核的一部分。下面是结构体struct module的完整定义,接下来会逐个解释
我们插入一个内核模块,一般会使用工具insmod,该工具实际上调用了系统调用init_module,在该系统调用函数中,首先调用 load_module,把用户空间传入的整个内核模块文件创建成一个内核模块,返回一个struct module结构体。内核中便以这个结构体代表这个内核模块。
    state是模块当前的状态。它是一个枚举型变量,可取的值为:MODULE_STATE_LIVE,MODULE_STATE_COMING,MODULE_STATE_GOING。
load_module函数中完成模块的部分创建工作后,把状态置为 MODULE_STATE_COMING,sys_init_module函数中完成模块的全部初始化工作后(包括把模块加入全局的模块列表,调用模块本身的初始化函数),把模块状态置为MODULE_STATE_LIVE,最后,使用rmmod工具卸载模块时,会调用系统调用 delete_module,会把模块的状态置为MODULE_STATE_GOING。这是模块内部维护的一个状态。
    list是作为一个列表的成员,所有的内核模块都被维护在一个全局链表中,链表头是一个全局变量struct module *modules。任何一个新创建的模块,都会被加入到这个链表的头部,通过modules->next即可引用到。
    name是模块的名字,一般会拿模块文件的文件名作为模块名。它是这个模块的一个标识。
    另外,还要介绍一下宏THIS_MODULE,它的定义如下是#define THIS_MODULE (&__this_module),__this_module是一个struct module变量,代表当前模块,跟current有几分相似。可以通过THIS_MODULE宏来引用模块的struct module结构,试试下面的模块:
owner是一个struct module *类型的结构体指针,现在告诉你的是每个struct module结构体在内核里都代表了一个内核模块,就像十七大里的每个代表都代表了一批人,至于代表了什么人,选他们的人才知道,同样,每个struct module结构体代表了什么模块,对它进行初始化的模块才知道。当然,初始化这个结构不是写驱动的人该做的事,是在刚才略过的那个从insmod或 modprobe到你驱动的xxx_init函数的曲折过程中做的事。insmod命令执行后,会调用kernel/module.c里的一个系统调用init_module,它会调用load_module函数,将用户空间传入的整个内核模块文件创建成一个内核模块,并返回一个struct module结构体,从此,内核中便以这个结构体代表这个内核模块。
 THIS_MODULE
它在include/linux/module.h里的定义是
 #define THIS_MODULE (&__this_module)
    是一个struct module变量,代表当前模块,与那个著名的current有几分相似,可以通过THIS_MODULE宏来引用模块的struct module结构,比如使用THIS_MODULE->state可以获得当前模块的状态。现在你应该明白为啥在那个岁月里,你需要毫不犹豫毫不迟疑的将struct usb_driver结构里的owner设置为THIS_MODULE了吧,这个owner指针指向的就是你的模块自己。那现在owner咋就说没就没了那?这个说来可就话长了,咱就长话短说吧。不知道那个时候你有没有忘记过初始化owner,反正是很多人都会忘记,大家都把注意力集中到probe、 disconnect等等需要动脑子的角色上面了,这个不需要动脑子,只需要花个几秒钟指定一下的owner反倒常常被忽视,这个就是容易得到的往往不去珍惜,不容易得到的往往日日思量着去争取。于是在2006年的春节前夕,在咱们都无心工作无心学习等着过春节的时候,Greg坚守一线,去掉了 owner,于是千千万万个写usb驱动的人再也不用去时刻谨记初始化owner了。咱们是不用设置owner了,可core里不能不设置,struct usb_driver结构里不是没有owner了么,可它里面嵌的那个struct device_driver结构里还有啊,设置了它就可以了。于是Greg同时又增加了usb_register_driver()这么一层,usb_register()可以通过将参数指定为THIS_MODULE去调用它,所有的事情都挪到它里面去做。反正usb_register() 也是内联的,并不会增加调用的开销。

参考
内核中结构体初始化

对结构体

struct a {

int b;

int c;

}

有几种初始化方式:

struct a a1 = {
 .b = 1,
 .c = 2
};
或者
struct a a1 = {
 b:1,
 c:2
}
或者
struct a a1 = { 1, 2};

内核喜欢用第一种,使用第一种和第二种时,成员初始化顺序可变。

6 usb_driver
  struct usb_driver                  usb_storage_driver
|-----------------------------------------------|
|const char                 *name               | "usb-storage"
|                           probe()             | storage_probe()
|                           disconnect()        | storage_disconnect()
|                           ioctl()             |
|                           suspend()           | storage_suspend()
|                           resume()            | storage_resume()
|                           pre_reset()         | storage_pre_reset()
|                           post_reset()        | storage_post_reset()
|const struct usb_device_id *id_table           | storage_usb_ids
|struct usb_dynids          dynids              |
|struct usbdrv_wrap         drvwrap             |
|unsigned int               no_dynamic_id       |
|unsigned int               supports_autosuspend|
|-----------------------------------------------|

usb_driver->id_table中保存着该接口驱动(usb_driver)所支持的全部usb设备的ID

  struct usbdrv_wrap
|--------------------------------|
|struct device_driver driver     |
|int                  for_devices|
|--------------------------------|
usb_storage_driver.drvwrap.driver.name   = "usb-storage"
usb_storage_driver.drvwrap.driver.bus    = &usb_bus_type
usb_storage_driver.drvwrap.driver.probe -= usb_probe_interface
usb_storage_driver.drvwrap.driver.remove = usb_unbind_interface


   struct--usb_device_id
|-------------------------|
|__u16 -match_flags       |
|__u16 -idVendor          |
|__u16 -idProduct         |
|__u16 -bcdDevice_lo      |
|__u16 -bcdDevice_hi      |
|__u8   bDeviceClass      |
|__u8   bDeviceSubClass   |
|__u8   bDeviceProtocol   |
|__u8   bInterfaceClass   |
|__u8   bInterfaceSubClass|
|__u8   bInterfaceProtocol|
|unsigned long driver_info|
|-------------------------|

 7  alloc_tty_driver
   
内核分配这个tty_driver。
struct tty_driver *alloc_tty_driver(int lines)
    {
          struct tty_driver *driver;
          driver = kzalloc(sizeof(struct tty_driver), GFP_KERNEL);
          if (driver) {
                   driver->magic = TTY_DRIVER_MAGIC;
                   driver->num = lines;
                   /* later we'll move allocation of tables here */
          }
          return driver;
    }

8  bus_register(&usb_serial_bus_type);


  为了满足新的要求,linux2.6提供了新的设备模型:总线、驱动、设备。基本关系简要的概括如下:
   驱动核心可以注册多种类型的总线。
   每种总线下面可以挂载许多设备。(通过kset devices)
   每种总线下可以用很多设备驱动。(通过包含一个ksetdrivers)}
   每个驱动可以处理一组设备。按照我的理解就是所有的设备都挂载到总线上,当加载驱动时,驱动就支总线上找到自己对应的设备。或者先把驱动加载上,来了一个设备就去总线找驱动。

     参考

struct device my_bus = {

  .bus_id   ="my_bus0",

 .release  = my_bus_release
};


struct bus_type my_bus_type = {
 .name = "my_bus",
 .match = my_match,
};

static int my_match(struct device *dev, structdevice_driver *driver)
{

 return !strncmp(dev->bus_id, driver->name,strlen(driver->name));

}

这是匹配的逻辑则是设备的名字与驱动的名字一样。



struct device my_dev = {
 .bus = &my_bus_type,//与总线接上关系
 .parent = &my_bus,//与总线设备接上关系
 .release = my_dev_release,
};



struct device_driver my_driver = {
 .name = "my_dev",//对应的设备名称
 .bus = &my_bus_type,//挂载的总线
 .probe = my_probe,//这个函数就是在找到与自己对应的设备时被调用。
       .remove = my_remove,
};

static int my_probe(struct device *dev)
{
 

  printk("Driver found device which my driver can handle!\n");
    return0;
}


9 linux的驱动过程
a 首先,我们必须提供一个.o的驱动模块文件,载运行它(insmod *.o)
b 驱动就会根据自己的类型(字符设备类型或块设备类型,例如鼠标就是字符设备而硬盘就是块设备)向系统注册,注册成功系统会反馈一个主设备号,这个主设备号就是系统对它的唯一标识(例如硬盘块设备在/proc/devices中显示的主设备号为3 ,我们用ls -l /dev/had看到的主设备就肯定是3)。
c 驱动就是根据此主设备号来创建一个一般放置在/dev目录下的设备文件(mknod命令用来创建它,它必须用主设备号这个参数)。
d 在我们要访问此硬件时,就可以对设备文件通过open、read、write等命令进行。而驱动就会接收到相应的read、write操作而根据自己的模块中的相应函数进行了。



原创粉丝点击