110024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood/*
210024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood * Copyright (C) 2010 The Android Open Source Project
310024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood *
410024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood * Licensed under the Apache License, Version 2.0 (the "License");
510024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood * you may not use this file except in compliance with the License.
610024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood * You may obtain a copy of the License at
710024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood *
810024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood *      http://www.apache.org/licenses/LICENSE-2.0
910024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood *
1010024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood * Unless required by applicable law or agreed to in writing, software
1110024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood * distributed under the License is distributed on an "AS IS" BASIS,
1210024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1310024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood * See the License for the specific language governing permissions and
1410024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood * limitations under the License.
1510024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood */
1610024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood
1710024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood#define LOG_TAG "UsbMidiDeviceJNI"
1810024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood#define LOG_NDEBUG 0
1910024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood#include "utils/Log.h"
2010024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood
2110024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood#include "jni.h"
2210024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood#include "JNIHelp.h"
2310024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood#include "android_runtime/AndroidRuntime.h"
2410024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood#include "android_runtime/Log.h"
2510024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood
2610024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood#include <stdio.h>
2710024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood#include <errno.h>
2810024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood#include <asm/byteorder.h>
2910024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood#include <sys/types.h>
3010024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood#include <sys/stat.h>
3110024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood#include <fcntl.h>
3210024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood#include <sys/ioctl.h>
3310024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood#include <sound/asound.h>
3410024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood
3510024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwoodnamespace android
3610024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood{
3710024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood
3810024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwoodstatic jclass sFileDescriptorClass;
396d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwoodstatic jfieldID sPipeFDField;
4010024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood
4110024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwoodstatic jint
4210024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwoodandroid_server_UsbMidiDevice_get_subdevice_count(JNIEnv *env, jobject /* thiz */,
4310024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood        jint card, jint device)
4410024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood{
4510024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood    char    path[100];
4632bfb874e32991685150ad7b9e8785b2b0ec9a7ePhil Burk    int     fd;
4732bfb874e32991685150ad7b9e8785b2b0ec9a7ePhil Burk    const   int kMaxRetries = 10;
4832bfb874e32991685150ad7b9e8785b2b0ec9a7ePhil Burk    const   int kSleepMicroseconds = 2000;
4910024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood
5010024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood    snprintf(path, sizeof(path), "/dev/snd/controlC%d", card);
5132bfb874e32991685150ad7b9e8785b2b0ec9a7ePhil Burk    // This control device may not have been created yet. So we should
5232bfb874e32991685150ad7b9e8785b2b0ec9a7ePhil Burk    // try to open it several times to prevent intermittent failure
5332bfb874e32991685150ad7b9e8785b2b0ec9a7ePhil Burk    // from a race condition.
5432bfb874e32991685150ad7b9e8785b2b0ec9a7ePhil Burk    int retryCounter = 0;
5532bfb874e32991685150ad7b9e8785b2b0ec9a7ePhil Burk    while ((fd = open(path, O_RDWR)) < 0) {
5632bfb874e32991685150ad7b9e8785b2b0ec9a7ePhil Burk        if (++retryCounter > kMaxRetries) {
5732bfb874e32991685150ad7b9e8785b2b0ec9a7ePhil Burk            ALOGE("timed out after %d tries, could not open %s", retryCounter, path);
5832bfb874e32991685150ad7b9e8785b2b0ec9a7ePhil Burk            return 0;
5932bfb874e32991685150ad7b9e8785b2b0ec9a7ePhil Burk        } else {
6032bfb874e32991685150ad7b9e8785b2b0ec9a7ePhil Burk            ALOGW("attempt #%d, could not open %s", retryCounter, path);
6132bfb874e32991685150ad7b9e8785b2b0ec9a7ePhil Burk            // Increase the sleep interval each time.
6232bfb874e32991685150ad7b9e8785b2b0ec9a7ePhil Burk            // 10 retries will total 2 * sum(1..10) = 110 milliseconds.
6332bfb874e32991685150ad7b9e8785b2b0ec9a7ePhil Burk            // Typically the device should be ready in 5-10 milliseconds.
6432bfb874e32991685150ad7b9e8785b2b0ec9a7ePhil Burk            usleep(kSleepMicroseconds * retryCounter);
6532bfb874e32991685150ad7b9e8785b2b0ec9a7ePhil Burk        }
6610024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood    }
6710024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood
6810024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood    struct snd_rawmidi_info info;
6910024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood    memset(&info, 0, sizeof(info));
7010024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood    info.device = device;
7110024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood    int ret = ioctl(fd, SNDRV_CTL_IOCTL_RAWMIDI_INFO, &info);
7210024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood    close(fd);
7310024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood
7410024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood    if (ret < 0) {
7510024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood        ALOGE("SNDRV_CTL_IOCTL_RAWMIDI_INFO failed, errno: %d path: %s", errno, path);
7610024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood        return -1;
7710024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood    }
7810024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood
7910024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood    ALOGD("subdevices_count: %d", info.subdevices_count);
8010024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood    return info.subdevices_count;
8110024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood}
8210024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood
8310024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwoodstatic jobjectArray
846d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwoodandroid_server_UsbMidiDevice_open(JNIEnv *env, jobject thiz, jint card, jint device,
8510024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood        jint subdevice_count)
8610024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood{
8710024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood    char    path[100];
8810024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood
8910024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood    snprintf(path, sizeof(path), "/dev/snd/midiC%dD%d", card, device);
9010024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood
916d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood    // allocate one extra file descriptor for close pipe
926d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood    jobjectArray fds = env->NewObjectArray(subdevice_count + 1, sFileDescriptorClass, NULL);
9310024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood    if (!fds) {
9410024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood        return NULL;
9510024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood    }
9610024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood
9710024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood    // to support multiple subdevices we open the same file multiple times
9810024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood    for (int i = 0; i < subdevice_count; i++) {
9910024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood        int fd = open(path, O_RDWR);
10010024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood        if (fd < 0) {
10110024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood            ALOGE("open failed on %s for index %d", path, i);
10210024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood            return NULL;
10310024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood        }
10410024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood
10510024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood        jobject fileDescriptor = jniCreateFileDescriptor(env, fd);
10610024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood        env->SetObjectArrayElement(fds, i, fileDescriptor);
10710024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood        env->DeleteLocalRef(fileDescriptor);
10810024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood    }
10910024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood
1106d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood    // create a pipe to use for unblocking our input thread
1116d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood    int pipeFD[2];
1126d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood    pipe(pipeFD);
1136d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood    jobject fileDescriptor = jniCreateFileDescriptor(env, pipeFD[0]);
1146d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood    env->SetObjectArrayElement(fds, subdevice_count, fileDescriptor);
1156d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood    env->DeleteLocalRef(fileDescriptor);
1166d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood    // store our end of the pipe in mPipeFD
1176d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood    env->SetIntField(thiz, sPipeFDField, pipeFD[1]);
1186d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood
11910024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood    return fds;
12010024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood}
12110024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood
122b7ce094c9e546c4a802bd8ce3a43592979a5e3dfMike Lockwoodstatic void
1236d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwoodandroid_server_UsbMidiDevice_close(JNIEnv *env, jobject thiz, jobjectArray fds)
124b7ce094c9e546c4a802bd8ce3a43592979a5e3dfMike Lockwood{
1256d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood    // write to mPipeFD to unblock input thread
1266d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood    jint pipeFD = env->GetIntField(thiz, sPipeFDField);
1276d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood    write(pipeFD, &pipeFD, sizeof(pipeFD));
1286d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood    close(pipeFD);
1296d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood    env->SetIntField(thiz, sPipeFDField, -1);
1306d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood
131b7ce094c9e546c4a802bd8ce3a43592979a5e3dfMike Lockwood    int count = env->GetArrayLength(fds);
132b7ce094c9e546c4a802bd8ce3a43592979a5e3dfMike Lockwood    for (int i = 0; i < count; i++) {
133b7ce094c9e546c4a802bd8ce3a43592979a5e3dfMike Lockwood        jobject fd = env->GetObjectArrayElement(fds, i);
134b7ce094c9e546c4a802bd8ce3a43592979a5e3dfMike Lockwood        close(jniGetFDFromFileDescriptor(env, fd));
135b7ce094c9e546c4a802bd8ce3a43592979a5e3dfMike Lockwood    }
136b7ce094c9e546c4a802bd8ce3a43592979a5e3dfMike Lockwood}
137b7ce094c9e546c4a802bd8ce3a43592979a5e3dfMike Lockwood
13810024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwoodstatic JNINativeMethod method_table[] = {
13910024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood    { "nativeGetSubdeviceCount", "(II)I", (void*)android_server_UsbMidiDevice_get_subdevice_count },
14010024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood    { "nativeOpen", "(III)[Ljava/io/FileDescriptor;", (void*)android_server_UsbMidiDevice_open },
141b7ce094c9e546c4a802bd8ce3a43592979a5e3dfMike Lockwood    { "nativeClose", "([Ljava/io/FileDescriptor;)V", (void*)android_server_UsbMidiDevice_close },
14210024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood};
14310024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood
14410024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwoodint register_android_server_UsbMidiDevice(JNIEnv *env)
14510024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood{
14610024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood    jclass clazz = env->FindClass("java/io/FileDescriptor");
14710024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood    if (clazz == NULL) {
14810024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood        ALOGE("Can't find java/io/FileDescriptor");
14910024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood        return -1;
15010024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood    }
1516d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood    sFileDescriptorClass = (jclass)env->NewGlobalRef(clazz);
15210024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood
15310024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood    clazz = env->FindClass("com/android/server/usb/UsbMidiDevice");
15410024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood    if (clazz == NULL) {
15510024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood        ALOGE("Can't find com/android/server/usb/UsbMidiDevice");
15610024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood        return -1;
15710024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood    }
1586d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood    sPipeFDField = env->GetFieldID(clazz, "mPipeFD", "I");
1596d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood    if (sPipeFDField == NULL) {
1606d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood        ALOGE("Can't find UsbMidiDevice.mPipeFD");
1616d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood        return -1;
1626d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood    }
16310024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood
16410024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood    return jniRegisterNativeMethods(env, "com/android/server/usb/UsbMidiDevice",
16510024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood            method_table, NELEM(method_table));
16610024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood}
16710024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood
16810024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood};
169