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
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);
+}