<repo_name>203Electronics/USBComposite_stm32f1<file_sep>/HIDReports.cpp
#include "USBComposite.h" 

#define REPORT(name, ...) \
    static uint8_t raw_ ## name[] = { __VA_ARGS__ }; \
    static const HIDReportDescriptor desc_ ## name = { raw_ ##name, sizeof(raw_ ##name) }; \
    const HIDReportDescriptor* hidReport ## name = & desc_ ## name;

REPORT(KeyboardMouseJoystick, HID_MOUSE_REPORT_DESCRIPTOR(), HID_KEYBOARD_REPORT_DESCRIPTOR(), HID_JOYSTICK_REPORT_DESCRIPTOR());
REPORT(KeyboardMouse, HID_MOUSE_REPORT_DESCRIPTOR(), HID_KEYBOARD_REPORT_DESCRIPTOR());
REPORT(Keyboard, HID_KEYBOARD_REPORT_DESCRIPTOR());
REPORT(Mouse, HID_MOUSE_REPORT_DESCRIPTOR());
REPORT(AbsMouse, HID_ABS_MOUSE_REPORT_DESCRIPTOR());
REPORT(KeyboardJoystick, HID_KEYBOARD_REPORT_DESCRIPTOR(), HID_JOYSTICK_REPORT_DESCRIPTOR());
REPORT(Joystick, HID_JOYSTICK_REPORT_DESCRIPTOR());
REPORT(BootKeyboard, HID_BOOT_KEYBOARD_REPORT_DESCRIPTOR());
REPORT(Consumer, HID_CONSUMER_REPORT_DESCRIPTOR());
<file_sep>/USBHID.cpp
/* Copyright (c) 2011, <NAME>
 * Copyright (c) 2017-2018, <NAME>
**
** Permission to use, copy, modify, and/or distribute this software for
** any purpose with or without fee is hereby granted, provided that the
** above copyright notice and this permission notice appear in all copies.
**
** THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
** WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
** WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR
** BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES
** OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
** WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
** ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
** SOFTWARE.
*/

#include "USBComposite.h" 

#include <string.h>
#include <stdint.h>
#include <libmaple/nvic.h>
#include "usb_hid.h"
#include "usb_composite_serial.h"
#include "usb_generic.h"
#include <libmaple/usb.h>
#include <string.h>
#include <libmaple/iwdg.h>

/*
 * USB HID interface
 */

bool USBHID::init(USBHID* me) {
    usb_hid_setTXEPSize(me->txPacketSize);
    
    HIDReporter* r = me->profiles;
    
    if (me->baseChunk.data != NULL) {
        /* user set an explicit report for USBHID */
        while (r != NULL) {
            r->reportID = r->userSuppliedReportID;
            if (r->reportID != 0)
                r->reportBuffer[0] = r->reportID;
            r = r->next;
        }
        usb_hid_set_report_descriptor(&(me->baseChunk));
    }
    else {
        uint8 reportID = HID_AUTO_REPORT_ID_START;
        
        while (r != NULL) {
            if (r->forceUserSuppliedReportID) 
                r->reportID = r->userSuppliedReportID;
            else 
                r->reportID = reportID++;
            
            if (r->reportID != 0) 
                r->reportBuffer[0] = r->reportID;
            
            r = r->next;
        }
        usb_hid_set_report_descriptor(me->chunkList);
    }

	return true;
}

bool USBHID::registerComponent() {
	return USBComposite.add(&usbHIDPart, this, (USBPartInitializer)&USBHID::init);
}

void USBHID::setReportDescriptor(const uint8_t* report_descriptor, uint16_t report_descriptor_length) {
    baseChunk.dataLength = report_descriptor_length;
    baseChunk.data = report_descriptor;
    baseChunk.next = NULL;
}

void USBHID::clear() {
    clearBuffers();
    baseChunk.data = NULL;
    baseChunk.dataLength = 0;
    baseChunk.next = NULL;
    chunkList = NULL;
    profiles = NULL;
}

void USBHID::addReport(HIDReporter* r, bool always) {
    if (! always && ! autoRegister)
        return;
    
    r->next = NULL;
    if (profiles == NULL) {
        profiles = r;
    }
    else {
        HIDReporter* tail = profiles;
        while (tail != r && tail->next != NULL) {
            tail = tail->next;
        }
        if (tail == r)
            return;
        tail->next = r;
    }

    struct usb_chunk* chunkTail = chunkList;
    
    if (chunkTail == NULL) {
        chunkList = &(r->reportChunks[0]);
        chunkTail = chunkList;
    }
    else {
        while (chunkTail->next != NULL)
            chunkTail = chunkTail->next;
        chunkTail->next = &(r->reportChunks[0]);
        chunkTail = chunkTail->next;
    }
    chunkTail->next = &(r->reportChunks[1]);
    chunkTail = chunkTail->next;
    chunkTail->next = &(r->reportChunks[2]);
    chunkTail = chunkTail->next;
    chunkTail->next = NULL;
}

void USBHID::setReportDescriptor(const HIDReportDescriptor* report) {
    if (report == NULL)
        setReportDescriptor(NULL, 0);
    else
        setReportDescriptor(report->descriptor, report->length);
}

void USBHID::begin(const uint8_t* report_descriptor, uint16_t report_descriptor_length) {
            
	if (enabledHID)
		return;
	
	setReportDescriptor(report_descriptor, report_descriptor_length);
	
	USBComposite.clear();
	registerComponent();

	USBComposite.begin(); 
	
	enabledHID = true;
}

void USBHID::begin(const HIDReportDescriptor* report) {
    if (report == NULL)
        begin(NULL, 0);
    else
        begin(report->descriptor, report->length);
}

void USBHID::setBuffers(uint8_t type, volatile HIDBuffer_t* fb, int count) {
    usb_hid_set_buffers(type, fb, count);
}

bool USBHID::addBuffer(uint8_t type, volatile HIDBuffer_t* buffer) {
    return 0 != usb_hid_add_buffer(type, buffer);
}

void USBHID::clearBuffers(uint8_t type) {
	usb_hid_clear_buffers(type);
}

void USBHID::clearBuffers() {
	clearBuffers(HID_REPORT_TYPE_OUTPUT);
	clearBuffers(HID_REPORT_TYPE_FEATURE);
}

void USBHID::end(void){
	if(enabledHID) {
		USBComposite.end();
		enabledHID = false;
	}
}

void USBHID::begin(USBCompositeSerial serial, const uint8_t* report_descriptor, uint16_t report_descriptor_length) {	
	USBComposite.clear();

	setReportDescriptor(report_descriptor, report_descriptor_length);
	registerComponent();

	serial.registerComponent();

	USBComposite.begin();
}
		
void USBHID::begin(USBCompositeSerial serial, const HIDReportDescriptor* report) {
    begin(serial, report->descriptor, report->length);
}

void HIDReporter::sendReport() {
//    while (usb_is_transmitting() != 0) {
//    }

    unsigned toSend = bufferSize;
    uint8* b = reportBuffer;
    
    while (toSend) {
        unsigned delta = usb_hid_tx(b, toSend);
        toSend -= delta;
        b += delta;
    }
    
//    while (usb_is_transmitting() != 0) {
//    }

    /* flush out to avoid having the pc wait for more data */
    usb_hid_tx(NULL, 0);
}

void HIDReporter::registerProfile(bool always) {
    for (uint32 i=0; i<3; i++) {
        reportChunks[i].data = NULL;
        reportChunks[i].dataLength = 0;
    }
    if (reportDescriptor.descriptor != NULL) {
        int32_t reportIDOffset = -1;
        if (! forceUserSuppliedReportID) {
            uint32_t i = 0;
            
            /*
             * parse bits of beginning of report looking for a single-byte report ID (0x85 ID)
             */
            while (i < reportDescriptor.length && reportIDOffset < 0) {
                if (reportDescriptor.descriptor[i]==0x85) {
                    if (i+1 < reportDescriptor.length)
                        reportIDOffset = i+1;
                }
                // the lowest 2 bit determine the data length of the tag
                if ((reportDescriptor.descriptor[i] & 3) == 3)
                    i += 5;
                else 
                    i += 1 + (reportDescriptor.descriptor[i] & 3);
            }
            if (i >= reportDescriptor.length) {
                forceUserSuppliedReportID = true;
            }
        }
        if (forceUserSuppliedReportID) {
            reportChunks[0].data = reportDescriptor.descriptor;
            reportChunks[0].dataLength = reportDescriptor.length;
        }
        else {
            reportChunks[0].data = reportDescriptor.descriptor;
            reportChunks[0].dataLength = reportIDOffset;
            reportChunks[1].data = &(reportID);
            reportChunks[1].dataLength = 1;
            reportChunks[2].data = reportDescriptor.descriptor+reportIDOffset+1;
            reportChunks[2].dataLength = reportDescriptor.length - (reportIDOffset+1);
        }
    }
    HID.addReport(this, always);
}
        
HIDReporter::HIDReporter(USBHID& _HID, const HIDReportDescriptor* r, uint8_t* _buffer, unsigned _size, uint8_t _reportID, bool forceReportID) : HID(_HID) {
    if (r != NULL) {
        memcpy(&reportDescriptor, r, sizeof(reportDescriptor));
    }
    else {
        reportDescriptor.descriptor = NULL;
        reportDescriptor.length = 0;
    }

    if (_reportID == 0) {
        reportBuffer = _buffer+1;
        bufferSize = _size-1;
    }
    else {
        reportBuffer = _buffer;
        bufferSize = _size;
    }
    memset(reportBuffer, 0, bufferSize);
    userSuppliedReportID = _reportID;

    if (_size > 0 && _reportID != 0 && ! forceReportID) {
        reportBuffer[0] = _reportID;
        forceUserSuppliedReportID = false;
    }
    else {
        forceUserSuppliedReportID = true;
    }
    
    registerProfile(false);
}

HIDReporter::HIDReporter(USBHID& _HID, const HIDReportDescriptor* r, uint8_t* _buffer, unsigned _size) : HID(_HID) {
    if (r != NULL) {
        memcpy(&reportDescriptor, r, sizeof(reportDescriptor));
    }
    else {
        reportDescriptor.descriptor = NULL;
        reportDescriptor.length = 0;
    }

    reportBuffer = _buffer;
    bufferSize = _size;
    memset(_buffer, 0, _size);
    userSuppliedReportID = 0;
    forceUserSuppliedReportID = true;

    registerProfile(false);
}

void HIDReporter::setFeature(uint8_t* in) {
    return usb_hid_set_feature(reportID, in);
}

uint16_t HIDReporter::getData(uint8_t type, uint8_t* out, uint8_t poll) {
    return usb_hid_get_data(type, reportID, out, poll);
}

uint16_t HIDReporter::getFeature(uint8_t* out, uint8_t poll) {
    return usb_hid_get_data(HID_REPORT_TYPE_FEATURE, reportID, out, poll);
}

uint16_t HIDReporter::getOutput(uint8_t* out, uint8_t poll) {
    return usb_hid_get_data(HID_REPORT_TYPE_OUTPUT, reportID, out, poll);
}

<file_sep>/USBMIDI.cpp
/******************************************************************************
 *  This started out as a munging of Tymm Twillman's arduino Midi Library into the Libusb class,
 *  though by now very little of the original code is left, except for the class API and
 *  comments. <NAME> kindly gave <NAME> permission to relicense his code under the MIT
 *  license, which fixed a nasty licensing mess.
 *
 * The MIT License
 *
 * Copyright (c) 2010 <NAME>.
 * Copyright (c) 2013 <NAME>.
 * Copyright (c) 2013 <NAME>, Suspect Devices.
 * (c) 2003-2008 <NAME> <<EMAIL>>
 *
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use, copy,
 * modify, merge, publish, distribute, sublicense, and/or sell copies
 * of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *
 *
 *****************************************************************************/

/**
 * @brief USB MIDI device with a class compatible with maplemidi
 */

#include "USBComposite.h"

#include <string.h>
#include <stdint.h>
#undef true
#undef false
#include <wirish.h>
#include "usb_midi_device.h"
#include <libmaple/usb.h>
#include "usb_generic.h"


/*
 * USBMIDI interface
 */

#define USB_TIMEOUT 50

void USBMIDI::setChannel(unsigned int channel) {
	channelIn_ = channel;

}

bool USBMIDI::init(USBMIDI* me) {
    usb_midi_setTXEPSize(me->txPacketSize);
    usb_midi_setRXEPSize(me->rxPacketSize);
	return true;
}

bool USBMIDI::registerComponent() {
    return USBComposite.add(&usbMIDIPart, this, (USBPartInitializer)&USBMIDI::init);
}

void USBMIDI::begin(unsigned channel) {
	setChannel(channel);

	if (enabled)
		return;

	USBComposite.clear();
	registerComponent();
	USBComposite.begin();

	enabled = true;
}

void USBMIDI::end(void) {
	if (enabled) {
		USBComposite.end();
		enabled = false;
	}
}

void USBMIDI::writePacket(uint32 p) {
    this->writePackets(&p, 1);
}

void USBMIDI::writePackets(const void *buf, uint32 len) {
    if (!this->isConnected() || !buf) {
        return;
    }

    uint32 txed = 0;
    uint32 old_txed = 0;
    uint32 start = millis();

    uint32 sent = 0;

    while (txed < len && (millis() - start < USB_TIMEOUT)) {
        sent = usb_midi_tx((const uint32*)buf + txed, len - txed);
        txed += sent;
        if (old_txed != txed) {
            start = millis();
        }
        old_txed = txed;
    }


    if (sent == usb_midi_txEPSize) {
        while (usb_midi_is_transmitting() != 0) {
        }
        /* flush out to avoid having the pc wait for more data */
        usb_midi_tx(NULL, 0);
    }
}

uint32 USBMIDI::available(void) {
    return usb_midi_data_available();
}

uint32 USBMIDI::readPackets(void *buf, uint32 len) {
    if (!buf) {
        return 0;
    }

    uint32 rxed = 0;
    while (rxed < len) {
        rxed += usb_midi_rx((uint32*)buf + rxed, len - rxed);
    }

    return rxed;
}

/* Blocks forever until 1 byte is received */
uint32 USBMIDI::readPacket(void) {
    uint32 p;
    this->readPackets(&p, 1);
    return p;
}

uint8 USBMIDI::pending(void) {
    return usb_midi_get_pending();
}

uint8 USBMIDI::isConnected(void) {
    return usb_is_connected(USBLIB) && usb_is_configured(USBLIB);
}




// These are midi status message types are defined in MidiSpec.h

union EVENT_t {
    uint32 i;
    uint8 b[4];
    MIDI_EVENT_PACKET_t p;
};

