• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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, &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 
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