本文 接着前面的文章 《Android下USB Accessory的实现分析 (二)— 底层驱动设计实现》

2.1.4 Android Open AccessoryProtocol

为了支持USB Accessory,让USB主从设备双方能够互相识别和兼容对方,Google定义了一套Android OpenAccessory Protocol(简称AOA),
此协议目前有两个版本:Version 1.0和Version2.0。

2.0版本是对对1.0版本的补充,增加了对Audio和HID类Accessory设备的支持。具体协议内容,
可参见如下链接:
http://source.android.com/accessories/aoa.html
http://source.android.com/accessories/aoa2.html

协议内容其实比较简单,主要是定义了一套USB控制传输命令,Accessory设备可以发起这些控制传输命令来获取和设置对应的Accessory功能。

另外,在Accessory模式下,USB Device端上报的VID和PID是Google指定的,V
ID固定为Google的官方VID – 0x18D1,PID则在不同的模式下定义如下:

●          0x2D00 - accessory
●          0x2D01 - accessory + adb
●          0x2D02 - audio
●          0x2D03 - audio + adb
●          0x2D04 - accessory + audio
●          0x2D05 - accessory + audio + adb

USB Accessory设备和Android设备两者双方整个枚举识别工作过程如下:

  1. (D从 -> H主 请求HOST枚举)
    USB Accessory设备发起USB控制传输进行正常的USB设备枚举,获取设备描述符和配置描述符等信息,
    此时大部分Android设备上报的还只是普通的U盘或MTP设备;

  2. (D从 -> H主 请求支持的AOA协议)
    接下来USB Accessory设备发起Vendor类型,request值为51(0x33)的控制传输命令(ACCESSORY_GET_PROTOCOL),
    看看该Android设备是否支持USB Accessory功能,如果支持的话会返回所支持的AOA协议版本;

  3. (D从 -> H主 上报设备信息)
    USB Accessory判断到该Android设备支持Accessory功能后,
    发起request值为52(0x34)的控制传输命令(ACCESSORY_SEND_STRING),
    并把该Accessory设备的相关信息(包括厂家,序列号等)告知Android设备;

  4. (D从 -> H主 请求进入Audio模式)
    如果是USB Audio Accessory设备,还会发起request值为58(0x3A)的控制传输命令(SET_AUDIO_MODE命令),
    通知Android设备进入到Audio Accessory模式;

  5. (D从 -> H主 请求AOA开始工作)
    最终,USB Accessory设备发起request值为53(0x35)的控制传输命令(ACCESSORY_START),
    通知Android设备切换到Accessory功能模式开始工作。
    接下来Android设备收到此信息后,会先把sys.usb.config设置为包含accessory功能

  6. (H主 根据init.usb.rc的配置USB)
    剩下的事情就是如前小节所述,按init.usb.rc的设置进行了,
    此时Android设备先断开与Accessory设备连接,/sys/class/android_usb/android0/functions节点写入accessory字符串,
    然后重新连接,使能Accessory接口,正式工作在USB Accessory模式;

下图是USB协议分析仪抓取的协议数据:

AOA 2.0协议中还有部分和HID设备有关的控制传输命令,具体可参见前面的Google官方链接,这里不再详叙。

2.1.5 f_accessory.c和f_audio_source.c驱动文件

f_accessory.c和f_audio_source.c文件为实现USB Accessory和USB Audio Accessory功能所对应的驱动代码。
我们先看f_accessory.c文件的处理内容。

f_accessory.c

f_accessory.c文件内所实现的功能主要有如下:

  1. 负责accessory_function结构体中接口函数的具体实现
    这些接口函数见如下代码:
