Android下USB Accessory的實現分析

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總線供電。


圖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/


圖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層的支持,如下是設計架構圖:


圖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)便對應一種類型的功能設備,需要實現與之對應的功能驅動。

 

圖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"

...

代碼組織架構如下:


圖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驅動內部涉及到的主要數據結構和相互之間關係見如下圖示說明:


圖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協議分析儀抓取的協議數據:


圖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設備之後大致的處理流程:


圖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的工作原理和流程。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章