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, ×tamp, 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