1 /* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #define LOG_TAG "UsbMidiDeviceJNI" 18 #define LOG_NDEBUG 0 19 #include "utils/Log.h" 20 21 #include "jni.h" 22 #include "JNIHelp.h" 23 #include "android_runtime/AndroidRuntime.h" 24 #include "android_runtime/Log.h" 25 26 #include <stdio.h> 27 #include <errno.h> 28 #include <asm/byteorder.h> 29 #include <sys/types.h> 30 #include <sys/stat.h> 31 #include <fcntl.h> 32 #include <sys/ioctl.h> 33 #include <sound/asound.h> 34 35 namespace android 36 { 37 38 static jclass sFileDescriptorClass; 39 static jfieldID sPipeFDField; 40 41 static jint 42 android_server_UsbMidiDevice_get_subdevice_count(JNIEnv *env, jobject /* thiz */, 43 jint card, jint device) 44 { 45 char path[100]; 46 47 snprintf(path, sizeof(path), "/dev/snd/controlC%d", card); 48 int fd = open(path, O_RDWR); 49 if (fd < 0) { 50 ALOGE("could not open %s", path); 51 return 0; 52 } 53 54 struct snd_rawmidi_info info; 55 memset(&info, 0, sizeof(info)); 56 info.device = device; 57 int ret = ioctl(fd, SNDRV_CTL_IOCTL_RAWMIDI_INFO, &info); 58 close(fd); 59 60 if (ret < 0) { 61 ALOGE("SNDRV_CTL_IOCTL_RAWMIDI_INFO failed, errno: %d path: %s", errno, path); 62 return -1; 63 } 64 65 ALOGD("subdevices_count: %d", info.subdevices_count); 66 return info.subdevices_count; 67 } 68 69 static jobjectArray 70 android_server_UsbMidiDevice_open(JNIEnv *env, jobject thiz, jint card, jint device, 71 jint subdevice_count) 72 { 73 char path[100]; 74 75 snprintf(path, sizeof(path), "/dev/snd/midiC%dD%d", card, device); 76 77 // allocate one extra file descriptor for close pipe 78 jobjectArray fds = env->NewObjectArray(subdevice_count + 1, sFileDescriptorClass, NULL); 79 if (!fds) { 80 return NULL; 81 } 82 83 // to support multiple subdevices we open the same file multiple times 84 for (int i = 0; i < subdevice_count; i++) { 85 int fd = open(path, O_RDWR); 86 if (fd < 0) { 87 ALOGE("open failed on %s for index %d", path, i); 88 return NULL; 89 } 90 91 jobject fileDescriptor = jniCreateFileDescriptor(env, fd); 92 env->SetObjectArrayElement(fds, i, fileDescriptor); 93 env->DeleteLocalRef(fileDescriptor); 94 } 95 96 // create a pipe to use for unblocking our input thread 97 int pipeFD[2]; 98 pipe(pipeFD); 99 jobject fileDescriptor = jniCreateFileDescriptor(env, pipeFD[0]); 100 env->SetObjectArrayElement(fds, subdevice_count, fileDescriptor); 101 env->DeleteLocalRef(fileDescriptor); 102 // store our end of the pipe in mPipeFD 103 env->SetIntField(thiz, sPipeFDField, pipeFD[1]); 104 105 return fds; 106 } 107 108 static void 109 android_server_UsbMidiDevice_close(JNIEnv *env, jobject thiz, jobjectArray fds) 110 { 111 // write to mPipeFD to unblock input thread 112 jint pipeFD = env->GetIntField(thiz, sPipeFDField); 113 write(pipeFD, &pipeFD, sizeof(pipeFD)); 114 close(pipeFD); 115 env->SetIntField(thiz, sPipeFDField, -1); 116 117 int count = env->GetArrayLength(fds); 118 for (int i = 0; i < count; i++) { 119 jobject fd = env->GetObjectArrayElement(fds, i); 120 close(jniGetFDFromFileDescriptor(env, fd)); 121 } 122 } 123 124 static JNINativeMethod method_table[] = { 125 { "nativeGetSubdeviceCount", "(II)I", (void*)android_server_UsbMidiDevice_get_subdevice_count }, 126 { "nativeOpen", "(III)[Ljava/io/FileDescriptor;", (void*)android_server_UsbMidiDevice_open }, 127 { "nativeClose", "([Ljava/io/FileDescriptor;)V", (void*)android_server_UsbMidiDevice_close }, 128 }; 129 130 int register_android_server_UsbMidiDevice(JNIEnv *env) 131 { 132 jclass clazz = env->FindClass("java/io/FileDescriptor"); 133 if (clazz == NULL) { 134 ALOGE("Can't find java/io/FileDescriptor"); 135 return -1; 136 } 137 sFileDescriptorClass = (jclass)env->NewGlobalRef(clazz); 138 139 clazz = env->FindClass("com/android/server/usb/UsbMidiDevice"); 140 if (clazz == NULL) { 141 ALOGE("Can't find com/android/server/usb/UsbMidiDevice"); 142 return -1; 143 } 144 sPipeFDField = env->GetFieldID(clazz, "mPipeFD", "I"); 145 if (sPipeFDField == NULL) { 146 ALOGE("Can't find UsbMidiDevice.mPipeFD"); 147 return -1; 148 } 149 150 return jniRegisterNativeMethods(env, "com/android/server/usb/UsbMidiDevice", 151 method_table, NELEM(method_table)); 152 } 153 154 }; 155