ALSA框架UAC1驱动程序入口宏DECLARE_USB_FUNCTION分析
题外话:为了学习看LINUX驱动,我也是拼了。对于LINUX源代码,虽然以前了解过LINUX的字符串驱动,不过突然跳到一个复杂的LINUX设备驱动,还是有点迷茫,自己什么不会。不过还是要狠下心来,像看了7天的UVC规范一样,强行让自己翻译。
自己看的Linux UAC源代码位于https://elixir.bootlin.com/linux/v5.5-rc2/source/drivers/usb/gadget/function
目录下。没有访问过的同学可以看一下,个人觉地很不错。主要是和我的习惯一样,全部WEB化,可随时学习。
UAC1源驱动源代码源文件为:function/f_uac1.c
当然,这个目录下也包括一些通用其它的代码,比如UAC2等。
记得LINUX驱动程序的入口一般是通过module_init和module_exit来指定驱动的出入口的。而ALSA驱动程序框架,对其做了一个封装,使用宏 DECLARE_USB_FUNCTION_INIT来实现。
#define DECLARE_USB_FUNCTION_INIT(_name, _inst_alloc, _func_alloc) \
DECLARE_USB_FUNCTION(_name, _inst_alloc, _func_alloc) \
static int __init _name ## mod_init(void) \
{ \
return usb_function_register(&_name ## usb_func); \
} \
static void __exit _name ## mod_exit(void) \
{ \
usb_function_unregister(&_name ## usb_func); \
} \
module_init(_name ## mod_init); \
module_exit(_name ## mod_exit)
这里使用了C语言的双#连接符号,如我们定义:
define delare(name) usbzh_##name
那么我们在使用时可以这样
declare(code)
则实际宏展开为:
usbzh_code
所以我们在f_uac1.c的最底部可以看到:
DECLARE_USB_FUNCTION_INIT(uac1, f_audio_alloc_inst, f_audio_alloc);
那展开就是:
DECLARE_USB_FUNCTION(uac1, f_audio_alloc_inst, f_audio_alloc)
static int __init uac1mod_init(void)
{
return usb_function_register(&uac1usb_func);
}
static void __exit uac1mod_exit(void)
{
usb_function_unregister(&uac1usb_func);
}
module_init(uac1mod_init);
module_exit(uac1mod_exit)
而DECLARE_USB_FUNCTION也是一个宏,是UAC驱动基本结构体的实例声名:
#define DECLARE_USB_FUNCTION(_name, _inst_alloc, _func_alloc) \
static struct usb_function_driver _name ## usb_func = { \
.name = __stringify(_name), \
.mod = THIS_MODULE, \
.alloc_inst = _inst_alloc, \
.alloc_func = _func_alloc, \
}; \
MODULE_ALIAS("usbfunc:"__stringify(_name));
则为:
static struct usb_function_driver uac1usb_func = {
.name = __stringify(uac1),
.mod = THIS_MODULE,
.alloc_inst = f_audio_alloc_inst,
.alloc_func = f_audio_alloc,
};
MODULE_ALIAS("usbfunc:"__stringify(uac1));
这两个宏的定义位于:
https://elixir.bootlin.com/linux/v5.5-rc2/source/include/linux/usb/composite.h#L617
可以看到,其最终落到了f_audio_alloc_inst和f_audio_alloc两个函数上。
而以上的宏通过这种嵌套帮助我们方便地指定驱动的名字和两个回调函数,就帮我们实现了UAC驱动程序的总入口。
至于f_audio_alloc_inst和f_audio_alloc这两个函数是干什么的,当前我还不清楚。不过按我们做驱动的经验,这两个函数是系统对该驱动程序的总入口,并且其是通过usb_function_register和usb_function_unregister通过结构体uac1usb_func实现注册传入的。