Home | History | Annotate | Download | only in midi
      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, &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 
    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