Android下USB Accessory的实现分析

 

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

关键词: Android,USB,Accessory, ADK

1.  背景介绍

自Android 3.1之后的版本,Google引入了USB Accessories的概念,并提供了相关的开发库。下面是developer.android.com上的一段相关说明:

Android 3.1 Platform Highlights

http://developer.android.com/sdk/android-3.1-highlights.html

●     Connectivity for USBaccessories

Android 3.1 addsbroad platform support for a variety of USB-connected peripherals andaccessories. Users can attach many types of input devices (keyboards, mice,game controllers) and digital cameras. Applications can build on the platform’sUSB support to extend connectivity to almost any type of USB device.

The platformalso adds new support for USB accessories — external hardware devices designedto attach to Android-powered devices as USB hosts. When an accessory isattached, the framework will look for a corresponding application and offer tolaunch it for the user. The accessory can also present a URL to the user, fordownloading an appropriate application if one is not already installed. Userscan interact with the application to control powered accessories such asrobotics controllers; docking stations; diagnostic and musical equipment;kiosks; card readers; and much more.

The platform’sUSB capabilities rely on components in device hardware, so support for USB onspecific devices may vary and is determined by device manufacturers.

 

上面一段文字说得比较清楚:Android3.1之后的版本不仅可以让Android设备作为USB Host的角色支持USB鼠标、键盘、游戏手柄等,还可以以USB Device的角色与一些具有USB Host功能,但却扮演着配件角色的设备相连,Google把这种设备称为“Accessory”(附件)。这类Accessory可能是如下设备:

机器人控制器、Dock(基座)、诊断设备、音响设备、配电设备、读卡器等等。

Google引入USB Accessory概念的原因应该主要有如下:

一、非常多的Android设备不具有USB Host的功能而只具有USB Device功能(例如绝大部分Android手机),或者即使具备USB Host的功能,也承担不起对USB外设供电的任务,因为便携式Android设备本身的电池容量就很有限。

二、原来的Android设备,作为USB Device所实现的功能相对比较简单,内置的功能只有U盘或ADB调试设备等,Google希望提供应用层的USB开发库,让更多的软硬件厂商来开发新的功能,比如说安装一个APK应用,然后通过USB连接到一个与电视机配套的Dock上,就可以让一台Android手机变身为一个电视机遥控器。

图1-1显示了USB Host和Accessory两种不同模式的区别。当Android设备作为Host模式时为USB总线供电;当Android设备连接到一个USB Accessory设备时,USB Accessory设备以Host身份为USB总线供电。

android的alpha图标 android aoa_android的alpha图标

图1-1 USB Host和Accessory两种模式

 

如果要在Android 3.1之前的Android 2.3.4版本上支持USB Accessory功能,可以通过添加“com.android.future.usb”add-on library的方式来支持,详情可见:http://developer.android.com/guide/topics/connectivity/usb/accessory.html

另外Android 4.1后的版本还增加了对USB AudioAccessory设备的支持,可实现USB接口的音箱功能,并在Google I/O 2012开发者年会上作了产品展示,见如下链接:

http://www.engadget.com/2012/06/30/gear4-speaker-dock-supports-usb-audio-for-jelly-bean-at-google-i/

android的alpha图标 android aoa_linux驱动_02

图1-2 USB Audio Accessory设备在Google I/O 2012上的展示

对于硬件开发者如何开发出USB Accessory设备,Google官方也给出了指导,并提供了ADK(The Accessory Development Kit)开发环境,详情见:

http://source.android.com/accessories/

http://developer.android.com/tools/adk/index.html

在Google官方文档的指导下,硬件设计爱好者可以很方便地在Arduino(一个开源硬件平台)等硬件平台上开发出自己感兴趣的Usb Accessory设备。

本文在此不准备深入探讨如何在其他硬件平台上开发Usb Accessory设备,而是旨在分析Android平台下对USB Accessory设备的支持具体是如何实现的。这里选择了比较主流的Android 4.1平台进行分析(事实上Android 4.1之后这部分代码变化不大)。

2.Android下Usb Accessory的设计实现

Android下对Usb Accessory设备的支持包括内核驱动层的支持和Android Framework层的支持,如下是设计架构图:

android的alpha图标 android aoa_android的alpha图标_03

图2-1 USB Accessory设计架构图

