1c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov/*
2c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov * Copyright (C) 2017 The Android Open Source Project
3c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov *
4c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov * Licensed under the Apache License, Version 2.0 (the "License");
5c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov * you may not use this file except in compliance with the License.
6c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov * You may obtain a copy of the License at
7c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov *
8c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov *      http://www.apache.org/licenses/LICENSE-2.0
9c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov *
10c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov * Unless required by applicable law or agreed to in writing, software
11c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov * distributed under the License is distributed on an "AS IS" BASIS,
12c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov * See the License for the specific language governing permissions and
14c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov * limitations under the License.
15c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov */
16c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov
17c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov#define LOG_TAG "NativeMIDI"
18c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov
19c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov#include <poll.h>
20c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov#include <unistd.h>
21c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov
22c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov#include <binder/Binder.h>
23c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov#include <utils/Errors.h>
24c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov#include <utils/Log.h>
25c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov
26c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov#include "android/media/midi/BpMidiDeviceServer.h"
27c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov#include "media/MidiDeviceInfo.h"
28c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov
29c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov#include "midi.h"
3071f672b98a0c31001ca28de86d9ac99fef0812b1Paul McLean#include "midi_internal.h"
31c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov
32c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganovusing android::IBinder;
33c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganovusing android::BBinder;
34c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganovusing android::OK;
35c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganovusing android::sp;
36c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganovusing android::status_t;
37c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganovusing android::base::unique_fd;
38c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganovusing android::binder::Status;
39c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganovusing android::media::midi::MidiDeviceInfo;
4071f672b98a0c31001ca28de86d9ac99fef0812b1Paul McLean
4171f672b98a0c31001ca28de86d9ac99fef0812b1Paul McLeanstruct AMIDI_Port {
4271f672b98a0c31001ca28de86d9ac99fef0812b1Paul McLean    std::atomic_int state;
4371f672b98a0c31001ca28de86d9ac99fef0812b1Paul McLean    AMIDI_Device    *device;
4471f672b98a0c31001ca28de86d9ac99fef0812b1Paul McLean    sp<IBinder>     binderToken;
4571f672b98a0c31001ca28de86d9ac99fef0812b1Paul McLean    unique_fd       ufd;
4671f672b98a0c31001ca28de86d9ac99fef0812b1Paul McLean};
47c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov
48c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov#define SIZE_MIDIRECEIVEBUFFER AMIDI_BUFFER_SIZE
49c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov
5071f672b98a0c31001ca28de86d9ac99fef0812b1Paul McLeanenum {
5171f672b98a0c31001ca28de86d9ac99fef0812b1Paul McLean    MIDI_PORT_STATE_CLOSED = 0,
5271f672b98a0c31001ca28de86d9ac99fef0812b1Paul McLean    MIDI_PORT_STATE_OPEN_IDLE,
5371f672b98a0c31001ca28de86d9ac99fef0812b1Paul McLean    MIDI_PORT_STATE_OPEN_ACTIVE
5471f672b98a0c31001ca28de86d9ac99fef0812b1Paul McLean};
5571f672b98a0c31001ca28de86d9ac99fef0812b1Paul McLean
5671f672b98a0c31001ca28de86d9ac99fef0812b1Paul McLeanenum {
5771f672b98a0c31001ca28de86d9ac99fef0812b1Paul McLean    PORTTYPE_OUTPUT = 0,
5871f672b98a0c31001ca28de86d9ac99fef0812b1Paul McLean    PORTTYPE_INPUT = 1
5971f672b98a0c31001ca28de86d9ac99fef0812b1Paul McLean};
6071f672b98a0c31001ca28de86d9ac99fef0812b1Paul McLean
61c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov/* TRANSFER PACKET FORMAT (as defined in MidiPortImpl.java)
62c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov *
63c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov * Transfer packet format is as follows (see MidiOutputPort.mThread.run() to see decomposition):
64c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov * |oc|md|md| ......... |md|ts|ts|ts|ts|ts|ts|ts|ts|
65c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov *  ^ +--------------------+-----------------------+
66c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov *  |  ^                    ^
67c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov *  |  |                    |
68c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov *  |  |                    + timestamp (8 bytes)
69c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov *  |  |
70c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov *  |  + MIDI data bytes (numBytes bytes)
71c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov *  |
72c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov *  + OpCode (AMIDI_OPCODE_DATA)
73c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov *
74c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov *  NOTE: The socket pair is configured to use SOCK_SEQPACKET mode.
75c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov *  SOCK_SEQPACKET, for a sequenced-packet socket that is connection-oriented, preserves message
76c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov *  boundaries, and delivers messages in the order that they were sent.
77c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov *  So 'read()' always returns a whole message.
78c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov */
79c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov
8071f672b98a0c31001ca28de86d9ac99fef0812b1Paul McLean/*
8171f672b98a0c31001ca28de86d9ac99fef0812b1Paul McLean * Device Functions
8271f672b98a0c31001ca28de86d9ac99fef0812b1Paul McLean */
8371f672b98a0c31001ca28de86d9ac99fef0812b1Paul McLeanstatus_t AMIDI_getDeviceInfo(AMIDI_Device *device, AMIDI_DeviceInfo *deviceInfoPtr) {
84c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov    MidiDeviceInfo deviceInfo;
8571f672b98a0c31001ca28de86d9ac99fef0812b1Paul McLean    Status txResult = device->server->getDeviceInfo(&deviceInfo);
86c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov    if (!txResult.isOk()) {
87c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov        ALOGE("AMIDI_getDeviceInfo transaction error: %d", txResult.transactionError());
88c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov        return txResult.transactionError();
89c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov    }
90c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov
91c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov    deviceInfoPtr->type = deviceInfo.getType();
92c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov    deviceInfoPtr->uid = deviceInfo.getUid();
93c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov    deviceInfoPtr->isPrivate = deviceInfo.isPrivate();
94c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov    deviceInfoPtr->inputPortCount = deviceInfo.getInputPortNames().size();
95c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov    deviceInfoPtr->outputPortCount = deviceInfo.getOutputPortNames().size();
9671f672b98a0c31001ca28de86d9ac99fef0812b1Paul McLean
97c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov    return OK;
98c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov}
99c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov
100c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov/*
10171f672b98a0c31001ca28de86d9ac99fef0812b1Paul McLean * Port Helpers
102c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov */
10371f672b98a0c31001ca28de86d9ac99fef0812b1Paul McLeanstatic status_t AMIDI_openPort(AMIDI_Device *device, int portNumber, int type,
10471f672b98a0c31001ca28de86d9ac99fef0812b1Paul McLean        AMIDI_Port **portPtr) {
105c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov    sp<BBinder> portToken(new BBinder());
106c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov    unique_fd ufd;
10771f672b98a0c31001ca28de86d9ac99fef0812b1Paul McLean    Status txResult = type == PORTTYPE_OUTPUT
10871f672b98a0c31001ca28de86d9ac99fef0812b1Paul McLean            ? device->server->openOutputPort(portToken, portNumber, &ufd)
10971f672b98a0c31001ca28de86d9ac99fef0812b1Paul McLean            : device->server->openInputPort(portToken, portNumber, &ufd);
110c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov    if (!txResult.isOk()) {
11171f672b98a0c31001ca28de86d9ac99fef0812b1Paul McLean        ALOGE("AMIDI_openPort transaction error: %d", txResult.transactionError());
112c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov        return txResult.transactionError();
113c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov    }
114c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov
11571f672b98a0c31001ca28de86d9ac99fef0812b1Paul McLean    AMIDI_Port* port = new AMIDI_Port;
11671f672b98a0c31001ca28de86d9ac99fef0812b1Paul McLean    port->state = MIDI_PORT_STATE_OPEN_IDLE;
11771f672b98a0c31001ca28de86d9ac99fef0812b1Paul McLean    port->device = device;
11871f672b98a0c31001ca28de86d9ac99fef0812b1Paul McLean    port->binderToken = portToken;
11971f672b98a0c31001ca28de86d9ac99fef0812b1Paul McLean    port->ufd = std::move(ufd);
12071f672b98a0c31001ca28de86d9ac99fef0812b1Paul McLean
12171f672b98a0c31001ca28de86d9ac99fef0812b1Paul McLean    *portPtr = port;
12271f672b98a0c31001ca28de86d9ac99fef0812b1Paul McLean
12371f672b98a0c31001ca28de86d9ac99fef0812b1Paul McLean    return OK;
12471f672b98a0c31001ca28de86d9ac99fef0812b1Paul McLean}
12571f672b98a0c31001ca28de86d9ac99fef0812b1Paul McLean
12671f672b98a0c31001ca28de86d9ac99fef0812b1Paul McLeanstatic status_t AMIDI_closePort(AMIDI_Port *port) {
12771f672b98a0c31001ca28de86d9ac99fef0812b1Paul McLean    int portState = MIDI_PORT_STATE_OPEN_IDLE;
12871f672b98a0c31001ca28de86d9ac99fef0812b1Paul McLean    while (!port->state.compare_exchange_weak(portState, MIDI_PORT_STATE_CLOSED)) {
12971f672b98a0c31001ca28de86d9ac99fef0812b1Paul McLean        if (portState == MIDI_PORT_STATE_CLOSED) {
13071f672b98a0c31001ca28de86d9ac99fef0812b1Paul McLean            return -EINVAL; // Already closed
13171f672b98a0c31001ca28de86d9ac99fef0812b1Paul McLean        }
13271f672b98a0c31001ca28de86d9ac99fef0812b1Paul McLean    }
13371f672b98a0c31001ca28de86d9ac99fef0812b1Paul McLean
13471f672b98a0c31001ca28de86d9ac99fef0812b1Paul McLean    Status txResult = port->device->server->closePort(port->binderToken);
13571f672b98a0c31001ca28de86d9ac99fef0812b1Paul McLean    if (!txResult.isOk()) {
13671f672b98a0c31001ca28de86d9ac99fef0812b1Paul McLean        return txResult.transactionError();
137c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov    }
13871f672b98a0c31001ca28de86d9ac99fef0812b1Paul McLean
13971f672b98a0c31001ca28de86d9ac99fef0812b1Paul McLean    delete port;
14071f672b98a0c31001ca28de86d9ac99fef0812b1Paul McLean
141c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov    return OK;
142c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov}
143c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov
14471f672b98a0c31001ca28de86d9ac99fef0812b1Paul McLean/*
14571f672b98a0c31001ca28de86d9ac99fef0812b1Paul McLean * Output (receiving) API
14671f672b98a0c31001ca28de86d9ac99fef0812b1Paul McLean */
14771f672b98a0c31001ca28de86d9ac99fef0812b1Paul McLeanstatus_t AMIDI_openOutputPort(AMIDI_Device *device, int portNumber,
14871f672b98a0c31001ca28de86d9ac99fef0812b1Paul McLean        AMIDI_OutputPort **outputPortPtr) {
14971f672b98a0c31001ca28de86d9ac99fef0812b1Paul McLean    return AMIDI_openPort(device, portNumber, PORTTYPE_OUTPUT, (AMIDI_Port**)outputPortPtr);
15071f672b98a0c31001ca28de86d9ac99fef0812b1Paul McLean}
15171f672b98a0c31001ca28de86d9ac99fef0812b1Paul McLean
15271f672b98a0c31001ca28de86d9ac99fef0812b1Paul McLeanssize_t AMIDI_receive(AMIDI_OutputPort *outputPort, AMIDI_Message *messages, ssize_t maxMessages) {
15371f672b98a0c31001ca28de86d9ac99fef0812b1Paul McLean    AMIDI_Port *port = (AMIDI_Port*)outputPort;
15471f672b98a0c31001ca28de86d9ac99fef0812b1Paul McLean    int portState = MIDI_PORT_STATE_OPEN_IDLE;
15571f672b98a0c31001ca28de86d9ac99fef0812b1Paul McLean    if (!port->state.compare_exchange_strong(portState, MIDI_PORT_STATE_OPEN_ACTIVE)) {
15671f672b98a0c31001ca28de86d9ac99fef0812b1Paul McLean        // The port has been closed.
15771f672b98a0c31001ca28de86d9ac99fef0812b1Paul McLean        return -EPIPE;
158c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov    }
159c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov
16071f672b98a0c31001ca28de86d9ac99fef0812b1Paul McLean    status_t result = OK;
161c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov    ssize_t messagesRead = 0;
162c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov    while (messagesRead < maxMessages) {
16371f672b98a0c31001ca28de86d9ac99fef0812b1Paul McLean        struct pollfd checkFds[1] = { { port->ufd, POLLIN, 0 } };
164c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov        int pollResult = poll(checkFds, 1, 0);
165c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov        if (pollResult < 1) {
166c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov            result = android::INVALID_OPERATION;
167c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov            break;
168c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov        }
169c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov
170c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov        AMIDI_Message *message = &messages[messagesRead];
171c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov        uint8_t readBuffer[AMIDI_PACKET_SIZE];
172c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov        memset(readBuffer, 0, sizeof(readBuffer));
17371f672b98a0c31001ca28de86d9ac99fef0812b1Paul McLean        ssize_t readCount = read(port->ufd, readBuffer, sizeof(readBuffer));
174c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov        if (readCount == EINTR) {
175c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov            continue;
176c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov        }
177c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov        if (readCount < 1) {
178c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov            result = android::NOT_ENOUGH_DATA;
179c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov            break;
180c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov        }
181c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov
182c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov        // set Packet Format definition at the top of this file.
183c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov        size_t dataSize = 0;
184c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov        message->opcode = readBuffer[0];
185c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov        message->timestamp = 0;
186c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov        if (message->opcode == AMIDI_OPCODE_DATA && readCount >= AMIDI_PACKET_OVERHEAD) {
187c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov            dataSize = readCount - AMIDI_PACKET_OVERHEAD;
188c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov            if (dataSize) {
189c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov                memcpy(message->buffer, readBuffer + 1, dataSize);
190c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov            }
19171f672b98a0c31001ca28de86d9ac99fef0812b1Paul McLean            message->timestamp = *(uint64_t*)(readBuffer + readCount - sizeof(uint64_t));
192c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov        }
193c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov        message->len = dataSize;
194c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov        ++messagesRead;
195c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov    }
196c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov
19771f672b98a0c31001ca28de86d9ac99fef0812b1Paul McLean    port->state.store(MIDI_PORT_STATE_OPEN_IDLE);
19871f672b98a0c31001ca28de86d9ac99fef0812b1Paul McLean
199c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov    return result == OK ? messagesRead : result;
200c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov}
201c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov
20271f672b98a0c31001ca28de86d9ac99fef0812b1Paul McLeanstatus_t AMIDI_closeOutputPort(AMIDI_OutputPort *outputPort) {
20371f672b98a0c31001ca28de86d9ac99fef0812b1Paul McLean    return AMIDI_closePort((AMIDI_Port*)outputPort);
204c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov}
205c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov
206c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov/*
207c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov * Input (sending) API
208c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov */
20971f672b98a0c31001ca28de86d9ac99fef0812b1Paul McLeanstatus_t AMIDI_openInputPort(AMIDI_Device *device, int portNumber, AMIDI_InputPort **inputPortPtr) {
21071f672b98a0c31001ca28de86d9ac99fef0812b1Paul McLean    return AMIDI_openPort(device, portNumber, PORTTYPE_INPUT, (AMIDI_Port**)inputPortPtr);
211c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov}
212c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov
21371f672b98a0c31001ca28de86d9ac99fef0812b1Paul McLeanstatus_t AMIDI_closeInputPort(AMIDI_InputPort *inputPort) {
21471f672b98a0c31001ca28de86d9ac99fef0812b1Paul McLean    return AMIDI_closePort((AMIDI_Port*)inputPort);
215c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov}
216c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov
21771f672b98a0c31001ca28de86d9ac99fef0812b1Paul McLeanssize_t AMIDI_getMaxMessageSizeInBytes(AMIDI_InputPort */*inputPort*/) {
218c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov    return SIZE_MIDIRECEIVEBUFFER;
219c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov}
220c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov
22171f672b98a0c31001ca28de86d9ac99fef0812b1Paul McLeanstatic ssize_t AMIDI_makeSendBuffer(
22271f672b98a0c31001ca28de86d9ac99fef0812b1Paul McLean        uint8_t *buffer, uint8_t *data, ssize_t numBytes,uint64_t timestamp) {
223c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov    buffer[0] = AMIDI_OPCODE_DATA;
224c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov    memcpy(buffer + 1, data, numBytes);
225c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov    memcpy(buffer + 1 + numBytes, &timestamp, sizeof(timestamp));
226c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov    return numBytes + AMIDI_PACKET_OVERHEAD;
227c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov}
228c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov
229c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov// Handy debugging function.
230c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov//static void AMIDI_logBuffer(uint8_t *data, size_t numBytes) {
231c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov//    for (size_t index = 0; index < numBytes; index++) {
232c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov//      ALOGI("  data @%zu [0x%X]", index, data[index]);
233c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov//    }
234c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov//}
235c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov
23671f672b98a0c31001ca28de86d9ac99fef0812b1Paul McLeanssize_t AMIDI_send(AMIDI_InputPort *inputPort, uint8_t *buffer, ssize_t numBytes) {
237c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov    return AMIDI_sendWithTimestamp(inputPort, buffer, numBytes, 0);
238c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov}
239c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov
24071f672b98a0c31001ca28de86d9ac99fef0812b1Paul McLeanssize_t AMIDI_sendWithTimestamp(AMIDI_InputPort *inputPort, uint8_t *data,
241c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov        ssize_t numBytes, int64_t timestamp) {
242c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov
243c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov    if (numBytes > SIZE_MIDIRECEIVEBUFFER) {
244c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov        return android::BAD_VALUE;
245c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov    }
246c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov
247c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov    // AMIDI_logBuffer(data, numBytes);
248c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov
249c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov    uint8_t writeBuffer[SIZE_MIDIRECEIVEBUFFER + AMIDI_PACKET_OVERHEAD];
250c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov    ssize_t numTransferBytes = AMIDI_makeSendBuffer(writeBuffer, data, numBytes, timestamp);
25171f672b98a0c31001ca28de86d9ac99fef0812b1Paul McLean    ssize_t numWritten = write(((AMIDI_Port*)inputPort)->ufd, writeBuffer, numTransferBytes);
252c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov
253c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov    if (numWritten < numTransferBytes) {
254c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov        ALOGE("AMIDI_sendWithTimestamp Couldn't write MIDI data buffer. requested:%zu, written%zu",
255c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov                numTransferBytes, numWritten);
256c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov    }
257c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov
258c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov    return numWritten - AMIDI_PACKET_OVERHEAD;
259c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov}
260c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov
26171f672b98a0c31001ca28de86d9ac99fef0812b1Paul McLeanstatus_t AMIDI_flush(AMIDI_InputPort *inputPort) {
262c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov    uint8_t opCode = AMIDI_OPCODE_FLUSH;
263c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov    ssize_t numTransferBytes = 1;
26471f672b98a0c31001ca28de86d9ac99fef0812b1Paul McLean    ssize_t numWritten = write(((AMIDI_Port*)inputPort)->ufd, &opCode, numTransferBytes);
265c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov
266c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov    if (numWritten < numTransferBytes) {
267c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov        ALOGE("AMIDI_flush Couldn't write MIDI flush. requested:%zu, written%zu",
268c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov                numTransferBytes, numWritten);
269c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov        return android::INVALID_OPERATION;
270c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov    }
271c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov
272c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov    return OK;
273c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov}
274c276c59eb5d0f36a1133ad718e5e1244390f4688Mikhail Naganov
275