summaryrefslogtreecommitdiffstats
path: root/libs/usb
diff options
context:
space:
mode:
authorMike Lockwood <lockwood@google.com>2014-08-04 14:48:45 -0700
committerMike Lockwood <lockwood@google.com>2014-08-04 15:29:18 -0700
commit6b524d9b4552f20e4e689178bdb954a94daf7ab8 (patch)
treeb44736299a8a9fc1c051f70e398b2515c4c88367 /libs/usb
parentf6b0c2f31b5ff08358f23cd30a743a741f345a1b (diff)
downloadframeworks_base-6b524d9b4552f20e4e689178bdb954a94daf7ab8.zip
frameworks_base-6b524d9b4552f20e4e689178bdb954a94daf7ab8.tar.gz
frameworks_base-6b524d9b4552f20e4e689178bdb954a94daf7ab8.tar.bz2
Add accessorytest Linux host tool for testing USB accessory audio and HID support.
The tool supports testing USB audio by default. It reads audio from the ALSA device for the android phone in accessory mode and outputs it to the ALSA device for the PC's speaker. The default values work on my PC, but can be changed via command line options. When the -a option is specified, accessorytest also acts as the host side of the AccessoryChat test, so audio can be tested side by side with the accessory bulk endpoint support. When the -h option is specified, accessorytest will look for HID devices on the PC and proxy them to the phone over the accessory protocol. This requires running accessorytest as root. Change-Id: I1ea06d7201cd845b95a92a42594464783a90189b
Diffstat (limited to 'libs/usb')
-rw-r--r--libs/usb/tests/accessorytest/Android.mk25
-rw-r--r--libs/usb/tests/accessorytest/accessory.c79
-rw-r--r--libs/usb/tests/accessorytest/accessory.h26
-rw-r--r--libs/usb/tests/accessorytest/audio.c221
-rw-r--r--libs/usb/tests/accessorytest/f_accessory.h148
-rw-r--r--libs/usb/tests/accessorytest/hid.c202
-rw-r--r--libs/usb/tests/accessorytest/usb.c227
7 files changed, 928 insertions, 0 deletions
diff --git a/libs/usb/tests/accessorytest/Android.mk b/libs/usb/tests/accessorytest/Android.mk
new file mode 100644
index 0000000..6d9a946
--- /dev/null
+++ b/libs/usb/tests/accessorytest/Android.mk
@@ -0,0 +1,25 @@
+LOCAL_PATH:= $(call my-dir)
+
+# Build for Linux host only
+ifeq ($(HOST_OS),linux)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := accessory.c \
+ audio.c \
+ hid.c \
+ usb.c
+
+LOCAL_C_INCLUDES += external/tinyalsa/include
+
+LOCAL_MODULE := accessorytest
+
+LOCAL_STATIC_LIBRARIES := libusbhost libcutils libtinyalsa
+LOCAL_LDLIBS += -lpthread
+LOCAL_CFLAGS := -g -O0
+
+include $(BUILD_HOST_EXECUTABLE)
+
+endif
diff --git a/libs/usb/tests/accessorytest/accessory.c b/libs/usb/tests/accessorytest/accessory.c
new file mode 100644
index 0000000..a3c47af
--- /dev/null
+++ b/libs/usb/tests/accessorytest/accessory.c
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+
+#include "accessory.h"
+
+static void usage(char* name) {
+ fprintf(stderr, "Usage: %s [-a] [-h] [-ic input card] [-id input device] "
+ "[-oc output card] [-d output device] [-a] [-h] [-i]\n\n"
+ "\t-ic, -id, -oc and -od specify ALSA card and device numbers\n"
+ "\t-a : enables AccessoryChat mode\n"
+ "\t-i : enables HID pass through (requires running as root\n"
+ "\t-h : prints this usage message\n", name);
+}
+
+int main(int argc, char* argv[])
+{
+ unsigned int input_card = 2;
+ unsigned int input_device = 0;
+ unsigned int output_card = 0;
+ unsigned int output_device = 0;
+ unsigned int enable_accessory = 0;
+ unsigned int enable_hid = 0;
+
+ /* parse command line arguments */
+ argv += 1;
+ while (*argv) {
+ if (strcmp(*argv, "-ic") == 0) {
+ argv++;
+ if (*argv)
+ input_card = atoi(*argv);
+ } else if (strcmp(*argv, "-id") == 0) {
+ argv++;
+ if (*argv)
+ input_device = atoi(*argv);
+ } else if (strcmp(*argv, "-oc") == 0) {
+ argv++;
+ if (*argv)
+ output_card = atoi(*argv);
+ } else if (strcmp(*argv, "-od") == 0) {
+ argv++;
+ if (*argv)
+ output_device = atoi(*argv);
+ } else if (strcmp(*argv, "-a") == 0) {
+ enable_accessory = 1;
+ } else if (strcmp(*argv, "-h") == 0) {
+ usage(argv[0]);
+ return 1;
+ } else if (strcmp(*argv, "-i") == 0) {
+ enable_hid = 1;
+ }
+ if (*argv)
+ argv++;
+ }
+
+ init_audio(input_card, input_device, output_card, output_device);
+ if (enable_hid)
+ init_hid();
+ usb_run(enable_accessory);
+
+ return 0;
+}
diff --git a/libs/usb/tests/accessorytest/accessory.h b/libs/usb/tests/accessorytest/accessory.h
new file mode 100644
index 0000000..55c4550
--- /dev/null
+++ b/libs/usb/tests/accessorytest/accessory.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __ACCESSORY_H__
+#define __ACCESSORY_H__
+
+int init_audio(unsigned int ic, unsigned int id, unsigned int oc, unsigned int od);
+void init_hid();
+void usb_run(int enable_accessory);
+
+struct usb_device* usb_wait_for_device();
+
+#endif /* __ACCESSORY_H__ */
diff --git a/libs/usb/tests/accessorytest/audio.c b/libs/usb/tests/accessorytest/audio.c
new file mode 100644
index 0000000..d23d9b3
--- /dev/null
+++ b/libs/usb/tests/accessorytest/audio.c
@@ -0,0 +1,221 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <signal.h>
+#include <unistd.h>
+#include <pthread.h>
+#include <errno.h>
+#include <string.h>
+
+#include <tinyalsa/asoundlib.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+
+#include "accessory.h"
+
+#define BUFFER_COUNT 2
+#define BUFFER_SIZE 16384
+
+#define BUFFER_EMPTY 0
+#define BUFFER_BUSY 1
+#define BUFFER_FULL 2
+
+static char* buffers[BUFFER_COUNT];
+static int buffer_states[BUFFER_COUNT];
+static int empty_index = 0;
+static int full_index = -1;
+
+static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t empty_cond = PTHREAD_COND_INITIALIZER;
+static pthread_cond_t full_cond = PTHREAD_COND_INITIALIZER;
+
+static unsigned int input_card;
+static unsigned int input_device;
+
+static int get_empty()
+{
+ int index, other;
+
+ pthread_mutex_lock(&mutex);
+
+ while (empty_index == -1)
+ pthread_cond_wait(&empty_cond, &mutex);
+
+ index = empty_index;
+ other = (index == 0 ? 1 : 0);
+ buffer_states[index] = BUFFER_BUSY;
+ if (buffer_states[other] == BUFFER_EMPTY)
+ empty_index = other;
+ else
+ empty_index = -1;
+
+ pthread_mutex_unlock(&mutex);
+ return index;
+}
+
+static void put_empty(int index)
+{
+ pthread_mutex_lock(&mutex);
+
+ buffer_states[index] = BUFFER_EMPTY;
+ if (empty_index == -1) {
+ empty_index = index;
+ pthread_cond_signal(&empty_cond);
+ }
+
+ pthread_mutex_unlock(&mutex);
+}
+
+static int get_full()
+{
+ int index, other;
+
+ pthread_mutex_lock(&mutex);
+
+ while (full_index == -1)
+ pthread_cond_wait(&full_cond, &mutex);
+
+ index = full_index;
+ other = (index == 0 ? 1 : 0);
+ buffer_states[index] = BUFFER_BUSY;
+ if (buffer_states[other] == BUFFER_FULL)
+ full_index = other;
+ else
+ full_index = -1;
+
+ pthread_mutex_unlock(&mutex);
+ return index;
+}
+
+static void put_full(int index)
+{
+ pthread_mutex_lock(&mutex);
+
+ buffer_states[index] = BUFFER_FULL;
+ if (full_index == -1) {
+ full_index = index;
+ pthread_cond_signal(&full_cond);
+ }
+
+ pthread_mutex_unlock(&mutex);
+}
+
+static void* capture_thread(void* arg)
+{
+ struct pcm_config config;
+ struct pcm *pcm = NULL;
+
+ fprintf(stderr, "capture_thread start\n");
+
+ memset(&config, 0, sizeof(config));
+
+ config.channels = 2;
+ config.rate = 44100;
+ config.period_size = 1024;
+ config.period_count = 4;
+ config.format = PCM_FORMAT_S16_LE;
+
+ while (1) {
+ while (!pcm) {
+ pcm = pcm_open(input_card, input_device, PCM_IN, &config);
+ if (pcm && !pcm_is_ready(pcm)) {
+ pcm_close(pcm);
+ pcm = NULL;
+ }
+ if (!pcm)
+ sleep(1);
+ }
+
+ while (pcm) {
+ int index = get_empty();
+ if (pcm_read(pcm, buffers[index], BUFFER_SIZE)) {
+ put_empty(index);
+ pcm_close(pcm);
+ pcm = NULL;
+ } else {
+ put_full(index);
+ }
+ }
+ }
+
+ fprintf(stderr, "capture_thread done\n");
+ return NULL;
+}
+
+static void* play_thread(void* arg)
+{
+ struct pcm *pcm = arg;
+ char *buffer;
+ int index, err;
+
+ fprintf(stderr, "play_thread start\n");
+
+ while (1) {
+ index = get_full();
+
+ err = pcm_write(pcm, buffers[index], BUFFER_SIZE);
+ if (err)
+ fprintf(stderr, "pcm_write err: %d\n", err);
+
+ put_empty(index);
+ }
+
+ fprintf(stderr, "play_thread done\n");
+ pcm_close(pcm);
+ free(buffer);
+
+ return NULL;
+}
+
+int init_audio(unsigned int ic, unsigned int id, unsigned int oc, unsigned int od)
+{
+ pthread_t tid;
+ struct pcm_config config;
+ struct pcm *pcm;
+ int i;
+
+ input_card = ic;
+ input_device = id;
+
+ for (i = 0; i < BUFFER_COUNT; i++) {
+ buffers[i] = malloc(BUFFER_SIZE);
+ buffer_states[i] = BUFFER_EMPTY;
+ }
+
+ memset(&config, 0, sizeof(config));
+ config.channels = 2;
+ config.rate = 44100;
+ config.period_size = 1024;
+ config.period_count = 4;
+ config.format = PCM_FORMAT_S16_LE;
+
+ pcm = pcm_open(oc, od, PCM_OUT, &config);
+ if (!pcm || !pcm_is_ready(pcm)) {
+ fprintf(stderr, "Unable to open PCM device %d/%d for output (%s)\n",
+ oc, od, pcm_get_error(pcm));
+ return -1;
+ }
+
+ pthread_create(&tid, NULL, capture_thread, NULL);
+ pthread_create(&tid, NULL, play_thread, pcm);
+ return 0;
+}
diff --git a/libs/usb/tests/accessorytest/f_accessory.h b/libs/usb/tests/accessorytest/f_accessory.h
new file mode 100644
index 0000000..312f4ba
--- /dev/null
+++ b/libs/usb/tests/accessorytest/f_accessory.h
@@ -0,0 +1,148 @@
+/*
+ * Gadget Function Driver for Android USB accessories
+ *
+ * Copyright (C) 2011 Google, Inc.
+ * Author: Mike Lockwood <lockwood@android.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef __LINUX_USB_F_ACCESSORY_H
+#define __LINUX_USB_F_ACCESSORY_H
+
+/* Use Google Vendor ID when in accessory mode */
+#define USB_ACCESSORY_VENDOR_ID 0x18D1
+
+
+/* Product ID to use when in accessory mode */
+#define USB_ACCESSORY_PRODUCT_ID 0x2D00
+
+/* Product ID to use when in accessory mode and adb is enabled */
+#define USB_ACCESSORY_ADB_PRODUCT_ID 0x2D01
+
+/* Indexes for strings sent by the host via ACCESSORY_SEND_STRING */
+#define ACCESSORY_STRING_MANUFACTURER 0
+#define ACCESSORY_STRING_MODEL 1
+#define ACCESSORY_STRING_DESCRIPTION 2
+#define ACCESSORY_STRING_VERSION 3
+#define ACCESSORY_STRING_URI 4
+#define ACCESSORY_STRING_SERIAL 5
+
+/* Control request for retrieving device's protocol version
+ *
+ * requestType: USB_DIR_IN | USB_TYPE_VENDOR
+ * request: ACCESSORY_GET_PROTOCOL
+ * value: 0
+ * index: 0
+ * data version number (16 bits little endian)
+ * 1 for original accessory support
+ * 2 adds audio and HID support
+ */
+#define ACCESSORY_GET_PROTOCOL 51
+
+/* Control request for host to send a string to the device
+ *
+ * requestType: USB_DIR_OUT | USB_TYPE_VENDOR
+ * request: ACCESSORY_SEND_STRING
+ * value: 0
+ * index: string ID
+ * data zero terminated UTF8 string
+ *
+ * The device can later retrieve these strings via the
+ * ACCESSORY_GET_STRING_* ioctls
+ */
+#define ACCESSORY_SEND_STRING 52
+
+/* Control request for starting device in accessory mode.
+ * The host sends this after setting all its strings to the device.
+ *
+ * requestType: USB_DIR_OUT | USB_TYPE_VENDOR
+ * request: ACCESSORY_START
+ * value: 0
+ * index: 0
+ * data none
+ */
+#define ACCESSORY_START 53
+
+/* Control request for registering a HID device.
+ * Upon registering, a unique ID is sent by the accessory in the
+ * value parameter. This ID will be used for future commands for
+ * the device
+ *
+ * requestType: USB_DIR_OUT | USB_TYPE_VENDOR
+ * request: ACCESSORY_REGISTER_HID_DEVICE
+ * value: Accessory assigned ID for the HID device
+ * index: total length of the HID report descriptor
+ * data none
+ */
+#define ACCESSORY_REGISTER_HID 54
+
+/* Control request for unregistering a HID device.
+ *
+ * requestType: USB_DIR_OUT | USB_TYPE_VENDOR
+ * request: ACCESSORY_REGISTER_HID
+ * value: Accessory assigned ID for the HID device
+ * index: 0
+ * data none
+ */
+#define ACCESSORY_UNREGISTER_HID 55
+
+/* Control request for sending the HID report descriptor.
+ * If the HID descriptor is longer than the endpoint zero max packet size,
+ * the descriptor will be sent in multiple ACCESSORY_SET_HID_REPORT_DESC
+ * commands. The data for the descriptor must be sent sequentially
+ * if multiple packets are needed.
+ *
+ * requestType: USB_DIR_OUT | USB_TYPE_VENDOR
+ * request: ACCESSORY_SET_HID_REPORT_DESC
+ * value: Accessory assigned ID for the HID device
+ * index: offset of data in descriptor
+ * (needed when HID descriptor is too big for one packet)
+ * data the HID report descriptor
+ */
+#define ACCESSORY_SET_HID_REPORT_DESC 56
+
+/* Control request for sending HID events.
+ *
+ * requestType: USB_DIR_OUT | USB_TYPE_VENDOR
+ * request: ACCESSORY_SEND_HID_EVENT
+ * value: Accessory assigned ID for the HID device
+ * index: 0
+ * data the HID report for the event
+ */
+#define ACCESSORY_SEND_HID_EVENT 57
+
+/* Control request for setting the audio mode.
+ *
+ * requestType: USB_DIR_OUT | USB_TYPE_VENDOR
+ * request: ACCESSORY_SET_AUDIO_MODE
+ * value: 0 - no audio
+ * 1 - device to host, 44100 16-bit stereo PCM
+ * index: 0
+ * data the HID report for the event
+ */
+#define ACCESSORY_SET_AUDIO_MODE 58
+
+
+
+/* ioctls for retrieving strings set by the host */
+#define ACCESSORY_GET_STRING_MANUFACTURER _IOW('M', 1, char[256])
+#define ACCESSORY_GET_STRING_MODEL _IOW('M', 2, char[256])
+#define ACCESSORY_GET_STRING_DESCRIPTION _IOW('M', 3, char[256])
+#define ACCESSORY_GET_STRING_VERSION _IOW('M', 4, char[256])
+#define ACCESSORY_GET_STRING_URI _IOW('M', 5, char[256])
+#define ACCESSORY_GET_STRING_SERIAL _IOW('M', 6, char[256])
+/* returns 1 if there is a start request pending */
+#define ACCESSORY_IS_START_REQUESTED _IO('M', 7)
+/* returns audio mode (set via the ACCESSORY_SET_AUDIO_MODE control request) */
+#define ACCESSORY_GET_AUDIO_MODE _IO('M', 8)
+
+#endif /* __LINUX_USB_F_ACCESSORY_H */
diff --git a/libs/usb/tests/accessorytest/hid.c b/libs/usb/tests/accessorytest/hid.c
new file mode 100644
index 0000000..b70d678
--- /dev/null
+++ b/libs/usb/tests/accessorytest/hid.c
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <pthread.h>
+#include <time.h>
+
+#include <sys/inotify.h>
+#include <linux/hidraw.h>
+#include <usbhost/usbhost.h>
+
+#include "f_accessory.h"
+#include "accessory.h"
+
+static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
+
+static int next_id = 1;
+
+static void milli_sleep(int millis) {
+ struct timespec tm;
+
+ tm.tv_sec = 0;
+ tm.tv_nsec = millis * 1000000;
+ nanosleep(&tm, NULL);
+}
+
+static void* hid_thread(void* arg) {
+ int fd = (int)arg;
+ char buffer[4096];
+ int id, ret, offset;
+ struct usb_device *device;
+ struct usb_device_descriptor *device_desc;
+ int max_packet;
+ struct hidraw_report_descriptor desc;
+ int desc_length;
+
+ fprintf(stderr, "hid_thread start fd: %d\n", fd);
+
+ if (ioctl(fd, HIDIOCGRDESCSIZE, &desc_length)) {
+ fprintf(stderr, "HIDIOCGRDESCSIZE failed\n");
+ close(fd);
+ goto err;
+ }
+
+ desc.size = HID_MAX_DESCRIPTOR_SIZE - 1;
+ if (ioctl(fd, HIDIOCGRDESC, &desc)) {
+ fprintf(stderr, "HIDIOCGRDESC failed\n");
+ close(fd);
+ goto err;
+ }
+
+wait_for_device:
+ fprintf(stderr, "waiting for device fd: %d\n", fd);
+ device = usb_wait_for_device();
+ max_packet = usb_device_get_device_descriptor(device)->bMaxPacketSize0;
+ // FIXME
+ max_packet--;
+
+ // FIXME
+ milli_sleep(500);
+
+ pthread_mutex_lock(&mutex);
+ id = next_id++;
+
+ ret = usb_device_control_transfer(device, USB_DIR_OUT | USB_TYPE_VENDOR,
+ ACCESSORY_REGISTER_HID, id, desc_length, NULL, 0, 1000);
+ fprintf(stderr, "ACCESSORY_REGISTER_HID returned %d\n", ret);
+
+ // FIXME
+ milli_sleep(500);
+
+ for (offset = 0; offset < desc_length; ) {
+ int count = desc_length - offset;
+ if (count > max_packet) count = max_packet;
+
+ fprintf(stderr, "sending ACCESSORY_SET_HID_REPORT_DESC offset: %d count: %d desc_length: %d\n",
+ offset, count, desc_length);
+ ret = usb_device_control_transfer(device, USB_DIR_OUT | USB_TYPE_VENDOR,
+ ACCESSORY_SET_HID_REPORT_DESC, id, offset, &desc.value[offset], count, 1000);
+ fprintf(stderr, "ACCESSORY_SET_HID_REPORT_DESC returned %d errno %d\n", ret, errno);
+ offset += count;
+ }
+
+ pthread_mutex_unlock(&mutex);
+
+ while (1) {
+ ret = read(fd, buffer, sizeof(buffer));
+ if (ret < 0) {
+fprintf(stderr, "read failed, errno: %d, fd: %d\n", errno, fd);
+ break;
+ }
+
+ ret = usb_device_control_transfer(device, USB_DIR_OUT | USB_TYPE_VENDOR,
+ ACCESSORY_SEND_HID_EVENT, id, 0, buffer, ret, 1000);
+ if (ret < 0 && errno != EPIPE) {
+fprintf(stderr, "ACCESSORY_SEND_HID_EVENT returned %d errno: %d\n", ret, errno);
+ goto wait_for_device;
+ }
+ }
+
+fprintf(stderr, "ACCESSORY_UNREGISTER_HID\n");
+ ret = usb_device_control_transfer(device, USB_DIR_OUT | USB_TYPE_VENDOR,
+ ACCESSORY_UNREGISTER_HID, id, 0, NULL, 0, 1000);
+
+fprintf(stderr, "hid thread exiting\n");
+err:
+ return NULL;
+}
+
+static void open_hid(const char* name)
+{
+ char path[100];
+
+ snprintf(path, sizeof(path), "/dev/%s", name);
+ int fd = open(path, O_RDWR);
+ if (fd < 0) return;
+
+ fprintf(stderr, "opened /dev/%s\n", name);
+ pthread_t th;
+ pthread_create(&th, NULL, hid_thread, (void *)fd);
+}
+
+static void* inotify_thread(void* arg)
+{
+ open_hid("hidraw0");
+ open_hid("hidraw1");
+ open_hid("hidraw2");
+ open_hid("hidraw3");
+ open_hid("hidraw4");
+ open_hid("hidraw5");
+ open_hid("hidraw6");
+ open_hid("hidraw7");
+ open_hid("hidraw8");
+ open_hid("hidraw9");
+
+ int inotify_fd = inotify_init();
+ inotify_add_watch(inotify_fd, "/dev", IN_DELETE | IN_CREATE);
+
+ while (1) {
+ char event_buf[512];
+ struct inotify_event *event;
+ int event_pos = 0;
+ int event_size;
+
+ int count = read(inotify_fd, event_buf, sizeof(event_buf));
+ if (count < (int)sizeof(*event)) {
+ if(errno == EINTR)
+ continue;
+ fprintf(stderr, "could not get event, %s\n", strerror(errno));
+ break;
+ }
+ while (count >= (int)sizeof(*event)) {
+ event = (struct inotify_event *)(event_buf + event_pos);
+ //fprintf(stderr, "%d: %08x \"%s\"\n", event->wd, event->mask,
+ // event->len ? event->name : "");
+ if (event->len) {
+ if(event->mask & IN_CREATE) {
+ fprintf(stderr, "created %s\n", event->name);
+ // FIXME
+ milli_sleep(50);
+ open_hid(event->name);
+ } else {
+ fprintf(stderr, "lost %s\n", event->name);
+ }
+ }
+ event_size = sizeof(*event) + event->len;
+ count -= event_size;
+ event_pos += event_size;
+ }
+ }
+
+ close(inotify_fd);
+ return NULL;
+}
+
+void init_hid()
+{
+ pthread_t th;
+ pthread_create(&th, NULL, inotify_thread, NULL);
+}
diff --git a/libs/usb/tests/accessorytest/usb.c b/libs/usb/tests/accessorytest/usb.c
new file mode 100644
index 0000000..ac72b35
--- /dev/null
+++ b/libs/usb/tests/accessorytest/usb.c
@@ -0,0 +1,227 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <signal.h>
+#include <pthread.h>
+#include <errno.h>
+#include <string.h>
+
+#include <usbhost/usbhost.h>
+#include "f_accessory.h"
+
+#include "accessory.h"
+
+static struct usb_device *current_device = NULL;
+static uint8_t read_ep;
+static uint8_t write_ep;
+
+static pthread_mutex_t device_mutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t device_cond = PTHREAD_COND_INITIALIZER;
+
+static void milli_sleep(int millis) {
+ struct timespec tm;
+
+ tm.tv_sec = 0;
+ tm.tv_nsec = millis * 1000000;
+ nanosleep(&tm, NULL);
+}
+
+static void* read_thread(void* arg) {
+ int ret = 0;
+
+ while (current_device && ret >= 0) {
+ char buffer[16384];
+
+ ret = usb_device_bulk_transfer(current_device, read_ep, buffer, sizeof(buffer), 1000);
+ if (ret < 0 && errno == ETIMEDOUT)
+ ret = 0;
+ if (ret > 0) {
+ fwrite(buffer, 1, ret, stdout);
+ fprintf(stderr, "\n");
+ fflush(stdout);
+ }
+ }
+
+ return NULL;
+}
+
+static void* write_thread(void* arg) {
+ int ret = 0;
+
+ while (ret >= 0) {
+ char buffer[16384];
+ char *line = fgets(buffer, sizeof(buffer), stdin);
+ if (!line || !current_device)
+ break;
+ ret = usb_device_bulk_transfer(current_device, write_ep, line, strlen(line), 1000);
+ }
+
+ return NULL;
+}
+
+static void send_string(struct usb_device *device, int index, const char* string) {
+ usb_device_control_transfer(device, USB_DIR_OUT | USB_TYPE_VENDOR,
+ ACCESSORY_SEND_STRING, 0, index, (void *)string, strlen(string) + 1, 0);
+
+ // some devices can't handle back-to-back requests, so delay a bit
+ milli_sleep(10);
+}
+
+static int usb_device_added(const char *devname, void* client_data) {
+ uint16_t vendorId, productId;
+ int ret;
+ int enable_accessory = (int)client_data;
+
+ struct usb_device *device = usb_device_open(devname);
+ if (!device) {
+ fprintf(stderr, "usb_device_open failed\n");
+ return 0;
+ }
+
+ vendorId = usb_device_get_vendor_id(device);
+ productId = usb_device_get_product_id(device);
+
+ if (!current_device && vendorId == 0x18D1 && productId >= 0x2D00 && productId <= 0x2D05) {
+
+ pthread_mutex_lock(&device_mutex);
+ fprintf(stderr, "Found android device in accessory mode\n");
+ current_device = device;
+ pthread_cond_broadcast(&device_cond);
+ pthread_mutex_unlock(&device_mutex);
+
+ if (enable_accessory) {
+ struct usb_descriptor_header* desc;
+ struct usb_descriptor_iter iter;
+ struct usb_interface_descriptor *intf = NULL;
+ struct usb_endpoint_descriptor *ep1 = NULL;
+ struct usb_endpoint_descriptor *ep2 = NULL;
+ pthread_t th;
+
+ usb_descriptor_iter_init(device, &iter);
+ while ((desc = usb_descriptor_iter_next(&iter)) != NULL && (!intf || !ep1 || !ep2)) {
+ if (desc->bDescriptorType == USB_DT_INTERFACE) {
+ intf = (struct usb_interface_descriptor *)desc;
+ } else if (desc->bDescriptorType == USB_DT_ENDPOINT) {
+ if (ep1)
+ ep2 = (struct usb_endpoint_descriptor *)desc;
+ else
+ ep1 = (struct usb_endpoint_descriptor *)desc;
+ }
+ }
+
+ if (!intf) {
+ fprintf(stderr, "interface not found\n");
+ exit(1);
+ }
+ if (!ep1 || !ep2) {
+ fprintf(stderr, "endpoints not found\n");
+ exit(1);
+ }
+
+ if (usb_device_claim_interface(device, intf->bInterfaceNumber)) {
+ fprintf(stderr, "usb_device_claim_interface failed errno: %d\n", errno);
+ exit(1);
+ }
+
+ if ((ep1->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN) {
+ read_ep = ep1->bEndpointAddress;
+ write_ep = ep2->bEndpointAddress;
+ } else {
+ read_ep = ep2->bEndpointAddress;
+ write_ep = ep1->bEndpointAddress;
+ }
+
+ pthread_create(&th, NULL, read_thread, NULL);
+ pthread_create(&th, NULL, write_thread, NULL);
+ }
+ } else {
+// fprintf(stderr, "Found new device - attempting to switch to accessory mode\n");
+
+ uint16_t protocol = -1;
+ ret = usb_device_control_transfer(device, USB_DIR_IN | USB_TYPE_VENDOR,
+ ACCESSORY_GET_PROTOCOL, 0, 0, &protocol, sizeof(protocol), 1000);
+ if (ret < 0) {
+ // fprintf(stderr, "ACCESSORY_GET_PROTOCOL returned %d errno: %d\n", ret, errno);
+ } else {
+ fprintf(stderr, "device supports protocol version %d\n", protocol);
+ if (protocol >= 2) {
+ if (enable_accessory) {
+ send_string(device, ACCESSORY_STRING_MANUFACTURER, "Google, Inc.");
+ send_string(device, ACCESSORY_STRING_MODEL, "AccessoryChat");
+ send_string(device, ACCESSORY_STRING_DESCRIPTION, "Accessory Chat");
+ send_string(device, ACCESSORY_STRING_VERSION, "1.0");
+ send_string(device, ACCESSORY_STRING_URI, "http://www.android.com");
+ send_string(device, ACCESSORY_STRING_SERIAL, "1234567890");
+ }
+
+ fprintf(stderr, "sending ACCESSORY_SET_AUDIO_MODE\n");
+ ret = usb_device_control_transfer(device, USB_DIR_OUT | USB_TYPE_VENDOR,
+ ACCESSORY_SET_AUDIO_MODE, 1, 0, NULL, 0, 1000);
+ if (ret < 0)
+ fprintf(stderr, "ACCESSORY_SET_AUDIO_MODE returned %d errno: %d\n", ret, errno);
+
+ fprintf(stderr, "sending ACCESSORY_START\n");
+ ret = usb_device_control_transfer(device, USB_DIR_OUT | USB_TYPE_VENDOR,
+ ACCESSORY_START, 0, 0, NULL, 0, 1000);
+ fprintf(stderr, "did ACCESSORY_START\n");
+ if (ret < 0)
+ fprintf(stderr, "ACCESSORY_START returned %d errno: %d\n", ret, errno);
+ }
+ }
+
+ return 0;
+ }
+
+ if (device != current_device)
+ usb_device_close(device);
+
+ return 0;
+}
+
+static int usb_device_removed(const char *devname, void* client_data) {
+ pthread_mutex_lock(&device_mutex);
+
+ if (current_device && !strcmp(usb_device_get_name(current_device), devname)) {
+ fprintf(stderr, "current device disconnected\n");
+ usb_device_close(current_device);
+ current_device = NULL;
+ }
+
+ pthread_mutex_unlock(&device_mutex);
+ return 0;
+}
+
+struct usb_device* usb_wait_for_device() {
+ struct usb_device* device = NULL;
+
+ pthread_mutex_lock(&device_mutex);
+ while (!current_device)
+ pthread_cond_wait(&device_cond, &device_mutex);
+ device = current_device;
+ pthread_mutex_unlock(&device_mutex);
+
+ return device;
+}
+
+void usb_run(int enable_accessory) {
+ struct usb_host_context* context = usb_host_init();
+
+ usb_host_run(context, usb_device_added, usb_device_removed, NULL, (void *)enable_accessory);
+}
+