下面就分别从Linux内核驱动,Android Framework层,以及相关的应用层代码来进行分析。

2.1 USB Accessory底层驱动的设计实现

2.1.1 什么是USB composite设备

因为目前的Android平台设备在与PC进行连接时,大都表现为USB Composite设备,因此这里有必要对USB Composite设备进行一下介绍。

什么是USB Composite设备呢?对于大部分USB Device设备来说,它仅仅只有一个功能,比如大部分U盘,单个的USB鼠标等;但是也有些USB设备不止实现一个功能,比如某些USB上网卡有无线上网的功能,同时还有U盘存储的功能;又比如有的鼠标和键盘二合一设备,它只有一个USB接口,却同时支持了鼠标和键盘两个功能。

这种一个USB接口扩展出多个设备功能的实现方法有两种,一种是在设备外部或内部加Hub扩展;另一种就是以Usb Composite Device方式实现(一般称为复合设备)。复合设备其实只是一个USB设备,只有一个USB设备地址,它实现多个功能的原因主要在于它扩展实现了多个USB接口,每个接口具有不同的设备类型。这里涉及到USB协议的一些知识,有兴趣的读者可以去找资料了解一下,这里我们重点是要知道Android下采用了USB Composite Device这种方式来实现一个USB口的情况下扩展出多个功能设备,这种情况下一个USB接口(Interface)便对应一种类型的功能设备,需要实现与之对应的功能驱动。

 

android的alpha图标 android aoa_硬件_04

图2-2 USB Composite Device描述符结构

 

2.1.2 Android下USB Composite设备驱动实现

Android下的USBComposite驱动实现代码在Linux内核的drivers/usb/gadget/目录下,编译出来的驱动模块为g_android.ko,主要相关的代码有android.c和f_xxx.c一类的接口驱动文件,包括实现USB Accessory驱动的f_accessory.c,f_audio_source.c文件等。f_xxx.c以从属于android.c的USB Composite设备接口驱动的形式被include在android.c文件中,见android.c代码:

...
#include "f_audio_source.c"
#include "f_mass_storage.c"
#include "u_serial.c"
#include "f_acm.c"
#include "f_adb.c"
#include "f_mtp.c"
#include "f_accessory.c"
#include "f_rndis.c"
#include "rndis.c"
#include "u_ether.c"
...

代码组织架构如下:

android的alpha图标 android aoa_usb_05

图2-3 Android下USB Composite设备驱动代码架构

 

同时,在android.c中,定义了一个非常重要的数据结构的指针数组,见如下:

static struct android_usb_function *supported_functions[] = {
    &adb_function,
    &acm_function,
    &mtp_function,
    &ptp_function,
    &rndis_function,
    &mass_storage_function,
    &accessory_function,
    &audio_source_function,
    NULL
};

从上我们可以看到Android设备上每一个所支持的USB Device功能都可以在其中看到定义,包括如下功能:

●          adb(USB调试功能);

●          acm(USB串口功能);

●          mtp(mtp多媒体设备功能);

●          ptp(数码相机一类的多媒体存储设备功能);

●          rndis(USB Net功能);

●          mass_storage(这个就是我们最常见的U盘功能);

●          accessory(USB Accessory功能);

●          audio_source(USB Audio Accessory功能);

    structandroid_usb_function是为实现USB Composite设备的每个Interface功能所定义的数据结构,其中包括init功能初始化接口,enable和bin_config接口等,这些接口的实现在对应的f_xxx.c文件中。structandroid_usb_function结构定义如下:

struct android_usb_function {
char *name;
void *config;
 
struct device *dev;
char *dev_name;
struct device_attribute **attributes;
 
/* for android_dev.enabled_functions */
struct list_head enabled_list;
 
/* Optional: initialization during gadget bind */
int (*init)(struct android_usb_function *, struct usb_composite_dev *);
/* Optional: cleanup during gadget unbind */
void (*cleanup)(struct android_usb_function *);
/* Optional: called when the function is added the list of
     *      enabled functions */
void (*enable)(struct android_usb_function *);
/* Optional: called when it is removed */
void (*disable)(struct android_usb_function *);
 
int (*bind_config)(struct android_usb_function *,
struct usb_configuration *);
 
/* Optional: called when the configuration is removed */
void (*unbind_config)(struct android_usb_function *,
struct usb_configuration *);
/* Optional: handle ctrl requests before the device is configured */
int (*ctrlrequest)(struct android_usb_function *,
struct usb_composite_dev *,
const  struct usb_ctrlrequest *);
};

 

    g_android.ko驱动内部涉及到的主要数据结构和相互之间关系见如下图示说明:

