USB Gadget
+ -

基于Linux4.1.15源代码修改USB Gadget同时支持麦克风和扬声器

2025-09-23 本文链接为:http://www.usbzh.com/article/detail-1587.html ,欢迎转载,转载请附上本文链接。

学习USB为了精益求精。

为了搞清楚Llinux下USB设备的驱动程序,本人从USB Gadget开始,在做了大量的试验之后,发现UAC1并不能创建麦克风。所以,不得不从KS文件系统配置到了源代码的分析上。通过分析UAC1源代码(f_uac1.c)并没有实现麦克风功能。这一点在也RK的文档上有说明(Rockchip Developer Guide Linux4.4 USB Gadget UAC CN)。

通过阅读源代码f_uac1.c,对下面的这句话有了深刻的认知:

[1] Kernel 4.4 ⽀持USB Gadget UAC1/UAC2 录⾳和放⾳功能 问题描述: Kernel-4.4 的 USB Gadget UAC1/UAC2 驱动存在如下的问题: UAC1只⽀持放⾳功能,并且需要实际声卡配合使⽤ UAC2⽆法兼容Windows,虽然可以⽀持录⾳和放⾳,但是功能不完善

自己吐槽半天,也无济于事。也只能自己亲自上阵,结合自己丰富的USB/UAC知识,手搓一下,让其同时支持麦克风和扬声器。就当是对自己Linux/USB入门的第一次课内实践。经过本人的反复调试与修改,终于大功告成,现将结果展示如下。
165237580965
165258948627

番外

由于本人对linux kernel及USB Gadget相关的API函数不熟悉,这里将一些常用的函数进行总结。

端点数据包的创建与释放

