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 */ 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 */ 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 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 */ 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 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 202 status_t AMIDI_closeOutputPort(AMIDI_OutputPort *outputPort) { 203 return AMIDI_closePort((AMIDI_Port*)outputPort); 204 } 205 206 /* 207 * Input (sending) API 208 */ 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 213 status_t AMIDI_closeInputPort(AMIDI_InputPort *inputPort) { 214 return AMIDI_closePort((AMIDI_Port*)inputPort); 215 } 216 217 ssize_t AMIDI_getMaxMessageSizeInBytes(AMIDI_InputPort */*inputPort*/) { 218 return SIZE_MIDIRECEIVEBUFFER; 219 } 220 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 236 ssize_t AMIDI_send(AMIDI_InputPort *inputPort, uint8_t *buffer, ssize_t numBytes) { 237 return AMIDI_sendWithTimestamp(inputPort, buffer, numBytes, 0); 238 } 239 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 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