android的alpha图标 android aoa_android的alpha图标_06

图2-4 g_android.ko主要数据结构和引用关系

    在g_android.ko驱动初始化的时候,会调用usb_composite_probe,进而调用usb_gadget_probe_driver来向udc-core注册一个usb_gadget_driver(即composite_driver),并与当前的udc(USB Device Controller)关联起来。

    作为一个USB Gadget功能驱动,g_android.ko工作的主要流程和普通的USB Gadget功能驱动类似,主要区别的地方在于它内部定义了很多android_usb_function结构体,并维护了一个usb_composite_dev数据结构,在需要的时候可以从supported_functions中选择需要的function链接到android_dev的enabled_functions链表上,并添加信息到usb_composite_dev的usb configuration中。

2.1.3 如何使能某一Interface功能

    前面讲到g_android驱动包含了对USB Accessory功能在内的众多USB功能Interface的支持,那么具体是如何提供接口供应用层来选择使能的呢?其实在g_android驱动初始化的时候,已经通过调用device_create(android_class, dev->dev,MKDEV(0, index), f,f->dev_name),创建了"/sys/class/android_usb/android0"目录和下面的众多文件节点,包括functions和enable文件节点,对应的功能Interface正是通过往此文件节点写入信息来实现的。下面摘取Android系统下init.usb.rc中相关代码片段来举例说明:

......
# adb only USB configuration
# This should only be used during device bringup
# and as a fallback if the USB manager fails to set a standard configuration
on property:sys.usb.config=adb
    write /sys/class/android_usb/android0/enable 0
    write /sys/class/android_usb/android0/idVendor 18d1
    write /sys/class/android_usb/android0/idProduct D002
    write /sys/class/android_usb/android0/functions ${sys.usb.config}
    write /sys/class/android_usb/android0/enable 1
    start adbd
    setprop sys.usb.state ${sys.usb.config}
 
# USB accessory configuration
on property:sys.usb.config=accessory
    write /sys/class/android_usb/android0/enable 0
    write /sys/class/android_usb/android0/idVendor 18d1
    write /sys/class/android_usb/android0/idProduct 2d00
    write /sys/class/android_usb/android0/functions ${sys.usb.config}
    write /sys/class/android_usb/android0/enable 1
    setprop sys.usb.state ${sys.usb.config}
 
# USB accessory configuration, with adb
on property:sys.usb.config=accessory,adb
    write /sys/class/android_usb/android0/enable 0
    write /sys/class/android_usb/android0/idVendor 18d1
    write /sys/class/android_usb/android0/idProduct 2d01
    write /sys/class/android_usb/android0/functions ${sys.usb.config}
    write /sys/class/android_usb/android0/enable 1
    start adbd
    setprop sys.usb.state ${sys.usb.config}
......

    从上面我们可以看到,当“sys.usb.config”属性值发生变化时候,属性服务进程会针对不同的设置来向“/sys/class/android_usb/android0/”目录下的文件节点写入对应的配置值,比如当“sys.usb.config”设置为accessory时,会执行如下动作:

write /sys/class/android_usb/android0/enable 0
    write /sys/class/android_usb/android0/idVendor 18d1
    write /sys/class/android_usb/android0/idProduct 2d00
    write /sys/class/android_usb/android0/functions ${sys.usb.config}
    write /sys/class/android_usb/android0/enable 1
setprop sys.usb.state ${sys.usb.config}

    其中idVendor和idProduct节点记录了设备的VID和PID号,functions节点记录了当前设置使能的功能(比如accessory),enable节点写入1的时候,驱动内部会调用android_enable函数,真正使能并让USB Host端识别到对应的USB功能设备。

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指定的,VID固定为Google的官方VID -- 0x18D1,PID则在不同的模式下定义如下:

●          0x2D00 - accessory

●          0x2D01 - accessory + adb

●          0x2D02 - audio

●          0x2D03 - audio + adb

●          0x2D04 - accessory + audio

●          0x2D05 - accessory + audio + adb

 

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

一、USB Accessory设备发起USB控制传输进行正常的USB设备枚举,获取设备描述符和配置描述符等信息,此时大部分Android设备上报的还只是普通的U盘或MTP设备;

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

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