// /kernel/msm-3.18/drivers/usb/gadget/android.c
static int accessory_function_init(struct android_usb_function *f,struct usb_composite_dev *cdev)
{return acc_setup();
}static void accessory_function_cleanup(struct android_usb_function *f)
{acc_cleanup();
}static int accessory_function_bind_config(struct android_usb_function *f,struct usb_configuration *c)
{return acc_bind_config(c);
}static int accessory_function_ctrlrequest(struct android_usb_function *f,struct usb_composite_dev *cdev,const struct usb_ctrlrequest *c)
{return acc_ctrlrequest(cdev, c);
}static struct android_usb_function accessory_function = {.name        = "accessory",.init      = accessory_function_init,.cleanup = accessory_function_cleanup,.bind_config  = accessory_function_bind_config,.ctrlrequest  = accessory_function_ctrlrequest,
};
// /kernel/msm-3.18/drivers/usb/gadget/function/f_accessory.c
static struct usb_function *acc_alloc(struct usb_function_instance *fi)
{struct acc_dev *dev = _acc_dev;pr_info("acc_alloc\n");dev->function.name = "accessory";dev->function.strings = acc_strings,dev->function.fs_descriptors = fs_acc_descs;dev->function.hs_descriptors = hs_acc_descs;dev->function.bind = acc_function_bind_configfs;dev->function.unbind = acc_function_unbind;dev->function.set_alt = acc_function_set_alt;dev->function.disable = acc_function_disable;dev->function.free_func = acc_free;dev->function.setup = acc_ctrlrequest_configfs;return &dev->function;
}
DECLARE_USB_FUNCTION_INIT(accessory, acc_alloc_inst, acc_alloc);
MODULE_LICENSE("GPL");
  1. 实现Android Open Accessory (AOA) protocol
    AOA协议的相关处理代码主要在控制传输处理代码中,具体可见acc_ctrlrequest函数,
int acc_ctrlrequest(struct usb_composite_dev *cdev,const struct usb_ctrlrequest *ctrl)
{struct acc_dev *dev = _acc_dev;int    value = -EOPNOTSUPP;struct acc_hid_dev *hid;int offset;u8 b_requestType = ctrl->bRequestType;u8 b_request = ctrl->bRequest;u16 w_index = le16_to_cpu(ctrl->wIndex);u16 w_value = le16_to_cpu(ctrl->wValue);u16 w_length = le16_to_cpu(ctrl->wLength);unsigned long flags;/*printk(KERN_INFO "acc_ctrlrequest ""%02x.%02x v%04x i%04x l%u\n",b_requestType, b_request,w_value, w_index, w_length);
*/if (b_requestType == (USB_DIR_OUT | USB_TYPE_VENDOR)) {if (b_request == ACCESSORY_START) {dev->start_requested = 1;schedule_delayed_work( &dev->start_work, msecs_to_jiffies(10));value = 0;} else if (b_request == ACCESSORY_SEND_STRING) {dev->string_index = w_index;cdev->gadget->ep0->driver_data = dev;cdev->req->complete = acc_complete_set_string;value = w_length;} else if (b_request == ACCESSORY_SET_AUDIO_MODE && w_index == 0 && w_length == 0) {dev->audio_mode = w_value;value = 0;} else if (b_request == ACCESSORY_REGISTER_HID) {value = acc_register_hid(dev, w_value, w_index);} else if (b_request == ACCESSORY_UNREGISTER_HID) {value = acc_unregister_hid(dev, w_value);} else if (b_request == ACCESSORY_SET_HID_REPORT_DESC) {spin_lock_irqsave(&dev->lock, flags);hid = acc_hid_get(&dev->new_hid_list, w_value);spin_unlock_irqrestore(&dev->lock, flags);offset = w_index;if (offset != hid->report_desc_offset || offset + w_length > hid->report_desc_len) {value = -EINVAL;goto err;}cdev->req->context = hid;cdev->req->complete = acc_complete_set_hid_report_desc;value = w_length;} else if (b_request == ACCESSORY_SEND_HID_EVENT) {spin_lock_irqsave(&dev->lock, flags);hid = acc_hid_get(&dev->hid_list, w_value);spin_unlock_irqrestore(&dev->lock, flags);cdev->req->context = hid;cdev->req->complete = acc_complete_send_hid_event;value = w_length;}} else if (b_requestType == (USB_DIR_IN | USB_TYPE_VENDOR)) {if (b_request == ACCESSORY_GET_PROTOCOL) {*((u16 *)cdev->req->buf) = PROTOCOL_VERSION;value = sizeof(u16);/* clear any string left over from a previous session */memset(dev->manufacturer, 0, sizeof(dev->manufacturer));memset(dev->model, 0, sizeof(dev->model));memset(dev->description, 0, sizeof(dev->description));memset(dev->version, 0, sizeof(dev->version));memset(dev->uri, 0, sizeof(dev->uri));memset(dev->serial, 0, sizeof(dev->serial));dev->start_requested = 0;dev->audio_mode = 0;strlcpy(dev->manufacturer, "Android", ACC_STRING_SIZE);strlcpy(dev->model, "Android", ACC_STRING_SIZE);}}if (value >= 0) {cdev->req->zero = 0;cdev->req->length = value;value = usb_ep_queue(cdev->gadget->ep0, cdev->req, GFP_ATOMIC);if (value < 0)ERROR(cdev, "%s setup response queue error\n",__func__);}err:if (value == -EOPNOTSUPP)VDBG(cdev,"unknown class-specific control req ""%02x.%02x v%04x i%04x l%u\n",ctrl->bRequestType, ctrl->bRequest, w_value, w_index, w_length);return value;
}
EXPORT_SYMBOL_GPL(acc_ctrlrequest);

