blob: a9b498ffe0c801bf9647be176376e4618c26c900 [file] [log] [blame]
Mikhail Naganovc276c592016-08-31 18:08:10 -07001/*
Paul McLean8a3e33b2017-03-14 15:20:51 -07002 * Copyright (C) 2018 The Android Open Source Project
Mikhail Naganovc276c592016-08-31 18:08:10 -07003 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#define LOG_TAG "NativeMIDI"
18
19#include <poll.h>
20#include <unistd.h>
21
22#include <binder/Binder.h>
Paul McLean8a3e33b2017-03-14 15:20:51 -070023#include <android_util_Binder.h>
Mikhail Naganovc276c592016-08-31 18:08:10 -070024#include <utils/Log.h>
25
Paul McLean8a3e33b2017-03-14 15:20:51 -070026#include <core_jni_helpers.h>
27
Mikhail Naganovc276c592016-08-31 18:08:10 -070028#include "android/media/midi/BpMidiDeviceServer.h"
29#include "media/MidiDeviceInfo.h"
Mikhail Naganovc276c592016-08-31 18:08:10 -070030
Paul McLean8a3e33b2017-03-14 15:20:51 -070031#include "include/midi.h"
Paul McLean71f672b2017-03-05 08:12:18 -070032#include "midi_internal.h"
Mikhail Naganovc276c592016-08-31 18:08:10 -070033
Paul McLean8a3e33b2017-03-14 15:20:51 -070034using namespace android::media::midi;
35
Mikhail Naganovc276c592016-08-31 18:08:10 -070036using android::IBinder;
37using android::BBinder;
38using android::OK;
39using android::sp;
40using android::status_t;
41using android::base::unique_fd;
42using android::binder::Status;
Paul McLean71f672b2017-03-05 08:12:18 -070043
44struct AMIDI_Port {
Paul McLean8a3e33b2017-03-14 15:20:51 -070045 std::atomic_int state; // One of the port status constants below.
46 const AMidiDevice *device; // Points to the AMidiDevice associated with the port.
47 sp<IBinder> binderToken;// The Binder token associated with the port.
48 unique_fd ufd; // The unique file descriptor associated with the port.
Paul McLean71f672b2017-03-05 08:12:18 -070049};
Mikhail Naganovc276c592016-08-31 18:08:10 -070050
Paul McLean8a3e33b2017-03-14 15:20:51 -070051/*
52 * Port Status Constants
53 */
Paul McLean71f672b2017-03-05 08:12:18 -070054enum {
55 MIDI_PORT_STATE_CLOSED = 0,
56 MIDI_PORT_STATE_OPEN_IDLE,
57 MIDI_PORT_STATE_OPEN_ACTIVE
58};
59
Paul McLean8a3e33b2017-03-14 15:20:51 -070060/*
61 * Port Type Constants
62 */
Paul McLean71f672b2017-03-05 08:12:18 -070063enum {
64 PORTTYPE_OUTPUT = 0,
65 PORTTYPE_INPUT = 1
66};
67
Mikhail Naganovc276c592016-08-31 18:08:10 -070068/* TRANSFER PACKET FORMAT (as defined in MidiPortImpl.java)
69 *
70 * Transfer packet format is as follows (see MidiOutputPort.mThread.run() to see decomposition):
71 * |oc|md|md| ......... |md|ts|ts|ts|ts|ts|ts|ts|ts|
72 * ^ +--------------------+-----------------------+
73 * | ^ ^
74 * | | |
75 * | | + timestamp (8 bytes)
76 * | |
77 * | + MIDI data bytes (numBytes bytes)
78 * |
79 * + OpCode (AMIDI_OPCODE_DATA)
80 *
81 * NOTE: The socket pair is configured to use SOCK_SEQPACKET mode.
82 * SOCK_SEQPACKET, for a sequenced-packet socket that is connection-oriented, preserves message
83 * boundaries, and delivers messages in the order that they were sent.
84 * So 'read()' always returns a whole message.
85 */
Paul McLean8a3e33b2017-03-14 15:20:51 -070086#define AMIDI_PACKET_SIZE 1024
87#define AMIDI_PACKET_OVERHEAD 9
88#define AMIDI_BUFFER_SIZE (AMIDI_PACKET_SIZE - AMIDI_PACKET_OVERHEAD)
89
90// MidiDevice Fields
91static jobject deviceClassGlobalRef = nullptr; // A GlobalRef for MidiDevice Class
92static jfieldID fidDeviceClosed = nullptr; // MidiDevice.mIsDeviceClosed
93static jfieldID fidNativeHandle = nullptr; // MidiDevice.mNativeHandle
94static jfieldID fidDeviceServerBinder = nullptr; // MidiDevice.mDeviceServerBinder
95static jfieldID fidDeviceInfo = nullptr; // MidiDevice.mDeviceInfo
96
97// MidiDeviceInfo Fields
98static jobject deviceInfoClassGlobalRef = nullptr; // A GlobalRef for MidiDeviceInfoClass
99static jfieldID fidDeviceId = nullptr; // MidiDeviceInfo.mId
100
101static std::mutex openMutex; // Ensure that the device can be connected just once to 1 thread
102
103static void AMIDI_initJNI(JNIEnv *env) {
104 jclass deviceClass = android::FindClassOrDie(env, "android/media/midi/MidiDevice");
105 deviceClassGlobalRef = env->NewGlobalRef(deviceClass);
106
107 // MidiDevice Field IDs
108 fidDeviceClosed = android::GetFieldIDOrDie(env, deviceClass, "mIsDeviceClosed", "Z");
109 fidNativeHandle = android::GetFieldIDOrDie(env, deviceClass, "mNativeHandle", "J");
110 fidDeviceServerBinder = android::GetFieldIDOrDie(env, deviceClass,
111 "mDeviceServerBinder", "Landroid/os/IBinder;");
112 fidDeviceInfo = android::GetFieldIDOrDie(env, deviceClass,
113 "mDeviceInfo", "Landroid/media/midi/MidiDeviceInfo;");
114
115 // MidiDeviceInfo Field IDs
116 jclass deviceInfoClass = android::FindClassOrDie(env, "android/media/midi/MidiDeviceInfo");
117 deviceInfoClassGlobalRef = env->NewGlobalRef(deviceInfoClass);
118 fidDeviceId = android::GetFieldIDOrDie(env, deviceInfoClass, "mId", "I");
119}
120
121//// Handy debugging function.
122//static void AMIDI_logBuffer(const uint8_t *data, size_t numBytes) {
123// for (size_t index = 0; index < numBytes; index++) {
124// ALOGI(" data @%zu [0x%X]", index, data[index]);
125// }
126//}
Mikhail Naganovc276c592016-08-31 18:08:10 -0700127
Paul McLean71f672b2017-03-05 08:12:18 -0700128/*
129 * Device Functions
130 */
Paul McLean8a3e33b2017-03-14 15:20:51 -0700131/**
132 * Retrieves information for the native MIDI device.
133 *
134 * device The Native API token for the device. This value is obtained from the
135 * AMidiDevice_fromJava().
136 * outDeviceInfoPtr Receives the associated device info.
137 *
138 * Returns AMEDIA_OK or a negative error code.
139 * - AMEDIA_ERROR_INVALID_PARAMETER
140 * AMEDIA_ERROR_UNKNOWN
141 */
142static media_status_t AMIDI_API AMIDI_getDeviceInfo(const AMidiDevice *device,
143 AMidiDeviceInfo *outDeviceInfoPtr) {
144 if (device == nullptr) {
145 return AMEDIA_ERROR_INVALID_PARAMETER;
146 }
147
Mikhail Naganovc276c592016-08-31 18:08:10 -0700148 MidiDeviceInfo deviceInfo;
Paul McLean71f672b2017-03-05 08:12:18 -0700149 Status txResult = device->server->getDeviceInfo(&deviceInfo);
Mikhail Naganovc276c592016-08-31 18:08:10 -0700150 if (!txResult.isOk()) {
151 ALOGE("AMIDI_getDeviceInfo transaction error: %d", txResult.transactionError());
Paul McLean8a3e33b2017-03-14 15:20:51 -0700152 return AMEDIA_ERROR_UNKNOWN;
Mikhail Naganovc276c592016-08-31 18:08:10 -0700153 }
154
Paul McLean8a3e33b2017-03-14 15:20:51 -0700155 outDeviceInfoPtr->type = deviceInfo.getType();
156 outDeviceInfoPtr->inputPortCount = deviceInfo.getInputPortNames().size();
157 outDeviceInfoPtr->outputPortCount = deviceInfo.getOutputPortNames().size();
Paul McLean71f672b2017-03-05 08:12:18 -0700158
Paul McLean8a3e33b2017-03-14 15:20:51 -0700159 return AMEDIA_OK;
160}
161
162media_status_t AMIDI_API AMidiDevice_fromJava(JNIEnv *env, jobject j_midiDeviceObj,
163 AMidiDevice** devicePtrPtr)
164{
165 // Ensures JNI initialization is performed just once.
166 static std::once_flag initCallFlag;
167 std::call_once(initCallFlag, AMIDI_initJNI, env);
168
169 if (j_midiDeviceObj == nullptr) {
170 ALOGE("AMidiDevice_fromJava() invalid MidiDevice object.");
171 return AMEDIA_ERROR_INVALID_OBJECT;
172 }
173
174 {
175 std::lock_guard<std::mutex> guard(openMutex);
176
177 long handle = env->GetLongField(j_midiDeviceObj, fidNativeHandle);
178 if (handle != 0) {
179 // Already opened by someone.
180 return AMEDIA_ERROR_INVALID_OBJECT;
181 }
182
183 jobject serverBinderObj = env->GetObjectField(j_midiDeviceObj, fidDeviceServerBinder);
184 sp<IBinder> serverBinder = android::ibinderForJavaObject(env, serverBinderObj);
185 if (serverBinder.get() == nullptr) {
186 ALOGE("AMidiDevice_fromJava couldn't connect to native MIDI server.");
187 return AMEDIA_ERROR_UNKNOWN;
188 }
189
190 // don't check allocation failures, just abort..
191 AMidiDevice* devicePtr = new AMidiDevice;
192 devicePtr->server = new BpMidiDeviceServer(serverBinder);
193 jobject midiDeviceInfoObj = env->GetObjectField(j_midiDeviceObj, fidDeviceInfo);
194 devicePtr->deviceId = env->GetIntField(midiDeviceInfoObj, fidDeviceId);
195
196 // Synchronize with the associated Java MidiDevice.
197 env->SetLongField(j_midiDeviceObj, fidNativeHandle, (long)devicePtr);
198 env->GetJavaVM(&devicePtr->javaVM);
199 devicePtr->midiDeviceObj = env->NewGlobalRef(j_midiDeviceObj);
200
201 if (AMIDI_getDeviceInfo(devicePtr, &devicePtr->deviceInfo) != AMEDIA_OK) {
202 // This is weird, but maybe not fatal?
203 ALOGE("AMidiDevice_fromJava couldn't retrieve attributes of native device.");
204 }
205
206 *devicePtrPtr = devicePtr;
207 }
208
209 return AMEDIA_OK;
210}
211
212media_status_t AMIDI_API AMidiDevice_release(const AMidiDevice *device)
213{
214 if (device == nullptr || device->midiDeviceObj == nullptr) {
215 return AMEDIA_ERROR_INVALID_PARAMETER;
216 }
217
218 JNIEnv* env;
219 jint err = device->javaVM->GetEnv((void**)&env, JNI_VERSION_1_6);
220 LOG_ALWAYS_FATAL_IF(err != JNI_OK, "AMidiDevice_release Error accessing JNIEnv err:%d", err);
221
222 // Synchronize with the associated Java MidiDevice.
223 // env->CallVoidMethod(j_midiDeviceObj, midClearNativeHandle);
224 {
225 std::lock_guard<std::mutex> guard(openMutex);
226 long handle = env->GetLongField(device->midiDeviceObj, fidNativeHandle);
227 if (handle == 0) {
228 // Not opened as native.
229 ALOGE("AMidiDevice_release() device not opened in native client.");
230 return AMEDIA_ERROR_INVALID_OBJECT;
231 }
232
233 env->SetLongField(device->midiDeviceObj, fidNativeHandle, 0L);
234 }
235 env->DeleteGlobalRef(device->midiDeviceObj);
236
237 delete device;
238
239 return AMEDIA_OK;
240}
241
242int32_t AMIDI_API AMidiDevice_getType(const AMidiDevice *device) {
243 if (device == nullptr) {
244 return AMEDIA_ERROR_INVALID_PARAMETER;
245 }
246 return device->deviceInfo.type;
247}
248
249ssize_t AMIDI_API AMidiDevice_getNumInputPorts(const AMidiDevice *device) {
250 if (device == nullptr) {
251 return AMEDIA_ERROR_INVALID_PARAMETER;
252 }
253 return device->deviceInfo.inputPortCount;
254}
255
256ssize_t AMIDI_API AMidiDevice_getNumOutputPorts(const AMidiDevice *device) {
257 if (device == nullptr) {
258 return AMEDIA_ERROR_INVALID_PARAMETER;
259 }
260 return device->deviceInfo.outputPortCount;
Paul McLean71f672b2017-03-05 08:12:18 -0700261}
262
263/*
264 * Port Helpers
265 */
Paul McLean8a3e33b2017-03-14 15:20:51 -0700266static media_status_t AMIDI_openPort(const AMidiDevice *device, int32_t portNumber, int type,
Paul McLean71f672b2017-03-05 08:12:18 -0700267 AMIDI_Port **portPtr) {
Paul McLean8a3e33b2017-03-14 15:20:51 -0700268 if (device == nullptr) {
269 return AMEDIA_ERROR_INVALID_PARAMETER;
270 }
271
Paul McLean71f672b2017-03-05 08:12:18 -0700272 sp<BBinder> portToken(new BBinder());
273 unique_fd ufd;
274 Status txResult = type == PORTTYPE_OUTPUT
275 ? device->server->openOutputPort(portToken, portNumber, &ufd)
276 : device->server->openInputPort(portToken, portNumber, &ufd);
277 if (!txResult.isOk()) {
278 ALOGE("AMIDI_openPort transaction error: %d", txResult.transactionError());
Paul McLean8a3e33b2017-03-14 15:20:51 -0700279 return AMEDIA_ERROR_UNKNOWN;
Paul McLean71f672b2017-03-05 08:12:18 -0700280 }
281
Paul McLean8a3e33b2017-03-14 15:20:51 -0700282 AMIDI_Port *port = new AMIDI_Port;
Paul McLean71f672b2017-03-05 08:12:18 -0700283 port->state = MIDI_PORT_STATE_OPEN_IDLE;
284 port->device = device;
285 port->binderToken = portToken;
286 port->ufd = std::move(ufd);
287
288 *portPtr = port;
289
Paul McLean8a3e33b2017-03-14 15:20:51 -0700290 return AMEDIA_OK;
Paul McLean71f672b2017-03-05 08:12:18 -0700291}
292
Paul McLean8a3e33b2017-03-14 15:20:51 -0700293static void AMIDI_closePort(AMIDI_Port *port) {
294 if (port == nullptr) {
295 return;
296 }
297
Paul McLean71f672b2017-03-05 08:12:18 -0700298 int portState = MIDI_PORT_STATE_OPEN_IDLE;
299 while (!port->state.compare_exchange_weak(portState, MIDI_PORT_STATE_CLOSED)) {
300 if (portState == MIDI_PORT_STATE_CLOSED) {
Paul McLean8a3e33b2017-03-14 15:20:51 -0700301 return; // Already closed
Paul McLean71f672b2017-03-05 08:12:18 -0700302 }
303 }
304
305 Status txResult = port->device->server->closePort(port->binderToken);
306 if (!txResult.isOk()) {
Paul McLean8a3e33b2017-03-14 15:20:51 -0700307 ALOGE("Transaction error closing MIDI port:%d", txResult.transactionError());
Paul McLean71f672b2017-03-05 08:12:18 -0700308 }
309
310 delete port;
Mikhail Naganovc276c592016-08-31 18:08:10 -0700311}
312
313/*
314 * Output (receiving) API
315 */
Paul McLean8a3e33b2017-03-14 15:20:51 -0700316media_status_t AMIDI_API AMidiOutputPort_open(const AMidiDevice *device, int32_t portNumber,
317 AMidiOutputPort **outOutputPortPtr) {
318 return AMIDI_openPort(device, portNumber, PORTTYPE_OUTPUT, (AMIDI_Port**)outOutputPortPtr);
Mikhail Naganovc276c592016-08-31 18:08:10 -0700319}
320
Paul McLean8a3e33b2017-03-14 15:20:51 -0700321/*
322 * A little RAII (https://en.wikipedia.org/wiki/Resource_acquisition_is_initialization)
323 * class to ensure that the port state is correct irrespective of errors.
324 */
325class MidiReceiver {
326public:
327 MidiReceiver(AMIDI_Port *port) : mPort(port) {}
328
329 ~MidiReceiver() {
330 // flag the port state to idle
331 mPort->state.store(MIDI_PORT_STATE_OPEN_IDLE);
Mikhail Naganovc276c592016-08-31 18:08:10 -0700332 }
333
Paul McLean8a3e33b2017-03-14 15:20:51 -0700334 ssize_t receive(int32_t *opcodePtr, uint8_t *buffer, size_t maxBytes,
335 size_t *numBytesReceivedPtr, int64_t *timestampPtr) {
336 int portState = MIDI_PORT_STATE_OPEN_IDLE;
337 // check to see if the port is idle, then set to active
338 if (!mPort->state.compare_exchange_strong(portState, MIDI_PORT_STATE_OPEN_ACTIVE)) {
339 // The port not idle or has been closed.
340 return AMEDIA_ERROR_UNKNOWN;
Mikhail Naganovc276c592016-08-31 18:08:10 -0700341 }
342
Paul McLean8a3e33b2017-03-14 15:20:51 -0700343 struct pollfd checkFds[1] = { { mPort->ufd, POLLIN, 0 } };
344 if (poll(checkFds, 1, 0) < 1) {
345 // Nothing there
346 return 0;
347 }
348
Mikhail Naganovc276c592016-08-31 18:08:10 -0700349 uint8_t readBuffer[AMIDI_PACKET_SIZE];
Paul McLean8a3e33b2017-03-14 15:20:51 -0700350 ssize_t readCount = read(mPort->ufd, readBuffer, sizeof(readBuffer));
351 if (readCount == EINTR || readCount < 1) {
352 return AMEDIA_ERROR_UNKNOWN;
Mikhail Naganovc276c592016-08-31 18:08:10 -0700353 }
354
Paul McLean8a3e33b2017-03-14 15:20:51 -0700355 // see Packet Format definition at the top of this file.
356 size_t numMessageBytes = 0;
357 *opcodePtr = readBuffer[0];
358 if (*opcodePtr == AMIDI_OPCODE_DATA && readCount >= AMIDI_PACKET_OVERHEAD) {
359 numMessageBytes = readCount - AMIDI_PACKET_OVERHEAD;
360 numMessageBytes = std::min(maxBytes, numMessageBytes);
361 memcpy(buffer, readBuffer + 1, numMessageBytes);
362 if (timestampPtr != nullptr) {
363 *timestampPtr = *(uint64_t*)(readBuffer + readCount - sizeof(uint64_t));
Mikhail Naganovc276c592016-08-31 18:08:10 -0700364 }
Mikhail Naganovc276c592016-08-31 18:08:10 -0700365 }
Paul McLean8a3e33b2017-03-14 15:20:51 -0700366 *numBytesReceivedPtr = numMessageBytes;
367 return 1;
Mikhail Naganovc276c592016-08-31 18:08:10 -0700368 }
369
Paul McLean8a3e33b2017-03-14 15:20:51 -0700370private:
371 AMIDI_Port *mPort;
372};
Paul McLean71f672b2017-03-05 08:12:18 -0700373
Paul McLean8a3e33b2017-03-14 15:20:51 -0700374ssize_t AMIDI_API AMidiOutputPort_receive(const AMidiOutputPort *outputPort, int32_t *opcodePtr,
375 uint8_t *buffer, size_t maxBytes, size_t* numBytesReceivedPtr, int64_t *timestampPtr) {
376
377 if (outputPort == nullptr || buffer == nullptr) {
378 return -EINVAL;
379 }
380
381 return MidiReceiver((AMIDI_Port*)outputPort).receive(opcodePtr, buffer, maxBytes,
382 numBytesReceivedPtr, timestampPtr);
Mikhail Naganovc276c592016-08-31 18:08:10 -0700383}
384
Paul McLean8a3e33b2017-03-14 15:20:51 -0700385void AMIDI_API AMidiOutputPort_close(const AMidiOutputPort *outputPort) {
386 AMIDI_closePort((AMIDI_Port*)outputPort);
Mikhail Naganovc276c592016-08-31 18:08:10 -0700387}
388
389/*
390 * Input (sending) API
391 */
Paul McLean8a3e33b2017-03-14 15:20:51 -0700392media_status_t AMIDI_API AMidiInputPort_open(const AMidiDevice *device, int32_t portNumber,
393 AMidiInputPort **outInputPortPtr) {
394 return AMIDI_openPort(device, portNumber, PORTTYPE_INPUT, (AMIDI_Port**)outInputPortPtr);
Mikhail Naganovc276c592016-08-31 18:08:10 -0700395}
396
Paul McLean8a3e33b2017-03-14 15:20:51 -0700397void AMIDI_API AMidiInputPort_close(const AMidiInputPort *inputPort) {
398 AMIDI_closePort((AMIDI_Port*)inputPort);
Mikhail Naganovc276c592016-08-31 18:08:10 -0700399}
400
Paul McLean71f672b2017-03-05 08:12:18 -0700401static ssize_t AMIDI_makeSendBuffer(
Paul McLean8a3e33b2017-03-14 15:20:51 -0700402 uint8_t *buffer, const uint8_t *data, size_t numBytes, uint64_t timestamp) {
403 // Error checking will happen in the caller since this isn't an API function.
Mikhail Naganovc276c592016-08-31 18:08:10 -0700404 buffer[0] = AMIDI_OPCODE_DATA;
405 memcpy(buffer + 1, data, numBytes);
406 memcpy(buffer + 1 + numBytes, &timestamp, sizeof(timestamp));
407 return numBytes + AMIDI_PACKET_OVERHEAD;
408}
409
Paul McLean8a3e33b2017-03-14 15:20:51 -0700410ssize_t AMIDI_API AMidiInputPort_send(const AMidiInputPort *inputPort, const uint8_t *buffer,
411 size_t numBytes) {
412 return AMidiInputPort_sendWithTimestamp(inputPort, buffer, numBytes, 0);
Mikhail Naganovc276c592016-08-31 18:08:10 -0700413}
414
Paul McLean8a3e33b2017-03-14 15:20:51 -0700415ssize_t AMIDI_API AMidiInputPort_sendWithTimestamp(const AMidiInputPort *inputPort,
416 const uint8_t *data, size_t numBytes, int64_t timestamp) {
417 if (inputPort == nullptr || data == nullptr) {
418 return AMEDIA_ERROR_INVALID_PARAMETER;
Mikhail Naganovc276c592016-08-31 18:08:10 -0700419 }
420
421 // AMIDI_logBuffer(data, numBytes);
422
Paul McLean8a3e33b2017-03-14 15:20:51 -0700423 uint8_t writeBuffer[AMIDI_BUFFER_SIZE + AMIDI_PACKET_OVERHEAD];
424 size_t numSent = 0;
425 while (numSent < numBytes) {
426 size_t blockSize = AMIDI_BUFFER_SIZE;
427 blockSize = std::min(blockSize, numBytes - numSent);
Mikhail Naganovc276c592016-08-31 18:08:10 -0700428
Paul McLean8a3e33b2017-03-14 15:20:51 -0700429 ssize_t numTransferBytes =
430 AMIDI_makeSendBuffer(writeBuffer, data + numSent, blockSize, timestamp);
431 ssize_t numWritten = write(((AMIDI_Port*)inputPort)->ufd, writeBuffer, numTransferBytes);
432 if (numWritten < 0) {
433 break; // error so bail out.
434 }
435 if (numWritten < numTransferBytes) {
436 ALOGE("AMidiInputPort_sendWithTimestamp Couldn't write MIDI data buffer."
437 " requested:%zu, written%zu",numTransferBytes, numWritten);
438 break; // bail
439 }
440
441 numSent += numWritten - AMIDI_PACKET_OVERHEAD;
Mikhail Naganovc276c592016-08-31 18:08:10 -0700442 }
443
Paul McLean8a3e33b2017-03-14 15:20:51 -0700444 return numSent;
Mikhail Naganovc276c592016-08-31 18:08:10 -0700445}
446
Paul McLean8a3e33b2017-03-14 15:20:51 -0700447media_status_t AMIDI_API AMidiInputPort_sendFlush(const AMidiInputPort *inputPort) {
448 if (inputPort == nullptr) {
449 return AMEDIA_ERROR_INVALID_PARAMETER;
450 }
451
Mikhail Naganovc276c592016-08-31 18:08:10 -0700452 uint8_t opCode = AMIDI_OPCODE_FLUSH;
453 ssize_t numTransferBytes = 1;
Paul McLean71f672b2017-03-05 08:12:18 -0700454 ssize_t numWritten = write(((AMIDI_Port*)inputPort)->ufd, &opCode, numTransferBytes);
Mikhail Naganovc276c592016-08-31 18:08:10 -0700455
456 if (numWritten < numTransferBytes) {
Paul McLean8a3e33b2017-03-14 15:20:51 -0700457 ALOGE("AMidiInputPort_flush Couldn't write MIDI flush. requested:%zd, written:%zd",
Mikhail Naganovc276c592016-08-31 18:08:10 -0700458 numTransferBytes, numWritten);
Paul McLean8a3e33b2017-03-14 15:20:51 -0700459 return AMEDIA_ERROR_UNSUPPORTED;
Mikhail Naganovc276c592016-08-31 18:08:10 -0700460 }
461
Paul McLean8a3e33b2017-03-14 15:20:51 -0700462 return AMEDIA_OK;
Mikhail Naganovc276c592016-08-31 18:08:10 -0700463}
464