mirror of https://github.com/ARMmbed/mbed-os.git
398 lines
10 KiB
C++
398 lines
10 KiB
C++
|
/* mbed Microcontroller Library
|
||
|
* Copyright (c) 2018-2018 ARM Limited
|
||
|
*
|
||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||
|
* you may not use this file except in compliance with the License.
|
||
|
* You may obtain a copy of the License at
|
||
|
*
|
||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||
|
*
|
||
|
* Unless required by applicable law or agreed to in writing, software
|
||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
|
* See the License for the specific language governing permissions and
|
||
|
* limitations under the License.
|
||
|
*/
|
||
|
|
||
|
#include "stdint.h"
|
||
|
#include "USBMIDI.h"
|
||
|
#include "EndpointResolver.h"
|
||
|
#include "usb_phy_api.h"
|
||
|
|
||
|
#define FLAG_WRITE_DONE (1 << 0)
|
||
|
#define FLAG_DISCONNECT (1 << 1)
|
||
|
#define FLAG_CONNECT (1 << 2)
|
||
|
|
||
|
USBMIDI::USBMIDI(bool connect_blocking, uint16_t vendor_id, uint16_t product_id, uint16_t product_release)
|
||
|
: USBDevice(get_usb_phy(), vendor_id, product_id, product_release)
|
||
|
{
|
||
|
_init();
|
||
|
|
||
|
if (connect_blocking) {
|
||
|
USBDevice::connect();
|
||
|
wait_ready();
|
||
|
} else {
|
||
|
init();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
USBMIDI::USBMIDI(USBPhy *phy, uint16_t vendor_id, uint16_t product_id, uint16_t product_release)
|
||
|
: USBDevice(phy, vendor_id, product_id, product_release)
|
||
|
{
|
||
|
_init();
|
||
|
|
||
|
// User or child responsible for calling connect or init
|
||
|
}
|
||
|
|
||
|
USBMIDI::~USBMIDI()
|
||
|
{
|
||
|
deinit();
|
||
|
}
|
||
|
|
||
|
void USBMIDI::_init()
|
||
|
{
|
||
|
_bulk_buf_pos = 0;
|
||
|
_bulk_buf_size = 0;
|
||
|
|
||
|
_data_ready = false;
|
||
|
_cur_data = 0;
|
||
|
|
||
|
EndpointResolver resolver(endpoint_table());
|
||
|
|
||
|
resolver.endpoint_ctrl(64);
|
||
|
_bulk_in = resolver.endpoint_in(USB_EP_TYPE_BULK, MaxSize);
|
||
|
_bulk_out = resolver.endpoint_out(USB_EP_TYPE_BULK, MaxSize);
|
||
|
MBED_ASSERT(resolver.valid());
|
||
|
}
|
||
|
|
||
|
bool USBMIDI::ready()
|
||
|
{
|
||
|
return _flags.get() & FLAG_CONNECT ? true : false;
|
||
|
}
|
||
|
|
||
|
void USBMIDI::wait_ready()
|
||
|
{
|
||
|
_flags.wait_any(FLAG_CONNECT, osWaitForever, false);
|
||
|
}
|
||
|
|
||
|
// write plain MIDIMessage that will be converted to USBMidi event packet
|
||
|
bool USBMIDI::write(MIDIMessage m)
|
||
|
{
|
||
|
_write_mutex.lock();
|
||
|
|
||
|
bool ret = true;
|
||
|
// first byte keeped for retro-compatibility
|
||
|
for (int p = 1; p < m.length; p += 3) {
|
||
|
uint8_t buf[4];
|
||
|
// Midi message to USBMidi event packet
|
||
|
buf[0] = m.data[1] >> 4;
|
||
|
// SysEx
|
||
|
if (buf[0] == 0xF) {
|
||
|
if ((m.length - p) > 3) {
|
||
|
// SysEx start or continue
|
||
|
buf[0] = 0x4;
|
||
|
} else {
|
||
|
switch (m.length - p) {
|
||
|
case 1:
|
||
|
// SysEx end with one byte
|
||
|
buf[0] = 0x5;
|
||
|
break;
|
||
|
case 2:
|
||
|
// SysEx end with two bytes
|
||
|
buf[0] = 0x6;
|
||
|
break;
|
||
|
case 3:
|
||
|
// SysEx end with three bytes
|
||
|
buf[0] = 0x7;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
buf[1] = m.data[p];
|
||
|
|
||
|
if (p + 1 < m.length) {
|
||
|
buf[2] = m.data[p + 1];
|
||
|
} else {
|
||
|
buf[2] = 0;
|
||
|
}
|
||
|
|
||
|
if (p + 2 < m.length) {
|
||
|
buf[3] = m.data[p + 2];
|
||
|
} else {
|
||
|
buf[3] = 0;
|
||
|
}
|
||
|
|
||
|
_flags.clear(FLAG_WRITE_DONE);
|
||
|
USBDevice::write_start(_bulk_in, buf, 4);
|
||
|
uint32_t flags = _flags.wait_any(FLAG_WRITE_DONE | FLAG_DISCONNECT, osWaitForever, false);
|
||
|
if (flags & FLAG_DISCONNECT) {
|
||
|
ret = false;
|
||
|
break;
|
||
|
}
|
||
|
USBDevice::write_finish(_bulk_in);
|
||
|
}
|
||
|
|
||
|
_write_mutex.unlock();
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
bool USBMIDI::readable()
|
||
|
{
|
||
|
lock();
|
||
|
|
||
|
bool ret = _data_ready;
|
||
|
|
||
|
unlock();
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
bool USBMIDI::read(MIDIMessage *m)
|
||
|
{
|
||
|
lock();
|
||
|
|
||
|
// Invalidate message
|
||
|
m->length = 0;
|
||
|
|
||
|
if (!_data_ready) {
|
||
|
unlock();
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
m->from_raw(_data, _cur_data);
|
||
|
_cur_data = 0;
|
||
|
_next_message();
|
||
|
|
||
|
if (!_data_ready) {
|
||
|
read_start(_bulk_out, _bulk_buf, MaxSize);
|
||
|
}
|
||
|
|
||
|
unlock();
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
void USBMIDI::attach(Callback<void()> callback)
|
||
|
{
|
||
|
lock();
|
||
|
|
||
|
_callback = callback;
|
||
|
|
||
|
unlock();
|
||
|
}
|
||
|
|
||
|
void USBMIDI::callback_state_change(DeviceState new_state)
|
||
|
{
|
||
|
assert_locked();
|
||
|
|
||
|
if (new_state == Configured) {
|
||
|
_flags.set(FLAG_CONNECT);
|
||
|
_flags.clear(FLAG_DISCONNECT);
|
||
|
} else {
|
||
|
_flags.set(FLAG_DISCONNECT);
|
||
|
_flags.clear(FLAG_CONNECT | FLAG_WRITE_DONE);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void USBMIDI::callback_request(const setup_packet_t *setup)
|
||
|
{
|
||
|
assert_locked();
|
||
|
|
||
|
RequestResult result = PassThrough;
|
||
|
uint8_t *data = NULL;
|
||
|
uint32_t size = 0;
|
||
|
|
||
|
complete_request(result, data, size);
|
||
|
}
|
||
|
|
||
|
void USBMIDI::callback_request_xfer_done(const setup_packet_t *setup, bool aborted)
|
||
|
{
|
||
|
assert_locked();
|
||
|
|
||
|
complete_request_xfer_done(false);
|
||
|
}
|
||
|
|
||
|
void USBMIDI::callback_set_configuration(uint8_t configuration)
|
||
|
{
|
||
|
assert_locked();
|
||
|
|
||
|
if (configuration == DEFAULT_CONFIGURATION) {
|
||
|
complete_set_configuration(false);
|
||
|
}
|
||
|
|
||
|
endpoint_remove_all();
|
||
|
endpoint_add(_bulk_in, MaxSize, USB_EP_TYPE_BULK, &USBMIDI::_in_callback);
|
||
|
endpoint_add(_bulk_out, MaxSize, USB_EP_TYPE_BULK, &USBMIDI::_out_callback);
|
||
|
|
||
|
read_start(_bulk_out, _bulk_buf, MaxSize);
|
||
|
|
||
|
complete_set_configuration(true);
|
||
|
}
|
||
|
|
||
|
void USBMIDI::callback_set_interface(uint16_t interface, uint8_t alternate)
|
||
|
{
|
||
|
assert_locked();
|
||
|
|
||
|
complete_set_interface(true);
|
||
|
}
|
||
|
|
||
|
const uint8_t *USBMIDI::string_iinterface_desc()
|
||
|
{
|
||
|
static const uint8_t string_iinterface_descriptor[] = {
|
||
|
0x0c, //bLength
|
||
|
STRING_DESCRIPTOR, //bDescriptorType 0x03
|
||
|
'A', 0, 'u', 0, 'd', 0, 'i', 0, 'o', 0 //bString iInterface - Audio
|
||
|
};
|
||
|
return string_iinterface_descriptor;
|
||
|
}
|
||
|
|
||
|
const uint8_t *USBMIDI::string_iproduct_desc()
|
||
|
{
|
||
|
static const uint8_t string_iproduct_descriptor[] = {
|
||
|
0x16, //bLength
|
||
|
STRING_DESCRIPTOR, //bDescriptorType 0x03
|
||
|
'M', 0, 'b', 0, 'e', 0, 'd', 0, ' ', 0, 'A', 0, 'u', 0, 'd', 0, 'i', 0, 'o', 0 //bString iProduct - Mbed Audio
|
||
|
};
|
||
|
return string_iproduct_descriptor;
|
||
|
}
|
||
|
|
||
|
const uint8_t *USBMIDI::configuration_desc(uint8_t index)
|
||
|
{
|
||
|
if (index != 0) {
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
uint8_t config_descriptor_temp[] = {
|
||
|
// configuration descriptor
|
||
|
0x09, 0x02, 0x65, 0x00, 0x02, 0x01, 0x00, 0xc0, 0x50,
|
||
|
|
||
|
// The Audio Interface Collection
|
||
|
0x09, 0x04, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, // Standard AC Interface Descriptor
|
||
|
0x09, 0x24, 0x01, 0x00, 0x01, 0x09, 0x00, 0x01, 0x01, // Class-specific AC Interface Descriptor
|
||
|
0x09, 0x04, 0x01, 0x00, 0x02, 0x01, 0x03, 0x00, 0x00, // MIDIStreaming Interface Descriptors
|
||
|
0x07, 0x24, 0x01, 0x00, 0x01, 0x41, 0x00, // Class-Specific MS Interface Header Descriptor
|
||
|
|
||
|
// MIDI IN JACKS
|
||
|
0x06, 0x24, 0x02, 0x01, 0x01, 0x00,
|
||
|
0x06, 0x24, 0x02, 0x02, 0x02, 0x00,
|
||
|
|
||
|
// MIDI OUT JACKS
|
||
|
0x09, 0x24, 0x03, 0x01, 0x03, 0x01, 0x02, 0x01, 0x00,
|
||
|
0x09, 0x24, 0x03, 0x02, 0x06, 0x01, 0x01, 0x01, 0x00,
|
||
|
|
||
|
// OUT endpoint - Standard MS Bulk Data Endpoint Descriptor
|
||
|
0x09, // bLength
|
||
|
0x05, // bDescriptorType
|
||
|
_bulk_out, // bEndpointAddress
|
||
|
0x02, // bmAttributes
|
||
|
0x40, // wMaxPacketSize (LSB)
|
||
|
0x00, // wMaxPacketSize (MSB)
|
||
|
0x00, // bInterval (milliseconds)
|
||
|
0x00, // bRefresh
|
||
|
0x00, // bSynchAddress
|
||
|
|
||
|
0x05, 0x25, 0x01, 0x01, 0x01,
|
||
|
|
||
|
// IN endpoint - Standard MS Bulk Data Endpoint Descriptor
|
||
|
0x09, // bLength
|
||
|
0x05, // bDescriptorType
|
||
|
_bulk_in, // bEndpointAddress
|
||
|
0x02, // bmAttributes
|
||
|
0x40, // wMaxPacketSize (LSB)
|
||
|
0x00, // wMaxPacketSize (MSB)
|
||
|
0x00, // bInterval (milliseconds)
|
||
|
0x00, // bRefresh
|
||
|
0x00, // bSynchAddress
|
||
|
|
||
|
0x05, 0x25, 0x01, 0x01, 0x03,
|
||
|
};
|
||
|
MBED_ASSERT(sizeof(config_descriptor_temp) == sizeof(_config_descriptor));
|
||
|
memcpy(_config_descriptor, config_descriptor_temp, sizeof(config_descriptor_temp));
|
||
|
return _config_descriptor;
|
||
|
}
|
||
|
|
||
|
void USBMIDI::_in_callback(usb_ep_t ep)
|
||
|
{
|
||
|
assert_locked();
|
||
|
|
||
|
_flags.set(FLAG_WRITE_DONE);
|
||
|
}
|
||
|
|
||
|
void USBMIDI::_out_callback(usb_ep_t ep)
|
||
|
{
|
||
|
assert_locked();
|
||
|
|
||
|
_bulk_buf_size = read_finish(_bulk_out);
|
||
|
_bulk_buf_pos = 0;
|
||
|
|
||
|
if (_callback && _next_message()) {
|
||
|
_callback();
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
read_start(_bulk_out, _bulk_buf, MaxSize);
|
||
|
}
|
||
|
|
||
|
bool USBMIDI::_next_message()
|
||
|
{
|
||
|
assert_locked();
|
||
|
|
||
|
bool data_ready = false;
|
||
|
while (_bulk_buf_pos < _bulk_buf_size) {
|
||
|
uint8_t data_read;
|
||
|
bool data_end = true;
|
||
|
switch (_bulk_buf[_bulk_buf_pos]) {
|
||
|
case 0x2:
|
||
|
// Two-bytes System Common Message - undefined in USBMidi 1.0
|
||
|
data_read = 2;
|
||
|
break;
|
||
|
case 0x4:
|
||
|
// SysEx start or continue
|
||
|
data_end = false;
|
||
|
data_read = 3;
|
||
|
break;
|
||
|
case 0x5:
|
||
|
// Single-byte System Common Message or SysEx end with one byte
|
||
|
data_read = 1;
|
||
|
break;
|
||
|
case 0x6:
|
||
|
// SysEx end with two bytes
|
||
|
data_read = 2;
|
||
|
break;
|
||
|
case 0xC:
|
||
|
// Program change
|
||
|
data_read = 2;
|
||
|
break;
|
||
|
case 0xD:
|
||
|
// Channel pressure
|
||
|
data_read = 2;
|
||
|
break;
|
||
|
case 0xF:
|
||
|
// Single byte
|
||
|
data_read = 1;
|
||
|
break;
|
||
|
default:
|
||
|
// Others three-bytes messages
|
||
|
data_read = 3;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
for (uint8_t j = 1; j < data_read + 1; j++) {
|
||
|
if (_cur_data < sizeof(_data)) {
|
||
|
_data[_cur_data] = _bulk_buf[_bulk_buf_pos + j];
|
||
|
}
|
||
|
_cur_data++;
|
||
|
}
|
||
|
_bulk_buf_pos += 4;
|
||
|
|
||
|
if (data_end) {
|
||
|
// Message is ready to be read
|
||
|
data_ready = true;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
_data_ready = data_ready;
|
||
|
return data_ready;
|
||
|
}
|