以“ACCESSORY_START”命令为例,
收到该命令后驱动会调用schedule_delayed_work(&dev->start_work,msecs_to_jiffies(10))来延时10毫秒后发出"ACCESSORY=START"的UEVENT消息,dev->start_work对应的处理函数如下:

static void acc_start_work(struct work_struct *data)
{char *envp[2] = { "ACCESSORY=START", NULL };kobject_uevent_env(&acc_device.this_device->kobj, KOBJ_CHANGE, envp);
}
  1. 注册实现字符设备“/dev/usb_accessory”相关的驱动接口,并调用misc_register(&acc_device)注册该字符设备驱动,
    对应的file_operations结构体定义如下:
/* file operations for /dev/usb_accessory */
static const struct file_operations acc_fops = {.owner = THIS_MODULE,.read = acc_read,.write = acc_write,.unlocked_ioctl = acc_ioctl,
#ifdef CONFIG_COMPAT.compat_ioctl = acc_ioctl,
#endif.open = acc_open,.release = acc_release,
};static struct hid_driver acc_hid_driver = {.name = "USB accessory",.id_table = acc_hid_table,.probe = acc_hid_probe,
};

“/dev/usb_accessory”向应用层提供了read、write和ioctl接口,通过这些接口,应用层可以获取Accessory设备信息,实现Android设备与Accessory设备的数据交互工作。

  1. 定义hid_driver,并调用hid_register_driver(&acc_hid_driver)向内核注册
static const struct hid_device_id acc_hid_table[] = {{ HID_USB_DEVICE(HID_ANY_ID, HID_ANY_ID) },{ }
};static struct hid_driver acc_hid_driver = {.name = "USB accessory",.id_table = acc_hid_table,.probe = acc_hid_probe,
};

并在收到控制传输命令ACCESSORY_REGISTER_HID和ACCESSORY_UNREGISTER_HID时,
调用acc_register_hid和acc_unregister_hid进行相应处理。

以上是f_accessory.c文件主要的处理内容。

f_accessory.c

f_audio_source.c文件则针对Audio Accessory设备来进行处理,实现了音频相关接口函数,
并调用snd_card_register(card)函数向内核注册声卡设备。

其中snd_pcm_ops结构体定义如下:

static int snd_card_setup(struct usb_configuration *c,struct audio_source_config *config)
{err = snd_card_new(&c->cdev->gadget->dev,SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,THIS_MODULE, 0, &card);err = snd_pcm_new(card, "USB audio source", 0, 1, 0, &pcm);strlcpy(pcm->name, "USB gadget audio", sizeof(pcm->name));snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &audio_playback_ops);
}static struct snd_pcm_ops audio_playback_ops = {.open     = audio_pcm_open,.close        = audio_pcm_close,.ioctl       = snd_pcm_lib_ioctl,.hw_params = audio_pcm_hw_params,.hw_free = audio_pcm_hw_free,.prepare   = audio_pcm_prepare,.trigger   = audio_pcm_playback_trigger,.pointer  = audio_pcm_pointer,.mmap      = audio_pcm_mmap,
};

f_audio_source.c文件主要涉及音频处理相关代码,这里不再深入研究。

接下来,我们再看在连接到USB Accessory设备时Android上层的整个工作流程。

本文参考:
Android USB Accessory分析
Android USB通讯(完整版)

