xref: /aosp_15_r20/frameworks/base/media/native/midi/amidi.cpp (revision d57664e9bc4670b3ecf6748a746a57c557b6bc9e)
1*d57664e9SAndroid Build Coastguard Worker /*
2*d57664e9SAndroid Build Coastguard Worker  * Copyright (C) 2018 The Android Open Source Project
3*d57664e9SAndroid Build Coastguard Worker  *
4*d57664e9SAndroid Build Coastguard Worker  * Licensed under the Apache License, Version 2.0 (the "License");
5*d57664e9SAndroid Build Coastguard Worker  * you may not use this file except in compliance with the License.
6*d57664e9SAndroid Build Coastguard Worker  * You may obtain a copy of the License at
7*d57664e9SAndroid Build Coastguard Worker  *
8*d57664e9SAndroid Build Coastguard Worker  *      http://www.apache.org/licenses/LICENSE-2.0
9*d57664e9SAndroid Build Coastguard Worker  *
10*d57664e9SAndroid Build Coastguard Worker  * Unless required by applicable law or agreed to in writing, software
11*d57664e9SAndroid Build Coastguard Worker  * distributed under the License is distributed on an "AS IS" BASIS,
12*d57664e9SAndroid Build Coastguard Worker  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*d57664e9SAndroid Build Coastguard Worker  * See the License for the specific language governing permissions and
14*d57664e9SAndroid Build Coastguard Worker  * limitations under the License.
15*d57664e9SAndroid Build Coastguard Worker  */
16*d57664e9SAndroid Build Coastguard Worker 
17*d57664e9SAndroid Build Coastguard Worker #define LOG_TAG "NativeMIDI"
18*d57664e9SAndroid Build Coastguard Worker 
19*d57664e9SAndroid Build Coastguard Worker #include <poll.h>
20*d57664e9SAndroid Build Coastguard Worker #include <unistd.h>
21*d57664e9SAndroid Build Coastguard Worker 
22*d57664e9SAndroid Build Coastguard Worker #include <binder/Binder.h>
23*d57664e9SAndroid Build Coastguard Worker #include <android_util_Binder.h>
24*d57664e9SAndroid Build Coastguard Worker #include <utils/Log.h>
25*d57664e9SAndroid Build Coastguard Worker 
26*d57664e9SAndroid Build Coastguard Worker #include <core_jni_helpers.h>
27*d57664e9SAndroid Build Coastguard Worker 
28*d57664e9SAndroid Build Coastguard Worker #include "android/media/midi/BpMidiDeviceServer.h"
29*d57664e9SAndroid Build Coastguard Worker #include "MidiDeviceInfo.h"
30*d57664e9SAndroid Build Coastguard Worker 
31*d57664e9SAndroid Build Coastguard Worker #include "include/amidi/AMidi.h"
32*d57664e9SAndroid Build Coastguard Worker #include "amidi_internal.h"
33*d57664e9SAndroid Build Coastguard Worker 
34*d57664e9SAndroid Build Coastguard Worker using namespace android::media::midi;
35*d57664e9SAndroid Build Coastguard Worker 
36*d57664e9SAndroid Build Coastguard Worker using android::IBinder;
37*d57664e9SAndroid Build Coastguard Worker using android::BBinder;
38*d57664e9SAndroid Build Coastguard Worker using android::OK;
39*d57664e9SAndroid Build Coastguard Worker using android::sp;
40*d57664e9SAndroid Build Coastguard Worker using android::status_t;
41*d57664e9SAndroid Build Coastguard Worker using android::base::unique_fd;
42*d57664e9SAndroid Build Coastguard Worker using android::binder::Status;
43*d57664e9SAndroid Build Coastguard Worker 
44*d57664e9SAndroid Build Coastguard Worker struct AMIDI_Port {
45*d57664e9SAndroid Build Coastguard Worker     std::atomic_int     state;      // One of the port status constants below.
46*d57664e9SAndroid Build Coastguard Worker     const AMidiDevice  *device;    // Points to the AMidiDevice associated with the port.
47*d57664e9SAndroid Build Coastguard Worker     sp<IBinder>         binderToken;// The Binder token associated with the port.
48*d57664e9SAndroid Build Coastguard Worker     unique_fd           ufd;        // The unique file descriptor associated with the port.
49*d57664e9SAndroid Build Coastguard Worker };
50*d57664e9SAndroid Build Coastguard Worker 
51*d57664e9SAndroid Build Coastguard Worker /*
52*d57664e9SAndroid Build Coastguard Worker  * Port Status Constants
53*d57664e9SAndroid Build Coastguard Worker  */
54*d57664e9SAndroid Build Coastguard Worker enum {
55*d57664e9SAndroid Build Coastguard Worker     MIDI_PORT_STATE_CLOSED = 0,
56*d57664e9SAndroid Build Coastguard Worker     MIDI_PORT_STATE_OPEN_IDLE,
57*d57664e9SAndroid Build Coastguard Worker     MIDI_PORT_STATE_OPEN_ACTIVE
58*d57664e9SAndroid Build Coastguard Worker };
59*d57664e9SAndroid Build Coastguard Worker 
60*d57664e9SAndroid Build Coastguard Worker /*
61*d57664e9SAndroid Build Coastguard Worker  * Port Type Constants
62*d57664e9SAndroid Build Coastguard Worker  */
63*d57664e9SAndroid Build Coastguard Worker enum {
64*d57664e9SAndroid Build Coastguard Worker     PORTTYPE_OUTPUT = 0,
65*d57664e9SAndroid Build Coastguard Worker     PORTTYPE_INPUT = 1
66*d57664e9SAndroid Build Coastguard Worker };
67*d57664e9SAndroid Build Coastguard Worker 
68*d57664e9SAndroid Build Coastguard Worker /* TRANSFER PACKET FORMAT (as defined in MidiPortImpl.java)
69*d57664e9SAndroid Build Coastguard Worker  *
70*d57664e9SAndroid Build Coastguard Worker  * Transfer packet format is as follows (see MidiOutputPort.mThread.run() to see decomposition):
71*d57664e9SAndroid Build Coastguard Worker  * |oc|md|md| ......... |md|ts|ts|ts|ts|ts|ts|ts|ts|
72*d57664e9SAndroid Build Coastguard Worker  *  ^ +--------------------+-----------------------+
73*d57664e9SAndroid Build Coastguard Worker  *  |  ^                    ^
74*d57664e9SAndroid Build Coastguard Worker  *  |  |                    |
75*d57664e9SAndroid Build Coastguard Worker  *  |  |                    + timestamp (8 bytes)
76*d57664e9SAndroid Build Coastguard Worker  *  |  |
77*d57664e9SAndroid Build Coastguard Worker  *  |  + MIDI data bytes (numBytes bytes)
78*d57664e9SAndroid Build Coastguard Worker  *  |
79*d57664e9SAndroid Build Coastguard Worker  *  + OpCode (AMIDI_OPCODE_DATA)
80*d57664e9SAndroid Build Coastguard Worker  *
81*d57664e9SAndroid Build Coastguard Worker  *  NOTE: The socket pair is configured to use SOCK_SEQPACKET mode.
82*d57664e9SAndroid Build Coastguard Worker  *  SOCK_SEQPACKET, for a sequenced-packet socket that is connection-oriented, preserves message
83*d57664e9SAndroid Build Coastguard Worker  *  boundaries, and delivers messages in the order that they were sent.
84*d57664e9SAndroid Build Coastguard Worker  *  So 'read()' always returns a whole message.
85*d57664e9SAndroid Build Coastguard Worker  */
86*d57664e9SAndroid Build Coastguard Worker #define AMIDI_PACKET_SIZE       1024
87*d57664e9SAndroid Build Coastguard Worker #define AMIDI_PACKET_OVERHEAD   9
88*d57664e9SAndroid Build Coastguard Worker #define AMIDI_BUFFER_SIZE       (AMIDI_PACKET_SIZE - AMIDI_PACKET_OVERHEAD)
89*d57664e9SAndroid Build Coastguard Worker 
90*d57664e9SAndroid Build Coastguard Worker // JNI IDs (see android_media_midi.cpp)
91*d57664e9SAndroid Build Coastguard Worker namespace android { namespace midi {
92*d57664e9SAndroid Build Coastguard Worker //  MidiDevice Fields
93*d57664e9SAndroid Build Coastguard Worker extern jfieldID gFidMidiNativeHandle;         // MidiDevice.mNativeHandle
94*d57664e9SAndroid Build Coastguard Worker extern jfieldID gFidMidiDeviceServerBinder;   // MidiDevice.mDeviceServerBinder
95*d57664e9SAndroid Build Coastguard Worker extern jfieldID gFidMidiDeviceInfo;           // MidiDevice.mDeviceInfo
96*d57664e9SAndroid Build Coastguard Worker 
97*d57664e9SAndroid Build Coastguard Worker //  MidiDeviceInfo Fields
98*d57664e9SAndroid Build Coastguard Worker extern jfieldID mFidMidiDeviceId;             // MidiDeviceInfo.mId
99*d57664e9SAndroid Build Coastguard Worker }}
100*d57664e9SAndroid Build Coastguard Worker using namespace android::midi;
101*d57664e9SAndroid Build Coastguard Worker 
102*d57664e9SAndroid Build Coastguard Worker static std::mutex openMutex; // Ensure that the device can be connected just once to 1 thread
103*d57664e9SAndroid Build Coastguard Worker 
104*d57664e9SAndroid Build Coastguard Worker //// Handy debugging function.
105*d57664e9SAndroid Build Coastguard Worker //static void AMIDI_logBuffer(const uint8_t *data, size_t numBytes) {
106*d57664e9SAndroid Build Coastguard Worker //    for (size_t index = 0; index < numBytes; index++) {
107*d57664e9SAndroid Build Coastguard Worker //      ALOGI("  data @%zu [0x%X]", index, data[index]);
108*d57664e9SAndroid Build Coastguard Worker //    }
109*d57664e9SAndroid Build Coastguard Worker //}
110*d57664e9SAndroid Build Coastguard Worker 
111*d57664e9SAndroid Build Coastguard Worker /*
112*d57664e9SAndroid Build Coastguard Worker  * Device Functions
113*d57664e9SAndroid Build Coastguard Worker  */
114*d57664e9SAndroid Build Coastguard Worker /**
115*d57664e9SAndroid Build Coastguard Worker  * Retrieves information for the native MIDI device.
116*d57664e9SAndroid Build Coastguard Worker  *
117*d57664e9SAndroid Build Coastguard Worker  * device           The Native API token for the device. This value is obtained from the
118*d57664e9SAndroid Build Coastguard Worker  *                  AMidiDevice_fromJava().
119*d57664e9SAndroid Build Coastguard Worker  * outDeviceInfoPtr Receives the associated device info.
120*d57664e9SAndroid Build Coastguard Worker  *
121*d57664e9SAndroid Build Coastguard Worker  * Returns AMEDIA_OK or a negative error code.
122*d57664e9SAndroid Build Coastguard Worker  *  - AMEDIA_ERROR_INVALID_PARAMETER
123*d57664e9SAndroid Build Coastguard Worker  *  AMEDIA_ERROR_UNKNOWN
124*d57664e9SAndroid Build Coastguard Worker  */
AMIDI_getDeviceInfo(const AMidiDevice * device,AMidiDeviceInfo * outDeviceInfoPtr)125*d57664e9SAndroid Build Coastguard Worker static media_status_t AMIDI_getDeviceInfo(const AMidiDevice *device,
126*d57664e9SAndroid Build Coastguard Worker         AMidiDeviceInfo *outDeviceInfoPtr) {
127*d57664e9SAndroid Build Coastguard Worker     if (device == nullptr) {
128*d57664e9SAndroid Build Coastguard Worker         return AMEDIA_ERROR_INVALID_PARAMETER;
129*d57664e9SAndroid Build Coastguard Worker     }
130*d57664e9SAndroid Build Coastguard Worker 
131*d57664e9SAndroid Build Coastguard Worker     MidiDeviceInfo deviceInfo;
132*d57664e9SAndroid Build Coastguard Worker     Status txResult = device->server->getDeviceInfo(&deviceInfo);
133*d57664e9SAndroid Build Coastguard Worker     if (!txResult.isOk()) {
134*d57664e9SAndroid Build Coastguard Worker         ALOGE("%s server exception code: %d", __func__, txResult.exceptionCode());
135*d57664e9SAndroid Build Coastguard Worker         return AMEDIA_ERROR_UNKNOWN;
136*d57664e9SAndroid Build Coastguard Worker     }
137*d57664e9SAndroid Build Coastguard Worker 
138*d57664e9SAndroid Build Coastguard Worker     outDeviceInfoPtr->type = deviceInfo.getType();
139*d57664e9SAndroid Build Coastguard Worker     outDeviceInfoPtr->inputPortCount = deviceInfo.getInputPortNames().size();
140*d57664e9SAndroid Build Coastguard Worker     outDeviceInfoPtr->outputPortCount = deviceInfo.getOutputPortNames().size();
141*d57664e9SAndroid Build Coastguard Worker     outDeviceInfoPtr->defaultProtocol = deviceInfo.getDefaultProtocol();
142*d57664e9SAndroid Build Coastguard Worker 
143*d57664e9SAndroid Build Coastguard Worker     return AMEDIA_OK;
144*d57664e9SAndroid Build Coastguard Worker }
145*d57664e9SAndroid Build Coastguard Worker 
AMidiDevice_fromJava(JNIEnv * env,jobject j_midiDeviceObj,AMidiDevice ** devicePtrPtr)146*d57664e9SAndroid Build Coastguard Worker media_status_t AMIDI_API AMidiDevice_fromJava(JNIEnv *env, jobject j_midiDeviceObj,
147*d57664e9SAndroid Build Coastguard Worker         AMidiDevice** devicePtrPtr)
148*d57664e9SAndroid Build Coastguard Worker {
149*d57664e9SAndroid Build Coastguard Worker     if (j_midiDeviceObj == nullptr) {
150*d57664e9SAndroid Build Coastguard Worker         ALOGE("AMidiDevice_fromJava() invalid MidiDevice object.");
151*d57664e9SAndroid Build Coastguard Worker         return AMEDIA_ERROR_INVALID_OBJECT;
152*d57664e9SAndroid Build Coastguard Worker     }
153*d57664e9SAndroid Build Coastguard Worker 
154*d57664e9SAndroid Build Coastguard Worker     {
155*d57664e9SAndroid Build Coastguard Worker         std::lock_guard<std::mutex> guard(openMutex);
156*d57664e9SAndroid Build Coastguard Worker 
157*d57664e9SAndroid Build Coastguard Worker         long handle = env->GetLongField(j_midiDeviceObj, gFidMidiNativeHandle);
158*d57664e9SAndroid Build Coastguard Worker         if (handle != 0) {
159*d57664e9SAndroid Build Coastguard Worker             // Already opened by someone.
160*d57664e9SAndroid Build Coastguard Worker             return AMEDIA_ERROR_INVALID_OBJECT;
161*d57664e9SAndroid Build Coastguard Worker         }
162*d57664e9SAndroid Build Coastguard Worker 
163*d57664e9SAndroid Build Coastguard Worker         jobject serverBinderObj = env->GetObjectField(j_midiDeviceObj, gFidMidiDeviceServerBinder);
164*d57664e9SAndroid Build Coastguard Worker         sp<IBinder> serverBinder = android::ibinderForJavaObject(env, serverBinderObj);
165*d57664e9SAndroid Build Coastguard Worker         if (serverBinder.get() == nullptr) {
166*d57664e9SAndroid Build Coastguard Worker             ALOGE("AMidiDevice_fromJava couldn't connect to native MIDI server.");
167*d57664e9SAndroid Build Coastguard Worker             return AMEDIA_ERROR_UNKNOWN;
168*d57664e9SAndroid Build Coastguard Worker         }
169*d57664e9SAndroid Build Coastguard Worker 
170*d57664e9SAndroid Build Coastguard Worker         // don't check allocation failures, just abort..
171*d57664e9SAndroid Build Coastguard Worker         AMidiDevice* devicePtr = new AMidiDevice;
172*d57664e9SAndroid Build Coastguard Worker         devicePtr->server = new BpMidiDeviceServer(serverBinder);
173*d57664e9SAndroid Build Coastguard Worker         jobject midiDeviceInfoObj = env->GetObjectField(j_midiDeviceObj, gFidMidiDeviceInfo);
174*d57664e9SAndroid Build Coastguard Worker         devicePtr->deviceId = env->GetIntField(midiDeviceInfoObj, mFidMidiDeviceId);
175*d57664e9SAndroid Build Coastguard Worker 
176*d57664e9SAndroid Build Coastguard Worker         // Synchronize with the associated Java MidiDevice.
177*d57664e9SAndroid Build Coastguard Worker         env->SetLongField(j_midiDeviceObj, gFidMidiNativeHandle, (long)devicePtr);
178*d57664e9SAndroid Build Coastguard Worker         env->GetJavaVM(&devicePtr->javaVM);
179*d57664e9SAndroid Build Coastguard Worker         devicePtr->midiDeviceObj = env->NewGlobalRef(j_midiDeviceObj);
180*d57664e9SAndroid Build Coastguard Worker 
181*d57664e9SAndroid Build Coastguard Worker         if (AMIDI_getDeviceInfo(devicePtr, &devicePtr->deviceInfo) != AMEDIA_OK) {
182*d57664e9SAndroid Build Coastguard Worker             // This is weird, but maybe not fatal?
183*d57664e9SAndroid Build Coastguard Worker             ALOGE("AMidiDevice_fromJava couldn't retrieve attributes of native device.");
184*d57664e9SAndroid Build Coastguard Worker         }
185*d57664e9SAndroid Build Coastguard Worker 
186*d57664e9SAndroid Build Coastguard Worker         *devicePtrPtr = devicePtr;
187*d57664e9SAndroid Build Coastguard Worker     }
188*d57664e9SAndroid Build Coastguard Worker 
189*d57664e9SAndroid Build Coastguard Worker     return AMEDIA_OK;
190*d57664e9SAndroid Build Coastguard Worker }
191*d57664e9SAndroid Build Coastguard Worker 
AMidiDevice_release(const AMidiDevice * device)192*d57664e9SAndroid Build Coastguard Worker media_status_t AMIDI_API AMidiDevice_release(const AMidiDevice *device)
193*d57664e9SAndroid Build Coastguard Worker {
194*d57664e9SAndroid Build Coastguard Worker     if (device == nullptr || device->midiDeviceObj == nullptr) {
195*d57664e9SAndroid Build Coastguard Worker         return AMEDIA_ERROR_INVALID_PARAMETER;
196*d57664e9SAndroid Build Coastguard Worker     }
197*d57664e9SAndroid Build Coastguard Worker 
198*d57664e9SAndroid Build Coastguard Worker     JNIEnv* env;
199*d57664e9SAndroid Build Coastguard Worker     jint err = device->javaVM->GetEnv((void**)&env, JNI_VERSION_1_6);
200*d57664e9SAndroid Build Coastguard Worker     LOG_ALWAYS_FATAL_IF(err != JNI_OK, "AMidiDevice_release Error accessing JNIEnv err:%d", err);
201*d57664e9SAndroid Build Coastguard Worker 
202*d57664e9SAndroid Build Coastguard Worker     // Synchronize with the associated Java MidiDevice.
203*d57664e9SAndroid Build Coastguard Worker     {
204*d57664e9SAndroid Build Coastguard Worker         std::lock_guard<std::mutex> guard(openMutex);
205*d57664e9SAndroid Build Coastguard Worker         long handle = env->GetLongField(device->midiDeviceObj, gFidMidiNativeHandle);
206*d57664e9SAndroid Build Coastguard Worker         if (handle == 0) {
207*d57664e9SAndroid Build Coastguard Worker             // Not opened as native.
208*d57664e9SAndroid Build Coastguard Worker             ALOGE("AMidiDevice_release() device not opened in native client.");
209*d57664e9SAndroid Build Coastguard Worker             return AMEDIA_ERROR_INVALID_OBJECT;
210*d57664e9SAndroid Build Coastguard Worker         }
211*d57664e9SAndroid Build Coastguard Worker 
212*d57664e9SAndroid Build Coastguard Worker         env->SetLongField(device->midiDeviceObj, gFidMidiNativeHandle, 0L);
213*d57664e9SAndroid Build Coastguard Worker     }
214*d57664e9SAndroid Build Coastguard Worker     env->DeleteGlobalRef(device->midiDeviceObj);
215*d57664e9SAndroid Build Coastguard Worker 
216*d57664e9SAndroid Build Coastguard Worker     delete device;
217*d57664e9SAndroid Build Coastguard Worker 
218*d57664e9SAndroid Build Coastguard Worker     return AMEDIA_OK;
219*d57664e9SAndroid Build Coastguard Worker }
220*d57664e9SAndroid Build Coastguard Worker 
AMidiDevice_getType(const AMidiDevice * device)221*d57664e9SAndroid Build Coastguard Worker int32_t AMIDI_API AMidiDevice_getType(const AMidiDevice *device) {
222*d57664e9SAndroid Build Coastguard Worker     if (device == nullptr) {
223*d57664e9SAndroid Build Coastguard Worker         return AMEDIA_ERROR_INVALID_PARAMETER;
224*d57664e9SAndroid Build Coastguard Worker     }
225*d57664e9SAndroid Build Coastguard Worker     return device->deviceInfo.type;
226*d57664e9SAndroid Build Coastguard Worker }
227*d57664e9SAndroid Build Coastguard Worker 
AMidiDevice_getNumInputPorts(const AMidiDevice * device)228*d57664e9SAndroid Build Coastguard Worker ssize_t AMIDI_API AMidiDevice_getNumInputPorts(const AMidiDevice *device) {
229*d57664e9SAndroid Build Coastguard Worker     if (device == nullptr) {
230*d57664e9SAndroid Build Coastguard Worker         return AMEDIA_ERROR_INVALID_PARAMETER;
231*d57664e9SAndroid Build Coastguard Worker     }
232*d57664e9SAndroid Build Coastguard Worker     return device->deviceInfo.inputPortCount;
233*d57664e9SAndroid Build Coastguard Worker }
234*d57664e9SAndroid Build Coastguard Worker 
AMidiDevice_getNumOutputPorts(const AMidiDevice * device)235*d57664e9SAndroid Build Coastguard Worker ssize_t AMIDI_API AMidiDevice_getNumOutputPorts(const AMidiDevice *device) {
236*d57664e9SAndroid Build Coastguard Worker     if (device == nullptr) {
237*d57664e9SAndroid Build Coastguard Worker         return AMEDIA_ERROR_INVALID_PARAMETER;
238*d57664e9SAndroid Build Coastguard Worker     }
239*d57664e9SAndroid Build Coastguard Worker     return device->deviceInfo.outputPortCount;
240*d57664e9SAndroid Build Coastguard Worker }
241*d57664e9SAndroid Build Coastguard Worker 
AMidiDevice_getDefaultProtocol(const AMidiDevice * device)242*d57664e9SAndroid Build Coastguard Worker AMidiDevice_Protocol AMIDI_API AMidiDevice_getDefaultProtocol(const AMidiDevice *device) {
243*d57664e9SAndroid Build Coastguard Worker     if (device == nullptr) {
244*d57664e9SAndroid Build Coastguard Worker         return AMIDI_DEVICE_PROTOCOL_UNKNOWN;
245*d57664e9SAndroid Build Coastguard Worker     }
246*d57664e9SAndroid Build Coastguard Worker     return static_cast<AMidiDevice_Protocol>(device->deviceInfo.defaultProtocol);
247*d57664e9SAndroid Build Coastguard Worker }
248*d57664e9SAndroid Build Coastguard Worker 
249*d57664e9SAndroid Build Coastguard Worker /*
250*d57664e9SAndroid Build Coastguard Worker  * Port Helpers
251*d57664e9SAndroid Build Coastguard Worker  */
AMIDI_openPort(const AMidiDevice * device,int32_t portNumber,int type,AMIDI_Port ** portPtr)252*d57664e9SAndroid Build Coastguard Worker static media_status_t AMIDI_openPort(const AMidiDevice *device, int32_t portNumber, int type,
253*d57664e9SAndroid Build Coastguard Worker         AMIDI_Port **portPtr) {
254*d57664e9SAndroid Build Coastguard Worker     if (device == nullptr) {
255*d57664e9SAndroid Build Coastguard Worker         return AMEDIA_ERROR_INVALID_PARAMETER;
256*d57664e9SAndroid Build Coastguard Worker     }
257*d57664e9SAndroid Build Coastguard Worker 
258*d57664e9SAndroid Build Coastguard Worker     sp<BBinder> portToken(new BBinder());
259*d57664e9SAndroid Build Coastguard Worker     unique_fd ufd;
260*d57664e9SAndroid Build Coastguard Worker     Status txResult = type == PORTTYPE_OUTPUT
261*d57664e9SAndroid Build Coastguard Worker             ? device->server->openOutputPort(portToken, portNumber, &ufd)
262*d57664e9SAndroid Build Coastguard Worker             : device->server->openInputPort(portToken, portNumber, &ufd);
263*d57664e9SAndroid Build Coastguard Worker     if (!txResult.isOk()) {
264*d57664e9SAndroid Build Coastguard Worker         ALOGE("%s server exception code: %d", __func__, txResult.exceptionCode());
265*d57664e9SAndroid Build Coastguard Worker         return AMEDIA_ERROR_UNKNOWN;
266*d57664e9SAndroid Build Coastguard Worker     }
267*d57664e9SAndroid Build Coastguard Worker 
268*d57664e9SAndroid Build Coastguard Worker     AMIDI_Port *port = new AMIDI_Port;
269*d57664e9SAndroid Build Coastguard Worker     port->state = MIDI_PORT_STATE_OPEN_IDLE;
270*d57664e9SAndroid Build Coastguard Worker     port->device = device;
271*d57664e9SAndroid Build Coastguard Worker     port->binderToken = portToken;
272*d57664e9SAndroid Build Coastguard Worker     port->ufd = std::move(ufd);
273*d57664e9SAndroid Build Coastguard Worker 
274*d57664e9SAndroid Build Coastguard Worker     *portPtr = port;
275*d57664e9SAndroid Build Coastguard Worker 
276*d57664e9SAndroid Build Coastguard Worker     return AMEDIA_OK;
277*d57664e9SAndroid Build Coastguard Worker }
278*d57664e9SAndroid Build Coastguard Worker 
AMIDI_closePort(AMIDI_Port * port)279*d57664e9SAndroid Build Coastguard Worker static void AMIDI_closePort(AMIDI_Port *port) {
280*d57664e9SAndroid Build Coastguard Worker     if (port == nullptr) {
281*d57664e9SAndroid Build Coastguard Worker         return;
282*d57664e9SAndroid Build Coastguard Worker     }
283*d57664e9SAndroid Build Coastguard Worker 
284*d57664e9SAndroid Build Coastguard Worker     int portState = MIDI_PORT_STATE_OPEN_IDLE;
285*d57664e9SAndroid Build Coastguard Worker     while (!port->state.compare_exchange_weak(portState, MIDI_PORT_STATE_CLOSED)) {
286*d57664e9SAndroid Build Coastguard Worker         if (portState == MIDI_PORT_STATE_CLOSED) {
287*d57664e9SAndroid Build Coastguard Worker             return; // Already closed
288*d57664e9SAndroid Build Coastguard Worker         }
289*d57664e9SAndroid Build Coastguard Worker     }
290*d57664e9SAndroid Build Coastguard Worker 
291*d57664e9SAndroid Build Coastguard Worker     Status txResult = port->device->server->closePort(port->binderToken);
292*d57664e9SAndroid Build Coastguard Worker     if (!txResult.isOk()) {
293*d57664e9SAndroid Build Coastguard Worker         ALOGE("%s server exception code: %d", __func__, txResult.exceptionCode());
294*d57664e9SAndroid Build Coastguard Worker     }
295*d57664e9SAndroid Build Coastguard Worker 
296*d57664e9SAndroid Build Coastguard Worker     delete port;
297*d57664e9SAndroid Build Coastguard Worker }
298*d57664e9SAndroid Build Coastguard Worker 
299*d57664e9SAndroid Build Coastguard Worker /*
300*d57664e9SAndroid Build Coastguard Worker  * Output (receiving) API
301*d57664e9SAndroid Build Coastguard Worker  */
AMidiOutputPort_open(const AMidiDevice * device,int32_t portNumber,AMidiOutputPort ** outOutputPortPtr)302*d57664e9SAndroid Build Coastguard Worker media_status_t AMIDI_API AMidiOutputPort_open(const AMidiDevice *device, int32_t portNumber,
303*d57664e9SAndroid Build Coastguard Worker         AMidiOutputPort **outOutputPortPtr) {
304*d57664e9SAndroid Build Coastguard Worker     return AMIDI_openPort(device, portNumber, PORTTYPE_OUTPUT, (AMIDI_Port**)outOutputPortPtr);
305*d57664e9SAndroid Build Coastguard Worker }
306*d57664e9SAndroid Build Coastguard Worker 
307*d57664e9SAndroid Build Coastguard Worker /*
308*d57664e9SAndroid Build Coastguard Worker  *  A little RAII (https://en.wikipedia.org/wiki/Resource_acquisition_is_initialization)
309*d57664e9SAndroid Build Coastguard Worker  *  class to ensure that the port state is correct irrespective of errors.
310*d57664e9SAndroid Build Coastguard Worker  */
311*d57664e9SAndroid Build Coastguard Worker class MidiReceiver {
312*d57664e9SAndroid Build Coastguard Worker public:
MidiReceiver(AMIDI_Port * port)313*d57664e9SAndroid Build Coastguard Worker     MidiReceiver(AMIDI_Port *port) : mPort(port) {}
314*d57664e9SAndroid Build Coastguard Worker 
~MidiReceiver()315*d57664e9SAndroid Build Coastguard Worker     ~MidiReceiver() {
316*d57664e9SAndroid Build Coastguard Worker         // flag the port state to idle
317*d57664e9SAndroid Build Coastguard Worker         mPort->state.store(MIDI_PORT_STATE_OPEN_IDLE);
318*d57664e9SAndroid Build Coastguard Worker     }
319*d57664e9SAndroid Build Coastguard Worker 
receive(int32_t * opcodePtr,uint8_t * buffer,size_t maxBytes,size_t * numBytesReceivedPtr,int64_t * timestampPtr)320*d57664e9SAndroid Build Coastguard Worker     ssize_t receive(int32_t *opcodePtr, uint8_t *buffer, size_t maxBytes,
321*d57664e9SAndroid Build Coastguard Worker             size_t *numBytesReceivedPtr, int64_t *timestampPtr) {
322*d57664e9SAndroid Build Coastguard Worker         int portState = MIDI_PORT_STATE_OPEN_IDLE;
323*d57664e9SAndroid Build Coastguard Worker         // check to see if the port is idle, then set to active
324*d57664e9SAndroid Build Coastguard Worker         if (!mPort->state.compare_exchange_strong(portState, MIDI_PORT_STATE_OPEN_ACTIVE)) {
325*d57664e9SAndroid Build Coastguard Worker             // The port not idle or has been closed.
326*d57664e9SAndroid Build Coastguard Worker             return AMEDIA_ERROR_UNKNOWN;
327*d57664e9SAndroid Build Coastguard Worker         }
328*d57664e9SAndroid Build Coastguard Worker 
329*d57664e9SAndroid Build Coastguard Worker         struct pollfd checkFds[1] = { { mPort->ufd, POLLIN, 0 } };
330*d57664e9SAndroid Build Coastguard Worker         if (poll(checkFds, 1, 0) < 1) {
331*d57664e9SAndroid Build Coastguard Worker             // Nothing there
332*d57664e9SAndroid Build Coastguard Worker             return 0;
333*d57664e9SAndroid Build Coastguard Worker         }
334*d57664e9SAndroid Build Coastguard Worker 
335*d57664e9SAndroid Build Coastguard Worker         uint8_t readBuffer[AMIDI_PACKET_SIZE];
336*d57664e9SAndroid Build Coastguard Worker         ssize_t readCount = TEMP_FAILURE_RETRY(read(mPort->ufd, readBuffer, sizeof(readBuffer)));
337*d57664e9SAndroid Build Coastguard Worker         if (readCount < 1) {
338*d57664e9SAndroid Build Coastguard Worker             return  AMEDIA_ERROR_UNKNOWN;
339*d57664e9SAndroid Build Coastguard Worker         }
340*d57664e9SAndroid Build Coastguard Worker 
341*d57664e9SAndroid Build Coastguard Worker         // see Packet Format definition at the top of this file.
342*d57664e9SAndroid Build Coastguard Worker         size_t numMessageBytes = 0;
343*d57664e9SAndroid Build Coastguard Worker         *opcodePtr = readBuffer[0];
344*d57664e9SAndroid Build Coastguard Worker         if (*opcodePtr == AMIDI_OPCODE_DATA && readCount >= AMIDI_PACKET_OVERHEAD) {
345*d57664e9SAndroid Build Coastguard Worker             numMessageBytes = readCount - AMIDI_PACKET_OVERHEAD;
346*d57664e9SAndroid Build Coastguard Worker             numMessageBytes = std::min(maxBytes, numMessageBytes);
347*d57664e9SAndroid Build Coastguard Worker             memcpy(buffer, readBuffer + 1, numMessageBytes);
348*d57664e9SAndroid Build Coastguard Worker             if (timestampPtr != nullptr) {
349*d57664e9SAndroid Build Coastguard Worker                 memcpy(timestampPtr, readBuffer + readCount - sizeof(uint64_t),
350*d57664e9SAndroid Build Coastguard Worker                         sizeof(*timestampPtr));
351*d57664e9SAndroid Build Coastguard Worker             }
352*d57664e9SAndroid Build Coastguard Worker         }
353*d57664e9SAndroid Build Coastguard Worker         *numBytesReceivedPtr = numMessageBytes;
354*d57664e9SAndroid Build Coastguard Worker         return 1;
355*d57664e9SAndroid Build Coastguard Worker     }
356*d57664e9SAndroid Build Coastguard Worker 
357*d57664e9SAndroid Build Coastguard Worker private:
358*d57664e9SAndroid Build Coastguard Worker     AMIDI_Port *mPort;
359*d57664e9SAndroid Build Coastguard Worker };
360*d57664e9SAndroid Build Coastguard Worker 
AMidiOutputPort_receive(const AMidiOutputPort * outputPort,int32_t * opcodePtr,uint8_t * buffer,size_t maxBytes,size_t * numBytesReceivedPtr,int64_t * timestampPtr)361*d57664e9SAndroid Build Coastguard Worker ssize_t AMIDI_API AMidiOutputPort_receive(const AMidiOutputPort *outputPort, int32_t *opcodePtr,
362*d57664e9SAndroid Build Coastguard Worker          uint8_t *buffer, size_t maxBytes, size_t* numBytesReceivedPtr, int64_t *timestampPtr) {
363*d57664e9SAndroid Build Coastguard Worker 
364*d57664e9SAndroid Build Coastguard Worker     if (outputPort == nullptr || buffer == nullptr) {
365*d57664e9SAndroid Build Coastguard Worker         return -EINVAL;
366*d57664e9SAndroid Build Coastguard Worker     }
367*d57664e9SAndroid Build Coastguard Worker 
368*d57664e9SAndroid Build Coastguard Worker    return MidiReceiver((AMIDI_Port*)outputPort).receive(opcodePtr, buffer, maxBytes,
369*d57664e9SAndroid Build Coastguard Worker            numBytesReceivedPtr, timestampPtr);
370*d57664e9SAndroid Build Coastguard Worker }
371*d57664e9SAndroid Build Coastguard Worker 
AMidiOutputPort_close(const AMidiOutputPort * outputPort)372*d57664e9SAndroid Build Coastguard Worker void AMIDI_API AMidiOutputPort_close(const AMidiOutputPort *outputPort) {
373*d57664e9SAndroid Build Coastguard Worker     AMIDI_closePort((AMIDI_Port*)outputPort);
374*d57664e9SAndroid Build Coastguard Worker }
375*d57664e9SAndroid Build Coastguard Worker 
376*d57664e9SAndroid Build Coastguard Worker /*
377*d57664e9SAndroid Build Coastguard Worker  * Input (sending) API
378*d57664e9SAndroid Build Coastguard Worker  */
AMidiInputPort_open(const AMidiDevice * device,int32_t portNumber,AMidiInputPort ** outInputPortPtr)379*d57664e9SAndroid Build Coastguard Worker media_status_t AMIDI_API AMidiInputPort_open(const AMidiDevice *device, int32_t portNumber,
380*d57664e9SAndroid Build Coastguard Worker         AMidiInputPort **outInputPortPtr) {
381*d57664e9SAndroid Build Coastguard Worker     return AMIDI_openPort(device, portNumber, PORTTYPE_INPUT, (AMIDI_Port**)outInputPortPtr);
382*d57664e9SAndroid Build Coastguard Worker }
383*d57664e9SAndroid Build Coastguard Worker 
AMidiInputPort_close(const AMidiInputPort * inputPort)384*d57664e9SAndroid Build Coastguard Worker void AMIDI_API AMidiInputPort_close(const AMidiInputPort *inputPort) {
385*d57664e9SAndroid Build Coastguard Worker     AMIDI_closePort((AMIDI_Port*)inputPort);
386*d57664e9SAndroid Build Coastguard Worker }
387*d57664e9SAndroid Build Coastguard Worker 
AMIDI_makeSendBuffer(uint8_t * buffer,const uint8_t * data,size_t numBytes,uint64_t timestamp)388*d57664e9SAndroid Build Coastguard Worker static ssize_t AMIDI_makeSendBuffer(
389*d57664e9SAndroid Build Coastguard Worker         uint8_t *buffer, const uint8_t *data, size_t numBytes, uint64_t timestamp) {
390*d57664e9SAndroid Build Coastguard Worker     // Error checking will happen in the caller since this isn't an API function.
391*d57664e9SAndroid Build Coastguard Worker     buffer[0] = AMIDI_OPCODE_DATA;
392*d57664e9SAndroid Build Coastguard Worker     memcpy(buffer + 1, data, numBytes);
393*d57664e9SAndroid Build Coastguard Worker     memcpy(buffer + 1 + numBytes, &timestamp, sizeof(timestamp));
394*d57664e9SAndroid Build Coastguard Worker     return numBytes + AMIDI_PACKET_OVERHEAD;
395*d57664e9SAndroid Build Coastguard Worker }
396*d57664e9SAndroid Build Coastguard Worker 
AMidiInputPort_send(const AMidiInputPort * inputPort,const uint8_t * buffer,size_t numBytes)397*d57664e9SAndroid Build Coastguard Worker ssize_t AMIDI_API AMidiInputPort_send(const AMidiInputPort *inputPort, const uint8_t *buffer,
398*d57664e9SAndroid Build Coastguard Worker                             size_t numBytes) {
399*d57664e9SAndroid Build Coastguard Worker     return AMidiInputPort_sendWithTimestamp(inputPort, buffer, numBytes, 0);
400*d57664e9SAndroid Build Coastguard Worker }
401*d57664e9SAndroid Build Coastguard Worker 
AMidiInputPort_sendWithTimestamp(const AMidiInputPort * inputPort,const uint8_t * data,size_t numBytes,int64_t timestamp)402*d57664e9SAndroid Build Coastguard Worker ssize_t AMIDI_API AMidiInputPort_sendWithTimestamp(const AMidiInputPort *inputPort,
403*d57664e9SAndroid Build Coastguard Worker         const uint8_t *data, size_t numBytes, int64_t timestamp) {
404*d57664e9SAndroid Build Coastguard Worker     if (inputPort == nullptr || data == nullptr || numBytes < 0 || timestamp < 0) {
405*d57664e9SAndroid Build Coastguard Worker         return AMEDIA_ERROR_INVALID_PARAMETER;
406*d57664e9SAndroid Build Coastguard Worker     }
407*d57664e9SAndroid Build Coastguard Worker 
408*d57664e9SAndroid Build Coastguard Worker     if (numBytes == 0) {
409*d57664e9SAndroid Build Coastguard Worker         return 0;
410*d57664e9SAndroid Build Coastguard Worker     }
411*d57664e9SAndroid Build Coastguard Worker 
412*d57664e9SAndroid Build Coastguard Worker     // AMIDI_logBuffer(data, numBytes);
413*d57664e9SAndroid Build Coastguard Worker 
414*d57664e9SAndroid Build Coastguard Worker     uint8_t writeBuffer[AMIDI_BUFFER_SIZE + AMIDI_PACKET_OVERHEAD];
415*d57664e9SAndroid Build Coastguard Worker     size_t numSent = 0;
416*d57664e9SAndroid Build Coastguard Worker     while (numSent < numBytes) {
417*d57664e9SAndroid Build Coastguard Worker         size_t blockSize = AMIDI_BUFFER_SIZE;
418*d57664e9SAndroid Build Coastguard Worker         blockSize = std::min(blockSize, numBytes - numSent);
419*d57664e9SAndroid Build Coastguard Worker 
420*d57664e9SAndroid Build Coastguard Worker         ssize_t numTransferBytes =
421*d57664e9SAndroid Build Coastguard Worker                 AMIDI_makeSendBuffer(writeBuffer, data + numSent, blockSize, timestamp);
422*d57664e9SAndroid Build Coastguard Worker         ssize_t numWritten = TEMP_FAILURE_RETRY(write(((AMIDI_Port*)inputPort)->ufd, writeBuffer,
423*d57664e9SAndroid Build Coastguard Worker                                                       numTransferBytes));
424*d57664e9SAndroid Build Coastguard Worker         if (numWritten < 0) {
425*d57664e9SAndroid Build Coastguard Worker             break;  // error so bail out.
426*d57664e9SAndroid Build Coastguard Worker         }
427*d57664e9SAndroid Build Coastguard Worker         if (numWritten < numTransferBytes) {
428*d57664e9SAndroid Build Coastguard Worker             ALOGE("AMidiInputPort_sendWithTimestamp Couldn't write MIDI data buffer."
429*d57664e9SAndroid Build Coastguard Worker                   " requested:%zu, written%zu",numTransferBytes, numWritten);
430*d57664e9SAndroid Build Coastguard Worker             break;  // bail
431*d57664e9SAndroid Build Coastguard Worker         }
432*d57664e9SAndroid Build Coastguard Worker 
433*d57664e9SAndroid Build Coastguard Worker         numSent += numWritten  - AMIDI_PACKET_OVERHEAD;
434*d57664e9SAndroid Build Coastguard Worker     }
435*d57664e9SAndroid Build Coastguard Worker 
436*d57664e9SAndroid Build Coastguard Worker     return numSent;
437*d57664e9SAndroid Build Coastguard Worker }
438*d57664e9SAndroid Build Coastguard Worker 
AMidiInputPort_sendFlush(const AMidiInputPort * inputPort)439*d57664e9SAndroid Build Coastguard Worker media_status_t AMIDI_API AMidiInputPort_sendFlush(const AMidiInputPort *inputPort) {
440*d57664e9SAndroid Build Coastguard Worker     if (inputPort == nullptr) {
441*d57664e9SAndroid Build Coastguard Worker         return AMEDIA_ERROR_INVALID_PARAMETER;
442*d57664e9SAndroid Build Coastguard Worker     }
443*d57664e9SAndroid Build Coastguard Worker 
444*d57664e9SAndroid Build Coastguard Worker     uint8_t opCode = AMIDI_OPCODE_FLUSH;
445*d57664e9SAndroid Build Coastguard Worker     ssize_t numTransferBytes = 1;
446*d57664e9SAndroid Build Coastguard Worker     ssize_t numWritten = TEMP_FAILURE_RETRY(write(((AMIDI_Port*)inputPort)->ufd, &opCode,
447*d57664e9SAndroid Build Coastguard Worker                                                   numTransferBytes));
448*d57664e9SAndroid Build Coastguard Worker 
449*d57664e9SAndroid Build Coastguard Worker     if (numWritten < numTransferBytes) {
450*d57664e9SAndroid Build Coastguard Worker         ALOGE("AMidiInputPort_flush Couldn't write MIDI flush. requested:%zd, written:%zd",
451*d57664e9SAndroid Build Coastguard Worker                 numTransferBytes, numWritten);
452*d57664e9SAndroid Build Coastguard Worker         return AMEDIA_ERROR_UNSUPPORTED;
453*d57664e9SAndroid Build Coastguard Worker     }
454*d57664e9SAndroid Build Coastguard Worker 
455*d57664e9SAndroid Build Coastguard Worker     return AMEDIA_OK;
456*d57664e9SAndroid Build Coastguard Worker }
457*d57664e9SAndroid Build Coastguard Worker 
458