1 /*
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
29 #include "midi.h"
30 #include "midi_internal.h"
31
32 using android::IBinder;
33 using android::BBinder;
34 using android::OK;
35 using android::sp;
36 using android::status_t;
37 using android::base::unique_fd;
38 using android::binder::Status;
39 using android::media::midi::MidiDeviceInfo;
40
41 struct AMIDI_Port {
42 std::atomic_int state;
43 AMIDI_Device *device;
44 sp<IBinder> binderToken;
45 unique_fd ufd;
46 };
47
48 #define SIZE_MIDIRECEIVEBUFFER AMIDI_BUFFER_SIZE
49
50 enum {
51 MIDI_PORT_STATE_CLOSED = 0,
52 MIDI_PORT_STATE_OPEN_IDLE,
53 MIDI_PORT_STATE_OPEN_ACTIVE
54 };
55
56 enum {
57 PORTTYPE_OUTPUT = 0,
58 PORTTYPE_INPUT = 1
59 };
60
61 /* 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
80 /*
81 * Device Functions
82 */
AMIDI_getDeviceInfo(AMIDI_Device * device,AMIDI_DeviceInfo * deviceInfoPtr)83 status_t AMIDI_getDeviceInfo(AMIDI_Device *device, AMIDI_DeviceInfo *deviceInfoPtr) {
84 MidiDeviceInfo deviceInfo;
85 Status txResult = device->server->getDeviceInfo(&deviceInfo);
86 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();
96
97 return OK;
98 }
99
100 /*
101 * Port Helpers
102 */
AMIDI_openPort(AMIDI_Device * device,int portNumber,int type,AMIDI_Port ** portPtr)103 static 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
AMIDI_closePort(AMIDI_Port * port)126 static 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
141 return OK;
142 }
143
144 /*
145 * Output (receiving) API
146 */
AMIDI_openOutputPort(AMIDI_Device * device,int portNumber,AMIDI_OutputPort ** outputPortPtr)147 status_t AMIDI_openOutputPort(AMIDI_Device *device, int portNumber,
148 AMIDI_OutputPort **outputPortPtr) {
149 return AMIDI_openPort(device, portNumber, PORTTYPE_OUTPUT, (AMIDI_Port**)outputPortPtr);
150 }
151
AMIDI_receive(AMIDI_OutputPort * outputPort,AMIDI_Message * messages,ssize_t maxMessages)152 ssize_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;
158 }
159
160 status_t result = OK;
161 ssize_t messagesRead = 0;
162 while (messagesRead < maxMessages) {
163 struct pollfd checkFds[1] = { { port->ufd, POLLIN, 0 } };
164 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));
173 ssize_t readCount = read(port->ufd, readBuffer, sizeof(readBuffer));
174 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 }
191 message->timestamp = *(uint64_t*)(readBuffer + readCount - sizeof(uint64_t));
192 }
193 message->len = dataSize;
194 ++messagesRead;
195 }
196
197 port->state.store(MIDI_PORT_STATE_OPEN_IDLE);
198
199 return result == OK ? messagesRead : result;
200 }
201
AMIDI_closeOutputPort(AMIDI_OutputPort * outputPort)202 status_t AMIDI_closeOutputPort(AMIDI_OutputPort *outputPort) {
203 return AMIDI_closePort((AMIDI_Port*)outputPort);
204 }
205
206 /*
207 * Input (sending) API
208 */
AMIDI_openInputPort(AMIDI_Device * device,int portNumber,AMIDI_InputPort ** inputPortPtr)209 status_t AMIDI_openInputPort(AMIDI_Device *device, int portNumber, AMIDI_InputPort **inputPortPtr) {
210 return AMIDI_openPort(device, portNumber, PORTTYPE_INPUT, (AMIDI_Port**)inputPortPtr);
211 }
212
AMIDI_closeInputPort(AMIDI_InputPort * inputPort)213 status_t AMIDI_closeInputPort(AMIDI_InputPort *inputPort) {
214 return AMIDI_closePort((AMIDI_Port*)inputPort);
215 }
216
AMIDI_getMaxMessageSizeInBytes(AMIDI_InputPort *)217 ssize_t AMIDI_getMaxMessageSizeInBytes(AMIDI_InputPort */*inputPort*/) {
218 return SIZE_MIDIRECEIVEBUFFER;
219 }
220
AMIDI_makeSendBuffer(uint8_t * buffer,uint8_t * data,ssize_t numBytes,uint64_t timestamp)221 static ssize_t AMIDI_makeSendBuffer(
222 uint8_t *buffer, uint8_t *data, ssize_t numBytes,uint64_t timestamp) {
223 buffer[0] = AMIDI_OPCODE_DATA;
224 memcpy(buffer + 1, data, numBytes);
225 memcpy(buffer + 1 + numBytes, ×tamp, 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
AMIDI_send(AMIDI_InputPort * inputPort,uint8_t * buffer,ssize_t numBytes)236 ssize_t AMIDI_send(AMIDI_InputPort *inputPort, uint8_t *buffer, ssize_t numBytes) {
237 return AMIDI_sendWithTimestamp(inputPort, buffer, numBytes, 0);
238 }
239
AMIDI_sendWithTimestamp(AMIDI_InputPort * inputPort,uint8_t * data,ssize_t numBytes,int64_t timestamp)240 ssize_t AMIDI_sendWithTimestamp(AMIDI_InputPort *inputPort, uint8_t *data,
241 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
249 uint8_t writeBuffer[SIZE_MIDIRECEIVEBUFFER + AMIDI_PACKET_OVERHEAD];
250 ssize_t numTransferBytes = AMIDI_makeSendBuffer(writeBuffer, data, numBytes, timestamp);
251 ssize_t numWritten = write(((AMIDI_Port*)inputPort)->ufd, writeBuffer, numTransferBytes);
252
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
AMIDI_flush(AMIDI_InputPort * inputPort)261 status_t AMIDI_flush(AMIDI_InputPort *inputPort) {
262 uint8_t opCode = AMIDI_OPCODE_FLUSH;
263 ssize_t numTransferBytes = 1;
264 ssize_t numWritten = write(((AMIDI_Port*)inputPort)->ufd, &opCode, numTransferBytes);
265
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
275