Android下USB Accessory的实现分析 (三)--- Android Open AccessoryProtocol相关推荐

  1. Android下USB Accessory的实现分析 (一)--- AOA背景介绍

    摘要 本文介绍了USB Accessory的一些背景知识,并从Linux驱动到Android Framework层,阐述了USB accessory的整个实现过程. 关键词: Android,USB, ...

  2. Android Q USB Tethering 端口切换分析

    需求:在开启 USB Tethering 后,同时需要开启 diag 端口供 QXDM 调试使用.于是 trace code 大概分析了一下设置 USB Tethering 过程,比较毛糙,如有不正之 ...

  3. Android SystemUI 状态栏网络图标显示分析(Android 11)

    最近看了一个wifi, ethernet切换,状态栏图表显示的问题.记录一下追踪由于网络状态变化,SystemUI 状态栏网络图标显示的流程. 先看一下SystemUI这边: /frameworks/ ...

  4. android下usb框架系列文章---(2)Usb mass_storage turn on的过程

    下面从framework层的ui来看一下插入usb后share的过程,这个动作的触发是从status bar下面弹出的usb connect开始的. frameworks/base/packages/ ...

  5. Android Applicaion组件创建的源代码分析(Android 9,含序列图)

    Application组件源代码分析 1. Applicaion启动流程源代码分析 2. 启动过程中应用进程.系统服务进程通信的分界点 3. 组件生命周期与系统服务的关系 4. Application ...

  6. android emulator虚拟设备之qemu pipe分析(三)

    一.概述 qemu pipe也是一个虚拟设备,是一个通用的虚拟设备,用于提供guest os和emulator通信的功能,类似于一个抽象的通信层,这样就不用写很多虚拟设备了. 之前在guest os中 ...

  7. Android用usb命令控制音量,如何在Android平台上使用USB Audio设备

    上网搜了有关USB Audio Hotplug的东西,比较适用的资源如下: 题目看起来很吻合我们的问题,事实上并没有多少参考价值.其中脚本/etc/hotplug/usb/extigy或许可以捕捉到U ...

  8. android下音频采集功能,音频采集:Android基于AudioRecord的实现

    前言 这篇文章简单详情下手机端Android系统下利使用AudioRecord进行音频采集方法. 开始前先提供一份源码 AudioRecordLib . AudioRecord采集的核心实现在于 Au ...

  9. android 下拉刷新监听,说说在 Android 的 RecyclerView 中如何实现下拉刷新

    1 SwipeRefreshLayout 修改布局文件,新增 SwipeRefreshLayout :<?xml  version="1.0" encoding=" ...

最新文章

  1. ELK 性能优化实践
  2. dd指令打包iso文件 linux_从零开始学Linux运维|33.文件的打包压缩和解压
  3. 卷积、相关(matlab)
  4. Java项目构建工具Gradle是否可以完全替代Maven?
  5. Python之seaborn:利用seaborn的color_palette()函数改变绘图界面风格
  6. C++ STL快速入门
  7. leetcode 306. Additive Number | 306. 累加数(Java)
  8. APC UPS报错一例
  9. Codeforces Round 504
  10. oracle vm中的xp添加共享文件夹
  11. linux进程互斥要点,linux进程之间互斥
  12. 安装oracle 11g时,报启动服务出现错误,找不到OracleMTSRecoveryService的解决方法
  13. matlab bs2rv.m,matlab遗传算法工具箱中的例子不能用?
  14. new运算符做了什么
  15. Java Mail 相关资料
  16. 《算法分析与设计(第5版)》——王晓东 - 学习记录 / 期末复习
  17. JavaScript中的事件
  18. [病毒分析]远程木马创建傀儡进程分析
  19. 列举1000以内的水仙花数,并求出水仙花数的个数
  20. Flowable 快速入门教程:通过 Comment 保存审核信息

热门文章

  1. 在VScode运行代码出现错误:【% matplotlib inline^ SyntaxError: invalid syntax】
  2. 操作系统(十五)——mmap
  3. 基于麻雀算法改进的DELM预测-附代码
  4. Origin——绘制两幅图一起显示,并切换排版方式
  5. 360 php SQL注入,CMSeasy SQL注入漏洞一发(bypass自身与360waf)
  6. wget下载提示:无法建立SSL连接
  7. Comparable和Cloneable
  8. 电脑爱好者十五周年庆典活动之“15创意秀”
  9. 介绍一windows桌面日历+提醒软件
  10. C语言编写web server