Loading services/core/jni/com_android_server_UsbMidiDevice.cpp +52 −67 Original line number Diff line number Diff line Loading @@ -24,14 +24,14 @@ #include "android_runtime/AndroidRuntime.h" #include "android_runtime/Log.h" #include <stdio.h> #include <errno.h> #include <asm/byteorder.h> #include <sys/types.h> #include <sys/stat.h> #include <errno.h> #include <fcntl.h> #include <sys/ioctl.h> #include <sound/asound.h> #include <stdio.h> #include <sys/ioctl.h> #include <sys/stat.h> #include <sys/types.h> namespace android { Loading @@ -39,78 +39,62 @@ namespace android static jclass sFileDescriptorClass; static jfieldID sPipeFDField; static jint android_server_UsbMidiDevice_get_subdevice_count(JNIEnv *env, jobject /* thiz */, jint card, jint device) { // This function returns an array of integers, each representing a file descriptor. // The will be in the order of inputs then outputs. // The last input fd will be for a file descriptor that simply allows Os.poll() to keep working. // For example, if numInputs is 2 and numOutputs is 1, the resulting fds are as follows: // 1. Input O_RDONLY file descriptor // 2. Special input file descriptor to block the input thread // 3. Output O_WRONLY file descriptor static jobjectArray android_server_UsbMidiDevice_open(JNIEnv *env, jobject thiz, jint card, jint device, jint numInputs, jint numOutputs) { char path[100]; int fd; const int kMaxRetries = 10; const int kSleepMicroseconds = 2000; snprintf(path, sizeof(path), "/dev/snd/controlC%d", card); // This control device may not have been created yet. So we should // try to open it several times to prevent intermittent failure // from a race condition. int retryCounter = 0; while ((fd = open(path, O_RDWR)) < 0) { if (++retryCounter > kMaxRetries) { ALOGE("timed out after %d tries, could not open %s", retryCounter, path); return 0; } else { ALOGW("attempt #%d, could not open %s", retryCounter, path); // Increase the sleep interval each time. // 10 retries will total 2 * sum(1..10) = 110 milliseconds. // Typically the device should be ready in 5-10 milliseconds. usleep(kSleepMicroseconds * retryCounter); } } struct snd_rawmidi_info info; memset(&info, 0, sizeof(info)); info.device = device; int ret = ioctl(fd, SNDRV_CTL_IOCTL_RAWMIDI_INFO, &info); close(fd); if (ret < 0) { ALOGE("SNDRV_CTL_IOCTL_RAWMIDI_INFO failed, errno: %d path: %s", errno, path); return -1; } ALOGD("subdevices_count: %d", info.subdevices_count); return info.subdevices_count; } static jobjectArray android_server_UsbMidiDevice_open(JNIEnv *env, jobject thiz, jint card, jint device, jint subdevice_count) { char path[100]; snprintf(path, sizeof(path), "/dev/snd/midiC%dD%d", card, device); // allocate one extra file descriptor for close pipe jobjectArray fds = env->NewObjectArray(subdevice_count + 1, sFileDescriptorClass, NULL); ALOGD("Opening %d inputs and %d outputs", numInputs, numOutputs); jobjectArray fds = env->NewObjectArray(numInputs + numOutputs, sFileDescriptorClass, NULL); if (!fds) { return NULL; } // to support multiple subdevices we open the same file multiple times for (int i = 0; i < subdevice_count; i++) { int fd = open(path, O_RDWR); // open the path for the read pipes. The last one is special and used to // unblock Os.poll() for (int i = 0; i < numInputs - 1; i++) { fd = open(path, O_RDONLY); if (fd < 0) { ALOGE("open failed on %s for index %d", path, i); goto release_fds; } ScopedLocalRef<jobject> jifd(env, jniCreateFileDescriptor(env, fd)); if (jifd.get() == NULL) { close(fd); goto release_fds; } env->SetObjectArrayElement(fds, i, jifd.get()); } // create a pipe to use for unblocking our input thread { // open the path for the write pipes for (int i = 0; i < numOutputs; i++) { fd = open(path, O_WRONLY); if (fd < 0) { ALOGE("open failed on %s for index %d", path, i); goto release_fds; } ScopedLocalRef<jobject> jifd(env, jniCreateFileDescriptor(env, fd)); if (jifd.get() == NULL) { close(fd); goto release_fds; } env->SetObjectArrayElement(fds, i + numInputs, jifd.get()); } // create a pipe to use for unblocking our input thread. The caller should // set numInputs as 0 when there are zero real input threads. if (numInputs > 0) { int pipeFD[2]; if (pipe(pipeFD) == -1) { ALOGE("pipe() failed, errno = %d", errno); Loading @@ -123,21 +107,22 @@ android_server_UsbMidiDevice_open(JNIEnv *env, jobject thiz, jint card, jint dev close(pipeFD[1]); goto release_fds; } env->SetObjectArrayElement(fds, subdevice_count, jifd.get()); // store as last input file descriptor env->SetObjectArrayElement(fds, numInputs - 1, jifd.get()); // store our end of the pipe in mPipeFD env->SetIntField(thiz, sPipeFDField, pipeFD[1]); } return fds; release_fds: for (int i = 0; i < subdevice_count + 1; ++i) { for (int i = 0; i < numInputs + numOutputs; ++i) { ScopedLocalRef<jobject> jifd(env, env->GetObjectArrayElement(fds, i)); if (jifd.get() == NULL) { break; } if (jifd.get() != NULL) { int fd = jniGetFDFromFileDescriptor(env, jifd.get()); close(fd); } } return NULL; } Loading @@ -158,8 +143,8 @@ android_server_UsbMidiDevice_close(JNIEnv *env, jobject thiz, jobjectArray fds) } static JNINativeMethod method_table[] = { { "nativeGetSubdeviceCount", "(II)I", (void*)android_server_UsbMidiDevice_get_subdevice_count }, { "nativeOpen", "(III)[Ljava/io/FileDescriptor;", (void*)android_server_UsbMidiDevice_open }, {"nativeOpen", "(IIII)[Ljava/io/FileDescriptor;", (void *)android_server_UsbMidiDevice_open}, {"nativeClose", "([Ljava/io/FileDescriptor;)V", (void *)android_server_UsbMidiDevice_close}, }; Loading services/usb/java/com/android/server/usb/UsbAlsaManager.java +5 −2 Original line number Diff line number Diff line Loading @@ -257,6 +257,8 @@ public final class UsbAlsaManager { // look for MIDI devices boolean hasMidi = parser.hasMIDIInterface(); int midiNumInputs = parser.calculateNumMidiInputs(); int midiNumOutputs = parser.calculateNumMidiOutputs(); if (DEBUG) { Slog.d(TAG, "hasMidi: " + hasMidi + " mHasMidiFeature:" + mHasMidiFeature); } Loading Loading @@ -285,7 +287,7 @@ public final class UsbAlsaManager { properties.putParcelable(MidiDeviceInfo.PROPERTY_USB_DEVICE, usbDevice); UsbMidiDevice usbMidiDevice = UsbMidiDevice.create(mContext, properties, cardRec.getCardNum(), 0 /*device*/); cardRec.getCardNum(), 0 /*device*/, midiNumInputs, midiNumOutputs); if (usbMidiDevice != null) { mMidiDevices.put(deviceAddress, usbMidiDevice); } Loading Loading @@ -338,7 +340,8 @@ public final class UsbAlsaManager { com.android.internal.R.string.usb_midi_peripheral_product_name)); properties.putInt(MidiDeviceInfo.PROPERTY_ALSA_CARD, card); properties.putInt(MidiDeviceInfo.PROPERTY_ALSA_DEVICE, device); mPeripheralMidiDevice = UsbMidiDevice.create(mContext, properties, card, device); mPeripheralMidiDevice = UsbMidiDevice.create(mContext, properties, card, device, 1 /* numInputs */, 1 /* numOutputs */); } else if (!enabled && mPeripheralMidiDevice != null) { IoUtils.closeQuietly(mPeripheralMidiDevice); mPeripheralMidiDevice = null; Loading services/usb/java/com/android/server/usb/UsbMidiDevice.java +80 −72 Original line number Diff line number Diff line Loading @@ -48,8 +48,10 @@ public final class UsbMidiDevice implements Closeable { private final int mAlsaCard; private final int mAlsaDevice; private final int mSubdeviceCount; private final InputReceiverProxy[] mInputPortReceivers; // USB outputs are MIDI inputs private final InputReceiverProxy[] mMidiInputPortReceivers; private final int mNumInputs; private final int mNumOutputs; private MidiDeviceServer mServer; Loading Loading @@ -139,15 +141,14 @@ public final class UsbMidiDevice implements Closeable { } } public static UsbMidiDevice create(Context context, Bundle properties, int card, int device) { // FIXME - support devices with different number of input and output ports int subDeviceCount = nativeGetSubdeviceCount(card, device); if (subDeviceCount <= 0) { Log.e(TAG, "nativeGetSubdeviceCount failed"); return null; } UsbMidiDevice midiDevice = new UsbMidiDevice(card, device, subDeviceCount); /** * Creates an UsbMidiDevice based on the input parameters. Read/Write streams * will be created individually as some devices don't have the same number of * inputs and outputs. */ public static UsbMidiDevice create(Context context, Bundle properties, int card, int device, int numInputs, int numOutputs) { UsbMidiDevice midiDevice = new UsbMidiDevice(card, device, numInputs, numOutputs); if (!midiDevice.register(context, properties)) { IoUtils.closeQuietly(midiDevice); Log.e(TAG, "createDeviceServer failed"); Loading @@ -156,35 +157,40 @@ public final class UsbMidiDevice implements Closeable { return midiDevice; } private UsbMidiDevice(int card, int device, int subdeviceCount) { private UsbMidiDevice(int card, int device, int numInputs, int numOutputs) { mAlsaCard = card; mAlsaDevice = device; mSubdeviceCount = subdeviceCount; mNumInputs = numInputs; mNumOutputs = numOutputs; // FIXME - support devices with different number of input and output ports int inputPortCount = subdeviceCount; mInputPortReceivers = new InputReceiverProxy[inputPortCount]; for (int port = 0; port < inputPortCount; port++) { mInputPortReceivers[port] = new InputReceiverProxy(); // Create MIDI port receivers based on the number of output ports. The // output of USB is the input of MIDI. mMidiInputPortReceivers = new InputReceiverProxy[numOutputs]; for (int port = 0; port < numOutputs; port++) { mMidiInputPortReceivers[port] = new InputReceiverProxy(); } } private boolean openLocked() { // FIXME - support devices with different number of input and output ports FileDescriptor[] fileDescriptors = nativeOpen(mAlsaCard, mAlsaDevice, mSubdeviceCount); int inputStreamCount = mNumInputs; // Create an extra stream for unblocking Os.poll() if (inputStreamCount > 0) { inputStreamCount++; } int outputStreamCount = mNumOutputs; // The resulting file descriptors will be O_RDONLY following by O_WRONLY FileDescriptor[] fileDescriptors = nativeOpen(mAlsaCard, mAlsaDevice, inputStreamCount, outputStreamCount); if (fileDescriptors == null) { Log.e(TAG, "nativeOpen failed"); return false; } mFileDescriptors = fileDescriptors; int inputStreamCount = fileDescriptors.length; // last file descriptor returned from nativeOpen() is only used for unblocking Os.poll() // in our input thread int outputStreamCount = fileDescriptors.length - 1; mPollFDs = new StructPollfd[inputStreamCount]; mInputStreams = new FileInputStream[inputStreamCount]; for (int i = 0; i < inputStreamCount; i++) { FileDescriptor fd = fileDescriptors[i]; StructPollfd pollfd = new StructPollfd(); Loading @@ -196,16 +202,18 @@ public final class UsbMidiDevice implements Closeable { mOutputStreams = new FileOutputStream[outputStreamCount]; mEventSchedulers = new MidiEventScheduler[outputStreamCount]; for (int i = 0; i < outputStreamCount; i++) { mOutputStreams[i] = new FileOutputStream(fileDescriptors[i]); int curOutputStream = 0; for (int i = 0; i < outputStreamCount; i++) { mOutputStreams[i] = new FileOutputStream(fileDescriptors[inputStreamCount + i]); MidiEventScheduler scheduler = new MidiEventScheduler(); mEventSchedulers[i] = scheduler; mInputPortReceivers[i].setReceiver(scheduler.getReceiver()); mMidiInputPortReceivers[i].setReceiver(scheduler.getReceiver()); } final MidiReceiver[] outputReceivers = mServer.getOutputPortReceivers(); if (inputStreamCount > 0) { // Create input thread which will read from all output ports of the physical device new Thread("UsbMidiDevice input thread") { @Override Loading @@ -227,9 +235,8 @@ public final class UsbMidiDevice implements Closeable { } else if ((pfd.revents & OsConstants.POLLIN) != 0) { // clear readable flag pfd.revents = 0; if (index == mInputStreams.length - 1) { // last file descriptor is used only for unblocking Os.poll() // last fd is used only for unblocking Os.poll() break; } Loading @@ -250,6 +257,7 @@ public final class UsbMidiDevice implements Closeable { Log.d(TAG, "input thread exit"); } }.start(); } // Create output thread for each input port of the physical device for (int port = 0; port < outputStreamCount; port++) { Loading Loading @@ -294,7 +302,7 @@ public final class UsbMidiDevice implements Closeable { return false; } mServer = midiManager.createDeviceServer(mInputPortReceivers, mSubdeviceCount, mServer = midiManager.createDeviceServer(mMidiInputPortReceivers, mNumInputs, null, null, properties, MidiDeviceInfo.TYPE_USB, mCallback); if (mServer == null) { return false; Loading @@ -318,7 +326,7 @@ public final class UsbMidiDevice implements Closeable { private void closeLocked() { for (int i = 0; i < mEventSchedulers.length; i++) { mInputPortReceivers[i].setReceiver(null); mMidiInputPortReceivers[i].setReceiver(null); mEventSchedulers[i].close(); } mEventSchedulers = null; Loading Loading @@ -354,7 +362,7 @@ public final class UsbMidiDevice implements Closeable { dump.end(token); } private static native int nativeGetSubdeviceCount(int card, int device); private native FileDescriptor[] nativeOpen(int card, int device, int subdeviceCount); private native FileDescriptor[] nativeOpen(int card, int device, int numInputs, int numOutputs); private native void nativeClose(FileDescriptor[] fileDescriptors); } services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java +39 −0 Original line number Diff line number Diff line Loading @@ -656,6 +656,45 @@ public final class UsbDescriptorParser { return false; } private int calculateNumMidiPorts(boolean isOutput) { int count = 0; ArrayList<UsbDescriptor> descriptors = getInterfaceDescriptorsForClass(UsbDescriptor.CLASSID_AUDIO); for (UsbDescriptor descriptor : descriptors) { // ensure that this isn't an unrecognized interface descriptor if (descriptor instanceof UsbInterfaceDescriptor) { UsbInterfaceDescriptor interfaceDescr = (UsbInterfaceDescriptor) descriptor; if (interfaceDescr.getUsbSubclass() == UsbDescriptor.AUDIO_MIDISTREAMING) { for (int i = 0; i < interfaceDescr.getNumEndpoints(); i++) { UsbEndpointDescriptor endpoint = interfaceDescr.getEndpointDescriptor(i); // 0 is output, 1 << 7 is input. if ((endpoint.getDirection() == 0) == isOutput) { count++; } } } } else { Log.w(TAG, "Undefined Audio Class Interface l: " + descriptor.getLength() + " t:0x" + Integer.toHexString(descriptor.getType())); } } return count; } /** * @hide */ public int calculateNumMidiInputs() { return calculateNumMidiPorts(false /*isOutput*/); } /** * @hide */ public int calculateNumMidiOutputs() { return calculateNumMidiPorts(true /*isOutput*/); } /** * @hide */ Loading Loading
services/core/jni/com_android_server_UsbMidiDevice.cpp +52 −67 Original line number Diff line number Diff line Loading @@ -24,14 +24,14 @@ #include "android_runtime/AndroidRuntime.h" #include "android_runtime/Log.h" #include <stdio.h> #include <errno.h> #include <asm/byteorder.h> #include <sys/types.h> #include <sys/stat.h> #include <errno.h> #include <fcntl.h> #include <sys/ioctl.h> #include <sound/asound.h> #include <stdio.h> #include <sys/ioctl.h> #include <sys/stat.h> #include <sys/types.h> namespace android { Loading @@ -39,78 +39,62 @@ namespace android static jclass sFileDescriptorClass; static jfieldID sPipeFDField; static jint android_server_UsbMidiDevice_get_subdevice_count(JNIEnv *env, jobject /* thiz */, jint card, jint device) { // This function returns an array of integers, each representing a file descriptor. // The will be in the order of inputs then outputs. // The last input fd will be for a file descriptor that simply allows Os.poll() to keep working. // For example, if numInputs is 2 and numOutputs is 1, the resulting fds are as follows: // 1. Input O_RDONLY file descriptor // 2. Special input file descriptor to block the input thread // 3. Output O_WRONLY file descriptor static jobjectArray android_server_UsbMidiDevice_open(JNIEnv *env, jobject thiz, jint card, jint device, jint numInputs, jint numOutputs) { char path[100]; int fd; const int kMaxRetries = 10; const int kSleepMicroseconds = 2000; snprintf(path, sizeof(path), "/dev/snd/controlC%d", card); // This control device may not have been created yet. So we should // try to open it several times to prevent intermittent failure // from a race condition. int retryCounter = 0; while ((fd = open(path, O_RDWR)) < 0) { if (++retryCounter > kMaxRetries) { ALOGE("timed out after %d tries, could not open %s", retryCounter, path); return 0; } else { ALOGW("attempt #%d, could not open %s", retryCounter, path); // Increase the sleep interval each time. // 10 retries will total 2 * sum(1..10) = 110 milliseconds. // Typically the device should be ready in 5-10 milliseconds. usleep(kSleepMicroseconds * retryCounter); } } struct snd_rawmidi_info info; memset(&info, 0, sizeof(info)); info.device = device; int ret = ioctl(fd, SNDRV_CTL_IOCTL_RAWMIDI_INFO, &info); close(fd); if (ret < 0) { ALOGE("SNDRV_CTL_IOCTL_RAWMIDI_INFO failed, errno: %d path: %s", errno, path); return -1; } ALOGD("subdevices_count: %d", info.subdevices_count); return info.subdevices_count; } static jobjectArray android_server_UsbMidiDevice_open(JNIEnv *env, jobject thiz, jint card, jint device, jint subdevice_count) { char path[100]; snprintf(path, sizeof(path), "/dev/snd/midiC%dD%d", card, device); // allocate one extra file descriptor for close pipe jobjectArray fds = env->NewObjectArray(subdevice_count + 1, sFileDescriptorClass, NULL); ALOGD("Opening %d inputs and %d outputs", numInputs, numOutputs); jobjectArray fds = env->NewObjectArray(numInputs + numOutputs, sFileDescriptorClass, NULL); if (!fds) { return NULL; } // to support multiple subdevices we open the same file multiple times for (int i = 0; i < subdevice_count; i++) { int fd = open(path, O_RDWR); // open the path for the read pipes. The last one is special and used to // unblock Os.poll() for (int i = 0; i < numInputs - 1; i++) { fd = open(path, O_RDONLY); if (fd < 0) { ALOGE("open failed on %s for index %d", path, i); goto release_fds; } ScopedLocalRef<jobject> jifd(env, jniCreateFileDescriptor(env, fd)); if (jifd.get() == NULL) { close(fd); goto release_fds; } env->SetObjectArrayElement(fds, i, jifd.get()); } // create a pipe to use for unblocking our input thread { // open the path for the write pipes for (int i = 0; i < numOutputs; i++) { fd = open(path, O_WRONLY); if (fd < 0) { ALOGE("open failed on %s for index %d", path, i); goto release_fds; } ScopedLocalRef<jobject> jifd(env, jniCreateFileDescriptor(env, fd)); if (jifd.get() == NULL) { close(fd); goto release_fds; } env->SetObjectArrayElement(fds, i + numInputs, jifd.get()); } // create a pipe to use for unblocking our input thread. The caller should // set numInputs as 0 when there are zero real input threads. if (numInputs > 0) { int pipeFD[2]; if (pipe(pipeFD) == -1) { ALOGE("pipe() failed, errno = %d", errno); Loading @@ -123,21 +107,22 @@ android_server_UsbMidiDevice_open(JNIEnv *env, jobject thiz, jint card, jint dev close(pipeFD[1]); goto release_fds; } env->SetObjectArrayElement(fds, subdevice_count, jifd.get()); // store as last input file descriptor env->SetObjectArrayElement(fds, numInputs - 1, jifd.get()); // store our end of the pipe in mPipeFD env->SetIntField(thiz, sPipeFDField, pipeFD[1]); } return fds; release_fds: for (int i = 0; i < subdevice_count + 1; ++i) { for (int i = 0; i < numInputs + numOutputs; ++i) { ScopedLocalRef<jobject> jifd(env, env->GetObjectArrayElement(fds, i)); if (jifd.get() == NULL) { break; } if (jifd.get() != NULL) { int fd = jniGetFDFromFileDescriptor(env, jifd.get()); close(fd); } } return NULL; } Loading @@ -158,8 +143,8 @@ android_server_UsbMidiDevice_close(JNIEnv *env, jobject thiz, jobjectArray fds) } static JNINativeMethod method_table[] = { { "nativeGetSubdeviceCount", "(II)I", (void*)android_server_UsbMidiDevice_get_subdevice_count }, { "nativeOpen", "(III)[Ljava/io/FileDescriptor;", (void*)android_server_UsbMidiDevice_open }, {"nativeOpen", "(IIII)[Ljava/io/FileDescriptor;", (void *)android_server_UsbMidiDevice_open}, {"nativeClose", "([Ljava/io/FileDescriptor;)V", (void *)android_server_UsbMidiDevice_close}, }; Loading
services/usb/java/com/android/server/usb/UsbAlsaManager.java +5 −2 Original line number Diff line number Diff line Loading @@ -257,6 +257,8 @@ public final class UsbAlsaManager { // look for MIDI devices boolean hasMidi = parser.hasMIDIInterface(); int midiNumInputs = parser.calculateNumMidiInputs(); int midiNumOutputs = parser.calculateNumMidiOutputs(); if (DEBUG) { Slog.d(TAG, "hasMidi: " + hasMidi + " mHasMidiFeature:" + mHasMidiFeature); } Loading Loading @@ -285,7 +287,7 @@ public final class UsbAlsaManager { properties.putParcelable(MidiDeviceInfo.PROPERTY_USB_DEVICE, usbDevice); UsbMidiDevice usbMidiDevice = UsbMidiDevice.create(mContext, properties, cardRec.getCardNum(), 0 /*device*/); cardRec.getCardNum(), 0 /*device*/, midiNumInputs, midiNumOutputs); if (usbMidiDevice != null) { mMidiDevices.put(deviceAddress, usbMidiDevice); } Loading Loading @@ -338,7 +340,8 @@ public final class UsbAlsaManager { com.android.internal.R.string.usb_midi_peripheral_product_name)); properties.putInt(MidiDeviceInfo.PROPERTY_ALSA_CARD, card); properties.putInt(MidiDeviceInfo.PROPERTY_ALSA_DEVICE, device); mPeripheralMidiDevice = UsbMidiDevice.create(mContext, properties, card, device); mPeripheralMidiDevice = UsbMidiDevice.create(mContext, properties, card, device, 1 /* numInputs */, 1 /* numOutputs */); } else if (!enabled && mPeripheralMidiDevice != null) { IoUtils.closeQuietly(mPeripheralMidiDevice); mPeripheralMidiDevice = null; Loading
services/usb/java/com/android/server/usb/UsbMidiDevice.java +80 −72 Original line number Diff line number Diff line Loading @@ -48,8 +48,10 @@ public final class UsbMidiDevice implements Closeable { private final int mAlsaCard; private final int mAlsaDevice; private final int mSubdeviceCount; private final InputReceiverProxy[] mInputPortReceivers; // USB outputs are MIDI inputs private final InputReceiverProxy[] mMidiInputPortReceivers; private final int mNumInputs; private final int mNumOutputs; private MidiDeviceServer mServer; Loading Loading @@ -139,15 +141,14 @@ public final class UsbMidiDevice implements Closeable { } } public static UsbMidiDevice create(Context context, Bundle properties, int card, int device) { // FIXME - support devices with different number of input and output ports int subDeviceCount = nativeGetSubdeviceCount(card, device); if (subDeviceCount <= 0) { Log.e(TAG, "nativeGetSubdeviceCount failed"); return null; } UsbMidiDevice midiDevice = new UsbMidiDevice(card, device, subDeviceCount); /** * Creates an UsbMidiDevice based on the input parameters. Read/Write streams * will be created individually as some devices don't have the same number of * inputs and outputs. */ public static UsbMidiDevice create(Context context, Bundle properties, int card, int device, int numInputs, int numOutputs) { UsbMidiDevice midiDevice = new UsbMidiDevice(card, device, numInputs, numOutputs); if (!midiDevice.register(context, properties)) { IoUtils.closeQuietly(midiDevice); Log.e(TAG, "createDeviceServer failed"); Loading @@ -156,35 +157,40 @@ public final class UsbMidiDevice implements Closeable { return midiDevice; } private UsbMidiDevice(int card, int device, int subdeviceCount) { private UsbMidiDevice(int card, int device, int numInputs, int numOutputs) { mAlsaCard = card; mAlsaDevice = device; mSubdeviceCount = subdeviceCount; mNumInputs = numInputs; mNumOutputs = numOutputs; // FIXME - support devices with different number of input and output ports int inputPortCount = subdeviceCount; mInputPortReceivers = new InputReceiverProxy[inputPortCount]; for (int port = 0; port < inputPortCount; port++) { mInputPortReceivers[port] = new InputReceiverProxy(); // Create MIDI port receivers based on the number of output ports. The // output of USB is the input of MIDI. mMidiInputPortReceivers = new InputReceiverProxy[numOutputs]; for (int port = 0; port < numOutputs; port++) { mMidiInputPortReceivers[port] = new InputReceiverProxy(); } } private boolean openLocked() { // FIXME - support devices with different number of input and output ports FileDescriptor[] fileDescriptors = nativeOpen(mAlsaCard, mAlsaDevice, mSubdeviceCount); int inputStreamCount = mNumInputs; // Create an extra stream for unblocking Os.poll() if (inputStreamCount > 0) { inputStreamCount++; } int outputStreamCount = mNumOutputs; // The resulting file descriptors will be O_RDONLY following by O_WRONLY FileDescriptor[] fileDescriptors = nativeOpen(mAlsaCard, mAlsaDevice, inputStreamCount, outputStreamCount); if (fileDescriptors == null) { Log.e(TAG, "nativeOpen failed"); return false; } mFileDescriptors = fileDescriptors; int inputStreamCount = fileDescriptors.length; // last file descriptor returned from nativeOpen() is only used for unblocking Os.poll() // in our input thread int outputStreamCount = fileDescriptors.length - 1; mPollFDs = new StructPollfd[inputStreamCount]; mInputStreams = new FileInputStream[inputStreamCount]; for (int i = 0; i < inputStreamCount; i++) { FileDescriptor fd = fileDescriptors[i]; StructPollfd pollfd = new StructPollfd(); Loading @@ -196,16 +202,18 @@ public final class UsbMidiDevice implements Closeable { mOutputStreams = new FileOutputStream[outputStreamCount]; mEventSchedulers = new MidiEventScheduler[outputStreamCount]; for (int i = 0; i < outputStreamCount; i++) { mOutputStreams[i] = new FileOutputStream(fileDescriptors[i]); int curOutputStream = 0; for (int i = 0; i < outputStreamCount; i++) { mOutputStreams[i] = new FileOutputStream(fileDescriptors[inputStreamCount + i]); MidiEventScheduler scheduler = new MidiEventScheduler(); mEventSchedulers[i] = scheduler; mInputPortReceivers[i].setReceiver(scheduler.getReceiver()); mMidiInputPortReceivers[i].setReceiver(scheduler.getReceiver()); } final MidiReceiver[] outputReceivers = mServer.getOutputPortReceivers(); if (inputStreamCount > 0) { // Create input thread which will read from all output ports of the physical device new Thread("UsbMidiDevice input thread") { @Override Loading @@ -227,9 +235,8 @@ public final class UsbMidiDevice implements Closeable { } else if ((pfd.revents & OsConstants.POLLIN) != 0) { // clear readable flag pfd.revents = 0; if (index == mInputStreams.length - 1) { // last file descriptor is used only for unblocking Os.poll() // last fd is used only for unblocking Os.poll() break; } Loading @@ -250,6 +257,7 @@ public final class UsbMidiDevice implements Closeable { Log.d(TAG, "input thread exit"); } }.start(); } // Create output thread for each input port of the physical device for (int port = 0; port < outputStreamCount; port++) { Loading Loading @@ -294,7 +302,7 @@ public final class UsbMidiDevice implements Closeable { return false; } mServer = midiManager.createDeviceServer(mInputPortReceivers, mSubdeviceCount, mServer = midiManager.createDeviceServer(mMidiInputPortReceivers, mNumInputs, null, null, properties, MidiDeviceInfo.TYPE_USB, mCallback); if (mServer == null) { return false; Loading @@ -318,7 +326,7 @@ public final class UsbMidiDevice implements Closeable { private void closeLocked() { for (int i = 0; i < mEventSchedulers.length; i++) { mInputPortReceivers[i].setReceiver(null); mMidiInputPortReceivers[i].setReceiver(null); mEventSchedulers[i].close(); } mEventSchedulers = null; Loading Loading @@ -354,7 +362,7 @@ public final class UsbMidiDevice implements Closeable { dump.end(token); } private static native int nativeGetSubdeviceCount(int card, int device); private native FileDescriptor[] nativeOpen(int card, int device, int subdeviceCount); private native FileDescriptor[] nativeOpen(int card, int device, int numInputs, int numOutputs); private native void nativeClose(FileDescriptor[] fileDescriptors); }
services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java +39 −0 Original line number Diff line number Diff line Loading @@ -656,6 +656,45 @@ public final class UsbDescriptorParser { return false; } private int calculateNumMidiPorts(boolean isOutput) { int count = 0; ArrayList<UsbDescriptor> descriptors = getInterfaceDescriptorsForClass(UsbDescriptor.CLASSID_AUDIO); for (UsbDescriptor descriptor : descriptors) { // ensure that this isn't an unrecognized interface descriptor if (descriptor instanceof UsbInterfaceDescriptor) { UsbInterfaceDescriptor interfaceDescr = (UsbInterfaceDescriptor) descriptor; if (interfaceDescr.getUsbSubclass() == UsbDescriptor.AUDIO_MIDISTREAMING) { for (int i = 0; i < interfaceDescr.getNumEndpoints(); i++) { UsbEndpointDescriptor endpoint = interfaceDescr.getEndpointDescriptor(i); // 0 is output, 1 << 7 is input. if ((endpoint.getDirection() == 0) == isOutput) { count++; } } } } else { Log.w(TAG, "Undefined Audio Class Interface l: " + descriptor.getLength() + " t:0x" + Integer.toHexString(descriptor.getType())); } } return count; } /** * @hide */ public int calculateNumMidiInputs() { return calculateNumMidiPorts(false /*isOutput*/); } /** * @hide */ public int calculateNumMidiOutputs() { return calculateNumMidiPorts(true /*isOutput*/); } /** * @hide */ Loading