码迷,mamicode.com
首页 > 系统相关 > 详细

Linux USB子系统(十一)——Gadget function驱动分析

时间:2021-02-05 10:40:22      阅读:0      评论:0      收藏:0      [点我收藏+]

标签:bsp   parameter   idt   cat   inter   info   sys   读写   short   

function目录汇集了很多功能层的功能接口(interface)的具体实现
技术图片

我们这里分析UAC2.


一.UAC2 function驱动分析
代码位置 drivers\usb\gadget\function\f_uac2.c
里面实现usb设置中的接口和端点相关功能。
这里的DECLARE_USB_FUNCTION_INIT就是入口函数。
DECLARE_USB_FUNCTION_INIT(uac2, afunc_alloc_inst, afunc_alloc);
不过看起来好像有点不一样啊,我们来带入宏定义,后面就得到了熟悉的表达试。usb_function_register主要是把usb_function_driver放入func_list链表。
/* 这里是DECLARE_USB_FUNCTION的定义
#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));
    这里是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)
*/

//我们把宏定义化简之后,注册什么的都有了
static struct usb_function_driver uac2usb_func = {		//这里定义的是一个usb_function_driver驱动
		.name = __stringify(uac2),				
		.mod  = THIS_MODULE,					
		.alloc_inst = afunc_alloc_inst,				
		.alloc_func = afunc_alloc,				
	};								
	MODULE_ALIAS("usbfunc:"__stringify(uac2));

static int __init uac2mod_init(void)			
	{								
		return usb_function_register(&uac2usb_func);	 //这里主要是把这个uac2usb_func这个usb_function_driver放入func_list链表
	}								
static void __exit uac2mod_exit(void)			
	{								
		usb_function_unregister(&uac2usb_func);		
	}								
module_init(uac2mod_init);					
module_exit(uac2mod_init)
我们再来看看afunc_alloc_inst里面主要是分配一个usb_function_instance实例结构体,并赋值一些默认参数
static struct usb_function_instance *afunc_alloc_inst(void)
{
    struct f_uac2_opts *opts;

    opts = kzalloc(sizeof(*opts), GFP_KERNEL);
    if (!opts)
        return ERR_PTR(-ENOMEM);

    mutex_init(&opts->lock);
    opts->func_inst.free_func_inst = afunc_free_inst;

    config_group_init_type_name(&opts->func_inst.group, "",
                    &f_uac2_func_type); //这里是用来给用户空间操作的节点,比如p_srate,播放的采样率

    opts->p_chmask = UAC2_DEF_PCHMASK;
    opts->p_srate = UAC2_DEF_PSRATE;
    opts->p_ssize = UAC2_DEF_PSSIZE;
    opts->c_chmask = UAC2_DEF_CCHMASK;
    opts->c_srate = UAC2_DEF_CSRATE;
    opts->c_ssize = UAC2_DEF_CSSIZE;
    opts->req_number = UAC2_DEF_REQ_NUM;
    return &opts->func_inst;
}
我们再来看看afunc_alloc,主要是设置一些操作函数,初始化接口端点描述符,最后初始化一个虚拟ALSA声卡
static struct usb_function *afunc_alloc(struct usb_function_instance *fi)
{
    struct f_uac2    *uac2;
    struct f_uac2_opts *opts;

    uac2 = kzalloc(sizeof(*uac2), GFP_KERNEL);
    if (uac2 == NULL)
        return ERR_PTR(-ENOMEM);

    opts = container_of(fi, struct f_uac2_opts, func_inst);
    mutex_lock(&opts->lock);
    ++opts->refcnt;
    mutex_unlock(&opts->lock);

    uac2->g_audio.func.name = "uac2_func"; //这里是function的名字
    uac2->g_audio.func.bind = afunc_bind;  //用来绑定设备和function
    uac2->g_audio.func.unbind = afunc_unbind;
    uac2->g_audio.func.set_alt = afunc_set_alt;
    uac2->g_audio.func.get_alt = afunc_get_alt;
    uac2->g_audio.func.disable = afunc_disable;
    uac2->g_audio.func.setup = afunc_setup;
    uac2->g_audio.func.free_func = afunc_free;


