freebsd源代码中设备驱动接口bus_setup_intr怎么实现的
小弟最近在研究freebsd设备驱动源代码,看到代码中设备驱动接口bus_setup_intr,在手册中查看是注册设备的中断处理程序,该函数是对BUS_SETUP_INTR函数的封装,但找遍整个源代码,都没有找到BUS_SETUP_INTR该函数的定义,请问各位大神们该函数接口是如何实现的 没有高手回复吗,那分享一下我自己研究的心得吧:bus_intr_setup函数显示调用BUS_SETUP_INTR函数接口,该接口的实现如下:static __inline int BUS_SETUP_INTR(device_t _dev, device_t _child,
struct resource *_irq, int _flags,
driver_filter_t *_filter,
driver_intr_t *_intr, void *_arg,
void **_cookiep)
{
kobjop_t _m;
KOBJOPLOOKUP(((kobj_t)_dev)->ops,bus_setup_intr);
return ((bus_setup_intr_t *) _m)(_dev, _child, _irq, _flags, _filter, _intr, _arg, _cookiep);
}
其中:KOBJOPLOOKUP宏定义如下所示:
#define KOBJOPLOOKUP(OPS,OP) do { \
kobjop_desc_t _desc = &OP##_##desc; \
kobj_method_t **_cep = \
&OPS->cache; \
kobj_method_t *_ce = *_cep; \
if (_ce->desc != _desc) \
_ce = kobj_lookup_method(OPS->cls, \
_cep, _desc); \
_m = _ce->func; \
} while(0)
以上代码表示寻找父设备节点的驱动方法_m;然后用指定的函数_m来完成后续的工作,单从名字上理解该bus_intr_setup就是注册该设备的中断服务程序,但实现的过程如此复杂,且函数_m未知,真不知道freebsd为何如此设计?? 这种用define的手段在FreeBSD源代码中非常常见,前几天追踪logo显示也遇到了它,感觉在功能上,就像定义oop里面的接口一样,好让后面的具体实例来实现。 回复 3# lsstarboy
所以我认为bus_intr_setup函数其实并没有真正注册该设备驱动的中断处理程序,该函数接口只是一个抽象的方法,它其实只是根据具体定义的设备实例中所绑定的方法来注册中断处理程序,Linux这一块的实现则简单明了得多了,只需一个request_irq函数就完成该设备驱动的中断注册。
不知道其他高手是怎么认为的,不妨来一起讨论研究一下。 俺是业余级的,不懂源代码,最多能算是了解点C语言。
@71v5
呼叫 71v5 ,他才是高手。 其实看代码最有意思的不是知道它怎么干的,而是为什么这么干.
bus_setup_intr这个函数类似C++的虚函数,意图是由真正负责中断服务的模快来实现,以arm版本的实现为例正常情况下这个函数最终调用函数nexus_setup_intr,向intr_events注册一个事件,如果需要还会创建一个线程.中断发生后调用intr_event_handle然后是intr_filter_loop,根据注册中断服务时的参数决定是启动一个ithread还是直接调用注册的filter函数.
毕竟不是作者,只能推测bsd采用这种方式的原因如下
1.freebsd的作者是一帮完美主义者,如果有可能,那么一定采用通用性的实现,考虑下边的硬件模型
GIC(CortexA9)
-->USB
-->Host
-->Endpoint
-->HUB
-->Device
-->OTG
-->SPI
-->SDIO
-->Wifi module
-->GPIO
-->Key
可以看到每一层都有可能使用中断服务,但是不幸的是这不可能由某一层单独解决问题,那么如果不是分别实现不同的函数,由驱动程序各自调用(例如 gpio_setup_intr/sdio_setup_intr/...),就是用C++的虚函数方式实现,这样好处出现了,驱动程序永远只需要调用bus_setup_intr,交给各自的模块来实现,那么设备树就变成下面的样子
nexus bus service(GIC implenment)
-->usb bus service(USB intr implenment)
-->Endpoint bus service(如果需要的话)
这下完美主义者的freebsdman爽了,读代码的人累了
2.freebsd的作者想的很多,只要有可能,那么就支持
正常情况下,一个设备的中断服务程序只有一个,我们叫它复合型中断服务,例如网卡驱动,我们的中断服务不仅处理载波信息还处理错误情况还有数据收发服务.但是这对于一个完美主义者来说是不可忍受的,如果有100种服务那么就应该写100个函数,而不是写99个,现在知道上面我列出的USB intr implenment干嘛了吧,你完全可以写一个USB中断服务程序只进行服务分拣,然后再调用各自的中断服务,靠,这个程序写出来就漂亮了,又清晰又易读,爽了吧
3.freebsd的作者有强迫症
如果只是实现上面的一些考虑,那么我们其实还有别的方法,例如建立一个统一的中断模块,采用注册制用intr ID的分段来实现,为啥用C++的方式呢?
我们把视线提到最高,不局限于中断服务,那么会发现,其实中断也只是快速响应事件的一种方式而已,那么为啥别的服务比如什么probe/load等等都以C++实现而你丫中断就特殊,这对于强迫症患者是忍不了的,更何况,反过来想,凭啥中断就只能设备用,凭啥我一些别的软件服务不能用,我大软件也要用中断,虚拟的中断。作者云:满足你。
最终这种实现方式出炉了。 下面是建议:
1:
认真看一下<Freebsd操作系统设计与实现>第7章第5节,其中关键一句话--"newbus系统在设计上的关键目标是给编写驱动程序的程序员提供一个
稳定的应用程序二进制接口".
2:
参考<Freebsd系统结构手册>第三章的简单描述.
3:
对于/usr/src/sys/kern/ 目录下的.m结尾的文件,以bus_if.m文件为例,可以执行下面:
awk -f /usr/src/sys/tools/makeobjops.awk ./bus_if.m -h -> 生成bus_if.c
awk -f /usr/src/sys/tools/makeobjops.awk ./bus_if.m -c -> 生成bus_if.h
在bus_if.c里面能找到BUS_SETUP_INTR的定义.
上面awk的处理实在构建kernel阶段自动完成的.
4:
看一下freebsd驱动程序的框架.
typedef struct kobj_class driver_t;
宏DRIVER_MODULE以及驱动程序中如何使用DRIVER_MODULE.
分析完宏DRIVER_MODULE的实现以及在驱动程序代码中如何使用后,你会发现完全可以导出freebsd的设备树结构的简单模型.
5:
在初始化阶段,会先对DRIVER_MODULE注册到内核的模块进行处理,建立必要的数据结构等.
6:
配置设备阶段,看一下函数configure_first就可以,当这个函数执行完后,设备的树形结构就会建立.
SYSINIT(configure1, SI_SUB_CONFIGURE, SI_ORDER_FIRST, configure_first, NULL);/*
* Determine i/o configuration for a machine.
*/
static void
configure_first(dummy)
void *dummy;
{
/* nexus0 is the top of the amd64 device tree */
device_add_child(root_bus, "nexus", 0);
}
下面是一个简单的设备树:
nexus0
acpi0
pcib0
pci0
em0 <----intel网卡
当探测到intel网卡时,驱动程序就会调用函数bus_setup_intr建立中断等.
/**
* @brief Wrapper function for BUS_SETUP_INTR().
*
* This function simply calls the BUS_SETUP_INTR() method of the
* parent of @p dev.
*/
int
bus_setup_intr(device_t dev, struct resource *r, int flags,
driver_filter_t filter, driver_intr_t handler, void *arg, void **cookiep)
{
int error;
if (dev->parent == NULL)
return (EINVAL);
error = BUS_SETUP_INTR(dev->parent, dev, r, flags, filter, handler,
arg, cookiep);
if (error != 0)
return (error);
if (handler != NULL && !(flags & INTR_MPSAFE))
device_printf(dev, "\n");
return (0);
}
BUS_SETUP_INTR基本上都是调用父设备的bus_setup_intr方法,在到达设备nexus0之前,相应设备的bus_setup_intr方法可以简单的认为是调用bus_generic_setup_intr方法.
/**
* @brief Helper function for implementing BUS_SETUP_INTR().
*
* This simple implementation of BUS_SETUP_INTR() simply calls the
* BUS_SETUP_INTR() method of the parent of @p dev.
*/
int
bus_generic_setup_intr(device_t dev, device_t child, struct resource *irq,
int flags, driver_filter_t *filter, driver_intr_t *intr, void *arg,
void **cookiep)
{
/* Propagate up the bus hierarchy until someone handles it. */
if (dev->parent)
return (BUS_SETUP_INTR(dev->parent, child, irq, flags,
filter, intr, arg, cookiep));
return (EINVAL);
}
当到达设备nexus0时,将调用nexus_setup_intr方法:
下面的在文件/usr/src/sys/x86/x86/nexus.c中:
/*
* This code implements a `root nexus' for Intel Architecture
* machines.The function of the root nexus is to serve as an
* attachment point for both processors and buses, and to manage
* resources which are common to all of them.In particular,
* this code implements the core resource managers for interrupt
* requests, DMA requests (which rightfully should be a part of the
* ISA code but it's easier to do it here for now), I/O port addresses,
* and I/O memory address space.
*/
static device_method_t nexus_methods[] = {
..........................................................
DEVMETHOD(bus_print_child, nexus_print_child),
DEVMETHOD(bus_add_child, nexus_add_child),
DEVMETHOD(bus_alloc_resource, nexus_alloc_resource),
DEVMETHOD(bus_adjust_resource,nexus_adjust_resource),
DEVMETHOD(bus_release_resource, nexus_release_resource),
DEVMETHOD(bus_activate_resource, nexus_activate_resource),
DEVMETHOD(bus_deactivate_resource, nexus_deactivate_resource),
DEVMETHOD(bus_setup_intr, nexus_setup_intr), <-------------
DEVMETHOD(bus_teardown_intr, nexus_teardown_intr),
...........................................................
{ 0, 0 }
}; 多谢以上高手的分析,我现在是做Linux相关开发,一直对freebsd的代码很感兴趣,发现freebsd的代码确实比Linux的代码更加完美和简洁,这一点不得不佩服,呵呵 个人感觉freebsd的驱动模型更加抽象和完美,Linux的驱动模型相对简洁,对程序员来说更容易看懂吧。
页:
[1]