四、如果是USB Audio Accessory设备,还会发起request值为58(0x3A)的控制传输命令(SET_AUDIO_MODE命令),通知Android设备进入到Audio Accessory模式;

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

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

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

android的alpha图标 android aoa_android的alpha图标_07

图2-5 AOA协议通讯过程

    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文件内所实现的功能主要有如下:

1、负责accessory_function结构体中接口函数的具体实现,这些接口函数见如下代码:

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,
};

 

2、实现Android Open Accessory (AOA) protocol。AOA协议的相关处理代码主要在控制传输处理代码中,具体可见acc_ctrlrequest函数:

static  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;
 
    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;
            DAMN_WARN_ACC_0906("audio_mode=%d\n",dev->audio_mode);
            value = 0;
        }  else if (b_request == ACCESSORY_REGISTER_HID) {
        ......

以“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);
}

 

3、注册实现字符设备“/dev/usb_accessory”相关的驱动接口,并调用misc_register(&acc_device)注册该字符设备驱动,对应的file_operations结构体定义如下:

/* file operations for /dev/acc_usb */
static  const struct file_operations acc_fops = {
    .owner = THIS_MODULE,
    .read = acc_read,
    .write = acc_write,
    .unlocked_ioctl = acc_ioctl,
    .open = acc_open,
    .release = acc_release,
};

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

 

4、定义hid_driver,并调用hid_register_driver(&acc_hid_driver)向内核注册:

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_audio_source.c文件则针对Audio Accessory设备来进行处理,实现了音频相关接口函数,并调用snd_card_register(card)函数向内核注册声卡设备。其中snd_pcm_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,
};

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

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

 

2.2 Android上层对USBAccessory设备的处理流程

下图是g_android.ko驱动检测到USB Accessory设备之后大致的处理流程:

android的alpha图标 android aoa_linux驱动_08

图2-6Android下USB Accessory设备连接后处理流程

首先g_android.ko驱动发现USB Accessory设备发送了“ACCESSORY_START”控制传输命令后,会发出一个UEVENT消息("ACCESSORY=START")。

    UsbDeviceMannager内部实现了一个UEventObserver类成员(mUEventObserver),会监测usb_accessory相关的UEVENT信息,当收到到"ACCESSORY=START"信息时,开始重新设置sys.usb.config属性,并由后台的property service进程执行由init.usb.rc文件所指定的设置动作,往“/sys/class/android_usb/android0/”路径下写入对应的配置信息。

    配置完成后,“/sys/class/android_usb/android0/enable”文件会被写入“1”,Android设备切换打开USB Accessory功能接口,重新执行枚举动作。枚举完成后,再发出UEVENT信息“USB_STATE=CONFIGURED”。

    接下来该UEVENT信息再次被mUEventObserver所监测到,并经过几次调用,最终由UsbSettingsManager发出定义为USB_ACCESSORY_ATTACHED的Intent。如果安装有相应的应用程序,则该Intent则会激活对应的Activity开始执行。

    Framework中相关的代码路径如下:

Ø        frameworks\base\services\java\com\android\server\usb

Ø        frameworks\base\packages\SystemUI\src\com\android\systemui\usb

Ø        frameworks\base\services\jni\ com_android_server_UsbDeviceManager.cpp

 

Android对外提供的Accessory设备编程接口见如下package:

"com.android.future.usb"

“android.hardware.usb”

对应的代码路径如下:

Ø        frameworks\base\libs\usb\src\com\android\future\usb

Ø        frameworks\base\core\java\android\hardware\usb

 

至于如何编写应用与Accessory设备交互,可参见:

http://developer.android.com/guide/topics/connectivity/usb/accessory.html

Google官方也提供了示例代码,在源代码包的如下路径:

Ø        device\google\accessory\demokit\app\src\com\google\android\DemoKit

3. 小结

Android系统对USBAccessory设备的支持,为扩展Android平台的外围设备功能,吸引更多的硬件厂商和开发者开发出更多有趣和有创新意义的应用产品提供了机会,也会让整个Android阵营变得更加丰富多彩。本文从USB功能驱动到Android Framework层对USB Accessory的整个工作流程作了大致的说明,希望有助于有兴趣的开发者更好地理解USB Accessory的工作原理和流程。