ALSA框架UAC1函数f_audio_alloc_inst和f_audio_alloc
前面大概分析到了UAC1的系统级的入口函数f_audio_alloc_inst和f_audio_alloc。
从代码上来看,会先调用usb_function_instance,再是f_audio_alloc。这是因为f_audio_alloc函数中引用了usb_function_instance申请的f_uac1_opts结构体。
机智如我。。。
两个函数的源代码如下:
static struct usb_function_instance *f_audio_alloc_inst(void)
{
struct f_uac1_opts *opts;
opts = kzalloc(sizeof(*opts), GFP_KERNEL);
if (!opts)
return ERR_PTR(-ENOMEM);
mutex_init(&opts->lock);
opts->func_inst.free_func_inst = f_audio_free_inst;
config_group_init_type_name(&opts->func_inst.group, "",
&f_uac1_func_type);
opts->c_chmask = UAC1_DEF_CCHMASK;
opts->c_srate = UAC1_DEF_CSRATE;
opts->c_ssize = UAC1_DEF_CSSIZE;
opts->p_chmask = UAC1_DEF_PCHMASK;
opts->p_srate = UAC1_DEF_PSRATE;
opts->p_ssize = UAC1_DEF_PSSIZE;
opts->req_number = UAC1_DEF_REQ_NUM;
return &opts->func_inst;
}
static struct usb_function *f_audio_alloc(struct usb_function_instance *fi)
{
struct f_uac1 *uac1;
struct f_uac1_opts *opts;
/* allocate and initialize one new instance */
uac1 = kzalloc(sizeof(*uac1), GFP_KERNEL);
if (!uac1)
return ERR_PTR(-ENOMEM);
opts = container_of(fi, struct f_uac1_opts, func_inst);
mutex_lock(&opts->lock);
++opts->refcnt;
mutex_unlock(&opts->lock);
uac1->g_audio.func.name = "uac1_func";
uac1->g_audio.func.bind = f_audio_bind;
uac1->g_audio.func.unbind = f_audio_unbind;
uac1->g_audio.func.set_alt = f_audio_set_alt;
uac1->g_audio.func.get_alt = f_audio_get_alt;
uac1->g_audio.func.setup = f_audio_setup;
uac1->g_audio.func.disable = f_audio_disable;
uac1->g_audio.func.free_func = f_audio_free;
return &uac1->g_audio.func;
}
可以看到,主要是分配两个关键的结构体和初始化一堆堆的回调函数。
所以这里我画了一张草图来表述它们之间的关系:
从当前来看 f_uac1_opts结构体好像有用的就是这个引用计数和其锁,而这些掩码是用来对一些初始化参数用的。
另外f_uac1这个结构体通过继承USB通用框架结构体usb_function func和引用usb_gadget* gdaget的指针。这就体现了C++类的继承方面的思想,也和UAC规范的设计思想一致,其是建立在USB规范基础上的,而LINUX相关的USB框架又与gdaget相关,这与USB的设备相关。
另外,在结构体uac1中:
struct f_uac1 {
struct g_audio g_audio;
u8 ac_intf, as_in_intf, as_out_intf;
u8 ac_alt, as_in_alt, as_out_alt; /* needed for get_alt() */
};
这里有一些相关的整数型,分别为ac和as开头的,这其实对应的是Audio Control和Audio Stream的简单。所也有就是了输入,输入接口的一些参数。
- ac_intf:音频控制接口
- as_in_intf音频流输入接口
- as_out_intf:音频输出流接口
而下面的alt对应的是alternat,即转换或者叫备用接口索引,用于音频设备打开时的数据端点。所以后面的函数get_alt与set_alt的功能就呼之欲出。
而bind和unbind就类似于windows中IRP_MN_START和IRP_MN_REMOVE。
f_audio_free函数从名称上来看是f_audio_alloc的逆向函数,f_audio_alloc类似DriverEntry,那么f_audio_free就是类似DriverUnload,不过f_audio_free中并未内存释放,而仅是引用计数减1,所以内存释放应该是modeinstall remove来实现调用f_audio_free吧。
f_audio_setup就是控制端点的控制命令,就是SET_CUR,GET_MAX,GET_CUR,GET_MIN等这些特定类请求。
另外,我们在看LINUX关于UAC源代码时,发现大量函数的入口参数第一个参数为usb_function的指针类型,其实这个指针也就是我们的struct f_uac1,也是的指针struct g_audio指针。不过为了防止usb_function成员没有在第一个,也使用了宏func_to_g_audio来进行转换。这个宏的功能和windows的CONTANING_RECORD的功能一致。
#define CONTAINING_RECORD(address, type, field) ((type *)( \
(PCHAR)(address) - \
(ULONG_PTR)(&((type *)0)->field)))
#ifndef container_of
#define container_of(ptr, type, member) \
(type *)((char *)(ptr) - (char *) &((type *)0)->member)
#endif
static inline struct g_audio *func_to_g_audio(struct usb_function *f)
{
return container_of(f, struct g_audio, func);
}
所以,惊不惊喜,意不意外。其实仔细想想也是理所当然了,虽然linux和windows是两个不同的操作系统,不过其设计思路基本一致,只是实现细节不同而已。