2016-03-21 15:35:40 +01:00
/*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* USB - MIDI class driver for USB Host Shield 2.0 Library
2021-05-09 17:06:47 +02:00
* Copyright ( c ) 2012 - 2021 Yuuichi Akagawa
2016-03-21 15:35:40 +01:00
*
* Idea from LPK25 USB - MIDI to Serial MIDI converter
* by Collin Cunningham - makezine . com , narbotic . com
*
* for use with USB Host Shield 2.0 from Circuitsathome . com
* https : //github.com/felis/USB_Host_Shield_2.0
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program . If not , see < http : //www.gnu.org/licenses/>
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*/
# include "usbh_midi.h"
2021-05-09 17:06:47 +02:00
// To enable serial debugging see "settings.h"
//#define EXTRADEBUG // Uncomment to get even more debugging data
2016-03-21 15:35:40 +01:00
//////////////////////////
2016-04-14 23:13:35 +02:00
// MIDI MESAGES
2016-03-21 15:35:40 +01:00
// midi.org/techspecs/
//////////////////////////
// STATUS BYTES
// 0x8n == noteOff
// 0x9n == noteOn
// 0xAn == afterTouch
// 0xBn == controlChange
// n == Channel(0x0-0xf)
//////////////////////////
//DATA BYTE 1
// note# == (0-127)
// or
// control# == (0-119)
//////////////////////////
// DATA BYTE 2
// velocity == (0-127)
// or
// controlVal == (0-127)
///////////////////////////////////////////////////////////////////////////////
// USB-MIDI Event Packets
// usb.org - Universal Serial Bus Device Class Definition for MIDI Devices 1.0
///////////////////////////////////////////////////////////////////////////////
//+-------------+-------------+-------------+-------------+
//| Byte 0 | Byte 1 | Byte 2 | Byte 3 |
//+------+------+-------------+-------------+-------------+
//|Cable | Code | | | |
//|Number|Index | MIDI_0 | MIDI_1 | MIDI_2 |
//| |Number| | | |
//|(4bit)|(4bit)| (8bit) | (8bit) | (8bit) |
//+------+------+-------------+-------------+-------------+
// CN == 0x0-0xf
//+-----+-----------+-------------------------------------------------------------------
//| CIN |MIDI_x size|Description
//+-----+-----------+-------------------------------------------------------------------
//| 0x0 | 1, 2 or 3 |Miscellaneous function codes. Reserved for future extensions.
//| 0x1 | 1, 2 or 3 |Cable events. Reserved for future expansion.
//| 0x2 | 2 |Two-byte System Common messages like MTC, SongSelect, etc.
//| 0x3 | 3 |Three-byte System Common messages like SPP, etc.
//| 0x4 | 3 |SysEx starts or continues
//| 0x5 | 1 |Single-byte System Common Message or SysEx ends with following single byte.
//| 0x6 | 2 |SysEx ends with following two bytes.
//| 0x7 | 3 |SysEx ends with following three bytes.
//| 0x8 | 3 |Note-off
//| 0x9 | 3 |Note-on
//| 0xA | 3 |Poly-KeyPress
//| 0xB | 3 |Control Change
//| 0xC | 2 |Program Change
//| 0xD | 2 |Channel Pressure
//| 0xE | 3 |PitchBend Change
//| 0xF | 1 |Single Byte
//+-----+-----------+-------------------------------------------------------------------
USBH_MIDI : : USBH_MIDI ( USB * p ) :
pUsb ( p ) ,
bAddress ( 0 ) ,
bPollEnable ( false ) ,
readPtr ( 0 ) {
// initialize endpoint data structures
for ( uint8_t i = 0 ; i < MIDI_MAX_ENDPOINTS ; i + + ) {
2017-02-26 15:01:08 +01:00
epInfo [ i ] . epAddr = 0 ;
2016-03-21 15:35:40 +01:00
epInfo [ i ] . maxPktSize = ( i ) ? 0 : 8 ;
2017-02-26 15:01:08 +01:00
epInfo [ i ] . bmNakPower = ( i ) ? USB_NAK_NOWAIT : USB_NAK_MAX_POWER ;
2016-03-21 15:35:40 +01:00
}
// register in USB subsystem
if ( pUsb ) {
pUsb - > RegisterDeviceClass ( this ) ;
}
}
/* Connection initialization of an MIDI Device */
uint8_t USBH_MIDI : : Init ( uint8_t parent , uint8_t port , bool lowspeed )
{
2017-02-26 15:01:08 +01:00
uint8_t buf [ sizeof ( USB_DEVICE_DESCRIPTOR ) ] ;
2016-03-21 15:35:40 +01:00
USB_DEVICE_DESCRIPTOR * udd = reinterpret_cast < USB_DEVICE_DESCRIPTOR * > ( buf ) ;
uint8_t rcode ;
UsbDevice * p = NULL ;
EpInfo * oldep_ptr = NULL ;
uint8_t num_of_conf ; // number of configurations
2021-05-09 17:06:47 +02:00
uint8_t bConfNum = 0 ; // configuration number
uint8_t bNumEP = 1 ; // total number of EP in the configuration
2016-03-21 15:35:40 +01:00
2016-04-26 16:44:07 +02:00
USBTRACE ( " \r MIDI Init \r \n " ) ;
2021-05-09 17:06:47 +02:00
# ifdef DEBUG_USB_HOST
Notify ( PSTR ( " USBH_MIDI version " ) , 0x80 ) , D_PrintHex ( ( uint8_t ) ( USBH_MIDI_VERSION / 10000 ) , 0x80 ) , D_PrintHex ( ( uint8_t ) ( USBH_MIDI_VERSION / 100 % 100 ) , 0x80 ) , D_PrintHex ( ( uint8_t ) ( USBH_MIDI_VERSION % 100 ) , 0x80 ) , Notify ( PSTR ( " \r \n " ) , 0x80 ) ;
# endif
2017-02-26 15:01:08 +01:00
2017-03-02 11:47:04 +01:00
//for reconnect
2017-02-26 15:01:08 +01:00
for ( uint8_t i = epDataInIndex ; i < = epDataOutIndex ; i + + ) {
epInfo [ i ] . bmSndToggle = 0 ;
epInfo [ i ] . bmRcvToggle = 0 ;
2021-05-09 17:06:47 +02:00
// If you want to retry if you get a NAK response when sending, enable the following:
// epInfo[i].bmNakPower = (i==epDataOutIndex) ? 10 : USB_NAK_NOWAIT;
2017-02-26 15:01:08 +01:00
}
2016-03-21 15:35:40 +01:00
// get memory address of USB device address pool
AddressPool & addrPool = pUsb - > GetAddressPool ( ) ;
2016-04-26 16:44:07 +02:00
2016-03-21 15:35:40 +01:00
// check if address has already been assigned to an instance
if ( bAddress ) {
return USB_ERROR_CLASS_INSTANCE_ALREADY_IN_USE ;
}
// Get pointer to pseudo device with address 0 assigned
p = addrPool . GetUsbDevicePtr ( bAddress ) ;
if ( ! p ) {
return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL ;
}
if ( ! p - > epinfo ) {
return USB_ERROR_EPINFO_IS_NULL ;
}
// Save old pointer to EP_RECORD of address 0
oldep_ptr = p - > epinfo ;
// Temporary assign new pointer to epInfo to p->epinfo in order to avoid toggle inconsistence
p - > epinfo = epInfo ;
p - > lowspeed = lowspeed ;
2020-11-23 12:08:07 +01:00
// First Device Descriptor Request (Initially first 8 bytes)
2020-11-23 13:51:36 +01:00
// https://techcommunity.microsoft.com/t5/microsoft-usb-blog/how-does-usb-stack-enumerate-a-device/ba-p/270685#_First_Device_Descriptor
2020-11-23 12:08:07 +01:00
rcode = pUsb - > getDevDescr ( 0 , 0 , 8 , ( uint8_t * ) buf ) ;
2016-03-21 15:35:40 +01:00
// Restore p->epinfo
p - > epinfo = oldep_ptr ;
2016-04-14 23:13:35 +02:00
if ( rcode ) {
2016-03-21 15:35:40 +01:00
goto FailGetDevDescr ;
}
// Allocate new address according to device class
bAddress = addrPool . AllocAddress ( parent , false , port ) ;
if ( ! bAddress ) {
return USB_ERROR_OUT_OF_ADDRESS_SPACE_IN_POOL ;
}
// Extract Max Packet Size from device descriptor
2016-04-14 23:13:35 +02:00
epInfo [ 0 ] . maxPktSize = udd - > bMaxPacketSize0 ;
2016-03-21 15:35:40 +01:00
// Assign new address to the device
rcode = pUsb - > setAddr ( 0 , 0 , bAddress ) ;
if ( rcode ) {
p - > lowspeed = false ;
addrPool . FreeAddress ( bAddress ) ;
bAddress = 0 ;
return rcode ;
} //if (rcode...
USBTRACE2 ( " Addr: " , bAddress ) ;
2016-04-26 16:44:07 +02:00
2016-03-21 15:35:40 +01:00
p - > lowspeed = false ;
//get pointer to assigned address record
p = addrPool . GetUsbDevicePtr ( bAddress ) ;
if ( ! p ) {
return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL ;
}
p - > lowspeed = lowspeed ;
2020-11-23 12:08:07 +01:00
// Second Device Descriptor Request (Full)
rcode = pUsb - > getDevDescr ( bAddress , 0 , sizeof ( USB_DEVICE_DESCRIPTOR ) , ( uint8_t * ) buf ) ;
if ( rcode ) {
goto FailGetDevDescr ;
}
vid = udd - > idVendor ;
pid = udd - > idProduct ;
2016-03-21 15:35:40 +01:00
num_of_conf = udd - > bNumConfigurations ;
// Assign epInfo to epinfo pointer
rcode = pUsb - > setEpInfoEntry ( bAddress , 1 , epInfo ) ;
if ( rcode ) {
USBTRACE ( " setEpInfoEntry failed " ) ;
goto FailSetDevTblEntry ;
}
2016-04-26 16:44:07 +02:00
USBTRACE ( " VID: " ) , D_PrintHex ( vid , 0x80 ) ;
USBTRACE ( " PID: " ) , D_PrintHex ( pid , 0x80 ) ;
USBTRACE2 ( " #Conf: " , num_of_conf ) ;
2018-03-24 15:48:55 +01:00
//Setup for well known vendor/device specific configuration
bTransferTypeMask = bmUSB_TRANSFER_TYPE ;
setupDeviceSpecific ( ) ;
2021-05-09 17:06:47 +02:00
// STEP1: Check if attached device is a MIDI device and fill endpoint data structure
USBTRACE ( " \r \n STEP1: MIDI Start \r \n " ) ;
for ( uint8_t i = 0 ; i < num_of_conf ; i + + ) {
MidiDescParser midiDescParser ( this , true ) ; // Check for MIDI device
rcode = pUsb - > getConfDescr ( bAddress , 0 , i , & midiDescParser ) ;
if ( rcode ) // Check error code
2017-02-26 15:01:08 +01:00
goto FailGetConfDescr ;
2021-05-09 17:06:47 +02:00
bNumEP + = midiDescParser . getNumEPs ( ) ;
if ( bNumEP > 1 ) { // All endpoints extracted
bConfNum = midiDescParser . getConfValue ( ) ;
2016-03-21 15:35:40 +01:00
break ;
2021-05-09 17:06:47 +02:00
}
}
USBTRACE2 ( " STEP1: MIDI,NumEP: " , bNumEP ) ;
//Found the MIDI device?
if ( bNumEP = = 1 ) { //Device not found.
USBTRACE ( " MIDI not found. \r \n STEP2: Attempts vendor specific bulk device \r \n " ) ;
// STEP2: Check if attached device is a MIDI device and fill endpoint data structure
for ( uint8_t i = 0 ; i < num_of_conf ; i + + ) {
MidiDescParser midiDescParser ( this , false ) ; // Allow all devices, vendor specific class with Bulk transfer
rcode = pUsb - > getConfDescr ( bAddress , 0 , i , & midiDescParser ) ;
if ( rcode ) // Check error code
goto FailGetConfDescr ;
bNumEP + = midiDescParser . getNumEPs ( ) ;
if ( bNumEP > 1 ) { // All endpoints extracted
bConfNum = midiDescParser . getConfValue ( ) ;
break ;
}
}
USBTRACE2 ( " \r \n STEP2: Vendor,NumEP: " , bNumEP ) ;
}
2016-04-26 16:44:07 +02:00
2017-02-26 15:01:08 +01:00
if ( bNumEP < 2 ) { //Device not found.
2016-04-26 16:44:07 +02:00
rcode = 0xff ;
2016-03-21 15:35:40 +01:00
goto FailGetConfDescr ;
}
// Assign epInfo to epinfo pointer
2017-02-26 15:01:08 +01:00
rcode = pUsb - > setEpInfoEntry ( bAddress , 3 , epInfo ) ;
2016-03-21 15:35:40 +01:00
USBTRACE2 ( " Conf: " , bConfNum ) ;
2016-04-26 16:44:07 +02:00
USBTRACE2 ( " EPin : " , ( uint8_t ) ( epInfo [ epDataInIndex ] . epAddr + 0x80 ) ) ;
USBTRACE2 ( " EPout: " , epInfo [ epDataOutIndex ] . epAddr ) ;
2016-03-21 15:35:40 +01:00
// Set Configuration Value
rcode = pUsb - > setConf ( bAddress , 0 , bConfNum ) ;
2021-05-09 17:06:47 +02:00
if ( rcode )
2016-03-21 15:35:40 +01:00
goto FailSetConfDescr ;
2021-05-09 17:06:47 +02:00
if ( pFuncOnInit )
pFuncOnInit ( ) ; // Call the user function
2016-03-21 15:35:40 +01:00
bPollEnable = true ;
2016-04-26 16:44:07 +02:00
USBTRACE ( " Init done. \r \n " ) ;
2016-03-21 15:35:40 +01:00
return 0 ;
FailGetDevDescr :
FailSetDevTblEntry :
FailGetConfDescr :
FailSetConfDescr :
Release ( ) ;
return rcode ;
}
/* Performs a cleanup after failed Init() attempt */
uint8_t USBH_MIDI : : Release ( )
{
pUsb - > GetAddressPool ( ) . FreeAddress ( bAddress ) ;
bAddress = 0 ;
bPollEnable = false ;
readPtr = 0 ;
return 0 ;
}
2018-03-24 15:48:55 +01:00
/* Setup for well known vendor/device specific configuration */
void USBH_MIDI : : setupDeviceSpecific ( )
{
// Novation
if ( vid = = 0x1235 ) {
2020-11-23 01:59:31 +01:00
// LaunchPad and LaunchKey endpoint attribute is interrupt
// https://github.com/YuuichiAkagawa/USBH_MIDI/wiki/Novation-USB-Product-ID-List
// LaunchPad: 0x20:S, 0x36:Mini, 0x51:Pro, 0x69:MK2
if ( pid = = 0x20 | | pid = = 0x36 | | pid = = 0x51 | | pid = = 0x69 ) {
bTransferTypeMask = 2 ;
return ;
}
// LaunchKey: 0x30-32, 0x35:Mini, 0x7B-0x7D:MK2
if ( ( 0x30 < = pid & & pid < = 0x32 ) | | pid = = 0x35 | | ( 0x7B < = pid & & pid < = 0x7D ) ) {
2018-03-24 15:48:55 +01:00
bTransferTypeMask = 2 ;
2020-11-23 01:59:31 +01:00
return ;
2018-03-24 15:48:55 +01:00
}
}
}
2016-03-21 15:35:40 +01:00
/* Receive data from MIDI device */
uint8_t USBH_MIDI : : RecvData ( uint16_t * bytes_rcvd , uint8_t * dataptr )
{
* bytes_rcvd = ( uint16_t ) epInfo [ epDataInIndex ] . maxPktSize ;
uint8_t r = pUsb - > inTransfer ( bAddress , epInfo [ epDataInIndex ] . epAddr , bytes_rcvd , dataptr ) ;
2021-05-09 17:06:47 +02:00
# ifdef EXTRADEBUG
if ( r )
USBTRACE2 ( " inTransfer(): " , r ) ;
# endif
2016-03-21 15:35:40 +01:00
if ( * bytes_rcvd < ( MIDI_EVENT_PACKET_SIZE - 4 ) ) {
dataptr [ * bytes_rcvd ] = ' \0 ' ;
dataptr [ ( * bytes_rcvd ) + 1 ] = ' \0 ' ;
}
return r ;
}
/* Receive data from MIDI device */
2017-02-26 15:01:08 +01:00
uint8_t USBH_MIDI : : RecvData ( uint8_t * outBuf , bool isRaw )
2016-03-21 15:35:40 +01:00
{
2016-04-26 16:44:07 +02:00
uint8_t rcode = 0 ; //return code
2016-03-21 15:35:40 +01:00
uint16_t rcvd ;
2017-02-26 15:01:08 +01:00
if ( bPollEnable = = false ) return 0 ;
2016-03-21 15:35:40 +01:00
//Checking unprocessed message in buffer.
if ( readPtr ! = 0 & & readPtr < MIDI_EVENT_PACKET_SIZE ) {
if ( recvBuf [ readPtr ] = = 0 & & recvBuf [ readPtr + 1 ] = = 0 ) {
//no unprocessed message left in the buffer.
} else {
goto RecvData_return_from_buffer ;
}
}
readPtr = 0 ;
rcode = RecvData ( & rcvd , recvBuf ) ;
if ( rcode ! = 0 ) {
return 0 ;
}
//if all data is zero, no valid data received.
if ( recvBuf [ 0 ] = = 0 & & recvBuf [ 1 ] = = 0 & & recvBuf [ 2 ] = = 0 & & recvBuf [ 3 ] = = 0 ) {
return 0 ;
}
RecvData_return_from_buffer :
2017-02-26 15:01:08 +01:00
uint8_t m ;
uint8_t cin = recvBuf [ readPtr ] ;
if ( isRaw = = true ) {
* ( outBuf + + ) = cin ;
}
2016-03-21 15:35:40 +01:00
readPtr + + ;
2017-02-26 15:01:08 +01:00
* ( outBuf + + ) = m = recvBuf [ readPtr + + ] ;
* ( outBuf + + ) = recvBuf [ readPtr + + ] ;
* ( outBuf + + ) = recvBuf [ readPtr + + ] ;
2021-05-09 17:06:47 +02:00
return getMsgSizeFromCin ( cin ) ;
2016-03-21 15:35:40 +01:00
}
/* Send data to MIDI device */
2016-04-26 16:44:07 +02:00
uint8_t USBH_MIDI : : SendData ( uint8_t * dataptr , uint8_t nCable )
2016-03-21 15:35:40 +01:00
{
2016-04-26 16:44:07 +02:00
uint8_t buf [ 4 ] ;
2021-05-09 17:06:47 +02:00
uint8_t status = dataptr [ 0 ] ;
2016-03-21 15:35:40 +01:00
2021-05-09 17:06:47 +02:00
uint8_t cin = convertStatus2Cin ( status ) ;
if ( status = = 0xf0 ) {
// SysEx long message
2016-03-21 15:35:40 +01:00
return SendSysEx ( dataptr , countSysExDataSize ( dataptr ) , nCable ) ;
}
//Building USB-MIDI Event Packets
2021-05-09 17:06:47 +02:00
buf [ 0 ] = ( uint8_t ) ( nCable < < 4 ) | cin ;
2016-03-21 15:35:40 +01:00
buf [ 1 ] = dataptr [ 0 ] ;
2021-05-09 17:06:47 +02:00
uint8_t msglen = getMsgSizeFromCin ( cin ) ;
switch ( msglen ) {
2016-03-21 15:35:40 +01:00
//3 bytes message
case 3 :
2021-05-09 17:06:47 +02:00
buf [ 2 ] = dataptr [ 1 ] ;
buf [ 3 ] = dataptr [ 2 ] ;
2016-03-21 15:35:40 +01:00
break ;
//2 bytes message
case 2 :
2021-05-09 17:06:47 +02:00
buf [ 2 ] = dataptr [ 1 ] ;
2016-03-21 15:35:40 +01:00
buf [ 3 ] = 0 ;
break ;
2016-04-26 16:44:07 +02:00
//1 byte message
2016-03-21 15:35:40 +01:00
case 1 :
buf [ 2 ] = 0 ;
buf [ 3 ] = 0 ;
break ;
2021-05-09 17:06:47 +02:00
default :
break ;
2016-03-21 15:35:40 +01:00
}
2021-05-09 17:06:47 +02:00
# ifdef EXTRADEBUG
//Dump for raw USB-MIDI event packet
Notify ( PSTR ( " SendData(): " ) , 0x80 ) , D_PrintHex ( ( buf [ 0 ] ) , 0x80 ) , D_PrintHex ( ( buf [ 1 ] ) , 0x80 ) , D_PrintHex ( ( buf [ 2 ] ) , 0x80 ) , D_PrintHex ( ( buf [ 3 ] ) , 0x80 ) , Notify ( PSTR ( " \r \n " ) , 0x80 ) ;
# endif
2016-03-21 15:35:40 +01:00
return pUsb - > outTransfer ( bAddress , epInfo [ epDataOutIndex ] . epAddr , 4 , buf ) ;
}
# ifdef DEBUG_USB_HOST
void USBH_MIDI : : PrintEndpointDescriptor ( const USB_ENDPOINT_DESCRIPTOR * ep_ptr )
{
2016-04-26 16:44:07 +02:00
USBTRACE ( " Endpoint descriptor: \r \n " ) ;
USBTRACE2 ( " Length: \t " , ep_ptr - > bLength ) ;
USBTRACE2 ( " Type: \t \t " , ep_ptr - > bDescriptorType ) ;
USBTRACE2 ( " Address: \t " , ep_ptr - > bEndpointAddress ) ;
USBTRACE2 ( " Attributes: \t " , ep_ptr - > bmAttributes ) ;
USBTRACE2 ( " MaxPktSize: \t " , ep_ptr - > wMaxPacketSize ) ;
USBTRACE2 ( " Poll Intrv: \t " , ep_ptr - > bInterval ) ;
2016-03-21 15:35:40 +01:00
}
# endif
/* look up a MIDI message size from spec */
/*Return */
/* 0 : undefined message */
/* 0<: Vaild message size(1-3) */
2021-05-09 17:06:47 +02:00
//uint8_t USBH_MIDI::lookupMsgSize(uint8_t midiMsg, uint8_t cin)
uint8_t USBH_MIDI : : lookupMsgSize ( uint8_t status , uint8_t cin )
2016-03-21 15:35:40 +01:00
{
2021-05-09 17:06:47 +02:00
if ( cin = = 0 ) {
cin = convertStatus2Cin ( status ) ;
2017-02-26 15:01:08 +01:00
}
2021-05-09 17:06:47 +02:00
return getMsgSizeFromCin ( cin ) ;
2016-03-21 15:35:40 +01:00
}
/* SysEx data size counter */
2017-02-12 17:14:01 +01:00
uint16_t USBH_MIDI : : countSysExDataSize ( uint8_t * dataptr )
2016-03-21 15:35:40 +01:00
{
2017-02-12 17:14:01 +01:00
uint16_t c = 1 ;
2016-03-21 15:35:40 +01:00
if ( * dataptr ! = 0xf0 ) { //not SysEx
return 0 ;
}
//Search terminator(0xf7)
2021-05-09 17:06:47 +02:00
while ( * dataptr ! = 0xf7 ) {
2016-03-21 15:35:40 +01:00
dataptr + + ;
c + + ;
2017-02-26 15:01:08 +01:00
//Limiter (default: 256 bytes)
if ( c > MIDI_MAX_SYSEX_SIZE ) {
2016-03-21 15:35:40 +01:00
c = 0 ;
break ;
}
}
return c ;
}
/* Send SysEx message to MIDI device */
2017-02-12 17:14:01 +01:00
uint8_t USBH_MIDI : : SendSysEx ( uint8_t * dataptr , uint16_t datasize , uint8_t nCable )
2016-03-21 15:35:40 +01:00
{
2017-02-26 15:01:08 +01:00
uint8_t buf [ MIDI_EVENT_PACKET_SIZE ] ;
uint8_t rc = 0 ;
2017-02-12 17:14:01 +01:00
uint16_t n = datasize ;
2016-04-26 16:44:07 +02:00
uint8_t wptr = 0 ;
uint8_t maxpkt = epInfo [ epDataInIndex ] . maxPktSize ;
USBTRACE ( " SendSysEx: \r \t " ) ;
USBTRACE2 ( " Length: \t " , datasize ) ;
2021-05-09 17:06:47 +02:00
# ifdef EXTRADEBUG
uint16_t pktSize = ( n + 2 ) / 3 ; //Calculate total USB MIDI packet size
2016-04-26 16:44:07 +02:00
USBTRACE2 ( " Total pktSize: \t " , pktSize ) ;
2021-05-09 17:06:47 +02:00
# endif
2016-03-21 15:35:40 +01:00
while ( n > 0 ) {
//Byte 0
2016-04-26 16:44:07 +02:00
buf [ wptr ] = ( nCable < < 4 ) | 0x4 ; //x4 SysEx starts or continues
2016-03-21 15:35:40 +01:00
switch ( n ) {
2016-04-26 16:44:07 +02:00
case 1 :
buf [ wptr + + ] = ( nCable < < 4 ) | 0x5 ; //x5 SysEx ends with following single byte.
buf [ wptr + + ] = * ( dataptr + + ) ;
buf [ wptr + + ] = 0x00 ;
buf [ wptr + + ] = 0x00 ;
2021-05-09 17:06:47 +02:00
n = 0 ;
2016-03-21 15:35:40 +01:00
break ;
2016-04-26 16:44:07 +02:00
case 2 :
buf [ wptr + + ] = ( nCable < < 4 ) | 0x6 ; //x6 SysEx ends with following two bytes.
buf [ wptr + + ] = * ( dataptr + + ) ;
buf [ wptr + + ] = * ( dataptr + + ) ;
buf [ wptr + + ] = 0x00 ;
2021-05-09 17:06:47 +02:00
n = 0 ;
2016-03-21 15:35:40 +01:00
break ;
2016-04-26 16:44:07 +02:00
case 3 :
buf [ wptr ] = ( nCable < < 4 ) | 0x7 ; //x7 SysEx ends with following three bytes.
2020-11-15 23:27:08 +01:00
// fall through
2016-04-26 16:44:07 +02:00
default :
wptr + + ;
buf [ wptr + + ] = * ( dataptr + + ) ;
buf [ wptr + + ] = * ( dataptr + + ) ;
buf [ wptr + + ] = * ( dataptr + + ) ;
2016-03-21 15:35:40 +01:00
n = n - 3 ;
break ;
}
2016-04-26 16:44:07 +02:00
if ( wptr > = maxpkt | | n = = 0 ) { //Reach a maxPktSize or data end.
USBTRACE2 ( " wptr: \t " , wptr ) ;
if ( ( rc = pUsb - > outTransfer ( bAddress , epInfo [ epDataOutIndex ] . epAddr , wptr , buf ) ) ! = 0 ) {
break ;
}
2021-05-09 17:06:47 +02:00
wptr = 0 ; //rewind write pointer
2016-04-26 16:44:07 +02:00
}
}
return ( rc ) ;
}
2017-02-26 15:01:08 +01:00
uint8_t USBH_MIDI : : extractSysExData ( uint8_t * p , uint8_t * buf )
2016-04-26 16:44:07 +02:00
{
2017-02-26 15:01:08 +01:00
uint8_t rc = 0 ;
2016-04-26 16:44:07 +02:00
uint8_t cin = * ( p ) & 0x0f ;
//SysEx message?
2017-02-26 15:01:08 +01:00
if ( ( cin & 0xc ) ! = 4 ) return rc ;
2016-04-26 16:44:07 +02:00
switch ( cin ) {
case 4 :
case 7 :
2017-02-26 15:01:08 +01:00
* buf + + = * ( p + 1 ) ;
* buf + + = * ( p + 2 ) ;
* buf + + = * ( p + 3 ) ;
rc = 3 ;
2016-04-26 16:44:07 +02:00
break ;
case 6 :
2017-02-26 15:01:08 +01:00
* buf + + = * ( p + 1 ) ;
* buf + + = * ( p + 2 ) ;
rc = 2 ;
break ;
case 5 :
* buf + + = * ( p + 1 ) ;
rc = 1 ;
2016-04-26 16:44:07 +02:00
break ;
default :
break ;
}
2016-03-21 15:35:40 +01:00
return ( rc ) ;
}
2021-05-09 17:06:47 +02:00
// Configuration Descriptor Parser
// Copied from confdescparser.h and modifiy.
MidiDescParser : : MidiDescParser ( UsbMidiConfigXtracter * xtractor , bool modeMidi ) :
theXtractor ( xtractor ) ,
stateParseDescr ( 0 ) ,
dscrLen ( 0 ) ,
dscrType ( 0 ) ,
nEPs ( 0 ) ,
isMidiSearch ( modeMidi ) {
theBuffer . pValue = varBuffer ;
valParser . Initialize ( & theBuffer ) ;
theSkipper . Initialize ( & theBuffer ) ;
}
void MidiDescParser : : Parse ( const uint16_t len , const uint8_t * pbuf , const uint16_t & offset __attribute__ ( ( unused ) ) ) {
uint16_t cntdn = ( uint16_t ) len ;
uint8_t * p = ( uint8_t * ) pbuf ;
while ( cntdn )
if ( ! ParseDescriptor ( & p , & cntdn ) )
return ;
}
bool MidiDescParser : : ParseDescriptor ( uint8_t * * pp , uint16_t * pcntdn ) {
USB_CONFIGURATION_DESCRIPTOR * ucd = reinterpret_cast < USB_CONFIGURATION_DESCRIPTOR * > ( varBuffer ) ;
USB_INTERFACE_DESCRIPTOR * uid = reinterpret_cast < USB_INTERFACE_DESCRIPTOR * > ( varBuffer ) ;
switch ( stateParseDescr ) {
case 0 :
theBuffer . valueSize = 2 ;
valParser . Initialize ( & theBuffer ) ;
stateParseDescr = 1 ;
// fall through
case 1 :
if ( ! valParser . Parse ( pp , pcntdn ) )
return false ;
dscrLen = * ( ( uint8_t * ) theBuffer . pValue ) ;
dscrType = * ( ( uint8_t * ) theBuffer . pValue + 1 ) ;
stateParseDescr = 2 ;
// fall through
case 2 :
// This is a sort of hack. Assuming that two bytes are all ready in the buffer
// the pointer is positioned two bytes ahead in order for the rest of descriptor
// to be read right after the size and the type fields.
// This should be used carefully. varBuffer should be used directly to handle data
// in the buffer.
theBuffer . pValue = varBuffer + 2 ;
stateParseDescr = 3 ;
// fall through
case 3 :
switch ( dscrType ) {
case USB_DESCRIPTOR_INTERFACE :
isGoodInterface = false ;
break ;
case USB_DESCRIPTOR_CONFIGURATION :
case USB_DESCRIPTOR_ENDPOINT :
case HID_DESCRIPTOR_HID :
break ;
}
theBuffer . valueSize = dscrLen - 2 ;
valParser . Initialize ( & theBuffer ) ;
stateParseDescr = 4 ;
// fall through
case 4 :
switch ( dscrType ) {
case USB_DESCRIPTOR_CONFIGURATION :
if ( ! valParser . Parse ( pp , pcntdn ) )
return false ;
confValue = ucd - > bConfigurationValue ;
break ;
case USB_DESCRIPTOR_INTERFACE :
if ( ! valParser . Parse ( pp , pcntdn ) )
return false ;
USBTRACE ( " Interface descriptor: \r \n " ) ;
USBTRACE2 ( " Inf#: \t \t " , uid - > bInterfaceNumber ) ;
USBTRACE2 ( " Alt: \t \t " , uid - > bAlternateSetting ) ;
USBTRACE2 ( " EPs: \t \t " , uid - > bNumEndpoints ) ;
USBTRACE2 ( " IntCl: \t \t " , uid - > bInterfaceClass ) ;
USBTRACE2 ( " IntSubcl: \t " , uid - > bInterfaceSubClass ) ;
USBTRACE2 ( " Protocol: \t " , uid - > bInterfaceProtocol ) ;
// MIDI check mode ?
if ( isMidiSearch ) { //true: MIDI Streaming, false: ALL
if ( uid - > bInterfaceClass = = USB_CLASS_AUDIO & & uid - > bInterfaceSubClass = = USB_SUBCLASS_MIDISTREAMING ) {
// MIDI found.
USBTRACE ( " +MIDI found \r \n \r \n " ) ;
} else {
USBTRACE ( " -MIDI not found \r \n \r \n " ) ;
break ;
}
}
isGoodInterface = true ;
// Initialize the counter if no two endpoints can be found in one interface.
if ( nEPs < 2 )
// reset endpoint counter
nEPs = 0 ;
break ;
case USB_DESCRIPTOR_ENDPOINT :
if ( ! valParser . Parse ( pp , pcntdn ) )
return false ;
if ( isGoodInterface & & nEPs < 2 ) {
USBTRACE ( " >Extracting endpoint \r \n " ) ;
if ( theXtractor - > EndpointXtract ( confValue , 0 , 0 , 0 , ( USB_ENDPOINT_DESCRIPTOR * ) varBuffer ) )
nEPs + + ;
}
break ;
default :
if ( ! theSkipper . Skip ( pp , pcntdn , dscrLen - 2 ) )
return false ;
}
theBuffer . pValue = varBuffer ;
stateParseDescr = 0 ;
}
return true ;
}
/* Extracts endpoint information from config descriptor */
bool USBH_MIDI : : EndpointXtract ( uint8_t conf __attribute__ ( ( unused ) ) ,
uint8_t iface __attribute__ ( ( unused ) ) ,
uint8_t alt __attribute__ ( ( unused ) ) ,
uint8_t proto __attribute__ ( ( unused ) ) ,
const USB_ENDPOINT_DESCRIPTOR * pep )
{
uint8_t index ;
# ifdef DEBUG_USB_HOST
PrintEndpointDescriptor ( pep ) ;
# endif
// Is the endpoint transfer type bulk?
if ( ( pep - > bmAttributes & bTransferTypeMask ) = = USB_TRANSFER_TYPE_BULK ) {
USBTRACE ( " +valid EP found. \r \n " ) ;
index = ( pep - > bEndpointAddress & 0x80 ) = = 0x80 ? epDataInIndex : epDataOutIndex ;
} else {
USBTRACE ( " -No valid EP found. \r \n " ) ;
return false ;
}
// Fill the rest of endpoint data structure
epInfo [ index ] . epAddr = ( pep - > bEndpointAddress & 0x0F ) ;
// The maximum packet size for the USB Host Shield 2.0 library is 64 bytes.
if ( pep - > wMaxPacketSize > MIDI_EVENT_PACKET_SIZE ) {
epInfo [ index ] . maxPktSize = MIDI_EVENT_PACKET_SIZE ;
} else {
epInfo [ index ] . maxPktSize = ( uint8_t ) pep - > wMaxPacketSize ;
}
return true ;
}