summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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 000000000000..6d9a9460c675
--- /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 000000000000..a3c47af7d14e
--- /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 000000000000..55c4550f79e0
--- /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 000000000000..d23d9b3fa5d3
--- /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 000000000000..312f4ba6eed3
--- /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 000000000000..b70d678543ca
--- /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 000000000000..ac72b35b908e
--- /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);
+}
+