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