// Handle decoding incoming MIDI traffic a word at a time -- remembers
//  what it needs to from one call to the next.
//
//  This is a private function & not meant to be called from outside this class.
//  It's used whenever data is available from the USB port.
//
void USBMIDI::dispatchPacket(uint32 p)
{
    union EVENT_t e;

    e.i=p;

    switch (e.p.cin) {
				case CIN_SYSEX ... CIN_SYSEX_ENDS_IN_3:
            appendSysex(e.p.cin,e.p.midi0,e.p.midi1,e.p.midi2);
            break;

        case CIN_3BYTE_SYS_COMMON:
            if (e.p.midi0 == MIDIv1_SONG_POSITION_PTR) {
                handleSongPosition(((uint16)e.p.midi2)<<7|((uint16)e.p.midi1));
            }
            break;

        case CIN_2BYTE_SYS_COMMON:
             switch (e.p.midi0) {
                 case MIDIv1_SONG_SELECT:
                     handleSongSelect(e.p.midi1);
                     break;
                 case MIDIv1_MTC_QUARTER_FRAME:
                     // reference library doesnt handle quarter frame.
                     break;
             }
            break;
        case CIN_NOTE_OFF:
            handleNoteOff(MIDIv1_VOICE_CHANNEL(e.p.midi0), e.p.midi1, e.p.midi2);
            break;
        case CIN_NOTE_ON:
            handleNoteOn(MIDIv1_VOICE_CHANNEL(e.p.midi0), e.p.midi1, e.p.midi2);
            break;
        case CIN_AFTER_TOUCH:
            handleVelocityChange(MIDIv1_VOICE_CHANNEL(e.p.midi0), e.p.midi1, e.p.midi2);
            break;
        case CIN_CONTROL_CHANGE:
            handleControlChange(MIDIv1_VOICE_CHANNEL(e.p.midi0), e.p.midi1, e.p.midi2);
            break;
        case CIN_PROGRAM_CHANGE:
            handleProgramChange(MIDIv1_VOICE_CHANNEL(e.p.midi0), e.p.midi1);
            break;
        case CIN_CHANNEL_PRESSURE:
            handleAfterTouch(MIDIv1_VOICE_CHANNEL(e.p.midi0), e.p.midi1);
            break;

        case CIN_PITCH_WHEEL:
            handlePitchChange(((uint16)e.p.midi2)<<7|((uint16)e.p.midi1));
            break;
        case CIN_1BYTE:
            switch (e.p.midi0) {
                case MIDIv1_CLOCK:
                    handleSync();
                    break;
                case MIDIv1_TICK:
                    break;
                case MIDIv1_START:
                    handleStart();
                    break;
                case MIDIv1_CONTINUE:
                    handleContinue();
                    break;
                case MIDIv1_STOP:
                    handleStop();
                    break;
                case MIDIv1_ACTIVE_SENSE:
                    handleActiveSense();
                    break;
                case MIDIv1_RESET:
                    handleReset();
                    break;
                case MIDIv1_TUNE_REQUEST:
                    handleTuneRequest();
                    break;

                default:
                    break;
            }
            break;
    }
}


// Try to read data from USB port & pass anything read to processing function
void USBMIDI::poll(void)
{   while(available()) {
        dispatchPacket(readPacket());
    }
}

static union EVENT_t outPacket; // since we only use one at a time no point in reallocating it

// Send Midi NOTE OFF message to a given channel, with note 0-127 and velocity 0-127
void USBMIDI::sendNoteOff(unsigned int channel, unsigned int note, unsigned int velocity)
{
    outPacket.p.cable=DEFAULT_MIDI_CABLE;
    outPacket.p.cin=CIN_NOTE_OFF;
    outPacket.p.midi0=MIDIv1_NOTE_OFF|(channel & 0x0f);
    outPacket.p.midi1=note;
    outPacket.p.midi2=velocity;
    writePacket(outPacket.i);

}


// Send Midi NOTE ON message to a given channel, with note 0-127 and velocity 0-127
void USBMIDI::sendNoteOn(unsigned int channel, unsigned int note, unsigned int velocity)
{
    outPacket.p.cable=DEFAULT_MIDI_CABLE;
    outPacket.p.cin=CIN_NOTE_ON;
    outPacket.p.midi0=MIDIv1_NOTE_ON|(channel & 0x0f);
    outPacket.p.midi1=note;
    outPacket.p.midi2=velocity;
    writePacket(outPacket.i);

}

// Send a Midi VELOCITY CHANGE message to a given channel, with given note 0-127,
// and new velocity 0-127
// Note velocity change == polyphonic aftertouch.
// Note aftertouch == channel pressure.
void USBMIDI::sendVelocityChange(unsigned int channel, unsigned int note, unsigned int velocity)
{
    outPacket.p.cable=DEFAULT_MIDI_CABLE;
    outPacket.p.cin=CIN_AFTER_TOUCH;
    outPacket.p.midi0=MIDIv1_AFTER_TOUCH |(channel & 0x0f);
    outPacket.p.midi1=note;
    outPacket.p.midi2=velocity;
    writePacket(outPacket.i);

}


// Send a Midi CC message to a given channel, as a given controller 0-127, with given
//  value 0-127
void USBMIDI::sendControlChange(unsigned int channel, unsigned int controller, unsigned int value)
{
    outPacket.p.cable=DEFAULT_MIDI_CABLE;
    outPacket.p.cin=CIN_CONTROL_CHANGE;
    outPacket.p.midi0=MIDIv1_CONTROL_CHANGE |(channel & 0x0f);
    outPacket.p.midi1=controller;
    outPacket.p.midi2=value;
    writePacket(outPacket.i);

}

// Send a Midi PROGRAM CHANGE message to given channel, with program ID 0-127
void USBMIDI::sendProgramChange(unsigned int channel, unsigned int program)
{
    outPacket.p.cable=DEFAULT_MIDI_CABLE;
    outPacket.p.cin=CIN_PROGRAM_CHANGE;
    outPacket.p.midi0=MIDIv1_PROGRAM_CHANGE |(channel & 0x0f);
    outPacket.p.midi1=program;
    writePacket(outPacket.i);

}

// Send a Midi AFTER TOUCH message to given channel, with velocity 0-127
void USBMIDI::sendAfterTouch(unsigned int channel, unsigned int velocity)
{
    outPacket.p.cable=DEFAULT_MIDI_CABLE;
    outPacket.p.cin=CIN_CHANNEL_PRESSURE;
    outPacket.p.midi0=MIDIv1_CHANNEL_PRESSURE |(channel & 0x0f);
    outPacket.p.midi1=velocity;
    writePacket(outPacket.i);

}

// Send a Midi PITCH CHANGE message, with a 14-bit pitch (always for all channels)
void USBMIDI::sendPitchChange(unsigned int pitch)
{
    outPacket.p.cable=DEFAULT_MIDI_CABLE;
    outPacket.p.cin=CIN_PITCH_WHEEL;
    outPacket.p.midi0=MIDIv1_PITCH_WHEEL;
    outPacket.p.midi1= (uint8) pitch & 0x07F;
    outPacket.p.midi2= (uint8)  (pitch>>7) & 0x7f;
    writePacket(outPacket.i);

}

// Send a Midi SONG POSITION message, with a 14-bit position (always for all channels)
void USBMIDI::sendSongPosition(unsigned int position)
{
    outPacket.p.cable=DEFAULT_MIDI_CABLE;
    outPacket.p.cin=CIN_3BYTE_SYS_COMMON;
    outPacket.p.midi0=MIDIv1_SONG_POSITION_PTR;
    outPacket.p.midi1= (uint8) position & 0x07F;
    outPacket.p.midi2= (uint8)  (position>>7) & 0x7f;
    writePacket(outPacket.i);

}

// Send a Midi SONG SELECT message, with a song ID of 0-127 (always for all channels)
void USBMIDI::sendSongSelect(unsigned int song)
{
    outPacket.p.cable=DEFAULT_MIDI_CABLE;
    outPacket.p.cin=CIN_2BYTE_SYS_COMMON;
    outPacket.p.midi0=MIDIv1_SONG_SELECT;
    outPacket.p.midi1= (uint8) song & 0x07F;
    writePacket(outPacket.i);

}

// Send a Midi TUNE REQUEST message (TUNE REQUEST is always for all channels)
void USBMIDI::sendTuneRequest(void)
{
    outPacket.p.cable=DEFAULT_MIDI_CABLE;
    outPacket.p.cin=CIN_1BYTE;
    outPacket.p.midi0=MIDIv1_TUNE_REQUEST;
    writePacket(outPacket.i);
}


// Send a Midi SYNC message (SYNC is always for all channels)
void USBMIDI::sendSync(void)
{
    outPacket.p.cable=DEFAULT_MIDI_CABLE;
    outPacket.p.cin=CIN_1BYTE;
    outPacket.p.midi0=MIDIv1_CLOCK;
    writePacket(outPacket.i);
}

// Send a Midi START message (START is always for all channels)
void USBMIDI::sendStart(void)
{
    outPacket.p.cable=DEFAULT_MIDI_CABLE;
    outPacket.p.cin=CIN_1BYTE;
    outPacket.p.midi0=MIDIv1_START ;
    writePacket(outPacket.i);
}


// Send a Midi CONTINUE message (CONTINUE is always for all channels)
void USBMIDI::sendContinue(void)
{
    outPacket.p.cable=DEFAULT_MIDI_CABLE;
    outPacket.p.cin=CIN_1BYTE;
    outPacket.p.midi0=MIDIv1_CONTINUE ;
    writePacket(outPacket.i);
}


// Send a Midi STOP message (STOP is always for all channels)
void USBMIDI::sendStop(void)
{
    outPacket.p.cable=DEFAULT_MIDI_CABLE;
    outPacket.p.cin=CIN_1BYTE;
    outPacket.p.midi0=MIDIv1_STOP ;
    writePacket(outPacket.i);
}

// Send a Midi ACTIVE SENSE message (ACTIVE SENSE is always for all channels)
void USBMIDI::sendActiveSense(void)
{
    outPacket.p.cable=DEFAULT_MIDI_CABLE;
    outPacket.p.cin=CIN_1BYTE;
    outPacket.p.midi0=MIDIv1_ACTIVE_SENSE ;
    writePacket(outPacket.i);
}

// Send a Midi RESET message (RESET is always for all channels)
void USBMIDI::sendReset(void)
{
    outPacket.p.cable=DEFAULT_MIDI_CABLE;
    outPacket.p.cin=CIN_1BYTE;
    outPacket.p.midi0=MIDIv1_RESET ;
    writePacket(outPacket.i);
}

const uint32 midiNoteFrequency_10ths[128] = {
	 82, 87, 92, 97, 103, 109, 116, 122, 130, 138, 146, 154, 164, 173, 184, 194,
	 206, 218, 231, 245, 260, 275, 291, 309, 327, 346, 367, 389, 412, 437, 462, 490,
	 519, 550, 583, 617, 654, 693, 734, 778, 824, 873, 925, 980, 1038, 1100, 1165, 1235,
	 1308, 1386, 1468, 1556, 1648, 1746, 1850, 1960, 2077, 2200, 2331, 2469, 2616, 2772, 2937, 3111,
	 3296, 3492, 3700, 3920, 4153, 4400, 4662, 4939, 5233, 5544, 5873, 6223, 6593, 6985, 7400, 7840,
	 8306, 8800, 9323, 9878, 10465, 11087, 11747, 12445, 13185, 13969, 14800, 15680, 16612, 17600, 18647, 19755,
	 20930, 22175, 23493, 24890, 26370, 27938, 29600, 31360, 33224, 35200, 37293, 39511, 41860, 44349, 46986, 49780,
	 52740, 55877, 59199, 62719, 66449, 70400, 74586, 79021, 83720, 88698, 93973, 99561, 105481, 111753, 118398, 125439 };

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter"
// Placeholders.  You should subclass the Midi base class and define these to have your
//  version called.
void USBMIDI::handleNoteOff(unsigned int channel, unsigned int note, unsigned int velocity) {}
void USBMIDI::handleNoteOn(unsigned int channel, unsigned int note, unsigned int velocity) {}
void USBMIDI::handleVelocityChange(unsigned int channel, unsigned int note, unsigned int velocity) {}
void USBMIDI::handleControlChange(unsigned int channel, unsigned int controller, unsigned int value) {}
void USBMIDI::handleProgramChange(unsigned int channel, unsigned int program) {}
void USBMIDI::handleAfterTouch(unsigned int channel, unsigned int velocity) {}
void USBMIDI::handlePitchChange(unsigned int pitch) {}
void USBMIDI::handleSongPosition(unsigned int position) {}
void USBMIDI::handleSongSelect(unsigned int song) {}
void USBMIDI::handleTuneRequest(void) {}
void USBMIDI::handleSync(void) {}
void USBMIDI::handleStart(void) {}
void USBMIDI::handleContinue(void) {}
void USBMIDI::handleStop(void) {}
void USBMIDI::handleActiveSense(void) {}
void USBMIDI::handleReset(void) {}

void USBMIDI::appendSysex(uint8_t cin, uint8_t midi0, uint8_t midi1, uint8_t midi2) {
	if (cin == CIN_SYSEX && midi0 == MIDIv1_SYSEX_START) // new sysex string
	{
		sysexstring[0] = midi1;
		sysexstring[1] = midi2;
		sysexpos = 2;
	}
	else if  (cin == CIN_SYSEX && midi1 != MIDIv1_SYSEX_START) // parsing sysex
	{
		sysexstring[sysexpos] = midi0;
		sysexstring[sysexpos + 1] = midi1;
		sysexstring[sysexpos + 2] = midi2;
		sysexpos += 3;
	}
	else // sysex ends
	{
		uint32_t sysexlen;
		if(midi0 == MIDIv1_SYSEX_START) //Edge case - only one variable was passed in Sysex
		{
			sysexstring[0] = midi1;
			sysexlen = 1;
		}
		else
		{
			switch(cin)
			{
				case CIN_SYSEX_ENDS_IN_1:
				sysexlen = sysexpos;
				break;
				case CIN_SYSEX_ENDS_IN_2:
				sysexlen = sysexpos + 1;
				break;
				case CIN_SYSEX_ENDS_IN_3:
				sysexlen = sysexpos + 2;
				break;
			}
			sysexstring[sysexpos] = midi0;
			sysexstring[sysexpos + 1] = midi1;
			sysexstring[sysexpos + 2] = midi2;
		}
		sysexpos = 0;
		handleSysex(sysexstring, sysexlen);
	}
}

void USBMIDI::handleSysex(uint8_t *sysexBuffer, uint32 len) {}

void USBMIDI::sendSysex(uint8_t *sysexBuffer, uint32 len) //Header&Ending excluded
{
	outPacket.p.cable=DEFAULT_MIDI_CABLE;
	if(len == 1)
	{
		outPacket.p.cin=CIN_SYSEX_ENDS_IN_3;
		outPacket.p.midi0= MIDIv1_SYSEX_START;
		outPacket.p.midi1= sysexBuffer[0];
		outPacket.p.midi2= MIDIv1_SYSEX_END;
		writePacket(outPacket.i);
	}
	else
	{
		outPacket.p.cin=CIN_SYSEX;
		outPacket.p.midi0= MIDIv1_SYSEX_START;
		outPacket.p.midi1= sysexBuffer[0];
		outPacket.p.midi2= sysexBuffer[1];
		writePacket(outPacket.i);
		if(len > 2) //Edge case
		{
			for(uint16_t i = 2; i < len - 2; i +=3)
			{
				outPacket.p.midi0= sysexBuffer[i];
				outPacket.p.midi1= sysexBuffer[i+1];
				outPacket.p.midi2= sysexBuffer[i+2];
				writePacket(outPacket.i);
			}
		}
		switch((len - 2) % 3)
		{
			case 2:
			outPacket.p.cin=CIN_SYSEX_ENDS_IN_3;
			outPacket.p.midi0= sysexBuffer[len-2];
			outPacket.p.midi1= sysexBuffer[len-1];
			outPacket.p.midi2= MIDIv1_SYSEX_END;
			writePacket(outPacket.i);
			break;
			case 1:
			outPacket.p.cin=CIN_SYSEX_ENDS_IN_2;
			outPacket.p.midi0= sysexBuffer[len-1];
			outPacket.p.midi1= MIDIv1_SYSEX_END;
			outPacket.p.midi2= NULL;
			writePacket(outPacket.i);
			break;
			case 0:
			outPacket.p.cin=CIN_SYSEX_ENDS_IN_1;
			outPacket.p.midi0= MIDIv1_SYSEX_END;
			outPacket.p.midi1= NULL;
			outPacket.p.midi2= NULL;
			writePacket(outPacket.i);
			break;
		}
	}
}