    return &uac2->g_audio.func;
}
我再看看afunc_bind
static struct usb_string strings_fn[] = { //字符串
	[STR_ASSOC].s = "Source/Sink",
	[STR_IF_CTRL].s = "Topology Control",
	[STR_CLKSRC_IN].s = clksrc_in,
	[STR_CLKSRC_OUT].s = clksrc_out,
	[STR_USB_IT].s = "USBH Out",
	[STR_IO_IT].s = "USBD Out",
	[STR_USB_OT].s = "USBH In",
	[STR_IO_OT].s = "USBD In",
	[STR_AS_OUT_ALT0].s = "Playback Inactive",
	[STR_AS_OUT_ALT1].s = "Playback Active",
	[STR_AS_IN_ALT0].s = "Capture Inactive",
	[STR_AS_IN_ALT1].s = "Capture Active",
	{ },
};
static int
afunc_bind(struct usb_configuration *cfg, struct usb_function *fn)
{
    us = usb_gstrings_attach(cdev, fn_strings, ARRAY_SIZE(strings_fn)); //获取字符串描述符
    if (IS_ERR(us))
        return PTR_ERR(us);
    //处理化function,ClockSource, Terminal,Interface,这里是从字符串里面获得的值赋值
    iad_desc.iFunction = us[STR_ASSOC].id; 
    std_ac_if_desc.iInterface = us[STR_IF_CTRL].id; 
    in_clk_src_desc.iClockSource = us[STR_CLKSRC_IN].id;
    out_clk_src_desc.iClockSource = us[STR_CLKSRC_OUT].id;
    usb_out_it_desc.iTerminal = us[STR_USB_IT].id;
    io_in_it_desc.iTerminal = us[STR_IO_IT].id;
    usb_in_ot_desc.iTerminal = us[STR_USB_OT].id;
    io_out_ot_desc.iTerminal = us[STR_IO_OT].id;
    std_as_out_if0_desc.iInterface = us[STR_AS_OUT_ALT0].id;
    std_as_out_if1_desc.iInterface = us[STR_AS_OUT_ALT1].id;
    std_as_in_if0_desc.iInterface = us[STR_AS_IN_ALT0].id;
    std_as_in_if1_desc.iInterface = us[STR_AS_IN_ALT1].id;

    /* Initialize the configurable parameters 初始化可配置参数*/
    usb_out_it_desc.bNrChannels = num_channels(uac2_opts->c_chmask);
    usb_out_it_desc.bmChannelConfig = cpu_to_le32(uac2_opts->c_chmask);
    io_in_it_desc.bNrChannels = num_channels(uac2_opts->p_chmask);
    io_in_it_desc.bmChannelConfig = cpu_to_le32(uac2_opts->p_chmask);
    as_out_hdr_desc.bNrChannels = num_channels(uac2_opts->c_chmask);
    as_out_hdr_desc.bmChannelConfig = cpu_to_le32(uac2_opts->c_chmask);
    as_in_hdr_desc.bNrChannels = num_channels(uac2_opts->p_chmask);
    as_in_hdr_desc.bmChannelConfig = cpu_to_le32(uac2_opts->p_chmask);
    as_out_fmt1_desc.bSubslotSize = uac2_opts->c_ssize;
    as_out_fmt1_desc.bBitResolution = uac2_opts->c_ssize * 8;
    as_in_fmt1_desc.bSubslotSize = uac2_opts->p_ssize;
    as_in_fmt1_desc.bBitResolution = uac2_opts->p_ssize * 8;

    snprintf(clksrc_in, sizeof(clksrc_in), "%uHz", uac2_opts->p_srate);
    snprintf(clksrc_out, sizeof(clksrc_out), "%uHz", uac2_opts->c_srate);

    ret = usb_interface_id(cfg, fn); //给这个function分配未使用的接口ID
    if (ret < 0) {
        dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
        return ret;
    }
    iad_desc.bFirstInterface = ret; //控制接口
    std_ac_if_desc.bInterfaceNumber = ret;
    uac2->ac_intf = ret;
    uac2->ac_alt = 0;

    if (EPOUT_EN(uac2_opts)) {
        ret = usb_interface_id(cfg, fn);
        if (ret < 0) {
            dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
            return ret;
        }
        std_as_out_if0_desc.bInterfaceNumber = ret; //音频流输出接口
        std_as_out_if1_desc.bInterfaceNumber = ret;
        uac2->as_out_intf = ret;
        uac2->as_out_alt = 0;
    }

    if (EPIN_EN(uac2_opts)) {
        ret = usb_interface_id(cfg, fn);
        if (ret < 0) {
            dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
            return ret;
        }
        std_as_in_if0_desc.bInterfaceNumber = ret;  //音频流输入接口
        std_as_in_if1_desc.bInterfaceNumber = ret;
        uac2->as_in_intf = ret;
        uac2->as_in_alt = 0;
    }
    /* Calculate wMaxPacketSize according to audio bandwidth 根据音频的带宽,计算最大包的大小*/
    set_ep_max_packet_size(uac2_opts, &fs_epin_desc, 1000, true);
    set_ep_max_packet_size(uac2_opts, &fs_epout_desc, 1000, false);
    set_ep_max_packet_size(uac2_opts, &hs_epin_desc, 8000, true);
    set_ep_max_packet_size(uac2_opts, &hs_epout_desc, 8000, false);

    if (EPOUT_EN(uac2_opts)) { //输出端点
        agdev->out_ep = usb_ep_autoconfig(gadget, &fs_epout_desc); //选择与描述符匹配的端点
        if (!agdev->out_ep) {
            dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
            return -ENODEV;
        }
    }

    if (EPIN_EN(uac2_opts)) { //输入端口
        agdev->in_ep = usb_ep_autoconfig(gadget, &fs_epin_desc);
        if (!agdev->in_ep) {
            dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
            return -ENODEV;
        }
    }
    agdev->in_ep_maxpsize = max_t(u16,
                le16_to_cpu(fs_epin_desc.wMaxPacketSize),
                le16_to_cpu(hs_epin_desc.wMaxPacketSize)); //最大包大小
    agdev->out_ep_maxpsize = max_t(u16,
                le16_to_cpu(fs_epout_desc.wMaxPacketSize),
                le16_to_cpu(hs_epout_desc.wMaxPacketSize));

    hs_epout_desc.bEndpointAddress = fs_epout_desc.bEndpointAddress; //高速输出端点
    hs_epin_desc.bEndpointAddress = fs_epin_desc.bEndpointAddress;//高速输入端点

    setup_descriptor(uac2_opts); //设置一些描述符

    ret = usb_assign_descriptors(fn, fs_audio_desc, hs_audio_desc, NULL,
                     NULL); //给全速和高速端点分配描述符
    if (ret)
        return ret;

    agdev->gadget = gadget;
    agdev->params.p_chmask = uac2_opts->p_chmask; //赋值一些参数
    agdev->params.p_srate = uac2_opts->p_srate;
    agdev->params.p_ssize = uac2_opts->p_ssize;
    agdev->params.c_chmask = uac2_opts->c_chmask;
    agdev->params.c_srate = uac2_opts->c_srate;
    agdev->params.c_ssize = uac2_opts->c_ssize;
    agdev->params.req_number = uac2_opts->req_number;
    ret = g_audio_setup(agdev, "UAC2 PCM", "UAC2_Gadget"); //初始化一个虚拟ALSA声卡
    if (ret)
        goto err_free_descs;
    return 0;
}
我们再来分析g_audio_setup里面是如何初始化一个虚拟ALSA声卡的,这样应用层就可以读写了。
static const struct snd_pcm_ops uac_pcm_ops = { //uac的操作函数。
	.open = uac_pcm_open,
	.close = uac_pcm_null,
	.ioctl = snd_pcm_lib_ioctl,
	.hw_params = uac_pcm_hw_params,
	.hw_free = uac_pcm_hw_free,
	.trigger = uac_pcm_trigger,
	.pointer = uac_pcm_pointer,
	.prepare = uac_pcm_null,
};

