blob: 17b92da3325e7cb47170b5c7452cdb5573d5b0e2 [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"
Mikhail Naganovc276c592016-08-31 18:08:10 -070028
29#include "midi.h"
Paul McLean71f672b2017-03-05 08:12:18 -070030#include "midi_internal.h"
Mikhail Naganovc276c592016-08-31 18:08:10 -070031
32using android::IBinder;
33using android::BBinder;
34using android::OK;
35using android::sp;
36using android::status_t;
37using android::base::unique_fd;
38using android::binder::Status;
Mikhail Naganovc276c592016-08-31 18:08:10 -070039using android::media::midi::MidiDeviceInfo;
Paul McLean71f672b2017-03-05 08:12:18 -070040
41struct AMIDI_Port {
42 std::atomic_int state;
43 AMIDI_Device *device;
44 sp<IBinder> binderToken;
45 unique_fd ufd;
46};
Mikhail Naganovc276c592016-08-31 18:08:10 -070047
48#define SIZE_MIDIRECEIVEBUFFER AMIDI_BUFFER_SIZE
49
Paul McLean71f672b2017-03-05 08:12:18 -070050enum {
51 MIDI_PORT_STATE_CLOSED = 0,
52 MIDI_PORT_STATE_OPEN_IDLE,
53 MIDI_PORT_STATE_OPEN_ACTIVE
54};
55
56enum {
57 PORTTYPE_OUTPUT = 0,
58 PORTTYPE_INPUT = 1
59};
60
Mikhail Naganovc276c592016-08-31 18:08:10 -070061/* TRANSFER PACKET FORMAT (as defined in MidiPortImpl.java)
62 *
63 * Transfer packet format is as follows (see MidiOutputPort.mThread.run() to see decomposition):
64 * |oc|md|md| ......... |md|ts|ts|ts|ts|ts|ts|ts|ts|
65 * ^ +--------------------+-----------------------+
66 * | ^ ^
67 * | | |
68 * | | + timestamp (8 bytes)
69 * | |
70 * | + MIDI data bytes (numBytes bytes)
71 * |
72 * + OpCode (AMIDI_OPCODE_DATA)
73 *
74 * NOTE: The socket pair is configured to use SOCK_SEQPACKET mode.
75 * SOCK_SEQPACKET, for a sequenced-packet socket that is connection-oriented, preserves message
76 * boundaries, and delivers messages in the order that they were sent.
77 * So 'read()' always returns a whole message.
78 */
79
Paul McLean71f672b2017-03-05 08:12:18 -070080/*
81 * Device Functions
82 */
83status_t AMIDI_getDeviceInfo(AMIDI_Device *device, AMIDI_DeviceInfo *deviceInfoPtr) {
Mikhail Naganovc276c592016-08-31 18:08:10 -070084 MidiDeviceInfo deviceInfo;
Paul McLean71f672b2017-03-05 08:12:18 -070085 Status txResult = device->server->getDeviceInfo(&deviceInfo);
Mikhail Naganovc276c592016-08-31 18:08:10 -070086 if (!txResult.isOk()) {
87 ALOGE("AMIDI_getDeviceInfo transaction error: %d", txResult.transactionError());
88 return txResult.transactionError();
89 }
90
91 deviceInfoPtr->type = deviceInfo.getType();
92 deviceInfoPtr->uid = deviceInfo.getUid();
93 deviceInfoPtr->isPrivate = deviceInfo.isPrivate();
94 deviceInfoPtr->inputPortCount = deviceInfo.getInputPortNames().size();
95 deviceInfoPtr->outputPortCount = deviceInfo.getOutputPortNames().size();
Paul McLean71f672b2017-03-05 08:12:18 -070096
97 return OK;
98}
99
100/*
101 * Port Helpers
102 */
103static status_t AMIDI_openPort(AMIDI_Device *device, int portNumber, int type,
104 AMIDI_Port **portPtr) {
105 sp<BBinder> portToken(new BBinder());
106 unique_fd ufd;
107 Status txResult = type == PORTTYPE_OUTPUT
108 ? device->server->openOutputPort(portToken, portNumber, &ufd)
109 : device->server->openInputPort(portToken, portNumber, &ufd);
110 if (!txResult.isOk()) {
111 ALOGE("AMIDI_openPort transaction error: %d", txResult.transactionError());
112 return txResult.transactionError();
113 }
114
115 AMIDI_Port* port = new AMIDI_Port;
116 port->state = MIDI_PORT_STATE_OPEN_IDLE;
117 port->device = device;
118 port->binderToken = portToken;
119 port->ufd = std::move(ufd);
120
121 *portPtr = port;
122
123 return OK;
124}
125
126static status_t AMIDI_closePort(AMIDI_Port *port) {
127 int portState = MIDI_PORT_STATE_OPEN_IDLE;
128 while (!port->state.compare_exchange_weak(portState, MIDI_PORT_STATE_CLOSED)) {
129 if (portState == MIDI_PORT_STATE_CLOSED) {
130 return -EINVAL; // Already closed
131 }
132 }
133
134 Status txResult = port->device->server->closePort(port->binderToken);
135 if (!txResult.isOk()) {
136 return txResult.transactionError();
137 }
138
139 delete port;
140
Mikhail Naganovc276c592016-08-31 18:08:10 -0700141 return OK;
142}
143
144/*
145 * Output (receiving) API
146 */
Paul McLean71f672b2017-03-05 08:12:18 -0700147status_t AMIDI_openOutputPort(AMIDI_Device *device, int portNumber,
148 AMIDI_OutputPort **outputPortPtr) {
149 return AMIDI_openPort(device, portNumber, PORTTYPE_OUTPUT, (AMIDI_Port**)outputPortPtr);
Mikhail Naganovc276c592016-08-31 18:08:10 -0700150}
151
Paul McLean71f672b2017-03-05 08:12:18 -0700152ssize_t AMIDI_receive(AMIDI_OutputPort *outputPort, AMIDI_Message *messages, ssize_t maxMessages) {
153 AMIDI_Port *port = (AMIDI_Port*)outputPort;
154 int portState = MIDI_PORT_STATE_OPEN_IDLE;
155 if (!port->state.compare_exchange_strong(portState, MIDI_PORT_STATE_OPEN_ACTIVE)) {
156 // The port has been closed.
157 return -EPIPE;
Mikhail Naganovc276c592016-08-31 18:08:10 -0700158 }
159
Paul McLean71f672b2017-03-05 08:12:18 -0700160 status_t result = OK;
Mikhail Naganovc276c592016-08-31 18:08:10 -0700161 ssize_t messagesRead = 0;
162 while (messagesRead < maxMessages) {
Paul McLean71f672b2017-03-05 08:12:18 -0700163 struct pollfd checkFds[1] = { { port->ufd, POLLIN, 0 } };
Mikhail Naganovc276c592016-08-31 18:08:10 -0700164 int pollResult = poll(checkFds, 1, 0);
165 if (pollResult < 1) {
166 result = android::INVALID_OPERATION;
167 break;
168 }
169
170 AMIDI_Message *message = &messages[messagesRead];
171 uint8_t readBuffer[AMIDI_PACKET_SIZE];
172 memset(readBuffer, 0, sizeof(readBuffer));
Paul McLean71f672b2017-03-05 08:12:18 -0700173 ssize_t readCount = read(port->ufd, readBuffer, sizeof(readBuffer));
Mikhail Naganovc276c592016-08-31 18:08:10 -0700174 if (readCount == EINTR) {
175 continue;
176 }
177 if (readCount < 1) {
178 result = android::NOT_ENOUGH_DATA;
179 break;
180 }
181
182 // set Packet Format definition at the top of this file.
183 size_t dataSize = 0;
184 message->opcode = readBuffer[0];
185 message->timestamp = 0;
186 if (message->opcode == AMIDI_OPCODE_DATA && readCount >= AMIDI_PACKET_OVERHEAD) {
187 dataSize = readCount - AMIDI_PACKET_OVERHEAD;
188 if (dataSize) {
189 memcpy(message->buffer, readBuffer + 1, dataSize);
190 }
Paul McLean71f672b2017-03-05 08:12:18 -0700191 message->timestamp = *(uint64_t*)(readBuffer + readCount - sizeof(uint64_t));
Mikhail Naganovc276c592016-08-31 18:08:10 -0700192 }
193 message->len = dataSize;
194 ++messagesRead;
195 }
196
Paul McLean71f672b2017-03-05 08:12:18 -0700197 port->state.store(MIDI_PORT_STATE_OPEN_IDLE);
198
Mikhail Naganovc276c592016-08-31 18:08:10 -0700199 return result == OK ? messagesRead : result;
200}
201
Paul McLean71f672b2017-03-05 08:12:18 -0700202status_t AMIDI_closeOutputPort(AMIDI_OutputPort *outputPort) {
203 return AMIDI_closePort((AMIDI_Port*)outputPort);
Mikhail Naganovc276c592016-08-31 18:08:10 -0700204}
205
206/*
207 * Input (sending) API
208 */
Paul McLean71f672b2017-03-05 08:12:18 -0700209status_t AMIDI_openInputPort(AMIDI_Device *device, int portNumber, AMIDI_InputPort **inputPortPtr) {
210 return AMIDI_openPort(device, portNumber, PORTTYPE_INPUT, (AMIDI_Port**)inputPortPtr);
Mikhail Naganovc276c592016-08-31 18:08:10 -0700211}
212
Paul McLean71f672b2017-03-05 08:12:18 -0700213status_t AMIDI_closeInputPort(AMIDI_InputPort *inputPort) {
214 return AMIDI_closePort((AMIDI_Port*)inputPort);
Mikhail Naganovc276c592016-08-31 18:08:10 -0700215}
216
Paul McLean71f672b2017-03-05 08:12:18 -0700217ssize_t AMIDI_getMaxMessageSizeInBytes(AMIDI_InputPort */*inputPort*/) {
Mikhail Naganovc276c592016-08-31 18:08:10 -0700218 return SIZE_MIDIRECEIVEBUFFER;
219}
220
Paul McLean71f672b2017-03-05 08:12:18 -0700221static ssize_t AMIDI_makeSendBuffer(
222 uint8_t *buffer, uint8_t *data, ssize_t numBytes,uint64_t timestamp) {
Mikhail Naganovc276c592016-08-31 18:08:10 -0700223 buffer[0] = AMIDI_OPCODE_DATA;
224 memcpy(buffer + 1, data, numBytes);
225 memcpy(buffer + 1 + numBytes, &timestamp, sizeof(timestamp));
226 return numBytes + AMIDI_PACKET_OVERHEAD;
227}
228
229// Handy debugging function.
230//static void AMIDI_logBuffer(uint8_t *data, size_t numBytes) {
231// for (size_t index = 0; index < numBytes; index++) {
232// ALOGI(" data @%zu [0x%X]", index, data[index]);
233// }
234//}
235
Paul McLean71f672b2017-03-05 08:12:18 -0700236ssize_t AMIDI_send(AMIDI_InputPort *inputPort, uint8_t *buffer, ssize_t numBytes) {
Mikhail Naganovc276c592016-08-31 18:08:10 -0700237 return AMIDI_sendWithTimestamp(inputPort, buffer, numBytes, 0);
238}
239
Paul McLean71f672b2017-03-05 08:12:18 -0700240ssize_t AMIDI_sendWithTimestamp(AMIDI_InputPort *inputPort, uint8_t *data,
Mikhail Naganovc276c592016-08-31 18:08:10 -0700241 ssize_t numBytes, int64_t timestamp) {
242
243 if (numBytes > SIZE_MIDIRECEIVEBUFFER) {
244 return android::BAD_VALUE;
245 }
246
247 // AMIDI_logBuffer(data, numBytes);
248
Mikhail Naganovc276c592016-08-31 18:08:10 -0700249 uint8_t writeBuffer[SIZE_MIDIRECEIVEBUFFER + AMIDI_PACKET_OVERHEAD];
250 ssize_t numTransferBytes = AMIDI_makeSendBuffer(writeBuffer, data, numBytes, timestamp);
Paul McLean71f672b2017-03-05 08:12:18 -0700251 ssize_t numWritten = write(((AMIDI_Port*)inputPort)->ufd, writeBuffer, numTransferBytes);
Mikhail Naganovc276c592016-08-31 18:08:10 -0700252
253 if (numWritten < numTransferBytes) {
254 ALOGE("AMIDI_sendWithTimestamp Couldn't write MIDI data buffer. requested:%zu, written%zu",
255 numTransferBytes, numWritten);
256 }
257
258 return numWritten - AMIDI_PACKET_OVERHEAD;
259}
260
Paul McLean71f672b2017-03-05 08:12:18 -0700261status_t AMIDI_flush(AMIDI_InputPort *inputPort) {
Mikhail Naganovc276c592016-08-31 18:08:10 -0700262 uint8_t opCode = AMIDI_OPCODE_FLUSH;
263 ssize_t numTransferBytes = 1;
Paul McLean71f672b2017-03-05 08:12:18 -0700264 ssize_t numWritten = write(((AMIDI_Port*)inputPort)->ufd, &opCode, numTransferBytes);
Mikhail Naganovc276c592016-08-31 18:08:10 -0700265
266 if (numWritten < numTransferBytes) {
267 ALOGE("AMIDI_flush Couldn't write MIDI flush. requested:%zu, written%zu",
268 numTransferBytes, numWritten);
269 return android::INVALID_OPERATION;
270 }
271
272 return OK;
273}
274