#pragma GCC diagnostic pop

<file_sep>/examples/sysex/sysex.ino
#include <USBComposite.h>

//Header(0xF0)and ending(0xF7) are excluded.

class myMidi : public USBMIDI {
 virtual void handleNoteOff(unsigned int channel, unsigned int note, unsigned int velocity) {
  CompositeSerial.print("Note off: ch:");
  CompositeSerial.print(channel);
  CompositeSerial.print(" note:");
  CompositeSerial.print(note);
  CompositeSerial.print(" velocity:");
  CompositeSerial.println(velocity);
 }
 virtual void handleNoteOn(unsigned int channel, unsigned int note, unsigned int velocity) {
  CompositeSerial.print("Note on: ch:");
  CompositeSerial.print(channel);
  CompositeSerial.print(" note:");
  CompositeSerial.print(note);
  CompositeSerial.print(" velocity:");
  CompositeSerial.println(velocity);
  }
  virtual void handleSysex(uint8_t *sysexBuffer, uint32 len)
  {
    CompositeSerial.print("Sysex - len:");
    CompositeSerial.print(len);
    CompositeSerial.print(" ");
    for(int i = 0; i < len; i++)
    {
      CompositeSerial.print(sysexBuffer[i]);
      CompositeSerial.print(" ");
    }
    CompositeSerial.println();
        USBMIDI::sendSysex(sysexBuffer, len);
     CompositeSerial.println("Sysex returned");
  }
};

myMidi midi;
USBCompositeSerial CompositeSerial;

void setup() {
    USBComposite.setProductId(0x0030);
    midi.registerComponent();
    CompositeSerial.registerComponent();
    USBComposite.begin();
}

void loop() {
  midi.poll();
}
<file_sep>/USBMIDI.h
/******************************************************************************
 * The MIT License
 *
 * Copyright (c) 2010 <NAME>.
 * Copyright (c) 2013 <NAME>.
 *
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use, copy,
 * modify, merge, publish, distribute, sublicense, and/or sell copies
 * of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *****************************************************************************/

/**
 * @brief Wirish USB MIDI port (MidiUSB).
 */

#ifndef _USBMIDI_H_
#define _USBMIDI_H_

#define MAX_SYSEX_SIZE 1024

//#include <Print.h>
#include <boards.h>
#include <USBComposite.h>
#include "usb_generic.h"

/*
 * This is the Midi class.  If you are just sending Midi data, you only need to make an
 *  instance of the class, passing it your USB port -- in most cases it looks like
 *
 *   USBMidi midi;
 *
 *  then you don't need to do anything else; you can start using it to send Midi messages,
 *  e.g.
 *
 *   midi.sendNoteOn(1, note, velocity);
 *
 * If you are using it to receive Midi data, it's a little more complex & you will need
 *  to subclass the Midi class.
 *
 *  For people not used to C++ this may look confusing, but it's actually pretty easy.
 *   Note that you only need to write the functions for event types you want to handle.
 *   They should match the names & prototypes of the functions in the class; look at
 *   the functions in the Midi class below that have the keyword "virtual" to see which
 *   ones you can use.
 *
 *   Here's an example of one that takes NOTE ON, NOTE OFF, and CONTROL CHANGE:
 *
 *   class MyMidi : public USBMidi {
 *     public:
 *
 *     // Need this to compile; it just hands things off to the Midi class.
 *     MyMidi() : USBMidi(s) {}
 *
 *     void handleNoteOn(unsigned int channel, unsigned int note, unsigned int velocity)
 *     {
 *         if (note == 40) {digitalWrite(13, HIGH); }
 *     }
 *
 *     void handleNoteOff(unsigned int channel, unsigned int note, unsigned int velocity)
 *     {
 *       if (note == 40) { digitalWrite(13, LOW); }
 *     }
 *
 *     void handleControlChange(unsigned int channel, unsigned int controller,
 *                              unsigned int value)
 *     {
 *       analogWrite(6, value * 2);
 *     }
 *
 *   Then you need to make an instance of this class:
 *
 *     MyMidi midi();
 *
 *   If receiving Midi data, you also need to call the poll function every time through
 *    loop(); e.g.
 *
 *    void loop() {
 *      midi.poll();
 *    }
 *
 *   This causes the Midi class to read data from the USB port and process it.
 */

class USBMIDI {
private:
    bool enabled = false;

    /* Private Receive Parameters */

    // The channel this Midi instance receives data for (0 means all channels)
    int channelIn_;

    /* Internal functions */

    // Called whenever data is read from the USB port
    void dispatchPacket(uint32 packet);

    uint32 txPacketSize = 64;
    uint32 rxPacketSize = 64;

public:
	static bool init(USBMIDI* me);
	// This registers this USB composite device component with the USBComposite class instance.
	bool registerComponent();
	void setChannel(unsigned channel=0);
	unsigned getChannel() {
		return channelIn_;
	}

    void setRXPacketSize(uint32 size=64) {
        rxPacketSize = size;
    }

    void setTXPacketSize(uint32 size=64) {
        txPacketSize = size;
    }

    // Call to start the USB port, at given baud.  For many applications
    //  the default parameters are just fine (which will cause messages for all
    //  MIDI channels to be delivered)
    void begin(unsigned int channel = 0);
    //void begin();
    void end();

    uint32 available(void);

    uint32 readPackets(void *buf, uint32 len);
    uint32 readPacket(void);

    void writePacket(uint32);
//    void write(const char *str);
    void writePackets(const void*, uint32);

    uint8 isConnected();
    uint8 pending();

    // poll() should be called every time through loop() IF dealing with incoming MIDI
    //  (if you're only SENDING MIDI events from the Arduino, you don't need to call
    //  poll); it causes data to be read from the USB port and processed.
    void poll();

    // Call these to send MIDI messages of the given types
    void sendNoteOff(unsigned int channel, unsigned int note, unsigned int velocity = 0);
    void sendNoteOn(unsigned int channel, unsigned int note, unsigned int velocity = 127);
    void sendVelocityChange(unsigned int channel, unsigned int note, unsigned int velocity);
    void sendControlChange(unsigned int channel, unsigned int controller, unsigned int value);
    void sendProgramChange(unsigned int channel, unsigned int program);
    void sendAfterTouch(unsigned int channel, unsigned int velocity);
    void sendPitchChange(unsigned int pitch);
    void sendSongPosition(unsigned int position);
    void sendSongSelect(unsigned int song);
    void sendTuneRequest(void);
    void sendSync(void);
    void sendStart(void);
    void sendContinue(void);
    void sendStop(void);
    void sendActiveSense(void);
    void sendReset(void);

    // Overload these in a subclass to get MIDI messages when they come in
    virtual void handleNoteOff(unsigned int channel, unsigned int note, unsigned int velocity);
    virtual void handleNoteOn(unsigned int channel, unsigned int note, unsigned int velocity);
    virtual void handleVelocityChange(unsigned int channel, unsigned int note, unsigned int velocity);
    virtual void handleControlChange(unsigned int channel, unsigned int controller, unsigned int value);
    virtual void handleProgramChange(unsigned int channel, unsigned int program);
    virtual void handleAfterTouch(unsigned int channel, unsigned int velocity);
    virtual void handlePitchChange(unsigned int pitch);
    virtual void handleSongPosition(unsigned int position);
    virtual void handleSongSelect(unsigned int song);
    virtual void handleTuneRequest(void);
    virtual void handleSync(void);
    virtual void handleStart(void);
    virtual void handleContinue(void);
    virtual void handleStop(void);
    virtual void handleActiveSense(void);
    virtual void handleReset(void);

    // sysex experimental
    void appendSysex(uint8_t cin, uint8_t midi0, uint8_t midi1, uint8_t midi2);
    virtual void handleSysex(uint8_t *sysexBuffer, uint32 len);
    uint8_t sysexstring[MAX_SYSEX_SIZE]; 
    uint32_t sysexpos;
    void sendSysex(uint8_t *sysexBuffer, uint32 len);
};

extern const uint32 midiNoteFrequency_10ths[128];

#endif
<file_sep>/usb_midi_device.c
/******************************************************************************
 * The MIT License
 *
 * Copyright (c) 2011 LeafLabs LLC.
 * Copyright (c) 2013 <NAME>.
 *
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use, copy,
 * modify, merge, publish, distribute, sublicense, and/or sell copies
 * of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *****************************************************************************/

/**
 * @file libmaple/usb/stm32f1/usb_midi_device.c
 * @brief USB MIDI.
 *
 * FIXME: this works on the STM32F1 USB peripherals, and probably no
 * place else. Nonportable bits really need to be factored out, and
 * the result made cleaner.
 */

#include <string.h>

#include "usb_generic.h"
#include "usb_midi_device.h"
#include <MidiSpecs.h>
#include <MinSysex.h>

#include <libmaple/usb.h>
#include <libmaple/delay.h>

/* Private headers */
#include "usb_lib_globals.h"
#include "usb_reg_map.h"

/* usb_lib headers */
#include "usb_type.h"
#include "usb_core.h"
#include "usb_def.h"

static void midiDataTxCb(void);
static void midiDataRxCb(void);

static void usbMIDIReset(void);

#define MIDI_ENDPOINT_RX 0
#define MIDI_ENDPOINT_TX 1
#define USB_MIDI_RX_ENDP (midiEndpoints[MIDI_ENDPOINT_RX].address)
#define USB_MIDI_TX_ENDP (midiEndpoints[MIDI_ENDPOINT_TX].address)
#define USB_MIDI_RX_ENDPOINT_INFO (&midiEndpoints[MIDI_ENDPOINT_RX])
#define USB_MIDI_TX_ENDPOINT_INFO (&midiEndpoints[MIDI_ENDPOINT_TX])
#define USB_MIDI_RX_PMA_PTR (midiEndpoints[MIDI_ENDPOINT_RX].pma)
#define USB_MIDI_TX_PMA_PTR (midiEndpoints[MIDI_ENDPOINT_TX].pma)

/*
 * Descriptors
 */

typedef struct {
//    usb_descriptor_config_header       Config_Header;
    /* Control Interface */
    usb_descriptor_interface           AC_Interface;
    AC_CS_INTERFACE_DESCRIPTOR(1)      AC_CS_Interface;
    /* Control Interface */
    usb_descriptor_interface           MS_Interface;
    MS_CS_INTERFACE_DESCRIPTOR         MS_CS_Interface;
    MIDI_IN_JACK_DESCRIPTOR            MIDI_IN_JACK_1;
    MIDI_IN_JACK_DESCRIPTOR            MIDI_IN_JACK_2;
    MIDI_OUT_JACK_DESCRIPTOR(1)        MIDI_OUT_JACK_3;
    MIDI_OUT_JACK_DESCRIPTOR(1)        MIDI_OUT_JACK_4;
    usb_descriptor_endpoint            DataOutEndpoint;
    MS_CS_BULK_ENDPOINT_DESCRIPTOR(1)  MS_CS_DataOutEndpoint;
    usb_descriptor_endpoint            DataInEndpoint;
    MS_CS_BULK_ENDPOINT_DESCRIPTOR(1)  MS_CS_DataInEndpoint;
} __packed usb_descriptor_config;

static const usb_descriptor_config usbMIDIDescriptor_Config = {
    /* .Config_Header = {
        .bLength              = sizeof(usb_descriptor_config_header),
        .bDescriptorType      = USB_DESCRIPTOR_TYPE_CONFIGURATION,
        .wTotalLength         = sizeof(usb_descriptor_config),
        .bNumInterfaces       = 0x02,
        .bConfigurationValue  = 0x01,
        .iConfiguration       = 0x00,
        .bmAttributes         = (USB_CONFIG_ATTR_BUSPOWERED |
                                 USB_CONFIG_ATTR_SELF_POWERED),
        .bMaxPower            = MAX_POWER,
    }, */

    .AC_Interface = {
        .bLength            = sizeof(usb_descriptor_interface),
        .bDescriptorType    = USB_DESCRIPTOR_TYPE_INTERFACE,
        .bInterfaceNumber   = 0x00, // PATCH
        .bAlternateSetting  = 0x00,
        .bNumEndpoints      = 0x00, 
        .bInterfaceClass    = USB_INTERFACE_CLASS_AUDIO,
        .bInterfaceSubClass = USB_INTERFACE_AUDIOCONTROL,
        .bInterfaceProtocol = 0x00,
        .iInterface         = 0x00,
    },

    .AC_CS_Interface = {
        .bLength            = AC_CS_INTERFACE_DESCRIPTOR_SIZE(1),
        .bDescriptorType    = USB_DESCRIPTOR_TYPE_CS_INTERFACE,
        .SubType            = 0x01,
        .bcdADC             = 0x0100,
        .wTotalLength       = AC_CS_INTERFACE_DESCRIPTOR_SIZE(1),
        .bInCollection      = 0x01,
        .baInterfaceNr      = {0x01},
    },

    .MS_Interface = {
        .bLength            = sizeof(usb_descriptor_interface),
        .bDescriptorType    = USB_DESCRIPTOR_TYPE_INTERFACE,
        .bInterfaceNumber   = 0x01, // PATCH
        .bAlternateSetting  = 0x00,
        .bNumEndpoints      = 0x02,
        .bInterfaceClass    = USB_INTERFACE_CLASS_AUDIO,
        .bInterfaceSubClass = USB_INTERFACE_MIDISTREAMING,
        .bInterfaceProtocol = 0x00,
        .iInterface         = 0, // was 0x04
    },

    .MS_CS_Interface = {
        .bLength            = sizeof(MS_CS_INTERFACE_DESCRIPTOR),
        .bDescriptorType    = USB_DESCRIPTOR_TYPE_CS_INTERFACE,
        .SubType            = 0x01,
        .bcdADC             = 0x0100,
        .wTotalLength       = sizeof(MS_CS_INTERFACE_DESCRIPTOR)
                              +sizeof(MIDI_IN_JACK_DESCRIPTOR)
                              +sizeof(MIDI_IN_JACK_DESCRIPTOR)
                              +MIDI_OUT_JACK_DESCRIPTOR_SIZE(1)
                              +MIDI_OUT_JACK_DESCRIPTOR_SIZE(1)
                              +sizeof(usb_descriptor_endpoint)
                              +MS_CS_BULK_ENDPOINT_DESCRIPTOR_SIZE(1)
                              +sizeof(usb_descriptor_endpoint)
                              +MS_CS_BULK_ENDPOINT_DESCRIPTOR_SIZE(1)                             
                                 /* 0x41-4 */,
    },

    .MIDI_IN_JACK_1 = {
        .bLength            = sizeof(MIDI_IN_JACK_DESCRIPTOR),
        .bDescriptorType    = USB_DESCRIPTOR_TYPE_CS_INTERFACE,
        .SubType            = MIDI_IN_JACK,
        .bJackType          = MIDI_JACK_EMBEDDED,
        .bJackId            = 0x01,
        .iJack              = 0x05,
    },

    .MIDI_IN_JACK_2 = {
        .bLength            = sizeof(MIDI_IN_JACK_DESCRIPTOR),
        .bDescriptorType    = USB_DESCRIPTOR_TYPE_CS_INTERFACE,
        .SubType            = MIDI_IN_JACK,
        .bJackType          = MIDI_JACK_EXTERNAL,
        .bJackId            = 0x02,
        .iJack              = 0x00,
    },

    .MIDI_OUT_JACK_3 = {
        .bLength            = MIDI_OUT_JACK_DESCRIPTOR_SIZE(1),
        .bDescriptorType    = USB_DESCRIPTOR_TYPE_CS_INTERFACE,
        .SubType            = MIDI_OUT_JACK,
        .bJackType          = MIDI_JACK_EMBEDDED,
        .bJackId            = 0x03,
        .bNrInputPins       = 0x01,
        .baSourceId         = {0x02},
        .baSourcePin        = {0x01},
        .iJack              = 0x00,
    },

    .MIDI_OUT_JACK_4 = {
        .bLength            = MIDI_OUT_JACK_DESCRIPTOR_SIZE(1),
        .bDescriptorType    = USB_DESCRIPTOR_TYPE_CS_INTERFACE,
        .SubType            = MIDI_OUT_JACK,
        .bJackType          = MIDI_JACK_EXTERNAL,
//        .bJackId            = 0x04,
        .bJackId            = 0x03,
        .bNrInputPins       = 0x01,
        .baSourceId         = {0x01},
        .baSourcePin        = {0x01},
        .iJack              = 0x00,
    },

    .DataOutEndpoint = {
        .bLength            = sizeof(usb_descriptor_endpoint),
        .bDescriptorType    = USB_DESCRIPTOR_TYPE_ENDPOINT,
        .bEndpointAddress   = (USB_DESCRIPTOR_ENDPOINT_OUT |
                             0), // PATCH: USB_MIDI_RX_ENDP
        .bmAttributes       = USB_EP_TYPE_BULK,
        .wMaxPacketSize     = 64, // PATCH
        .bInterval          = 0x00,
    },

    .MS_CS_DataOutEndpoint = {
      .bLength              = MS_CS_BULK_ENDPOINT_DESCRIPTOR_SIZE(1),
      .bDescriptorType      = USB_DESCRIPTOR_TYPE_CS_ENDPOINT,
      .SubType              = 0x01,
      .bNumEmbMIDIJack      = 0x01,
      .baAssocJackID        = {0x01},
	},

    .DataInEndpoint = {
        .bLength          = sizeof(usb_descriptor_endpoint),
        .bDescriptorType  = USB_DESCRIPTOR_TYPE_ENDPOINT,
        .bEndpointAddress = (USB_DESCRIPTOR_ENDPOINT_IN | 0), // PATCH: USB_MIDI_TX_ENDP
        .bmAttributes     = USB_EP_TYPE_BULK,
        .wMaxPacketSize   = 64, // PATCH
        .bInterval        = 0x00,
    },

    .MS_CS_DataInEndpoint = {
      .bLength              = MS_CS_BULK_ENDPOINT_DESCRIPTOR_SIZE(1),
      .bDescriptorType      = USB_DESCRIPTOR_TYPE_CS_ENDPOINT,
      .SubType              = 0x01,
      .bNumEmbMIDIJack      = 0x01,
      .baAssocJackID        = {0x03},
	},

};

