Android下USB Accessory的实现分析 (三)--- Android Open AccessoryProtocol

本文 接着前面的文章 《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.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)