struct usb_request* mreq = usb_ep_alloc_request(in_ep, GFP_ATOMIC);
if (mreq)
{
    mreq->buf = kzalloc(req->actual, GFP_ATOMIC);
    if (mreq->buf)
    {
        mreq->context = audio;
        mreq->complete = f_audio_complete;
        mreq->length = req->actual;
        memcpy(mreq->buf, req->buf, req->actual);
        usb_ep_queue(in_ep, mreq, GFP_ATOMIC);
    }
    else
    {
        usb_ep_free_request(in_ep,mreq);
    }
}
`

对于IN端点,usb_ep_queue的usb_request请求,是接收的,在完成例程中根据actual获取实际的数据长度。
对于OUT端点,usb_ep_queue的usb_request请求,是待发送的数据。

端点的启用与禁用

int usb_ep_enable(struct usb_ep *ep);
int usb_ep_disable(struct usb_ep *ep);

不过判断一个端点是已经启用还是禁用,则根据端点的driver_data来判断。
如果启用端点,则:

err = config_ep_by_speed(cdev->gadget, f, out_ep);
err = usb_ep_enable(out_ep);
if(!err)
{
    out_ep->driver_data = audio;
}

如果要禁用:

    if (in_ep->driver_data)
    {
        usb_ep_disable(in_ep);
        in_ep->driver_data = NULL;
    }

疑惑

在打开关闭断点时,个人的理解应是usb_ep_enable和usb_ep_disable端点,特别是对于同步传输时,应在set_alternate中实现。根据alt是0还是非0来进行enable或disable。但实际好像并非如此。

对于输入端点,进行set_alt之后,无论alt=0或非0,都是先禁用,然后再根据alt=1来enable,而alt=0则跳出。

static int sourcesink_set_alt(struct usb_function *f,unsigned intf, unsigned alt)
{
    //只针对的是输入同步
    if (ss->in_ep->driver_data)
        disable_source_sink(ss);

    return enable_source_sink(cdev, ss, alt);
}
static int enable_source_sink(struct usb_composite_dev *cdev, struct f_sourcesink *ss,int alt)
{
    int                    result = 0;
    int                    speed = cdev->gadget->speed;
    struct usb_ep                *ep;

//    bulk的输入端点
    /* one bulk endpoint writes (sources) zeroes IN (to the host) */
    ep = ss->in_ep;
    result = config_ep_by_speed(cdev->gadget, &(ss->function), ep);
    if (result)
        return result;
    result = usb_ep_enable(ep);
    if (result < 0)
        return result;
    ep->driver_data = ss;

//bulk输出端点
    result = source_sink_start_ep(ss, true, false, speed);
    if (result < 0) {
fail:
        ep = ss->in_ep;
        usb_ep_disable(ep);
        ep->driver_data = NULL;
        return result;
    }

    /* one bulk endpoint reads (sinks) anything OUT (from the host) */
    ep = ss->out_ep;
    result = config_ep_by_speed(cdev->gadget, &(ss->function), ep);
    if (result)
        goto fail;
    result = usb_ep_enable(ep);
    if (result < 0)
        goto fail;
    ep->driver_data = ss;

    result = source_sink_start_ep(ss, false, false, speed);
    if (result < 0) {
fail2:
        ep = ss->out_ep;
        usb_ep_disable(ep);
        ep->driver_data = NULL;
        goto fail;
    }

//后面的是同步端点
    if (alt == 0)
        goto out;

    /* one iso endpoint writes (sources) zeroes IN (to the host) */
    ep = ss->iso_in_ep;
    if (ep) {
        result = config_ep_by_speed(cdev->gadget, &(ss->function), ep);
        if (result)
            goto fail2;
        result = usb_ep_enable(ep);
        if (result < 0)
            goto fail2;
        ep->driver_data = ss;

        result = source_sink_start_ep(ss, true, true, speed);
        if (result < 0) {
fail3:
            ep = ss->iso_in_ep;
            if (ep) {
                usb_ep_disable(ep);
                ep->driver_data = NULL;
            }
            goto fail2;
        }
    }

    /* one iso endpoint reads (sinks) anything OUT (from the host) */
    ep = ss->iso_out_ep;
    if (ep) {
        result = config_ep_by_speed(cdev->gadget, &(ss->function), ep);
        if (result)
            goto fail3;
        result = usb_ep_enable(ep);
        if (result < 0)
            goto fail3;
        ep->driver_data = ss;

        result = source_sink_start_ep(ss, false, true, speed);
        if (result < 0) {
            usb_ep_disable(ep);
            ep->driver_data = NULL;
            goto fail3;
        }
    }
out:
    ss->cur_alt = alt;

    DBG(cdev, "%s enabled, alt intf %d\n", ss->function.name, alt);
    return result;
}

enable_source_sink中则先处理2个BULK端点,启用。
对于同步则是alt=0时直接跳出,所以导致的是:

  • 输出端点未disable
  • 输入端点在之前已经disable

当alt!=0时,对于输入输出端点都会调用

  • config_ep_by_speed
  • usb_ep_enable
  • source_sink_start_ep

然后在source_sink_start_ep给其端点输入队列


static int source_sink_start_ep(struct f_sourcesink *ss, bool is_in,bool is_iso, int speed)
{
    struct usb_ep        *ep;
    struct usb_request    *req;
    int            i, size, status;

    for (i = 0; i < 8; i++) {
        if (is_iso) {
            switch (speed) {
            case USB_SPEED_SUPER:
                size = isoc_maxpacket * (isoc_mult + 1) *(isoc_maxburst + 1);
                break;
            case USB_SPEED_HIGH:
                size = isoc_maxpacket * (isoc_mult + 1);
                break;
            default:
                size = isoc_maxpacket > 1023 ?1023 : isoc_maxpacket;
                break;
            }
            ep = is_in ? ss->iso_in_ep : ss->iso_out_ep;
            req = ss_alloc_ep_req(ep, size);
        } 
        else 
        {
            ep = is_in ? ss->in_ep : ss->out_ep;
            req = ss_alloc_ep_req(ep, 0);
        }

        if (!req)
            return -ENOMEM;

        req->complete = source_sink_complete;
        if (is_in)
            reinit_write_data(ep, req);
        else if (pattern != 2)
            memset(req->buf, 0x55, req->length);

        status = usb_ep_queue(ep, req, GFP_ATOMIC);
        if (status) {
            struct usb_composite_dev    *cdev;

            cdev = ss->function.config->cdev;
            ERROR(cdev, "start %s%s %s --> %d\n",is_iso ? "ISO-" : "", is_in ? "IN" : "OUT",ep->name, status);
            free_ep_req(ep, req);
        }

        if (!is_iso)
            break;
    }

    return status;
}
本文链接为:http://www.usbzh.com/article/detail-1587.html ,欢迎转载,转载请附上本文链接。

0 篇笔记 写笔记

USB Gadget CDC-ECM网卡实例
ECM和NCM网卡类似,也需要安装ECM驱动设备兼容ID为:USBClass_02&SubClass_06&Prot_00USBClass_02&SubClass_06USBClass_02创建脚本:#!/bin/bashmodprobe libcompos......
Linux Gadget驱动结构关系图
Linux Gadget驱动层级如下:Gadget Function驱动/gadget legacy驱动Gadget Compoiste驱动USB UDC驱动(USB Device Control)Gadget Function驱动/gadget legacy驱动function, 更加现......
gadget probe过程-以g_audio为例
g_audio中通过如下代码调用usb_composite_probe,其中入参为audio_driverstatic struct usb_composite_driver audio_driver = { .name = "g_audio", ......
configfs文件系统
Linux内核使用CONFIGFS配置项支持可以通过用户创建USB Compoiste Gadget。CONFIG_CONFIGFS_FS项用于支持配置文件系统。Menconfig -> FileSystems -> Psuedo File System -> {M}User......
Gadget legacy和Gadget Function的区别
在 Linux 的 USB Gadget 子系统中,Legacy GadgetGadget Function(FunctionFS 或 configfs) 是两种不同的实现方式,用于将 Linux 设备配置为 USB 外设(如 U 盘、网卡、串口等)。它们的区别主要体现在架构、使用方式和灵活性......
usb_gadget复合HID键盘和U盘实例
#!/bin/bash# 加载模块modprobe libcompositemodprobe usb_f_hidmodprobe usb_f_mass_storage.komount -t configfs none /sys/kernel/config# 创建 Gadgetcd......
usb gadget创建uvc相机脚本实例
#!/bin/bashmodprobe libcompositemodprobe usb_f_uvcmount -t configfs none /sys/kernel/configmkdir -p /sys/kernel/config/usb_gadget/g1cd /sys/kern......
USB Gadget CDC-ACM串口实例
符合USB ACM规范的设备由Windows提供其驱动程序usbser.sysuserser.infLinux Gadget对应的ACM驱动依赖为root@ATK-IMX6U:/sys/kernel/config/usb_gadget/g1# lsmodModule ......
USB Gadget CDC-NCM网卡实例
USG Gadget也提供了CDC-NCM网卡驱动程序,使用USB Gadget CDC-NCM驱动,这样在Windows主机端和Linux设备端都会创建一个USB网卡设备。root@ATK-IMX6U:/lib/modules/4.1.15# ifconfig -ausb0 Lin......
USB Gadget 打印机实例
打印机这一块应该要与实例相连,具体这一块没有研究过。生成的设备如下:设备模块如下:root@ATK-IMX6U:/lib/modules/4.1.15# lsmodModule Size Used byusb_f_printer 1137......
USB Gadget CDC-EEM网卡实例
从理论上来讲,EEM网卡Windows是应该支持的,实际上也确实不用像NCM、ECM需要手动安装驱动,系统会自动匹配兼容ID来进行安装驱动,但是驱动启动失败,这个Linux系统下就没有问题。#!/bin/bashmodprobe libcompositemodprobe usb_f_eemm......
USB Gadget LoopBack实例
Loopback Function 提供的功能简单,它分配了两个 bulk endpoint,所做的就是把 out_ep 接收到的数据转发到 in_ep。#!/bin/bashmodprobe libcompositemodprobe usb_f_ss_lbmount -t configfs......
USB Gadget RNDIS网络适配器实列
和NCM一样,由于兼容ID的问题,RNDIS网络适配器也需要手动安装驱动。其对应的驱动为:netrndis.infrndismp6.sys其创建脚本如下:#!/bin/bashmodprobe libcompositemodprobe usb_f_rndismount -t conf......
USB Gadget实例分析usb_function_instance和usb_function
linux usb gadtet的设备类驱动有两个很重要的概念:usb_function_instance和usb_function从名来上看,usb_function_instance像是usb_function的实例化,但实际是恰恰相反。这里以f_loopback为示例解释:USB设备信息执......
USB Gadget数据收发-批量传输
当执行命令ln -s functions/Loopback.0 configs/c.1之后,会调用loopback_alloc函数创建usb_functionstruct f_loopback { struct usb_function function; struct......
关注公众号
  • HID人机交互
  • Linux&USB
  • UAC音频
  • CDC
  • TYPE-C
  • USB规范
  • USB大容量存储
  • USB百科
  • USB周边
  • UVC摄像头
  • Windows系统USB
  • 音视频博客
  • 取消
    感谢您的支持,我会继续努力的!
    扫码支持
    扫码打赏,你说多少就多少

    打开支付宝扫一扫,即可进行扫码打赏哦

    您的支持,是我们前进的动力!