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 {
//通常情况下简单初始化为THIS_MODULE。
};
4 struct module
state是模块当前的状态。它是一个枚举型变量,可取的值为:MODULE_STATE_LIVE,MODULE_STATE_COMING,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结构,试试下面的模块:
#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_driverstruct 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",
};
struct bus_type my_bus_type = {
};
static int my_match(struct device *dev, structdevice_driver *driver)
{
}
这是匹配的逻辑则是设备的名字与驱动的名字一样。
struct device my_dev = {
};
struct device_driver my_driver = {
};
{
}
9 linux的驱动过程
a 首先,我们必须提供一个.o的驱动模块文件,载运行它(insmod *.o)
b 驱动就会根据自己的类型(字符设备类型或块设备类型,例如鼠标就是字符设备而硬盘就是块设备)向系统注册,注册成功系统会反馈一个主设备号,这个主设备号就是系统对它的唯一标识(例如硬盘块设备在/proc/devices中显示的主设备号为3 ,我们用ls -l /dev/had看到的主设备就肯定是3)。
c 驱动就是根据此主设备号来创建一个一般放置在/dev目录下的设备文件(mknod命令用来创建它,它必须用主设备号这个参数)。
d 在我们要访问此硬件时,就可以对设备文件通过open、read、write等命令进行。而驱动就会接收到相应的read、write操作而根据自己的模块中的相应函数进行了。