/* I/O state */

/* Received data */
static volatile uint32 midiBufferRx[64/4];
/* Read index into midiBufferRx */
static volatile uint32 rx_offset = 0;
/* Transmit data */
static volatile uint32 midiBufferTx[64/4];
/* Write index into midiBufferTx */
static volatile uint32 tx_offset = 0;
/* Number of bytes left to transmit */
static volatile uint32 n_unsent_packets = 0;
/* Are we currently sending an IN packet? */
static volatile uint8 transmitting = 0;
/* Number of unread bytes */
static volatile uint32 n_unread_packets = 0;

uint32_t usb_midi_txEPSize = 64;
static uint32_t rxEPSize = 64;

// eventually all of this should be in a place for settings which can be written to flash.
volatile uint8 myMidiChannel = DEFAULT_MIDI_CHANNEL;
volatile uint8 myMidiDevice = DEFAULT_MIDI_DEVICE;
volatile uint8 myMidiCable = DEFAULT_MIDI_CABLE;
volatile uint8 myMidiID[] = { LEAFLABS_MMA_VENDOR_1,LEAFLABS_MMA_VENDOR_2,LEAFLABS_MMA_VENDOR_3,0};

#define OUT_BYTE(s,v) out[(uint8*)&(s.v)-(uint8*)&s]
#define OUT_16(s,v) *(uint16_t*)&OUT_BYTE(s,v) // OK on Cortex which can handle unaligned writes

static USBEndpointInfo midiEndpoints[2] = {
    {
        .callback = midiDataRxCb,
        .pmaSize = 64, // patch
        .type = USB_GENERIC_ENDPOINT_TYPE_BULK, 
        .tx = 0
    },
    {
        .callback = midiDataTxCb,
        .pmaSize = 64, // patch
        .type = USB_GENERIC_ENDPOINT_TYPE_BULK, 
        .tx = 1,
    }
};

static void getMIDIPartDescriptor(uint8* out) {
    memcpy(out, &usbMIDIDescriptor_Config, sizeof(usbMIDIDescriptor_Config));
    // patch to reflect where the part goes in the descriptor
    OUT_BYTE(usbMIDIDescriptor_Config, AC_Interface.bInterfaceNumber) += usbMIDIPart.startInterface;
    OUT_BYTE(usbMIDIDescriptor_Config, MS_Interface.bInterfaceNumber) += usbMIDIPart.startInterface;
    OUT_BYTE(usbMIDIDescriptor_Config, AC_CS_Interface.baInterfaceNr) += usbMIDIPart.startInterface;
    OUT_BYTE(usbMIDIDescriptor_Config, DataOutEndpoint.bEndpointAddress) += USB_MIDI_RX_ENDP;
    OUT_BYTE(usbMIDIDescriptor_Config, DataInEndpoint.bEndpointAddress) += USB_MIDI_TX_ENDP;
    OUT_16(usbMIDIDescriptor_Config, DataInEndpoint.wMaxPacketSize) = usb_midi_txEPSize;
    OUT_16(usbMIDIDescriptor_Config, DataOutEndpoint.wMaxPacketSize) = usb_midi_txEPSize;
}

USBCompositePart usbMIDIPart = {
    .numInterfaces = 2,
    .numEndpoints = sizeof(midiEndpoints)/sizeof(*midiEndpoints),
    .descriptorSize = sizeof(usbMIDIDescriptor_Config),
    .getPartDescriptor = getMIDIPartDescriptor,
    .usbInit = NULL,
    .usbReset = usbMIDIReset,
    .usbDataSetup = NULL,
    .usbNoDataSetup = NULL,
    .endpoints = midiEndpoints
};

void usb_midi_setTXEPSize(uint32_t size) {
    size = (size+3)/4*4;
    if (size == 0 || size > 64)
        size = 64;
    midiEndpoints[1].pmaSize = size;
    usb_midi_txEPSize = size;
}

void usb_midi_setRXEPSize(uint32_t size) {
    size = (size+3)/4*4;
    if (size == 0 || size > 64)
        size = 64;
    midiEndpoints[0].pmaSize = size;
    rxEPSize = size;
}

/*
 * MIDI interface
 */

/* This function is non-blocking.
 *
 * It copies data from a usercode buffer into the USB peripheral TX
 * buffer, and returns the number of bytes copied. */
uint32 usb_midi_tx(const uint32* buf, uint32 packets) {
    uint32 bytes=packets*4;
    /* Last transmission hasn't finished, so abort. */
    if (usb_midi_is_transmitting()) {
		/* Copy to TxBuffer */
		
        return 0;  /* return len */
    }

    /* We can only put usb_midi_txEPSize bytes in the buffer. */
    if (bytes > usb_midi_txEPSize) {
        bytes = usb_midi_txEPSize;
        packets=bytes/4;
    }

    /* Queue bytes for sending. */
    if (packets) {
        usb_copy_to_pma_ptr((uint8 *)buf, bytes, USB_MIDI_TX_PMA_PTR);
    }
    // We still need to wait for the interrupt, even if we're sending
    // zero bytes. (Sending zero-size packets is useful for flushing
    // host-side buffers.)
    n_unsent_packets = packets;
    transmitting = 1;
    usb_generic_set_tx(USB_MIDI_TX_ENDPOINT_INFO, bytes);

    return packets;
}

uint32 usb_midi_data_available(void) {
    return n_unread_packets;
}

uint8 usb_midi_is_transmitting(void) {
    return transmitting;
}

uint16 usb_midi_get_pending(void) {
    return n_unsent_packets;
}

/* Nonblocking byte receive.
 *
 * Copies up to len bytes from our private data buffer (*NOT* the PMA)
 * into buf and deq's the FIFO. */
uint32 usb_midi_rx(uint32* buf, uint32 packets) {
    /* Copy bytes to buffer. */
    uint32 n_copied = usb_midi_peek(buf, packets);

    /* Mark bytes as read. */
    n_unread_packets -= n_copied;
    rx_offset += n_copied;

    /* If all bytes have been read, re-enable the RX endpoint, which
     * was set to NAK when the current batch of bytes was received. */
    if (n_unread_packets == 0) {
        usb_generic_enable_rx(USB_MIDI_RX_ENDPOINT_INFO);
        rx_offset = 0;
    }

    return n_copied;
}

/* Nonblocking byte lookahead.
 *
 * Looks at unread bytes without marking them as read. */
uint32 usb_midi_peek(uint32* buf, uint32 packets) {
    uint32 i;
    if (packets > n_unread_packets) {
        packets = n_unread_packets;
    }

    for (i = 0; i < packets; i++) {
        buf[i] = midiBufferRx[i + rx_offset];
    }

    return packets;
}

/*
 * Callbacks
 */

static void midiDataTxCb(void) {
    n_unsent_packets = 0;
    transmitting = 0;
}

static void midiDataRxCb(void) {
    usb_generic_pause_rx(USB_MIDI_RX_ENDPOINT_INFO);
    n_unread_packets = usb_get_ep_rx_count(USB_MIDI_RX_ENDP) / 4;
    /* This copy won't overwrite unread bytes, since we've set the RX
     * endpoint to NAK, and will only set it to VALID when all bytes
     * have been read. */
    
    usb_copy_from_pma_ptr((uint8*)midiBufferRx, n_unread_packets * 4,
                      USB_MIDI_RX_PMA_PTR);
    
    // discard volatile
    LglSysexHandler((uint32*)midiBufferRx,(uint32*)&rx_offset,(uint32*)&n_unread_packets);
    
    if (n_unread_packets == 0) {
        usb_generic_enable_rx(USB_MIDI_RX_ENDPOINT_INFO);
        rx_offset = 0;
    }

}

static void usbMIDIReset(void) {
    /* Reset the RX/TX state */
    n_unread_packets = 0;
    n_unsent_packets = 0;
    rx_offset = 0;
}


// .............THIS IS NOT WORKING YET................
// send debugging information to 
static uint8_t sysexbuffer[80]={CIN_SYSEX,0xF0,0x7D,0x33,CIN_SYSEX,0x33,0x00,0xf7}; // !!!bad hardcoded number foo !!!
uint8_t iSysHexLine(uint8_t rectype, uint16_t address, uint8_t *payload,uint8_t payloadlength, uint8_t *buffer);
void sendThroughSysex(char *printbuffer, int bufferlength) {
    int n;
    n = iSysHexLine(1, 0 , (uint8_t *) printbuffer, (uint8_t) bufferlength , sysexbuffer+6);
    usb_midi_tx((uint32*)sysexbuffer,n/4);
}

#define HIGHBYTE(x) ((uint8_t) (((x) >> 8) &  0x00ff) )
#define LOWBYTE(x) ((uint8_t) ((x) & 0x00ff) )
#define HIGHNIBBLE(x) ((((uint8_t)(x)) & 0xF0) >> 4)
#define LOWNIBBLE(x) (((uint8_t)(x)) & 0x0F)
#define HEXCHAR(c) ((c>9)?55+c:48+c)


uint8_t iSysHexLine(uint8_t rectype, uint16_t address, uint8_t *payload,uint8_t payloadlength, uint8_t *buffer) {
    
    int i=0; int j; int thirdone;
    uint8_t n=0;
    uint16_t checksum=0;
    //uint16_t length=0;
    
    buffer[i++]=':';
    
    checksum+=payloadlength;
    buffer[i++]=HEXCHAR(HIGHNIBBLE(payloadlength));
    buffer[i++]=HEXCHAR(LOWNIBBLE(payloadlength));
    buffer[i++]=CIN_SYSEX;
    
    n=HIGHBYTE(address);
    checksum+=n;
    buffer[i++]=HEXCHAR(HIGHNIBBLE(n));
    buffer[i++]=HEXCHAR(LOWNIBBLE(n));
    
    n=LOWBYTE(address);
    checksum+=n;
    buffer[i++]=HEXCHAR(HIGHNIBBLE(n));
    buffer[i++]=CIN_SYSEX;
    buffer[i++]=HEXCHAR(LOWNIBBLE(n));
    
    n=rectype;
    checksum+=n;
    buffer[i++]=HEXCHAR(HIGHNIBBLE(n));
    buffer[i++]=HEXCHAR(LOWNIBBLE(n));
    buffer[i++]=CIN_SYSEX;
    thirdone=0;
    for (j=0; j<payloadlength ; j++) {
        n=payload[j];
        checksum+=n;
        buffer[i++]=HEXCHAR(HIGHNIBBLE(n));
        if (++thirdone==3) {
            buffer[i++]=CIN_SYSEX;
            thirdone=0;
        }
        buffer[i++]=HEXCHAR(LOWNIBBLE(n));
        if (++thirdone==3) {
            buffer[i++]=CIN_SYSEX;
            thirdone=0;
        }
    }
    if (thirdone==0) {
        buffer[i-1]=CIN_SYSEX_ENDS_IN_3;
    } 
    n=~((uint8_t) checksum&0x00ff)+1;
    buffer[i++]=HEXCHAR(HIGHNIBBLE(n));
    if (thirdone==1) {
        buffer[i++]=CIN_SYSEX_ENDS_IN_2;
    }
    buffer[i++]=HEXCHAR(LOWNIBBLE(n));
    if (thirdone==2) {
        buffer[i++]=CIN_SYSEX_ENDS_IN_1;
    }
    buffer[i++]=0xf7;
    return i+thirdone;
}
<file_sep>/usb_generic.c
/******************************************************************************
 * The MIT License
 *
 * Copyright (c) 2011 LeafLabs LLC.
 *
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use, copy,
 * modify, merge, publish, distribute, sublicense, and/or sell copies
 * of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *****************************************************************************/

/**
 * @file libmaple/usb/stm32f1/usb_hid.c
 * @brief USB HID (human interface device) support
 *
 * FIXME: this works on the STM32F1 USB peripherals, and probably no
 * place else. Nonportable bits really need to be factored out, and
 * the result made cleaner.
 */

//#define MATCHING_ENDPOINT_RANGES // make RX and TX endpoints fall in the same range for each part

#include <string.h>
#include <libmaple/libmaple_types.h>
#include <libmaple/usb.h>
#include <libmaple/delay.h>
#include <libmaple/gpio.h>
#include <usb_lib_globals.h>
#include <usb_reg_map.h>
//#include <usb_core.h>
#include <board/board.h>



/* usb_lib headers */
#include "usb_type.h"
#include "usb_core.h"
#include "usb_def.h"

#include "usb_generic.h"

const char DEFAULT_PRODUCT[] = "Maple";
const char DEFAULT_MANUFACTURER[] = "LeafLabs";

