Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e619d867 authored by Mike Lockwood's avatar Mike Lockwood Committed by Android (Google) Code Review
Browse files

Merge "Add accessorytest Linux host tool for testing USB accessory audio and...

Merge "Add accessorytest Linux host tool for testing USB accessory audio and HID support." into lmp-dev
parents b80e42f9 6b524d9b
Loading
Loading
Loading
Loading
+25 −0
Original line number Diff line number Diff line
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
+79 −0
Original line number Diff line number Diff line
/*
 * 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;
}
+26 −0
Original line number Diff line number Diff line
/*
 * 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__ */
+221 −0
Original line number Diff line number Diff line
/*
 * 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;
}
+148 −0
Original line number Diff line number Diff line
/*
 * 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 */
Loading