blob: 1bf0bd080e94d65d6266b9d190c1dda15e764af3 [file] [log] [blame]
Mikhail Naganovc276c592016-08-31 18:08:10 -07001/*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
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>
23#include <utils/Errors.h>
24#include <utils/Log.h>
25
26#include "android/media/midi/BpMidiDeviceServer.h"
27#include "media/MidiDeviceInfo.h"
28#include "MidiDeviceRegistry.h"
29#include "MidiPortRegistry.h"
30
31#include "midi.h"
32
33using android::IBinder;
34using android::BBinder;
35using android::OK;
36using android::sp;
37using android::status_t;
38using android::base::unique_fd;
39using android::binder::Status;
40using android::media::midi::BpMidiDeviceServer;
41using android::media::midi::MidiDeviceInfo;
42using android::media::midi::MidiDeviceRegistry;
43using android::media::midi::MidiPortRegistry;
44
45#define SIZE_MIDIRECEIVEBUFFER AMIDI_BUFFER_SIZE
46
47/* TRANSFER PACKET FORMAT (as defined in MidiPortImpl.java)
48 *
49 * Transfer packet format is as follows (see MidiOutputPort.mThread.run() to see decomposition):
50 * |oc|md|md| ......... |md|ts|ts|ts|ts|ts|ts|ts|ts|
51 * ^ +--------------------+-----------------------+
52 * | ^ ^
53 * | | |
54 * | | + timestamp (8 bytes)
55 * | |
56 * | + MIDI data bytes (numBytes bytes)
57 * |
58 * + OpCode (AMIDI_OPCODE_DATA)
59 *
60 * NOTE: The socket pair is configured to use SOCK_SEQPACKET mode.
61 * SOCK_SEQPACKET, for a sequenced-packet socket that is connection-oriented, preserves message
62 * boundaries, and delivers messages in the order that they were sent.
63 * So 'read()' always returns a whole message.
64 */
65
66status_t AMIDI_getDeviceById(int32_t id, AMIDI_Device *devicePtr) {
67 return MidiDeviceRegistry::getInstance().obtainDeviceToken(id, devicePtr);
68}
69
70status_t AMIDI_getDeviceInfo(AMIDI_Device device, AMIDI_DeviceInfo *deviceInfoPtr) {
71 sp<BpMidiDeviceServer> deviceServer;
72 status_t result = MidiDeviceRegistry::getInstance().getDeviceByToken(device, &deviceServer);
73 if (result != OK) {
74 ALOGE("AMIDI_getDeviceInfo bad device token %d: %d", device, result);
75 return result;
76 }
77
78 MidiDeviceInfo deviceInfo;
79 Status txResult = deviceServer->getDeviceInfo(&deviceInfo);
80 if (!txResult.isOk()) {
81 ALOGE("AMIDI_getDeviceInfo transaction error: %d", txResult.transactionError());
82 return txResult.transactionError();
83 }
84
85 deviceInfoPtr->type = deviceInfo.getType();
86 deviceInfoPtr->uid = deviceInfo.getUid();
87 deviceInfoPtr->isPrivate = deviceInfo.isPrivate();
88 deviceInfoPtr->inputPortCount = deviceInfo.getInputPortNames().size();
89 deviceInfoPtr->outputPortCount = deviceInfo.getOutputPortNames().size();
90 return OK;
91}
92
93/*
94 * Output (receiving) API
95 */
96status_t AMIDI_openOutputPort(AMIDI_Device device, int portNumber, AMIDI_OutputPort *outputPortPtr) {
97 sp<BpMidiDeviceServer> deviceServer;
98 status_t result = MidiDeviceRegistry::getInstance().getDeviceByToken(device, &deviceServer);
99 if (result != OK) {
100 ALOGE("AMIDI_openOutputPort bad device token %d: %d", device, result);
101 return result;
102 }
103
104 sp<BBinder> portToken(new BBinder());
105 unique_fd ufd;
106 Status txResult = deviceServer->openOutputPort(portToken, portNumber, &ufd);
107 if (!txResult.isOk()) {
108 ALOGE("AMIDI_openOutputPort transaction error: %d", txResult.transactionError());
109 return txResult.transactionError();
110 }
111
112 result = MidiPortRegistry::getInstance().addOutputPort(
113 device, portToken, std::move(ufd), outputPortPtr);
114 if (result != OK) {
115 ALOGE("AMIDI_openOutputPort port registration error: %d", result);
116 // Close port
117 return result;
118 }
119 return OK;
120}
121
122ssize_t AMIDI_receive(AMIDI_OutputPort outputPort, AMIDI_Message *messages, ssize_t maxMessages) {
123 unique_fd *ufd;
124 // TODO: May return a nicer self-unlocking object
125 status_t result = MidiPortRegistry::getInstance().getOutputPortFdAndLock(outputPort, &ufd);
126 if (result != OK) {
127 return result;
128 }
129
130 ssize_t messagesRead = 0;
131 while (messagesRead < maxMessages) {
132 struct pollfd checkFds[1] = { { *ufd, POLLIN, 0 } };
133 int pollResult = poll(checkFds, 1, 0);
134 if (pollResult < 1) {
135 result = android::INVALID_OPERATION;
136 break;
137 }
138
139 AMIDI_Message *message = &messages[messagesRead];
140 uint8_t readBuffer[AMIDI_PACKET_SIZE];
141 memset(readBuffer, 0, sizeof(readBuffer));
142 ssize_t readCount = read(*ufd, readBuffer, sizeof(readBuffer));
143 if (readCount == EINTR) {
144 continue;
145 }
146 if (readCount < 1) {
147 result = android::NOT_ENOUGH_DATA;
148 break;
149 }
150
151 // set Packet Format definition at the top of this file.
152 size_t dataSize = 0;
153 message->opcode = readBuffer[0];
154 message->timestamp = 0;
155 if (message->opcode == AMIDI_OPCODE_DATA && readCount >= AMIDI_PACKET_OVERHEAD) {
156 dataSize = readCount - AMIDI_PACKET_OVERHEAD;
157 if (dataSize) {
158 memcpy(message->buffer, readBuffer + 1, dataSize);
159 }
160 message->timestamp = *(uint64_t*) (readBuffer + readCount - sizeof(uint64_t));
161 }
162 message->len = dataSize;
163 ++messagesRead;
164 }
165
166 MidiPortRegistry::getInstance().unlockOutputPort(outputPort);
167 return result == OK ? messagesRead : result;
168}
169
170status_t AMIDI_closeOutputPort(AMIDI_OutputPort outputPort) {
171 AMIDI_Device device;
172 sp<IBinder> portToken;
173 status_t result =
174 MidiPortRegistry::getInstance().removeOutputPort(outputPort, &device, &portToken);
175 if (result != OK) {
176 return result;
177 }
178
179 sp<BpMidiDeviceServer> deviceServer;
180 result = MidiDeviceRegistry::getInstance().getDeviceByToken(device, &deviceServer);
181 if (result != OK) {
182 return result;
183 }
184
185 Status txResult = deviceServer->closePort(portToken);
186 if (!txResult.isOk()) {
187 return txResult.transactionError();
188 }
189 return OK;
190}
191
192/*
193 * Input (sending) API
194 */
195status_t AMIDI_openInputPort(AMIDI_Device device, int portNumber, AMIDI_InputPort *inputPortPtr) {
196 sp<BpMidiDeviceServer> deviceServer;
197 status_t result = MidiDeviceRegistry::getInstance().getDeviceByToken(device, &deviceServer);
198 if (result != OK) {
199 ALOGE("AMIDI_openInputPort bad device token %d: %d", device, result);
200 return result;
201 }
202
203 sp<BBinder> portToken(new BBinder());
204 unique_fd ufd; // this is the file descriptor of the "receive" port s
205 Status txResult = deviceServer->openInputPort(portToken, portNumber, &ufd);
206 if (!txResult.isOk()) {
207 ALOGE("AMIDI_openInputPort transaction error: %d", txResult.transactionError());
208 return txResult.transactionError();
209 }
210
211 result = MidiPortRegistry::getInstance().addInputPort(
212 device, portToken, std::move(ufd), inputPortPtr);
213 if (result != OK) {
214 ALOGE("AMIDI_openInputPort port registration error: %d", result);
215 // Close port
216 return result;
217 }
218
219 return OK;
220}
221
222status_t AMIDI_closeInputPort(AMIDI_InputPort inputPort) {
223 AMIDI_Device device;
224 sp<IBinder> portToken;
225 status_t result = MidiPortRegistry::getInstance().removeInputPort(
226 inputPort, &device, &portToken);
227 if (result != OK) {
228 ALOGE("AMIDI_closeInputPort remove port error: %d", result);
229 return result;
230 }
231
232 sp<BpMidiDeviceServer> deviceServer;
233 result = MidiDeviceRegistry::getInstance().getDeviceByToken(device, &deviceServer);
234 if (result != OK) {
235 ALOGE("AMIDI_closeInputPort can't find device error: %d", result);
236 return result;
237 }
238
239 Status txResult = deviceServer->closePort(portToken);
240 if (!txResult.isOk()) {
241 result = txResult.transactionError();
242 ALOGE("AMIDI_closeInputPort transaction error: %d", result);
243 return result;
244 }
245
246 return OK;
247}
248
249ssize_t AMIDI_getMaxMessageSizeInBytes(AMIDI_InputPort /*inputPort*/) {
250 return SIZE_MIDIRECEIVEBUFFER;
251}
252
253static ssize_t AMIDI_makeSendBuffer(uint8_t *buffer, uint8_t *data, ssize_t numBytes, uint64_t timestamp) {
254 buffer[0] = AMIDI_OPCODE_DATA;
255 memcpy(buffer + 1, data, numBytes);
256 memcpy(buffer + 1 + numBytes, &timestamp, sizeof(timestamp));
257 return numBytes + AMIDI_PACKET_OVERHEAD;
258}
259
260// Handy debugging function.
261//static void AMIDI_logBuffer(uint8_t *data, size_t numBytes) {
262// for (size_t index = 0; index < numBytes; index++) {
263// ALOGI(" data @%zu [0x%X]", index, data[index]);
264// }
265//}
266
267ssize_t AMIDI_send(AMIDI_InputPort inputPort, uint8_t *buffer, ssize_t numBytes) {
268 return AMIDI_sendWithTimestamp(inputPort, buffer, numBytes, 0);
269}
270
271ssize_t AMIDI_sendWithTimestamp(AMIDI_InputPort inputPort, uint8_t *data,
272 ssize_t numBytes, int64_t timestamp) {
273
274 if (numBytes > SIZE_MIDIRECEIVEBUFFER) {
275 return android::BAD_VALUE;
276 }
277
278 // AMIDI_logBuffer(data, numBytes);
279
280 unique_fd *ufd = NULL;
281 status_t result = MidiPortRegistry::getInstance().getInputPortFd(inputPort, &ufd);
282 if (result != OK) {
283 return result;
284 }
285
286 uint8_t writeBuffer[SIZE_MIDIRECEIVEBUFFER + AMIDI_PACKET_OVERHEAD];
287 ssize_t numTransferBytes = AMIDI_makeSendBuffer(writeBuffer, data, numBytes, timestamp);
288 ssize_t numWritten = write(*ufd, writeBuffer, numTransferBytes);
289
290 if (numWritten < numTransferBytes) {
291 ALOGE("AMIDI_sendWithTimestamp Couldn't write MIDI data buffer. requested:%zu, written%zu",
292 numTransferBytes, numWritten);
293 }
294
295 return numWritten - AMIDI_PACKET_OVERHEAD;
296}
297
298status_t AMIDI_flush(AMIDI_InputPort inputPort) {
299 unique_fd *ufd = NULL;
300 status_t result = MidiPortRegistry::getInstance().getInputPortFd(inputPort, &ufd);
301 if (result != OK) {
302 return result;
303 }
304
305 uint8_t opCode = AMIDI_OPCODE_FLUSH;
306 ssize_t numTransferBytes = 1;
307 ssize_t numWritten = write(*ufd, &opCode, numTransferBytes);
308
309 if (numWritten < numTransferBytes) {
310 ALOGE("AMIDI_flush Couldn't write MIDI flush. requested:%zu, written%zu",
311 numTransferBytes, numWritten);
312 return android::INVALID_OPERATION;
313 }
314
315 return OK;
316}
317