static uint8* usbGetConfigDescriptor(uint16 length);
static void usbInit(void);
static void usbReset(void);
static void usbClearFeature(void);
static void usbSetConfiguration(void);
static RESULT usbDataSetup(uint8 request);
static RESULT usbNoDataSetup(uint8 request);
static RESULT usbGetInterfaceSetting(uint8 interface, uint8 alt_setting);
static uint8* usbGetStringDescriptor(uint16 length);
static uint8* usbGetConfigDescriptor(uint16 length);
static uint8* usbGetDeviceDescriptor(uint16 length);
static void usbSetConfiguration(void);
static void usbSetDeviceAddress(void);
static uint32 disconnect_delay = 500; // in microseconds

static struct usb_chunk* control_tx_chunk_list = NULL;
static uint8 control_tx_chunk_buffer[USB_EP0_BUFFER_SIZE];

static volatile uint8* control_tx_buffer = NULL;
static uint16 control_tx_length = 0;
static volatile uint8* control_tx_done = NULL;
static volatile uint8* control_rx_buffer = NULL;
static uint16 control_rx_length = 0;
static volatile uint8* control_rx_done = NULL;

uint16 epTypes[4] = {
    USB_EP_EP_TYPE_BULK,
    USB_EP_EP_TYPE_CONTROL,
    USB_EP_EP_TYPE_ISO, 
    USB_EP_EP_TYPE_INTERRUPT
};

#define LEAFLABS_ID_VENDOR                0x1EAF
#define MAPLE_ID_PRODUCT                  0x0024 // was 0x0024
#define USB_DEVICE_CLASS              	  0x00
#define USB_DEVICE_SUBCLASS	           	  0x00
#define DEVICE_PROTOCOL					  0x01
//#define REQUEST_TYPE                      0b01100000u
#define REQUEST_RECIPIENT                 0b00011111u

static usb_descriptor_device usbGenericDescriptor_Device =
  {                                                                     
      .bLength            = sizeof(usb_descriptor_device),              
      .bDescriptorType    = USB_DESCRIPTOR_TYPE_DEVICE,                 
      .bcdUSB             = 0x0200,                                     
      .bDeviceClass       = USB_DEVICE_CLASS,                       	
      .bDeviceSubClass    = USB_DEVICE_SUBCLASS,                    	
      .bDeviceProtocol    = DEVICE_PROTOCOL,                            
      .bMaxPacketSize0    = 0x40,                                       
      .idVendor           = LEAFLABS_ID_VENDOR,                         
      .idProduct          = MAPLE_ID_PRODUCT,                           
      .bcdDevice          = 0x0200,                                     
      .iManufacturer      = 0x01,                                       
      .iProduct           = 0x02,                                       
      .iSerialNumber      = 0x00,                                       
      .bNumConfigurations = 0x01,                                       
};

typedef struct {
    usb_descriptor_config_header Config_Header;
    uint8 descriptorData[MAX_USB_DESCRIPTOR_DATA_SIZE];
} __packed usb_descriptor_config;

static usb_descriptor_config usbConfig;

#define MAX_POWER (100 >> 1)

static const usb_descriptor_config_header Base_Header = {
        .bLength              = sizeof(usb_descriptor_config_header),
        .bDescriptorType      = USB_DESCRIPTOR_TYPE_CONFIGURATION,
        .wTotalLength         = 0,
        .bNumInterfaces       = 0, 
        .bConfigurationValue  = 0x01,
        .iConfiguration       = 0x00,
        .bmAttributes         = (USB_CONFIG_ATTR_BUSPOWERED |
                                 USB_CONFIG_ATTR_SELF_POWERED),
        .bMaxPower            = MAX_POWER,
};

static ONE_DESCRIPTOR Device_Descriptor = {
    (uint8*)&usbGenericDescriptor_Device,
    sizeof(usb_descriptor_device)
};

static ONE_DESCRIPTOR Config_Descriptor = {
    (uint8*)&usbConfig,
    0
};

static DEVICE my_Device_Table = {
    .Total_Endpoint      = 0,
    .Total_Configuration = 1
};

/* Unicode language identifier: 0x0409 is US English */
static const usb_descriptor_string usb_LangID = {
    .bLength         = USB_DESCRIPTOR_STRING_LEN(1),
    .bDescriptorType = USB_DESCRIPTOR_TYPE_STRING,
    .bString         = {0x09, 0x04},
};

static USB_DESCRIPTOR_STRING(USB_DESCRIPTOR_STRING_LEN(USB_MAX_STRING_DESCRIPTOR_LENGTH)) string_descriptor_buffer = {
    .bDescriptorType = USB_DESCRIPTOR_TYPE_STRING
};

static ONE_DESCRIPTOR generic_string_descriptor = {
    (uint8*)&string_descriptor_buffer, 0
};

#define MAX_PACKET_SIZE            0x40  /* 64B, maximum for USB FS Devices */
static const DEVICE_PROP my_Device_Property = {
    .Init                        = usbInit,
    .Reset                       = usbReset,
    .Process_Status_IN           = NOP_Process, 
    .Process_Status_OUT          = NOP_Process,
    .Class_Data_Setup            = usbDataSetup,
    .Class_NoData_Setup          = usbNoDataSetup,
    .Class_Get_Interface_Setting = usbGetInterfaceSetting,
    .GetDeviceDescriptor         = usbGetDeviceDescriptor,
    .GetConfigDescriptor         = usbGetConfigDescriptor,
    .GetStringDescriptor         = usbGetStringDescriptor,
    .RxEP_buffer                 = NULL,
    .MaxPacketSize               = MAX_PACKET_SIZE
};

static const USER_STANDARD_REQUESTS my_User_Standard_Requests = {
    .User_GetConfiguration   = NOP_Process,
    .User_SetConfiguration   = usbSetConfiguration,
    .User_GetInterface       = NOP_Process,
    .User_SetInterface       = NOP_Process,
    .User_GetStatus          = NOP_Process,
    .User_ClearFeature       = usbClearFeature,
    .User_SetEndPointFeature = NOP_Process,
    .User_SetDeviceFeature   = NOP_Process,
    .User_SetDeviceAddress   = usbSetDeviceAddress
};

static uint8 numStringDescriptors = 3;


#define MAX_STRING_DESCRIPTORS 4
static ONE_DESCRIPTOR String_Descriptor[MAX_STRING_DESCRIPTORS] = {
    {(uint8*)&usb_LangID,                  USB_DESCRIPTOR_STRING_LEN(1)},
    {(uint8*)DEFAULT_MANUFACTURER,         0}, // a 0 in the length field indicates that we synthesize this from an asciiz string
    {(uint8*)DEFAULT_PRODUCT,              0},
    {NULL,                                 0},
};

static USBCompositePart** parts;
static uint32 numParts;
static DEVICE saved_Device_Table;
static DEVICE_PROP saved_Device_Property;
static USER_STANDARD_REQUESTS saved_User_Standard_Requests;

static void (*ep_int_in[7])(void);
static void (*ep_int_out[7])(void);

static uint8 minimum_address;

static uint8 acceptable_endpoint_number(unsigned partNum, unsigned endpointNum, uint8 address) {
    USBEndpointInfo* ep = &(parts[partNum]->endpoints[endpointNum]);
    for (unsigned i = 0 ; i <= partNum ; i++)
        for(unsigned j = 0 ; (i < partNum && j < parts[partNum]->numEndpoints) || (i == partNum && j < endpointNum) ; j++) {
            USBEndpointInfo* ep1 = &(parts[i]->endpoints[j]);
            if (ep1->address != address)
                continue;
            if (ep1->tx == ep->tx || ep->exclusive || ep1->exclusive || ep1->type != ep->type || ep1->doubleBuffer != ep->doubleBuffer)
                return 0;
        }
    return 1;
}

static int8 allocate_endpoint_address(unsigned partNum, unsigned endpointNum) {
    for (uint8 address = minimum_address ; address < 8 ; address++)
        if (acceptable_endpoint_number(partNum, endpointNum, address))
            return address;
    return -1;
}

uint8 usb_generic_set_parts(USBCompositePart** _parts, unsigned _numParts) {
    parts = _parts;
    numParts = _numParts;
    unsigned numInterfaces = 0;
    minimum_address = 1;
    uint8 maxAddress = 0;

    uint16 usbDescriptorSize = 0;
    uint16 pmaOffset = USB_EP0_RX_BUFFER_ADDRESS + USB_EP0_BUFFER_SIZE;
    
    for (unsigned i = 0 ; i < 7 ; i++) {
        ep_int_in[i] = NOP_Process;
        ep_int_out[i] = NOP_Process;
    }
    
    usbDescriptorSize = 0;
    for (unsigned i = 0 ; i < _numParts ; i++ ) {
        USBCompositePart* part = parts[i];
        part->startInterface = numInterfaces;
        numInterfaces += part->numInterfaces;
        if (usbDescriptorSize + part->descriptorSize > MAX_USB_DESCRIPTOR_DATA_SIZE) {
            return 0;
		}
        USBEndpointInfo* ep = part->endpoints;
        for (unsigned j = 0 ; j < part->numEndpoints ; j++) {
            int8 address = allocate_endpoint_address(i, j);
            if (address < 0)
                return 0;

            ep[j].pma = usb_pma_ptr(pmaOffset);
            uint32 size = ep[j].pmaSize;
            // rx has special length alignment issues
            if (ep[j].doubleBuffer) {
                if (size <= 124 || ep[j].tx) {
                    size = (size+3)/4*4;
                }
                else {
                    size = (size+63)/64*64;
                }
            }
            else {
                if (size <= 62 || ep[j].tx) {
                    size = (size+1)/2*2;
                }
                else {
                    size = (size+31)/32*32;
                }
            }
            pmaOffset += size;
            if (pmaOffset > PMA_MEMORY_SIZE)
                return 0;
            if (ep[j].callback == NULL)
                ep[j].callback = NOP_Process;
            ep[j].address = address;
            if (ep[j].tx) {
                ep_int_in[address-1] = ep[j].callback;
            }
            else {
                ep_int_out[address-1] = ep[j].callback;
            }
            if (maxAddress < address)
                maxAddress = address;
        }
        part->getPartDescriptor(usbConfig.descriptorData + usbDescriptorSize);
        usbDescriptorSize += part->descriptorSize;
#ifdef MATCHING_ENDPOINT_RANGES        
        minimum_address = maxAddress + 1;
#endif
    }
    
    usbConfig.Config_Header = Base_Header;    
    usbConfig.Config_Header.bNumInterfaces = numInterfaces;
    usbConfig.Config_Header.wTotalLength = usbDescriptorSize + sizeof(Base_Header);
    Config_Descriptor.Descriptor_Size = usbConfig.Config_Header.wTotalLength;
    
    my_Device_Table.Total_Endpoint = maxAddress + 1;
    
    return 1;
}

void usb_generic_set_info(uint16 idVendor, uint16 idProduct, const char* iManufacturer, const char* iProduct, const char* iSerialNumber) {
    if (idVendor != 0)
        usbGenericDescriptor_Device.idVendor = idVendor;
    else
        usbGenericDescriptor_Device.idVendor = LEAFLABS_ID_VENDOR;
     
    if (idProduct != 0)
        usbGenericDescriptor_Device.idProduct = idProduct;
    else
        usbGenericDescriptor_Device.idProduct = MAPLE_ID_PRODUCT;
    
    if (iManufacturer == NULL) {
        iManufacturer = DEFAULT_MANUFACTURER;
    }
           
    String_Descriptor[1].Descriptor = (uint8*)iManufacturer;
    String_Descriptor[1].Descriptor_Size = 0;
     
    if (iProduct == NULL) {
        iProduct = DEFAULT_PRODUCT;
    }
           
    String_Descriptor[2].Descriptor = (uint8*)iProduct;
    String_Descriptor[2].Descriptor_Size = 0;
    
    if (iSerialNumber == NULL) {
        numStringDescriptors = 3;
        usbGenericDescriptor_Device.iSerialNumber = 0;
    }
    else {
        String_Descriptor[3].Descriptor = (uint8*)iSerialNumber;
        String_Descriptor[3].Descriptor_Size = 0;
        numStringDescriptors = 4;
        usbGenericDescriptor_Device.iSerialNumber = 3;
    }
}
 
void usb_generic_enable(void) {
    /* Present ourselves to the host. Writing 0 to "disc" pin must
     * pull USB_DP pin up while leaving USB_DM pulled down by the
     * transceiver. See USB 2.0 spec, section 7.1.7.3. */
     
#ifdef GENERIC_BOOTLOADER			
    //Reset the USB interface on generic boards - developed by <NAME>
    gpio_set_mode(GPIOA, 12, GPIO_OUTPUT_PP);
    gpio_write_bit(GPIOA, 12, 0);

    delay_us(disconnect_delay);
    gpio_set_mode(GPIOA, 12, GPIO_INPUT_FLOATING);
#endif			

    if (BOARD_USB_DISC_DEV != NULL) {
        gpio_set_mode(BOARD_USB_DISC_DEV, (uint8)(uint32)BOARD_USB_DISC_BIT, GPIO_OUTPUT_PP);
        gpio_write_bit(BOARD_USB_DISC_DEV, (uint8)(uint32)BOARD_USB_DISC_BIT, 0);
    }

    saved_Device_Table = Device_Table;
    saved_Device_Property = Device_Property;
    saved_User_Standard_Requests = User_Standard_Requests;
    Device_Table = my_Device_Table;
    Device_Property = my_Device_Property;
    User_Standard_Requests = my_User_Standard_Requests;
    
    /* Initialize the USB peripheral. */
    usb_init_usblib(USBLIB, ep_int_in, ep_int_out); 
}

void usb_generic_set_disconnect_delay(uint32 delay) {
    disconnect_delay = delay;
}

static void usbInit(void) {
    pInformation->Current_Configuration = 0;

    USB_BASE->CNTR = USB_CNTR_FRES;

    USBLIB->irq_mask = 0;
    USB_BASE->CNTR = USBLIB->irq_mask;
    USB_BASE->ISTR = 0;
    USBLIB->irq_mask = USB_CNTR_RESETM | USB_CNTR_SUSPM | USB_CNTR_WKUPM;
    USB_BASE->CNTR = USBLIB->irq_mask;

    USB_BASE->ISTR = 0;
    USBLIB->irq_mask = USB_ISR_MSK;
    USB_BASE->CNTR = USBLIB->irq_mask;

    usb_generic_enable_interrupts_ep0();

    for (unsigned i = 0 ; i < numParts ; i++)
        if(parts[i]->usbInit != NULL)
            parts[i]->usbInit();

    USBLIB->state = USB_UNCONNECTED;
}

#define BTABLE_ADDRESS 0x00

static inline uint16 pma_ptr_to_offset(uint32* p) {
    return (uint16)(((uint32*)p-(uint32*)USB_PMA_BASE) * 2);
}

