mbed-os/usb/device/USBMIDI/USBMIDI.cpp

398 lines
10 KiB
C++
Raw Normal View History

/* 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;
}