blob: fd8a06da5c6d1779807cf0f5a6eae6123e4699ab [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"
Marco Nelissen92672802019-10-22 13:44:43 -070029#include "MidiDeviceInfo.h"
Mikhail Naganovc276c592016-08-31 18:08:10 -070030
Paul McLeanf79b8d12019-02-06 13:27:43 -070031#include "include/amidi/AMidi.h"
32#include "amidi_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
Paul McLean84b491302018-05-09 10:03:57 -060090// JNI IDs (see android_media_midi.cpp)
91namespace android { namespace midi {
Paul McLean8a3e33b2017-03-14 15:20:51 -070092// MidiDevice Fields
Paul McLean84b491302018-05-09 10:03:57 -060093extern jfieldID gFidMidiNativeHandle; // MidiDevice.mNativeHandle
94extern jfieldID gFidMidiDeviceServerBinder; // MidiDevice.mDeviceServerBinder
95extern jfieldID gFidMidiDeviceInfo; // MidiDevice.mDeviceInfo
Paul McLean8a3e33b2017-03-14 15:20:51 -070096
97// MidiDeviceInfo Fields
Paul McLean84b491302018-05-09 10:03:57 -060098extern jfieldID mFidMidiDeviceId; // MidiDeviceInfo.mId
99}}
100using namespace android::midi;
Paul McLean8a3e33b2017-03-14 15:20:51 -0700101
102static std::mutex openMutex; // Ensure that the device can be connected just once to 1 thread
103
Paul McLean8a3e33b2017-03-14 15:20:51 -0700104//// Handy debugging function.
105//static void AMIDI_logBuffer(const uint8_t *data, size_t numBytes) {
106// for (size_t index = 0; index < numBytes; index++) {
107// ALOGI(" data @%zu [0x%X]", index, data[index]);
108// }
109//}
Mikhail Naganovc276c592016-08-31 18:08:10 -0700110
Paul McLean71f672b2017-03-05 08:12:18 -0700111/*
112 * Device Functions
113 */
Paul McLean8a3e33b2017-03-14 15:20:51 -0700114/**
115 * Retrieves information for the native MIDI device.
116 *
117 * device The Native API token for the device. This value is obtained from the
118 * AMidiDevice_fromJava().
119 * outDeviceInfoPtr Receives the associated device info.
120 *
121 * Returns AMEDIA_OK or a negative error code.
122 * - AMEDIA_ERROR_INVALID_PARAMETER
123 * AMEDIA_ERROR_UNKNOWN
124 */
Elliott Hughes4797e712019-07-22 16:39:52 -0700125static media_status_t AMIDI_getDeviceInfo(const AMidiDevice *device,
Paul McLean8a3e33b2017-03-14 15:20:51 -0700126 AMidiDeviceInfo *outDeviceInfoPtr) {
127 if (device == nullptr) {
128 return AMEDIA_ERROR_INVALID_PARAMETER;
129 }
130
Mikhail Naganovc276c592016-08-31 18:08:10 -0700131 MidiDeviceInfo deviceInfo;
Paul McLean71f672b2017-03-05 08:12:18 -0700132 Status txResult = device->server->getDeviceInfo(&deviceInfo);
Mikhail Naganovc276c592016-08-31 18:08:10 -0700133 if (!txResult.isOk()) {
Andy Hung0a8c9072020-12-10 21:56:01 -0800134 ALOGE("%s server exception code: %d", __func__, txResult.exceptionCode());
Paul McLean8a3e33b2017-03-14 15:20:51 -0700135 return AMEDIA_ERROR_UNKNOWN;
Mikhail Naganovc276c592016-08-31 18:08:10 -0700136 }
137
Paul McLean8a3e33b2017-03-14 15:20:51 -0700138 outDeviceInfoPtr->type = deviceInfo.getType();
139 outDeviceInfoPtr->inputPortCount = deviceInfo.getInputPortNames().size();
140 outDeviceInfoPtr->outputPortCount = deviceInfo.getOutputPortNames().size();
Robert Wua936a252021-12-22 19:47:20 +0000141 outDeviceInfoPtr->defaultProtocol = deviceInfo.getDefaultProtocol();
Paul McLean71f672b2017-03-05 08:12:18 -0700142
Paul McLean8a3e33b2017-03-14 15:20:51 -0700143 return AMEDIA_OK;
144}
145
Paul McLean5090a6d2019-08-05 12:30:56 -0600146media_status_t AMIDI_API AMidiDevice_fromJava(JNIEnv *env, jobject j_midiDeviceObj,
Paul McLean8a3e33b2017-03-14 15:20:51 -0700147 AMidiDevice** devicePtrPtr)
148{
Paul McLean8a3e33b2017-03-14 15:20:51 -0700149 if (j_midiDeviceObj == nullptr) {
150 ALOGE("AMidiDevice_fromJava() invalid MidiDevice object.");
151 return AMEDIA_ERROR_INVALID_OBJECT;
152 }
153
154 {
155 std::lock_guard<std::mutex> guard(openMutex);
156
Paul McLean84b491302018-05-09 10:03:57 -0600157 long handle = env->GetLongField(j_midiDeviceObj, gFidMidiNativeHandle);
Paul McLean8a3e33b2017-03-14 15:20:51 -0700158 if (handle != 0) {
159 // Already opened by someone.
160 return AMEDIA_ERROR_INVALID_OBJECT;
161 }
162
Paul McLean84b491302018-05-09 10:03:57 -0600163 jobject serverBinderObj = env->GetObjectField(j_midiDeviceObj, gFidMidiDeviceServerBinder);
Paul McLean8a3e33b2017-03-14 15:20:51 -0700164 sp<IBinder> serverBinder = android::ibinderForJavaObject(env, serverBinderObj);
165 if (serverBinder.get() == nullptr) {
166 ALOGE("AMidiDevice_fromJava couldn't connect to native MIDI server.");
167 return AMEDIA_ERROR_UNKNOWN;
168 }
169
170 // don't check allocation failures, just abort..
171 AMidiDevice* devicePtr = new AMidiDevice;
172 devicePtr->server = new BpMidiDeviceServer(serverBinder);
Paul McLean84b491302018-05-09 10:03:57 -0600173 jobject midiDeviceInfoObj = env->GetObjectField(j_midiDeviceObj, gFidMidiDeviceInfo);
174 devicePtr->deviceId = env->GetIntField(midiDeviceInfoObj, mFidMidiDeviceId);
Paul McLean8a3e33b2017-03-14 15:20:51 -0700175
176 // Synchronize with the associated Java MidiDevice.
Paul McLean84b491302018-05-09 10:03:57 -0600177 env->SetLongField(j_midiDeviceObj, gFidMidiNativeHandle, (long)devicePtr);
Paul McLean8a3e33b2017-03-14 15:20:51 -0700178 env->GetJavaVM(&devicePtr->javaVM);
179 devicePtr->midiDeviceObj = env->NewGlobalRef(j_midiDeviceObj);
180
181 if (AMIDI_getDeviceInfo(devicePtr, &devicePtr->deviceInfo) != AMEDIA_OK) {
182 // This is weird, but maybe not fatal?
183 ALOGE("AMidiDevice_fromJava couldn't retrieve attributes of native device.");
184 }
185
186 *devicePtrPtr = devicePtr;
187 }
188
189 return AMEDIA_OK;
190}
191
Paul McLean5090a6d2019-08-05 12:30:56 -0600192media_status_t AMIDI_API AMidiDevice_release(const AMidiDevice *device)
Paul McLean8a3e33b2017-03-14 15:20:51 -0700193{
194 if (device == nullptr || device->midiDeviceObj == nullptr) {
195 return AMEDIA_ERROR_INVALID_PARAMETER;
196 }
197
198 JNIEnv* env;
199 jint err = device->javaVM->GetEnv((void**)&env, JNI_VERSION_1_6);
200 LOG_ALWAYS_FATAL_IF(err != JNI_OK, "AMidiDevice_release Error accessing JNIEnv err:%d", err);
201
202 // Synchronize with the associated Java MidiDevice.
Paul McLean8a3e33b2017-03-14 15:20:51 -0700203 {
204 std::lock_guard<std::mutex> guard(openMutex);
Paul McLean84b491302018-05-09 10:03:57 -0600205 long handle = env->GetLongField(device->midiDeviceObj, gFidMidiNativeHandle);
Paul McLean8a3e33b2017-03-14 15:20:51 -0700206 if (handle == 0) {
207 // Not opened as native.
208 ALOGE("AMidiDevice_release() device not opened in native client.");
209 return AMEDIA_ERROR_INVALID_OBJECT;
210 }
211
Paul McLean84b491302018-05-09 10:03:57 -0600212 env->SetLongField(device->midiDeviceObj, gFidMidiNativeHandle, 0L);
Paul McLean8a3e33b2017-03-14 15:20:51 -0700213 }
214 env->DeleteGlobalRef(device->midiDeviceObj);
215
216 delete device;
217
218 return AMEDIA_OK;
219}
220
Paul McLean5090a6d2019-08-05 12:30:56 -0600221int32_t AMIDI_API AMidiDevice_getType(const AMidiDevice *device) {
Paul McLean8a3e33b2017-03-14 15:20:51 -0700222 if (device == nullptr) {
223 return AMEDIA_ERROR_INVALID_PARAMETER;
224 }
225 return device->deviceInfo.type;
226}
227
Paul McLean5090a6d2019-08-05 12:30:56 -0600228ssize_t AMIDI_API AMidiDevice_getNumInputPorts(const AMidiDevice *device) {
Paul McLean8a3e33b2017-03-14 15:20:51 -0700229 if (device == nullptr) {
230 return AMEDIA_ERROR_INVALID_PARAMETER;
231 }
232 return device->deviceInfo.inputPortCount;
233}
234
Paul McLean5090a6d2019-08-05 12:30:56 -0600235ssize_t AMIDI_API AMidiDevice_getNumOutputPorts(const AMidiDevice *device) {
Paul McLean8a3e33b2017-03-14 15:20:51 -0700236 if (device == nullptr) {
237 return AMEDIA_ERROR_INVALID_PARAMETER;
238 }
239 return device->deviceInfo.outputPortCount;
Paul McLean71f672b2017-03-05 08:12:18 -0700240}
241
Robert Wua936a252021-12-22 19:47:20 +0000242AMidiDevice_Protocol AMIDI_API AMidiDevice_getDefaultProtocol(const AMidiDevice *device) {
243 if (device == nullptr) {
244 return AMIDI_DEVICE_PROTOCOL_UNKNOWN;
245 }
246 return static_cast<AMidiDevice_Protocol>(device->deviceInfo.defaultProtocol);
247}
248
Paul McLean71f672b2017-03-05 08:12:18 -0700249/*
250 * Port Helpers
251 */
Paul McLean8a3e33b2017-03-14 15:20:51 -0700252static media_status_t AMIDI_openPort(const AMidiDevice *device, int32_t portNumber, int type,
Paul McLean71f672b2017-03-05 08:12:18 -0700253 AMIDI_Port **portPtr) {
Paul McLean8a3e33b2017-03-14 15:20:51 -0700254 if (device == nullptr) {
255 return AMEDIA_ERROR_INVALID_PARAMETER;
256 }
257
Paul McLean71f672b2017-03-05 08:12:18 -0700258 sp<BBinder> portToken(new BBinder());
259 unique_fd ufd;
260 Status txResult = type == PORTTYPE_OUTPUT
261 ? device->server->openOutputPort(portToken, portNumber, &ufd)
262 : device->server->openInputPort(portToken, portNumber, &ufd);
263 if (!txResult.isOk()) {
Andy Hung0a8c9072020-12-10 21:56:01 -0800264 ALOGE("%s server exception code: %d", __func__, txResult.exceptionCode());
Paul McLean8a3e33b2017-03-14 15:20:51 -0700265 return AMEDIA_ERROR_UNKNOWN;
Paul McLean71f672b2017-03-05 08:12:18 -0700266 }
267
Paul McLean8a3e33b2017-03-14 15:20:51 -0700268 AMIDI_Port *port = new AMIDI_Port;
Paul McLean71f672b2017-03-05 08:12:18 -0700269 port->state = MIDI_PORT_STATE_OPEN_IDLE;
270 port->device = device;
271 port->binderToken = portToken;
272 port->ufd = std::move(ufd);
273
274 *portPtr = port;
275
Paul McLean8a3e33b2017-03-14 15:20:51 -0700276 return AMEDIA_OK;
Paul McLean71f672b2017-03-05 08:12:18 -0700277}
278
Paul McLean8a3e33b2017-03-14 15:20:51 -0700279static void AMIDI_closePort(AMIDI_Port *port) {
280 if (port == nullptr) {
281 return;
282 }
283
Paul McLean71f672b2017-03-05 08:12:18 -0700284 int portState = MIDI_PORT_STATE_OPEN_IDLE;
285 while (!port->state.compare_exchange_weak(portState, MIDI_PORT_STATE_CLOSED)) {
286 if (portState == MIDI_PORT_STATE_CLOSED) {
Paul McLean8a3e33b2017-03-14 15:20:51 -0700287 return; // Already closed
Paul McLean71f672b2017-03-05 08:12:18 -0700288 }
289 }
290
291 Status txResult = port->device->server->closePort(port->binderToken);
292 if (!txResult.isOk()) {
Andy Hung0a8c9072020-12-10 21:56:01 -0800293 ALOGE("%s server exception code: %d", __func__, txResult.exceptionCode());
Paul McLean71f672b2017-03-05 08:12:18 -0700294 }
295
296 delete port;
Mikhail Naganovc276c592016-08-31 18:08:10 -0700297}
298
299/*
300 * Output (receiving) API
301 */
Paul McLean5090a6d2019-08-05 12:30:56 -0600302media_status_t AMIDI_API AMidiOutputPort_open(const AMidiDevice *device, int32_t portNumber,
Paul McLean8a3e33b2017-03-14 15:20:51 -0700303 AMidiOutputPort **outOutputPortPtr) {
304 return AMIDI_openPort(device, portNumber, PORTTYPE_OUTPUT, (AMIDI_Port**)outOutputPortPtr);
Mikhail Naganovc276c592016-08-31 18:08:10 -0700305}
306
Paul McLean8a3e33b2017-03-14 15:20:51 -0700307/*
308 * A little RAII (https://en.wikipedia.org/wiki/Resource_acquisition_is_initialization)
309 * class to ensure that the port state is correct irrespective of errors.
310 */
311class MidiReceiver {
312public:
313 MidiReceiver(AMIDI_Port *port) : mPort(port) {}
314
315 ~MidiReceiver() {
316 // flag the port state to idle
317 mPort->state.store(MIDI_PORT_STATE_OPEN_IDLE);
Mikhail Naganovc276c592016-08-31 18:08:10 -0700318 }
319
Paul McLean8a3e33b2017-03-14 15:20:51 -0700320 ssize_t receive(int32_t *opcodePtr, uint8_t *buffer, size_t maxBytes,
321 size_t *numBytesReceivedPtr, int64_t *timestampPtr) {
322 int portState = MIDI_PORT_STATE_OPEN_IDLE;
323 // check to see if the port is idle, then set to active
324 if (!mPort->state.compare_exchange_strong(portState, MIDI_PORT_STATE_OPEN_ACTIVE)) {
325 // The port not idle or has been closed.
326 return AMEDIA_ERROR_UNKNOWN;
Mikhail Naganovc276c592016-08-31 18:08:10 -0700327 }
328
Paul McLean8a3e33b2017-03-14 15:20:51 -0700329 struct pollfd checkFds[1] = { { mPort->ufd, POLLIN, 0 } };
330 if (poll(checkFds, 1, 0) < 1) {
331 // Nothing there
332 return 0;
333 }
334
Mikhail Naganovc276c592016-08-31 18:08:10 -0700335 uint8_t readBuffer[AMIDI_PACKET_SIZE];
Keith Mok57e08822021-11-12 23:55:16 +0000336 ssize_t readCount = TEMP_FAILURE_RETRY(read(mPort->ufd, readBuffer, sizeof(readBuffer)));
337 if (readCount < 1) {
Paul McLean8a3e33b2017-03-14 15:20:51 -0700338 return AMEDIA_ERROR_UNKNOWN;
Mikhail Naganovc276c592016-08-31 18:08:10 -0700339 }
340
Paul McLean8a3e33b2017-03-14 15:20:51 -0700341 // see Packet Format definition at the top of this file.
342 size_t numMessageBytes = 0;
343 *opcodePtr = readBuffer[0];
344 if (*opcodePtr == AMIDI_OPCODE_DATA && readCount >= AMIDI_PACKET_OVERHEAD) {
345 numMessageBytes = readCount - AMIDI_PACKET_OVERHEAD;
346 numMessageBytes = std::min(maxBytes, numMessageBytes);
347 memcpy(buffer, readBuffer + 1, numMessageBytes);
348 if (timestampPtr != nullptr) {
Mikhail Naganov25f51c62018-11-07 09:26:30 -0800349 memcpy(timestampPtr, readBuffer + readCount - sizeof(uint64_t),
350 sizeof(*timestampPtr));
Mikhail Naganovc276c592016-08-31 18:08:10 -0700351 }
Mikhail Naganovc276c592016-08-31 18:08:10 -0700352 }
Paul McLean8a3e33b2017-03-14 15:20:51 -0700353 *numBytesReceivedPtr = numMessageBytes;
354 return 1;
Mikhail Naganovc276c592016-08-31 18:08:10 -0700355 }
356
Paul McLean8a3e33b2017-03-14 15:20:51 -0700357private:
358 AMIDI_Port *mPort;
359};
Paul McLean71f672b2017-03-05 08:12:18 -0700360
Paul McLean5090a6d2019-08-05 12:30:56 -0600361ssize_t AMIDI_API AMidiOutputPort_receive(const AMidiOutputPort *outputPort, int32_t *opcodePtr,
Paul McLean8a3e33b2017-03-14 15:20:51 -0700362 uint8_t *buffer, size_t maxBytes, size_t* numBytesReceivedPtr, int64_t *timestampPtr) {
363
364 if (outputPort == nullptr || buffer == nullptr) {
365 return -EINVAL;
366 }
367
368 return MidiReceiver((AMIDI_Port*)outputPort).receive(opcodePtr, buffer, maxBytes,
369 numBytesReceivedPtr, timestampPtr);
Mikhail Naganovc276c592016-08-31 18:08:10 -0700370}
371
Paul McLean5090a6d2019-08-05 12:30:56 -0600372void AMIDI_API AMidiOutputPort_close(const AMidiOutputPort *outputPort) {
Paul McLean8a3e33b2017-03-14 15:20:51 -0700373 AMIDI_closePort((AMIDI_Port*)outputPort);
Mikhail Naganovc276c592016-08-31 18:08:10 -0700374}
375
376/*
377 * Input (sending) API
378 */
Paul McLean5090a6d2019-08-05 12:30:56 -0600379media_status_t AMIDI_API AMidiInputPort_open(const AMidiDevice *device, int32_t portNumber,
Paul McLean8a3e33b2017-03-14 15:20:51 -0700380 AMidiInputPort **outInputPortPtr) {
381 return AMIDI_openPort(device, portNumber, PORTTYPE_INPUT, (AMIDI_Port**)outInputPortPtr);
Mikhail Naganovc276c592016-08-31 18:08:10 -0700382}
383
Paul McLean5090a6d2019-08-05 12:30:56 -0600384void AMIDI_API AMidiInputPort_close(const AMidiInputPort *inputPort) {
Paul McLean8a3e33b2017-03-14 15:20:51 -0700385 AMIDI_closePort((AMIDI_Port*)inputPort);
Mikhail Naganovc276c592016-08-31 18:08:10 -0700386}
387
Paul McLean71f672b2017-03-05 08:12:18 -0700388static ssize_t AMIDI_makeSendBuffer(
Paul McLean8a3e33b2017-03-14 15:20:51 -0700389 uint8_t *buffer, const uint8_t *data, size_t numBytes, uint64_t timestamp) {
390 // Error checking will happen in the caller since this isn't an API function.
Mikhail Naganovc276c592016-08-31 18:08:10 -0700391 buffer[0] = AMIDI_OPCODE_DATA;
392 memcpy(buffer + 1, data, numBytes);
393 memcpy(buffer + 1 + numBytes, &timestamp, sizeof(timestamp));
394 return numBytes + AMIDI_PACKET_OVERHEAD;
395}
396
Paul McLean5090a6d2019-08-05 12:30:56 -0600397ssize_t AMIDI_API AMidiInputPort_send(const AMidiInputPort *inputPort, const uint8_t *buffer,
Paul McLean8a3e33b2017-03-14 15:20:51 -0700398 size_t numBytes) {
399 return AMidiInputPort_sendWithTimestamp(inputPort, buffer, numBytes, 0);
Mikhail Naganovc276c592016-08-31 18:08:10 -0700400}
401
Paul McLean5090a6d2019-08-05 12:30:56 -0600402ssize_t AMIDI_API AMidiInputPort_sendWithTimestamp(const AMidiInputPort *inputPort,
Paul McLean8a3e33b2017-03-14 15:20:51 -0700403 const uint8_t *data, size_t numBytes, int64_t timestamp) {
Robert Wu2136d8d2022-01-25 19:36:02 +0000404 if (inputPort == nullptr || data == nullptr || numBytes < 0 || timestamp < 0) {
Paul McLean8a3e33b2017-03-14 15:20:51 -0700405 return AMEDIA_ERROR_INVALID_PARAMETER;
Mikhail Naganovc276c592016-08-31 18:08:10 -0700406 }
407
Robert Wu2136d8d2022-01-25 19:36:02 +0000408 if (numBytes == 0) {
409 return 0;
410 }
411
Mikhail Naganovc276c592016-08-31 18:08:10 -0700412 // AMIDI_logBuffer(data, numBytes);
413
Paul McLean8a3e33b2017-03-14 15:20:51 -0700414 uint8_t writeBuffer[AMIDI_BUFFER_SIZE + AMIDI_PACKET_OVERHEAD];
415 size_t numSent = 0;
416 while (numSent < numBytes) {
417 size_t blockSize = AMIDI_BUFFER_SIZE;
418 blockSize = std::min(blockSize, numBytes - numSent);
Mikhail Naganovc276c592016-08-31 18:08:10 -0700419
Paul McLean8a3e33b2017-03-14 15:20:51 -0700420 ssize_t numTransferBytes =
421 AMIDI_makeSendBuffer(writeBuffer, data + numSent, blockSize, timestamp);
Keith Mok57e08822021-11-12 23:55:16 +0000422 ssize_t numWritten = TEMP_FAILURE_RETRY(write(((AMIDI_Port*)inputPort)->ufd, writeBuffer,
423 numTransferBytes));
Paul McLean8a3e33b2017-03-14 15:20:51 -0700424 if (numWritten < 0) {
425 break; // error so bail out.
426 }
427 if (numWritten < numTransferBytes) {
428 ALOGE("AMidiInputPort_sendWithTimestamp Couldn't write MIDI data buffer."
429 " requested:%zu, written%zu",numTransferBytes, numWritten);
430 break; // bail
431 }
432
433 numSent += numWritten - AMIDI_PACKET_OVERHEAD;
Mikhail Naganovc276c592016-08-31 18:08:10 -0700434 }
435
Paul McLean8a3e33b2017-03-14 15:20:51 -0700436 return numSent;
Mikhail Naganovc276c592016-08-31 18:08:10 -0700437}
438
Paul McLean5090a6d2019-08-05 12:30:56 -0600439media_status_t AMIDI_API AMidiInputPort_sendFlush(const AMidiInputPort *inputPort) {
Paul McLean8a3e33b2017-03-14 15:20:51 -0700440 if (inputPort == nullptr) {
441 return AMEDIA_ERROR_INVALID_PARAMETER;
442 }
443
Mikhail Naganovc276c592016-08-31 18:08:10 -0700444 uint8_t opCode = AMIDI_OPCODE_FLUSH;
445 ssize_t numTransferBytes = 1;
Keith Mok57e08822021-11-12 23:55:16 +0000446 ssize_t numWritten = TEMP_FAILURE_RETRY(write(((AMIDI_Port*)inputPort)->ufd, &opCode,
447 numTransferBytes));
Mikhail Naganovc276c592016-08-31 18:08:10 -0700448
449 if (numWritten < numTransferBytes) {
Paul McLean8a3e33b2017-03-14 15:20:51 -0700450 ALOGE("AMidiInputPort_flush Couldn't write MIDI flush. requested:%zd, written:%zd",
Mikhail Naganovc276c592016-08-31 18:08:10 -0700451 numTransferBytes, numWritten);
Paul McLean8a3e33b2017-03-14 15:20:51 -0700452 return AMEDIA_ERROR_UNSUPPORTED;
Mikhail Naganovc276c592016-08-31 18:08:10 -0700453 }
454
Paul McLean8a3e33b2017-03-14 15:20:51 -0700455 return AMEDIA_OK;
Mikhail Naganovc276c592016-08-31 18:08:10 -0700456}
457