static void usbReset(void) {
    pInformation->Current_Configuration = 0;

    /* current feature is current bmAttributes */
    pInformation->Current_Feature = (USB_CONFIG_ATTR_BUSPOWERED |
                                     USB_CONFIG_ATTR_SELF_POWERED);

    USB_BASE->BTABLE = BTABLE_ADDRESS;

    /* setup control endpoint 0 */
    usb_set_ep_type(USB_EP0, USB_EP_EP_TYPE_CONTROL);
    usb_set_ep_tx_stat(USB_EP0, USB_EP_STAT_TX_STALL);
    usb_set_ep_rx_addr(USB_EP0, USB_EP0_RX_BUFFER_ADDRESS);
    usb_set_ep_tx_addr(USB_EP0, USB_EP0_TX_BUFFER_ADDRESS);
    usb_clear_status_out(USB_EP0);

    usb_set_ep_rx_count(USB_EP0, USB_EP0_BUFFER_SIZE);
    usb_set_ep_rx_stat(USB_EP0, USB_EP_STAT_RX_VALID);
    
    for (unsigned i = 1 ; i < 8 ; i++) {
        usb_set_ep_rx_stat(i, USB_EP_STAT_RX_DISABLED);
        usb_clear_ep_dtog_rx(i);
        usb_set_ep_tx_stat(i, USB_EP_STAT_TX_DISABLED);
        usb_clear_ep_dtog_tx(i);
   }
    
    for (unsigned i = 0 ; i < numParts ; i++) {
        for (unsigned j = 0 ; j < parts[i]->numEndpoints ; j++) {
            USBEndpointInfo* e = &(parts[i]->endpoints[j]);
            uint8 address = e->address;
            usb_set_ep_type(address, epTypes[e->type]);
            usb_set_ep_kind(address, e->doubleBuffer ? USB_EP_EP_KIND_DBL_BUF : 0);
            uint16 pmaOffset = pma_ptr_to_offset(e->pma);
            if (e->tx) {
                usb_set_ep_tx_addr(address, pmaOffset);
                usb_set_ep_tx_stat(address, USB_EP_STAT_TX_NAK);
                if (e->doubleBuffer) {
                    usb_set_ep_tx_buf0_addr(address, pmaOffset);
                    usb_set_ep_tx_buf1_addr(address, pmaOffset+e->pmaSize/2);
                    usb_set_ep_tx_buf0_count(address, e->pmaSize/2);
                    usb_set_ep_tx_buf1_count(address, e->pmaSize/2);
                }
            }
            else {
                usb_set_ep_rx_addr(address, pmaOffset);
				if (! e->doubleBuffer) {
					usb_set_ep_rx_count(address, e->pmaSize);
				}
				usb_set_ep_rx_stat(address, USB_EP_STAT_RX_VALID);
            }
        }
        if (parts[i]->usbReset != NULL)
            parts[i]->usbReset();
    }
    
    control_rx_length = 0;
    control_tx_length = 0;
    
    USBLIB->state = USB_ATTACHED;
    SetDeviceAddress(0);

}

static void usb_power_down(void) {
    USB_BASE->CNTR = USB_CNTR_FRES;
    USB_BASE->ISTR = 0;
    USB_BASE->CNTR = USB_CNTR_FRES + USB_CNTR_PDWN;
}

void usb_generic_disable(void) {
    /* Turn off the interrupt and signal disconnect (see e.g. USB 2.0
     * spec, section 7.1.7.3). */
    usb_generic_disable_interrupts_ep0();
    
    if (BOARD_USB_DISC_DEV != NULL) {
        gpio_write_bit(BOARD_USB_DISC_DEV, (uint8)(uint32)BOARD_USB_DISC_BIT, 1);
    }
    
    usb_power_down();

    Device_Table = saved_Device_Table;
    Device_Property = saved_Device_Property;
    User_Standard_Requests = saved_User_Standard_Requests;    
    
    for (uint32 i=0; i < numParts; i++)
        if (parts[i]->clear)
            parts[i]->clear();
}

static uint8* control_data_tx(uint16 length) {
    unsigned wOffset = pInformation->Ctrl_Info.Usb_wOffset;
    
    if (length == 0) {
        pInformation->Ctrl_Info.Usb_wLength = control_tx_length - wOffset;
        return NULL;
    }

    if (control_tx_done && pInformation->USBwLengths.w <= wOffset + pInformation->Ctrl_Info.PacketSize)
        *control_tx_done = USB_CONTROL_DONE; // this may be a bit premature, but it's our best try

    if (control_tx_buffer == NULL)
        return NULL;
    else
        return (uint8*)control_tx_buffer + wOffset;
}

void usb_generic_control_tx_setup(volatile void* buffer, uint16 length, volatile uint8* done) {
    control_tx_buffer = buffer;
    control_tx_length = length;
    control_tx_done = done;
    pInformation->Ctrl_Info.CopyData = control_data_tx;
    pInformation->Ctrl_Info.Usb_wOffset = 0;
    control_data_tx(0);
}

uint32 usb_generic_chunks_length(struct usb_chunk* chunk) {
    uint32 l=0;
    
    while (chunk != NULL) {
        l += chunk->dataLength;
        chunk = chunk->next;
    }

    return l;
}

static uint8* control_data_chunk_tx(uint16 length) {
    unsigned wOffset = pInformation->Ctrl_Info.Usb_wOffset;
    
    if (length == 0) {
        pInformation->Ctrl_Info.Usb_wLength = usb_generic_chunks_length(control_tx_chunk_list) - wOffset;
        
        return NULL;
    }

    if (control_tx_chunk_list == NULL) {
        return NULL;
    }
    else {
        uint32 chunks_offset = 0;
        uint32 buf_offset = 0;
        
        for (struct usb_chunk* chunk = control_tx_chunk_list ; chunk != NULL && chunks_offset < wOffset + length ; chunk = chunk->next ) {
            uint32 len = chunk->dataLength;
            
            if (len == 0)
                continue;
            
            if (wOffset < chunks_offset + len) {
                /* need to copy some data from this chunk */
                uint32 start;
                if (wOffset <= chunks_offset) {
                    start = 0;
                }
                else {
                    start = wOffset - chunks_offset;
                }
                
                uint32 to_copy;
                if (wOffset + length <= chunks_offset + len) {
                    to_copy = wOffset + length - chunks_offset - start;
                }
                else {
                    to_copy = len - start;
                }
                
                memcpy(control_tx_chunk_buffer + buf_offset, chunk->data + start, to_copy);
                buf_offset += to_copy;
            }
            
            chunks_offset += len;
        }
        
        return (uint8*)control_tx_chunk_buffer;
    }
}

void usb_generic_control_tx_chunk_setup(struct usb_chunk* chunk) {
    control_tx_chunk_list = chunk;
    pInformation->Ctrl_Info.CopyData = control_data_chunk_tx;
    pInformation->Ctrl_Info.Usb_wOffset = 0;
    control_data_chunk_tx(0);
}



static uint8* control_data_rx(uint16 length) {
    unsigned wOffset = pInformation->Ctrl_Info.Usb_wOffset;
    
    if (length ==0) {
        uint16 len = pInformation->USBwLengths.w;
        if (len > control_rx_length)
            len = control_rx_length;
        
        if (wOffset < len) { 
            pInformation->Ctrl_Info.Usb_wLength = len - wOffset;
        }
        else {
            pInformation->Ctrl_Info.Usb_wLength = 0; 
        }

        return NULL;
    }
    
    if (control_rx_done && pInformation->USBwLengths.w <= wOffset + pInformation->Ctrl_Info.PacketSize) {
        *control_rx_done = USB_CONTROL_DONE; // this may be a bit premature, but it's our best try
    }
    
    if (control_rx_buffer == NULL)
        return NULL;
    else
        return (uint8*)control_rx_buffer + wOffset;
}

void usb_generic_control_rx_setup(volatile void* buffer, uint16 length, volatile uint8* done) {
    control_rx_buffer = buffer;
    control_rx_length = length;
    control_rx_done = done;
    pInformation->Ctrl_Info.CopyData = control_data_rx;
    pInformation->Ctrl_Info.Usb_wOffset = 0;
    control_data_rx(0);
}

void usb_generic_control_descriptor_tx(ONE_DESCRIPTOR* d) {
    usb_generic_control_tx_setup(d->Descriptor, d->Descriptor_Size, NULL);
}

static RESULT usbDataSetup(uint8 request) {
    if ((Type_Recipient & REQUEST_RECIPIENT) == INTERFACE_RECIPIENT) {
        uint8 interface  = pInformation->USBwIndex0;
        for (unsigned i = 0 ; i < numParts ; i++) {
            USBCompositePart* p = parts[i];
            if (p->usbDataSetup && p->startInterface <= interface && interface < p->startInterface + p->numInterfaces) {
                // uint8 request, uint8 interface, uint8 requestType, uint8 wValue0, uint8 wValue1, uint16 wIndex, uint16 wLength
                return parts[i]->usbDataSetup(request, interface - p->startInterface, pInformation->USBbmRequestType, pInformation->USBwValue0, 
                    pInformation->USBwValue1, pInformation->USBwIndex, pInformation->USBwLength);
            }
        }
    }

    return USB_UNSUPPORT;
    
}

static RESULT usbNoDataSetup(uint8 request) {
    if ((Type_Recipient & REQUEST_RECIPIENT) == INTERFACE_RECIPIENT) {
        uint8 interface  = pInformation->USBwIndex0;
        
        for (unsigned i = 0 ; i < numParts ; i++) {
            USBCompositePart* p = parts[i];
            // uint8 request, uint8 interface, uint8 requestType, uint8 wValue0, uint8 wValue1, uint16 wIndex, uint16 wLength
            if (p->usbNoDataSetup && p->startInterface <= interface && interface < p->startInterface + p->numInterfaces)
                return parts[i]->usbNoDataSetup(request, interface - p->startInterface, pInformation->USBbmRequestType, pInformation->USBwValue0, 
                    pInformation->USBwValue1, pInformation->USBwIndex);
        }
    }

    return USB_UNSUPPORT;
}

static void usbSetConfiguration(void) {
    if (pInformation->Current_Configuration != 0) {
        USBLIB->state = USB_CONFIGURED;
    }
    for (unsigned i = 0 ; i < numParts ; i++) {
        if (parts[i]->usbSetConfiguration != NULL)
            parts[i]->usbSetConfiguration();
    }
}

static void usbClearFeature(void) {
    for (unsigned i = 0 ; i < numParts ; i++) {
        if (parts[i]->usbClearFeature != NULL)
            parts[i]->usbClearFeature();
    }
}

static void usbSetDeviceAddress(void) {
    USBLIB->state = USB_ADDRESSED;
}

static uint8* usbGetDeviceDescriptor(uint16 length) {
    return Standard_GetDescriptorData(length, &Device_Descriptor);
}

static uint8* usbGetConfigDescriptor(uint16 length) {
    return Standard_GetDescriptorData(length, &Config_Descriptor);
}

static uint8* usbGetStringDescriptor(uint16 length) {    
    uint8 wValue0 = pInformation->USBwValue0;
    
    if (wValue0 >= numStringDescriptors) {
        return NULL;
    }
    
    ONE_DESCRIPTOR* d = &String_Descriptor[wValue0];
    if (d->Descriptor_Size != 0) {
        return Standard_GetDescriptorData(length, &String_Descriptor[wValue0]);
    }
    else {
        const char* s = (char*)d->Descriptor;
        
        uint32 i = 0;
        
        while(*s && i < USB_MAX_STRING_DESCRIPTOR_LENGTH) {
            string_descriptor_buffer.bString[i] = (uint8)*s;
            s++;
            i++;
        }
        
        string_descriptor_buffer.bLength = USB_DESCRIPTOR_STRING_LEN(i);
        generic_string_descriptor.Descriptor_Size = string_descriptor_buffer.bLength;
        if (length > string_descriptor_buffer.bLength)
            length = string_descriptor_buffer.bLength;
        return Standard_GetDescriptorData(length, &generic_string_descriptor);
    }            
}


static RESULT usbGetInterfaceSetting(uint8 interface, uint8 alt_setting) {
    if (alt_setting > 1) {
        return USB_UNSUPPORT;
    } else if (interface >= usbConfig.Config_Header.bNumInterfaces) {
        return USB_UNSUPPORT;
    }

    return USB_SUCCESS;
}

void usb_copy_to_pma_ptr(volatile const uint8 *buf, uint16 len, uint32* dst) {
    uint16 n = len >> 1;
    uint16 i;
    for (i = 0; i < n; i++) {
        *(uint16*)dst = (uint16)(*buf) | *(buf + 1) << 8;
        buf += 2;
        dst++;
    }
    if (len & 1) {
        *dst = *buf;
    }
}

void usb_copy_from_pma_ptr(volatile uint8 *buf, uint16 len, uint32* src) {
    uint16 *dst = (uint16*)buf;
    uint16 n = len >> 1;
    uint16 i;
    for (i = 0; i < n; i++) {
        *dst++ = *src++;
    }
    if (len & 1) {
        *dst = *src & 0xFF;
    }
}

// return bytes read
uint32 usb_generic_read_to_circular_buffer(USBEndpointInfo* ep, volatile uint8* buf, uint32 circularBufferSize, volatile uint32* headP) {
    uint32 head = *headP;
    uint32 ep_rx_size = usb_get_ep_rx_count(ep->address);
    /* This copy won't overwrite unread bytes as long as there is
     * enough room in the USB Rx buffer for next packet */
    volatile uint32 *src = ep->pma;

    for (uint32 i = 0; i < ep_rx_size; i++) {
        uint16 tmp = *src++;
        buf[head] = (uint8)tmp;
        head = (head + 1) % circularBufferSize;
        i++;
        if (i >= ep_rx_size) {
            break;
        }
        buf[head] = (uint8)(tmp>>8);
        head = (head + 1) % circularBufferSize;
    }

    *headP = head;
    
    return ep_rx_size;
}

// returns number of bytes read
// buf should be uint16-aligned
uint32 usb_generic_read_to_buffer(USBEndpointInfo* ep, volatile uint8* buf, uint32 bufferSize) {
    uint32 ep_rx_size = usb_get_ep_rx_count(ep->address);
    if (ep_rx_size > bufferSize)
        ep_rx_size = bufferSize;
    usb_copy_from_pma_ptr(buf, ep_rx_size, ep->pma);
    return ep_rx_size;
}

uint32 usb_generic_send_from_buffer(USBEndpointInfo* ep, volatile uint8* buf, uint32 amount) {
    if (amount > ep->pmaSize)
        amount = ep->pmaSize;
    
    usb_copy_to_pma_ptr(buf, amount, ep->pma);
    usb_set_ep_tx_count(ep->address, amount);
    usb_generic_enable_tx(ep);
    
    return amount;
}

// transmitting = 1 when transmitting, 0 when done but not flushed, negative when done and flushed
uint32 usb_generic_send_from_circular_buffer(USBEndpointInfo* ep, volatile uint8* buf, uint32 circularBufferSize, uint32 head, volatile uint32* tailP, volatile int8* transmittingP) {
    uint32 tail = *tailP;
	int32 amount = (head - tail) % circularBufferSize;
    if (amount < 0) {
        // wish we could count on % returning a non-negative answer
        amount += circularBufferSize;
    }
    
	if (amount==0) {
        if (*transmittingP <= 0) {
            *transmittingP = -1;
            return 0; // it was already flushed, keep Tx endpoint disabled
        }
        *transmittingP = 0;
        goto flush; // no more data to send
	}
    
	*transmittingP = 1;

    if (amount > ep->pmaSize) {
        amount = ep->pmaSize;
    }
    
	// copy the bytes from USB Tx buffer to PMA buffer
	uint32 *dst = ep->pma;

    for (int32 i = 0; i < amount; i++) {
        uint16 low = buf[tail];
        tail = (tail + 1) % circularBufferSize;
        i++;
        if (i >= amount) {
            *dst = low;
            break;
        }
        *dst++ = ((uint16)buf[tail] << 8) | low;
        tail = (tail + 1) % circularBufferSize;
    }

    *tailP = tail; /* store volatile variable */
    
flush:
	// enable Tx endpoint
    usb_set_ep_tx_count(ep->address, amount);
    usb_generic_enable_tx(ep);
    
    return amount;
}


