本文 接着前面的文章 《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协议分析仪抓取的协议数据:
Android下USB Accessory的实现分析 (三)--- Android Open AccessoryProtocol_第1张图片
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.cstatic 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.cstatic 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;intvalue = -EOPNOTSUPP;struct acc_hid_dev *hid;int offset;u8 b_requestType = ctrl->bRequestType;u8 b_request = ctrl->bRequest;u16w_index = le16_to_cpu(ctrl->wIndex);u16w_value = le16_to_cpu(ctrl->wValue);u16w_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通讯(完整版)

更多相关文章

  1. android的m、mm、mmm编译命令的使用
  2. Android 命令行手动编译打包详解
  3. Android Service判断设备联网状态详解
  4. Android:adb shell am命令行发送Activity/Service/Broadcast
  5. Android 中多点触摸协议
  6. 使用命令行编译Qt Android apps
  7. Android通过TCPIP协议实现断点续传上传实现

随机推荐

  1. Android四大组件之Activity(一)
  2. Tween动画介绍
  3. android中设置控件边框以及如何保留上或
  4. Android(安卓)APP自动更新
  5. android Textview加下划线
  6. Gradle for Android(安卓)第四篇( 构建变
  7. Android使用WebView加载网页及数据__2020
  8. 2017年Android开源项目及库汇总
  9. Android(安卓)显示系统分析
  10. java 服务平台鸿鹄社交娱乐直播平台源码i