int g_audio_setup(struct g_audio *g_audio, const char *pcm_name,
                    const char *card_name)
{
    /* Choose any slot, with no id */
    err = snd_card_new(&g_audio->gadget->dev,
            -1, NULL, THIS_MODULE, 0, &card); //创建并初始化一个声卡结构
    if (err < 0)
        goto fail;
    uac->card = card;
    /*
     * Create first PCM device
     * Create a substream only for non-zero channel streams
     */
    err = snd_pcm_new(uac->card, pcm_name, 0,
                   p_chmask ? 1 : 0, c_chmask ? 1 : 0, &pcm); //创建一个新的PCM实例
    if (err < 0)
        goto snd_fail;

    strlcpy(pcm->name, pcm_name, sizeof(pcm->name));
    pcm->private_data = uac;
    uac->pcm = pcm;

    snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &uac_pcm_ops); //设置PCM操作符
    snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &uac_pcm_ops);

    strlcpy(card->driver, card_name, sizeof(card->driver));
    strlcpy(card->shortname, card_name, sizeof(card->shortname));
    sprintf(card->longname, "%s %i", card_name, card->dev->id);

    snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS,
        snd_dma_continuous_data(GFP_KERNEL), 0, BUFF_SIZE_MAX); //对于指定的DMA类型,对给定pcm的所有子流进行预先分配。

    err = snd_card_register(card); //注册声卡,这样应用层就可以读写了。
    if (!err)
        return 0;
}





Linux USB子系统(十一)——Gadget function驱动分析

标签:bsp   parameter   idt   cat   inter   info   sys   读写   short   

原文地址:https://www.cnblogs.com/wen123456/p/14373706.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!