基于Linux4.1.15源代码修改USB Gadget同时支持麦克风和扬声器
学习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入门的第一次课内实践。经过本人的反复调试与修改,终于大功告成,现将结果展示如下。

番外
由于本人对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 ,欢迎转载,转载请附上本文链接。
USB Gadget