uint32 usb_generic_send_from_circular_buffer_double_buffered(USBEndpointInfo* ep, volatile uint8* buf, uint32 circularBufferSize, uint32 amount, volatile uint32* tailP) {
    uint32 tail = *tailP; /* load volatile variable */

    uint32 dtog_tx = usb_get_ep_dtog_tx(ep->address);

    /* copy the bytes from USB Tx buffer to PMA buffer */
    uint32 *dst;
    if (dtog_tx)
        dst = PMA_PTR_BUF1(ep);
    else
        dst = PMA_PTR_BUF0(ep);
    
    if (amount > ep->pmaSize / 2)
        amount = ep->pmaSize / 2;

    for (uint32 i = 0; i < amount; i++) {
        uint16 low = buf[tail];
        tail = (tail + 1) % circularBufferSize;
        i++;
        if (i >= amount) {
            *dst = low;
            break;
        }
        *dst++ = ((uint16)buf[tail] << 8) | low;
        tail = (tail + 1) % circularBufferSize;
    }

    *tailP = tail; /* store volatile variable */

    if (dtog_tx)
        usb_set_ep_tx_buf1_count(ep->address, amount);
    else
        usb_set_ep_tx_buf0_count(ep->address, amount);
    
    return amount;
}


uint16_t usb_generic_roundUpToPowerOf2(uint16_t x) {
    uint16_t xx;
    for (xx = 1 ; xx < x && xx != 0; xx *= 2) ;
    if (xx == 0)
        return x;
    else
        return xx;
}
<file_sep>/usb_scsi.c
#include "usb_mass.h"
#include "usb_mass_mal.h"
#include "usb_mass_internal.h"
#include "usb_scsi.h"

#include <libmaple/usb.h>
#include <libmaple/delay.h>

/* Private headers */
#include "usb_lib_globals.h"
#include "usb_reg_map.h"
#include "usb_regs.h"

/* usb_lib headers */
#include "usb_type.h"
#include "usb_core.h"
#include "usb_def.h"

#define SCSI_READ_FORMAT_CAPACITY_DATA_LEN   0x0C
#define SCSI_READ_FORMAT_CAPACITY10_DATA_LEN 0x08
#define SCSI_MODE_SENSE6_DATA_LEN            0x04
#define SCSI_MODE_SENSE10_DATA_LEN           0x08

#define SCSI_TXFR_IDLE     0
#define SCSI_TXFR_ONGOING  1

extern uint32_t usb_mass_sil_write(uint8_t* pBufferPointer, uint32_t wBufferSize);

/* See usb_scsi_data.c */
extern uint8_t SCSI_page00InquiryData[];
extern uint8_t SCSI_standardInquiryData[];
extern uint8_t SCSI_standardInquiryData2[];
extern uint8_t SCSI_senseData[];
extern uint8_t SCSI_modeSense6Data[];
extern uint8_t SCSI_modeSense10Data[];
extern uint8_t SCSI_readFormatCapacityData[];
extern uint8_t SCSI_readFormatCapacity10Data[];

uint32_t SCSI_lba;
uint32_t SCSI_blkLen;
uint8_t SCSI_transferState = SCSI_TXFR_IDLE;
uint32_t SCSI_blockReadCount = 0;
uint32_t SCSI_blockOffset;
uint32_t SCSI_counter = 0;
uint8_t SCSI_dataBuffer[512]; /* 512 bytes (SDCard block size) */

uint8_t scsi_address_management(uint8_t lun, uint8_t cmd, uint32_t lba, uint32_t blockNbr);
void scsi_read_memory(uint8_t lun, uint32_t memoryOffset, uint32_t transferLength);
void scsi_write_memory(uint8_t lun, uint32_t memoryOffset, uint32_t transferLength);

void scsi_inquiry_cmd(uint8_t lun) {
  uint8_t* inquiryData;
  uint16_t inquiryDataLength;

  if (usb_mass_CBW.CB[1] & 0x01) /*Evpd is set*/ {
    inquiryData = SCSI_page00InquiryData;
    inquiryDataLength = 5;
  } else {
    if (lun == 0) {
      inquiryData = SCSI_standardInquiryData;
    } else {
      inquiryData = SCSI_standardInquiryData2;
    }

    if (usb_mass_CBW.CB[4] <= SCSI_STANDARD_INQUIRY_DATA_LEN) {
      inquiryDataLength = usb_mass_CBW.CB[4];
    } else {
      inquiryDataLength = SCSI_STANDARD_INQUIRY_DATA_LEN;
    }
  }
  usb_mass_transfer_data_request(inquiryData, inquiryDataLength);
}

void scsi_request_sense_cmd(uint8_t lun) {
  (void)lun;
  uint8_t requestSenseDataLength;
  if (usb_mass_CBW.CB[4] <= SCSI_REQUEST_SENSE_DATA_LEN) {
    requestSenseDataLength = usb_mass_CBW.CB[4];
  } else {
    requestSenseDataLength = SCSI_REQUEST_SENSE_DATA_LEN;
  }
  usb_mass_transfer_data_request(SCSI_senseData, requestSenseDataLength);
}

void scsi_start_stop_unit_cmd(uint8_t lun) {
  (void)lun;
  usb_mass_bot_set_csw(BOT_CSW_CMD_PASSED, BOT_SEND_CSW_ENABLE);
}

void scsi_mode_sense6_cmd(uint8_t lun) {
  (void)lun;
  usb_mass_transfer_data_request(SCSI_modeSense6Data, SCSI_MODE_SENSE6_DATA_LEN);
}

void scsi_mode_sense10_cmd(uint8_t lun) {
  (void)lun;
  usb_mass_transfer_data_request(SCSI_modeSense10Data, SCSI_MODE_SENSE10_DATA_LEN);
}

void scsi_read_format_capacity_cmd(uint8_t lun) {
  if (usb_mass_mal_get_status(lun)) {
    scsi_set_sense_data(usb_mass_CBW.bLUN, SCSI_NOT_READY, SCSI_MEDIUM_NOT_PRESENT);
    usb_mass_bot_set_csw(BOT_CSW_CMD_FAILED, BOT_SEND_CSW_ENABLE);
    usb_mass_bot_abort(BOT_DIR_IN);
    return;
  }
  SCSI_readFormatCapacityData[4] = (uint8_t) (usb_mass_drives[lun].blockCount >> 24);
  SCSI_readFormatCapacityData[5] = (uint8_t) (usb_mass_drives[lun].blockCount >> 16);
  SCSI_readFormatCapacityData[6] = (uint8_t) (usb_mass_drives[lun].blockCount >> 8);
  SCSI_readFormatCapacityData[7] = (uint8_t) (usb_mass_drives[lun].blockCount);

  SCSI_readFormatCapacityData[9] = (uint8_t) (SCSI_BLOCK_SIZE >> 16);
  SCSI_readFormatCapacityData[10] = (uint8_t) (SCSI_BLOCK_SIZE >> 8);
  SCSI_readFormatCapacityData[11] = (uint8_t) (SCSI_BLOCK_SIZE);
  usb_mass_transfer_data_request(SCSI_readFormatCapacityData, SCSI_READ_FORMAT_CAPACITY_DATA_LEN);
}

void scsi_read_capacity10_cmd(uint8_t lun) {
  if (usb_mass_mal_get_status(lun)) {
    scsi_set_sense_data(usb_mass_CBW.bLUN, SCSI_NOT_READY, SCSI_MEDIUM_NOT_PRESENT);
    usb_mass_bot_set_csw(BOT_CSW_CMD_FAILED, BOT_SEND_CSW_ENABLE);
    usb_mass_bot_abort(BOT_DIR_IN);
    return;
  }
  
  SCSI_readFormatCapacity10Data[0] = (uint8_t) ((usb_mass_drives[lun].blockCount - 1) >> 24);
  SCSI_readFormatCapacity10Data[1] = (uint8_t) ((usb_mass_drives[lun].blockCount - 1) >> 16);
  SCSI_readFormatCapacity10Data[2] = (uint8_t) ((usb_mass_drives[lun].blockCount - 1) >> 8);
  SCSI_readFormatCapacity10Data[3] = (uint8_t) (usb_mass_drives[lun].blockCount - 1);

  SCSI_readFormatCapacity10Data[4] = (uint8_t) (SCSI_BLOCK_SIZE >> 24);
  SCSI_readFormatCapacity10Data[5] = (uint8_t) (SCSI_BLOCK_SIZE >> 16);
  SCSI_readFormatCapacity10Data[6] = (uint8_t) (SCSI_BLOCK_SIZE >> 8);
  SCSI_readFormatCapacity10Data[7] = (uint8_t) (SCSI_BLOCK_SIZE);
  usb_mass_transfer_data_request(SCSI_readFormatCapacity10Data, SCSI_READ_FORMAT_CAPACITY10_DATA_LEN);
}

void scsi_read10_cmd(uint8_t lun, uint32_t lba, uint32_t blockNbr) {
  if (usb_mass_botState == BOT_STATE_IDLE) {
    if (!(scsi_address_management(usb_mass_CBW.bLUN, SCSI_READ10, lba, blockNbr))) /*address out of range*/ {
      return;
    }

    if ((usb_mass_CBW.bmFlags & 0x80) != 0) {
      usb_mass_botState = BOT_STATE_DATA_IN;
      scsi_read_memory(lun, lba, blockNbr);
    } else {
      usb_mass_bot_abort(BOT_DIR_BOTH);
      scsi_set_sense_data(usb_mass_CBW.bLUN, SCSI_ILLEGAL_REQUEST, SCSI_INVALID_FIELED_IN_COMMAND);
      usb_mass_bot_set_csw(BOT_CSW_CMD_FAILED, BOT_SEND_CSW_ENABLE);
    }
    return;
  } else if (usb_mass_botState == BOT_STATE_DATA_IN) {
    scsi_read_memory(lun, lba, blockNbr);
  }
}

void scsi_write10_cmd(uint8_t lun, uint32_t lba, uint32_t blockNbr) {
  if (usb_mass_botState == BOT_STATE_IDLE) {
    if (!(scsi_address_management(usb_mass_CBW.bLUN, SCSI_WRITE10, lba, blockNbr)))/*address out of range*/ {
      return;
    }

    if ((usb_mass_CBW.bmFlags & 0x80) == 0) {
      usb_mass_botState = BOT_STATE_DATA_OUT;
      usb_generic_enable_rx(USB_MASS_RX_ENDPOINT_INFO); 
    } else {
      usb_mass_bot_abort(BOT_DIR_IN);
      scsi_set_sense_data(usb_mass_CBW.bLUN, SCSI_ILLEGAL_REQUEST, SCSI_INVALID_FIELED_IN_COMMAND);
      usb_mass_bot_set_csw(BOT_CSW_CMD_FAILED, BOT_SEND_CSW_DISABLE);
    }
    return;
  } else if (usb_mass_botState == BOT_STATE_DATA_OUT) {
    scsi_write_memory(lun, lba, blockNbr);
  }
}

void scsi_test_unit_ready_cmd(uint8_t lun) {
  if (usb_mass_mal_get_status(lun)) {
    scsi_set_sense_data(usb_mass_CBW.bLUN, SCSI_NOT_READY, SCSI_MEDIUM_NOT_PRESENT);
    usb_mass_bot_set_csw(BOT_CSW_CMD_FAILED, BOT_SEND_CSW_ENABLE);
    usb_mass_bot_abort(BOT_DIR_IN);
    return;
  } else {
    usb_mass_bot_set_csw(BOT_CSW_CMD_PASSED, BOT_SEND_CSW_ENABLE);
  }
}

void scsi_verify10_cmd(uint8_t lun) {
  (void)lun;
  if ((usb_mass_CBW.dDataLength == 0) && !(usb_mass_CBW.CB[1] & SCSI_BLKVFY))/* BLKVFY not set*/ {
    usb_mass_bot_set_csw(BOT_CSW_CMD_PASSED, BOT_SEND_CSW_ENABLE);
  } else {
    usb_mass_bot_abort(BOT_DIR_BOTH);
    scsi_set_sense_data(usb_mass_CBW.bLUN, SCSI_ILLEGAL_REQUEST, SCSI_INVALID_FIELED_IN_COMMAND);
    usb_mass_bot_set_csw(BOT_CSW_CMD_FAILED, BOT_SEND_CSW_DISABLE);
  }
}

void scsi_format_cmd(uint8_t lun) {
  if (usb_mass_mal_get_status(lun)) {
    scsi_set_sense_data(usb_mass_CBW.bLUN, SCSI_NOT_READY, SCSI_MEDIUM_NOT_PRESENT);
    usb_mass_bot_set_csw(BOT_CSW_CMD_FAILED, BOT_SEND_CSW_ENABLE);
    usb_mass_bot_abort(BOT_DIR_IN);
    return;
  }
  usb_mass_mal_format(lun);
  usb_mass_bot_set_csw(BOT_CSW_CMD_PASSED, BOT_SEND_CSW_ENABLE);
}

void scsi_set_sense_data(uint8_t lun, uint8_t sensKey, uint8_t asc) {
  (void)lun;
  SCSI_senseData[2] = sensKey;
  SCSI_senseData[12] = asc;
}

void scsi_invalid_cmd(uint8_t lun) {
  (void)lun;
  if (usb_mass_CBW.dDataLength == 0) {
    usb_mass_bot_abort(BOT_DIR_IN);
  } else {
    if ((usb_mass_CBW.bmFlags & 0x80) != 0) {
      usb_mass_bot_abort(BOT_DIR_IN);
    } else {
      usb_mass_bot_abort(BOT_DIR_BOTH);
    }
  }
  scsi_set_sense_data(usb_mass_CBW.bLUN, SCSI_ILLEGAL_REQUEST, SCSI_INVALID_COMMAND);
  usb_mass_bot_set_csw(BOT_CSW_CMD_FAILED, BOT_SEND_CSW_DISABLE);
}

uint8_t scsi_address_management(uint8_t lun, uint8_t cmd, uint32_t lba, uint32_t blockNbr) {

  if ((lba + blockNbr) > usb_mass_drives[lun].blockCount) {
    if (cmd == SCSI_WRITE10) {
      usb_mass_bot_abort(BOT_DIR_BOTH);
    }
    usb_mass_bot_abort(BOT_DIR_IN);
    scsi_set_sense_data(lun, SCSI_ILLEGAL_REQUEST, SCSI_ADDRESS_OUT_OF_RANGE);
    usb_mass_bot_set_csw(BOT_CSW_CMD_FAILED, BOT_SEND_CSW_DISABLE);
    return (FALSE);
  }


  if (usb_mass_CBW.dDataLength != blockNbr * SCSI_BLOCK_SIZE) {
    if (cmd == SCSI_WRITE10) {
      usb_mass_bot_abort(BOT_DIR_BOTH);
    } else {
      usb_mass_bot_abort(BOT_DIR_IN);
    }
    scsi_set_sense_data(usb_mass_CBW.bLUN, SCSI_ILLEGAL_REQUEST, SCSI_INVALID_FIELED_IN_COMMAND);
    usb_mass_bot_set_csw(BOT_CSW_CMD_FAILED, BOT_SEND_CSW_DISABLE);
    return (FALSE);
  }
  return (TRUE);
}

void scsi_read_memory(uint8_t lun, uint32_t startSector, uint32_t numSectors) {
  static uint32_t length;
  static uint64_t offset;

  if (SCSI_transferState == SCSI_TXFR_IDLE) {
    offset = (uint64_t)startSector * SCSI_BLOCK_SIZE;
    length = numSectors * SCSI_BLOCK_SIZE;
    SCSI_transferState = SCSI_TXFR_ONGOING;
  }

  if (SCSI_transferState == SCSI_TXFR_ONGOING) {
    if (SCSI_blockReadCount == 0) {
      usb_mass_mal_read_memory(lun, SCSI_dataBuffer, (uint32_t)(offset/SCSI_BLOCK_SIZE), 1);

      SCSI_blockReadCount = SCSI_BLOCK_SIZE - MAX_BULK_PACKET_SIZE;
      SCSI_blockOffset = MAX_BULK_PACKET_SIZE;

      usb_mass_sil_write(SCSI_dataBuffer, MAX_BULK_PACKET_SIZE);
    } else {
      SCSI_blockReadCount -= MAX_BULK_PACKET_SIZE;
      SCSI_blockOffset += MAX_BULK_PACKET_SIZE;

      usb_mass_sil_write(SCSI_dataBuffer + SCSI_blockOffset, MAX_BULK_PACKET_SIZE);
    }

    offset += MAX_BULK_PACKET_SIZE;
    length -= MAX_BULK_PACKET_SIZE;

    usb_mass_CSW.dDataResidue -= MAX_BULK_PACKET_SIZE;
    usb_mass_CSW.bStatus = BOT_CSW_CMD_PASSED;
    // TODO: Led_RW_ON();
  }

  if (length == 0) {
    SCSI_blockReadCount = 0;
    SCSI_blockOffset = 0;
    offset = 0;
    usb_mass_botState = BOT_STATE_DATA_IN_LAST;
    SCSI_transferState = SCSI_TXFR_IDLE;
    // TODO: Led_RW_OFF();
  }
}

void scsi_write_memory(uint8_t lun, uint32_t startSector, uint32_t numSectors) {
  static uint32_t length;
  static uint64_t offset;
  uint32_t idx;
  uint32_t temp = SCSI_counter + 64;

  if (SCSI_transferState == SCSI_TXFR_IDLE) {
    offset = (uint64_t)startSector * SCSI_BLOCK_SIZE;
    length = numSectors * SCSI_BLOCK_SIZE;
    SCSI_transferState = SCSI_TXFR_ONGOING;
  }

  if (SCSI_transferState == SCSI_TXFR_ONGOING) {

    for (idx = 0; SCSI_counter < temp; SCSI_counter++) {
      *((uint8_t *) SCSI_dataBuffer + SCSI_counter) = usb_mass_bulkDataBuff[idx++];
    }

    offset += usb_mass_dataLength;
    length -= usb_mass_dataLength;

    if (!(length % SCSI_BLOCK_SIZE)) {
      SCSI_counter = 0;
      usb_mass_mal_write_memory(lun, SCSI_dataBuffer, (uint32_t)(offset/SCSI_BLOCK_SIZE) - 1, 1);
    }

    usb_mass_CSW.dDataResidue -= usb_mass_dataLength;
    usb_generic_enable_rx(USB_MASS_RX_ENDPOINT_INFO); /* enable the next transaction*/ // TODO

    // TODO: Led_RW_ON();
  }

  if ((length == 0) || (usb_mass_botState == BOT_STATE_CSW_Send)) {
    SCSI_counter = 0;
    usb_mass_bot_set_csw(BOT_CSW_CMD_PASSED, BOT_SEND_CSW_ENABLE);
    SCSI_transferState = SCSI_TXFR_IDLE;
    // TODO: Led_RW_OFF();
  }
}
<file_sep>/usb_generic.h
#ifndef _USB_GENERIC_H
#define _USB_GENERIC_H
#include <libmaple/libmaple_types.h>
typedef unsigned short u16;
typedef unsigned char u8;

#include <libmaple/usb.h>
#include <libmaple/nvic.h>
#include <libmaple/delay.h>
#include "usb_lib_globals.h"
#include "usb_reg_map.h"

#define USB_CONTROL_DONE 1

#define PMA_MEMORY_SIZE 512
#define MAX_USB_DESCRIPTOR_DATA_SIZE 200
#define USB_MAX_STRING_DESCRIPTOR_LENGTH 32

#define USB_EP0_BUFFER_SIZE       0x40
#define USB_EP0_TX_BUFFER_ADDRESS 0x40
#define USB_EP0_RX_BUFFER_ADDRESS (USB_EP0_TX_BUFFER_ADDRESS+USB_EP0_BUFFER_SIZE) 

#ifdef __cplusplus
extern "C" {
#endif

enum USB_ENDPOINT_TYPES {
    USB_GENERIC_ENDPOINT_TYPE_BULK = 0,
    USB_GENERIC_ENDPOINT_TYPE_CONTROL,
    USB_GENERIC_ENDPOINT_TYPE_ISO,
    USB_GENERIC_ENDPOINT_TYPE_INTERRUPT
};

extern const usb_descriptor_string usb_generic_default_iManufacturer;
extern const usb_descriptor_string usb_generic_default_iProduct;

typedef struct USBEndpointInfo {
    void (*callback)(void);
    void* pma;
    uint16 pmaSize;
    uint8 address;    
    uint8 type:2;
    uint8 doubleBuffer:1;
    uint8 tx:1; // 1 if TX, 0 if RX
    uint8 exclusive:1; // 1 if cannot use the same endpoint number for both rx and tx
} USBEndpointInfo;

typedef struct USBCompositePart {
    uint8 numInterfaces;
    uint8 numEndpoints;
	uint8 startInterface;
    uint16 descriptorSize;
    void (*getPartDescriptor)(uint8* out);
    void (*usbInit)(void);
    void (*usbReset)(void);
    void (*usbSetConfiguration)(void);
    void (*usbClearFeature)(void);
    void (*clear)(void);
    RESULT (*usbDataSetup)(uint8 request, uint8 interface, uint8 requestType, uint8 wValue0, uint8 wValue1, uint16 wIndex, uint16 wLength );
    RESULT (*usbNoDataSetup)(uint8 request, uint8 interface, uint8 requestType, uint8 wValue0, uint8 wValue1, uint16 wIndex);
    USBEndpointInfo* endpoints;
} USBCompositePart;

struct usb_chunk {
    uint32 dataLength;
    const uint8* data;
    struct usb_chunk* next;
} __packed;

uint32 usb_generic_chunks_length(struct usb_chunk* chunk);

static inline void usb_generic_enable_rx(USBEndpointInfo* ep) {
    usb_set_ep_rx_stat(ep->address, USB_EP_STAT_RX_VALID);
}

static inline void usb_generic_enable_tx(USBEndpointInfo* ep) {
    usb_set_ep_tx_stat(ep->address, USB_EP_STAT_TX_VALID);
}

static inline void usb_generic_stall_rx(USBEndpointInfo* ep) {
    usb_set_ep_rx_stat(ep->address, USB_EP_STAT_RX_STALL);
}

static inline void usb_generic_stall_tx(USBEndpointInfo* ep) {
    usb_set_ep_tx_stat(ep->address, USB_EP_STAT_TX_STALL);
}

static inline void usb_generic_disable_rx(USBEndpointInfo* ep) {
    usb_set_ep_rx_stat(ep->address, USB_EP_STAT_RX_DISABLED);
}

static inline void usb_generic_pause_rx(USBEndpointInfo* ep) {
    usb_set_ep_rx_stat(ep->address, USB_EP_STAT_RX_NAK);
}

static inline void usb_generic_disable_tx(USBEndpointInfo* ep) {
    usb_set_ep_tx_stat(ep->address, USB_EP_STAT_TX_DISABLED);
}

static inline void usb_generic_enable_rx_ep0(void) {
    usb_set_ep_rx_stat(USB_EP0, USB_EP_STAT_RX_VALID);
}

static inline void usb_generic_pause_rx_ep0(void) {
    usb_set_ep_rx_stat(USB_EP0, USB_EP_STAT_RX_NAK);
}

static inline void usb_generic_disable_interrupts_ep0(void) {
    nvic_irq_disable(NVIC_USB_LP_CAN_RX0);
}

static inline void usb_generic_enable_interrupts_ep0(void) {
    nvic_irq_enable(NVIC_USB_LP_CAN_RX0);
}

static inline void usb_generic_prepare_tx(USBEndpointInfo* ep, uint32 length) {
    usb_set_ep_tx_count(ep->address, length);
}

static inline void usb_generic_set_tx(USBEndpointInfo* ep, uint32 length) {
    usb_set_ep_tx_count(ep->address, length);
    usb_generic_enable_tx(ep);
}

// for double buffering
#define PMA_PTR_BUF1(ep) ((void*)((uint8*)(ep)->pma+(ep)->pmaSize))
#define PMA_PTR_BUF0(ep) ((ep)->pma)

uint32 usb_generic_send_from_circular_buffer_double_buffered(USBEndpointInfo* ep, volatile uint8* buf, uint32 circularBufferSize, uint32 amount, volatile uint32* tailP);
void usb_generic_set_disconnect_delay(uint32 delay);
void usb_generic_set_info(uint16 idVendor, uint16 idProduct, const char* iManufacturer, const char* iProduct, const char* iSerialNumber);
uint8 usb_generic_set_parts(USBCompositePart** _parts, unsigned _numParts);
void usb_generic_control_rx_setup(volatile void* buffer, uint16 length, volatile uint8* done);
void usb_generic_control_tx_setup(volatile void* buffer, uint16 length, volatile uint8* done);
void usb_generic_control_tx_chunk_setup(struct usb_chunk* chunk);
void usb_generic_control_descriptor_tx(ONE_DESCRIPTOR* d);
void usb_generic_disable(void);
void usb_generic_enable(void);
void usb_copy_from_pma_ptr(volatile uint8 *buf, uint16 len, uint32* pma);
void usb_copy_to_pma_ptr(volatile const uint8 *buf, uint16 len, uint32* pma);
uint32 usb_generic_read_to_circular_buffer(USBEndpointInfo* ep, volatile uint8* buf, uint32 bufferSize, volatile uint32* headP);
#define USB_GENERIC_UNLIMITED_BUFFER 0xFFFFFFFFul
uint32 usb_generic_read_to_buffer(USBEndpointInfo* ep, volatile uint8* buf, uint32 bufferSize);
uint32 usb_generic_send_from_circular_buffer(USBEndpointInfo* ep, volatile uint8* buf, uint32 bufferSize, uint32 head, volatile uint32* tailP, volatile int8* transmittingP);
uint32 usb_generic_send_from_buffer(USBEndpointInfo* ep, volatile uint8* buf, uint32 amount);
uint16_t usb_generic_roundUpToPowerOf2(uint16_t x);

#ifdef __cplusplus
}
#endif

#endif
<file_sep>/usb_hid.h
/******************************************************************************
 * The MIT License
 *
 * Copyright (c) 2011 LeafLabs LLC.
 *
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use, copy,
 * modify, merge, publish, distribute, sublicense, and/or sell copies
 * of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *****************************************************************************/

/**
 * @file libmaple/include/libmaple/usb_device.h
 * @brief USB Composite with CDC ACM and HID support
 *
 * IMPORTANT: this API is unstable, and may change without notice.
 */

#ifndef _USB_HID_H_
#define _USB_HID_H_

#include <libmaple/libmaple_types.h>
#include <libmaple/usb.h>
#include "usb_generic.h"

#define MAX_HID_BUFFERS 8
#define HID_BUFFER_SIZE(n,reportID) ((n)+((reportID)!=0))
#define HID_BUFFER_ALLOCATE_SIZE(n,reportID) ((HID_BUFFER_SIZE((n),(reportID))+1)/2*2)

#define HID_BUFFER_MODE_NO_WAIT 1
#define HID_BUFFER_MODE_OUTPUT  2

#define HID_BUFFER_EMPTY    0 
#define HID_BUFFER_UNREAD   USB_CONTROL_DONE
#define HID_BUFFER_READ     2

extern USBCompositePart usbHIDPart;

typedef struct HIDBuffer_t {
    volatile uint8_t* buffer; // use HID_BUFFER_ALLOCATE_SIZE() to calculate amount of memory to allocate                            
    uint16_t bufferSize; // this should match HID_BUFFER_SIZE
    uint8_t  reportID;
    uint8_t  mode;
    uint8_t  state; // HID_BUFFER_EMPTY, etc.
#ifdef __cplusplus
    inline HIDBuffer_t(volatile uint8_t* _buffer=NULL, uint16_t _bufferSize=0, uint8_t _reportID=0, uint8_t _mode=0) {
        reportID = _reportID;
        buffer = _buffer;
        bufferSize = _bufferSize;
        mode = _mode;
    }
#endif
} HIDBuffer_t;

#ifdef __cplusplus
extern "C" {
#endif

void usb_hid_set_report_descriptor(struct usb_chunk* chunks);
void usb_hid_clear_buffers(uint8_t type);
uint8_t usb_hid_add_buffer(uint8_t type, volatile HIDBuffer_t* buf);
void usb_hid_set_buffers(uint8_t type, volatile HIDBuffer_t* featureBuffers, int count);    
uint16_t usb_hid_get_data(uint8_t type, uint8_t reportID, uint8_t* out, uint8_t poll);
void usb_hid_set_feature(uint8_t reportID, uint8_t* data);
void usb_hid_setTXEPSize(uint32_t size); 
uint32 usb_hid_get_pending(void);

/*
 * HID Requests
 */

typedef enum _HID_REQUESTS
{
 
  GET_REPORT = 1,
  GET_IDLE,
  GET_PROTOCOL,
 
  SET_REPORT = 9,
  SET_IDLE,
  SET_PROTOCOL
 
} HID_REQUESTS;
 
#define HID_REPORT_TYPE_INPUT         0x01
#define HID_REPORT_TYPE_OUTPUT        0x02
#define HID_REPORT_TYPE_FEATURE       0x03 
 

/*
 * HID Descriptors, etc.
 */
 
#define HID_ENDPOINT_INT 				1
 
#define HID_DESCRIPTOR_TYPE             0x21 
#define REPORT_DESCRIPTOR               0x22


typedef struct
{
	uint8_t len;			// 9
	uint8_t dtype;			// 0x21
	uint8_t	versionL;		// 0x101
	uint8_t	versionH;		// 0x101
	uint8_t	country;
	uint8_t	numDesc;
	uint8_t	desctype;		// 0x22 report
	uint8_t	descLenL;
	uint8_t	descLenH;
} HIDDescriptor;


#define USB_INTERFACE_CLASS_HID           0x03
#define USB_INTERFACE_SUBCLASS_HID		  0x01

 /*
 * HID interface
 */

uint32 usb_hid_tx(const uint8* buf, uint32 len);
uint32 usb_hid_tx_mod(const uint8* buf, uint32 len);

uint32 usb_hid_data_available(void); /* in RX buffer */


#ifdef __cplusplus
}
#endif

#endif
<file_sep>/scripts/send.py
from pywinusb import hid
from time import sleep

SIZE=1
REPORT_ID=20

def sample_handler(data):
    print("Raw data: {0}".format(data))
    
device = hid.HidDeviceFilter(vendor_id = 0x1EAF, product_id = 0x0004).get_devices()[0] 
print(device)
device.open()
device.set_raw_data_handler(sample_handler)

n = 0
while True:
    """
    print("sending")
    out_report=device.find_output_reports()[0]
    buffer=[i for i in range(SIZE+1)]
    buffer[0]=REPORT_ID # report id
    buffer[-1] = n
    out_report.set_raw_data(buffer)
    if out_report.send():
        n = (n+1)&0xFF            
"""        
    sleep(0.5)
    #sleep(0.005)
        <file_sep>/branchlog.sh
svn log https://github.com/arpruss/USBComposite_stm32f1/branches/abstracted
<file_sep>/library.properties
name=USBComposite for STM32F1
version=0.95
author=Various
email=<EMAIL>
sentence=USB HID / MIDI / mass storage / Audio library for STM32F1
paragraph=USB HID / MIDI / mass storage / Audio library for STM32F1
url=https://github.com/arpruss/USBHID_stm32f1
architectures=STM32F1
maintainer=<EMAIL>
category=Communication