usbcomposite library
This commit is contained in:
parent
f56c37e97e
commit
18d1654195
|
@ -0,0 +1,56 @@
|
|||
#include "USBHID.h"
|
||||
|
||||
//================================================================================
|
||||
//================================================================================
|
||||
// Mouse
|
||||
|
||||
void HIDAbsMouse::begin(void){
|
||||
}
|
||||
|
||||
void HIDAbsMouse::end(void){
|
||||
}
|
||||
|
||||
void HIDAbsMouse::click(uint8_t b)
|
||||
{
|
||||
report.wheel = 0;
|
||||
report.buttons = b;
|
||||
sendReport();
|
||||
report.buttons = 0;
|
||||
sendReport();
|
||||
}
|
||||
|
||||
void HIDAbsMouse::move(int16 x, int16 y, int8 wheel)
|
||||
{
|
||||
report.x = x;
|
||||
report.y = y;
|
||||
report.wheel = wheel;
|
||||
|
||||
sendReport();
|
||||
}
|
||||
|
||||
void HIDAbsMouse::buttons(uint8_t b)
|
||||
{
|
||||
if (b != report.buttons)
|
||||
{
|
||||
report.wheel = 0;
|
||||
report.buttons = b;
|
||||
sendReport();
|
||||
}
|
||||
}
|
||||
|
||||
void HIDAbsMouse::press(uint8_t b)
|
||||
{
|
||||
buttons(report.buttons | b);
|
||||
}
|
||||
|
||||
void HIDAbsMouse::release(uint8_t b)
|
||||
{
|
||||
buttons(report.buttons & ~b);
|
||||
}
|
||||
|
||||
bool HIDAbsMouse::isPressed(uint8_t b)
|
||||
{
|
||||
if ((b & report.buttons) != 0)
|
||||
return true;
|
||||
return false;
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
#include "USBHID.h"
|
||||
|
||||
//================================================================================
|
||||
//================================================================================
|
||||
// Keyboard
|
||||
|
||||
HIDKeyboard BootKeyboard(0);
|
|
@ -0,0 +1,13 @@
|
|||
#include "USBHID.h"
|
||||
|
||||
void HIDConsumer::begin(void) {}
|
||||
void HIDConsumer::end(void) {}
|
||||
void HIDConsumer::press(uint16_t button) {
|
||||
report.button = button;
|
||||
sendReport();
|
||||
}
|
||||
|
||||
void HIDConsumer::release() {
|
||||
report.button = 0;
|
||||
sendReport();
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
#include "USBHID.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(KeyboardJoystick, HID_KEYBOARD_REPORT_DESCRIPTOR(), HID_JOYSTICK_REPORT_DESCRIPTOR());
|
||||
REPORT(Joystick, HID_JOYSTICK_REPORT_DESCRIPTOR());
|
||||
REPORT(BootKeyboard, HID_BOOT_KEYBOARD_REPORT_DESCRIPTOR());
|
|
@ -0,0 +1,118 @@
|
|||
#include "USBHID.h"
|
||||
|
||||
// This code requires gcc on low-endian devices.
|
||||
|
||||
//================================================================================
|
||||
//================================================================================
|
||||
// Joystick
|
||||
|
||||
void HIDJoystick::begin(void){
|
||||
}
|
||||
|
||||
void HIDJoystick::end(void){
|
||||
}
|
||||
|
||||
void HIDJoystick::setManualReportMode(bool mode) {
|
||||
manualReport = mode;
|
||||
}
|
||||
|
||||
bool HIDJoystick::getManualReportMode() {
|
||||
return manualReport;
|
||||
}
|
||||
|
||||
void HIDJoystick::safeSendReport() {
|
||||
if (!manualReport) {
|
||||
sendReport();
|
||||
}
|
||||
}
|
||||
|
||||
void HIDJoystick::button(uint8_t button, bool val){
|
||||
uint32_t mask = ((uint32_t)1 << (button-1));
|
||||
|
||||
if (val) {
|
||||
joyReport.buttons |= mask;
|
||||
} else {
|
||||
joyReport.buttons &= ~mask;
|
||||
}
|
||||
|
||||
safeSendReport();
|
||||
}
|
||||
|
||||
void HIDJoystick::X(uint16_t val){
|
||||
if (val > 1023) val = 1023;
|
||||
joyReport.x = val;
|
||||
|
||||
safeSendReport();
|
||||
}
|
||||
|
||||
void HIDJoystick::Y(uint16_t val){
|
||||
if (val > 1023) val = 1023;
|
||||
joyReport.y = val;
|
||||
|
||||
safeSendReport();
|
||||
}
|
||||
|
||||
void HIDJoystick::position(uint16_t x, uint16_t y){
|
||||
if (x > 1023) x = 1023;
|
||||
if (y > 1023) y = 1023;
|
||||
joyReport.x = x;
|
||||
joyReport.y = y;
|
||||
|
||||
safeSendReport();
|
||||
}
|
||||
|
||||
void HIDJoystick::Xrotate(uint16_t val){
|
||||
if (val > 1023) val = 1023;
|
||||
joyReport.rx = val;
|
||||
|
||||
safeSendReport();
|
||||
}
|
||||
|
||||
void HIDJoystick::Yrotate(uint16_t val){
|
||||
if (val > 1023) val = 1023;
|
||||
joyReport.ry = val;
|
||||
|
||||
safeSendReport();
|
||||
}
|
||||
|
||||
void HIDJoystick::sliderLeft(uint16_t val){
|
||||
if (val > 1023) val = 1023;
|
||||
joyReport.sliderLeft = val;
|
||||
|
||||
safeSendReport();
|
||||
}
|
||||
|
||||
void HIDJoystick::sliderRight(uint16_t val){
|
||||
if (val > 1023) val = 1023;
|
||||
joyReport.sliderRight = val;
|
||||
|
||||
safeSendReport();
|
||||
}
|
||||
|
||||
void HIDJoystick::slider(uint16_t val){
|
||||
if (val > 1023) val = 1023;
|
||||
joyReport.sliderLeft = val;
|
||||
joyReport.sliderRight = val;
|
||||
|
||||
safeSendReport();
|
||||
}
|
||||
|
||||
void HIDJoystick::hat(int16_t dir){
|
||||
uint8_t val;
|
||||
if (dir < 0) val = 15;
|
||||
else if (dir < 23) val = 0;
|
||||
else if (dir < 68) val = 1;
|
||||
else if (dir < 113) val = 2;
|
||||
else if (dir < 158) val = 3;
|
||||
else if (dir < 203) val = 4;
|
||||
else if (dir < 245) val = 5;
|
||||
else if (dir < 293) val = 6;
|
||||
else if (dir < 338) val = 7;
|
||||
else val = 15;
|
||||
|
||||
joyReport.hat = val;
|
||||
|
||||
safeSendReport();
|
||||
}
|
||||
|
||||
HIDJoystick Joystick;
|
|
@ -0,0 +1,258 @@
|
|||
#include "USBHID.h"
|
||||
#include <string.h>
|
||||
|
||||
//================================================================================
|
||||
//================================================================================
|
||||
// Keyboard
|
||||
|
||||
#define SHIFT 0x80
|
||||
static const uint8_t ascii_to_hid[128] =
|
||||
{
|
||||
0x00, // NUL
|
||||
0x00, // SOH
|
||||
0x00, // STX
|
||||
0x00, // ETX
|
||||
0x00, // EOT
|
||||
0x00, // ENQ
|
||||
0x00, // ACK
|
||||
0x00, // BEL
|
||||
0x2a, // BS Backspace
|
||||
0x2b, // TAB Tab
|
||||
0x28, // LF Enter
|
||||
0x00, // VT
|
||||
0x00, // FF
|
||||
0x00, // CR
|
||||
0x00, // SO
|
||||
0x00, // SI
|
||||
0x00, // DEL
|
||||
0x00, // DC1
|
||||
0x00, // DC2
|
||||
0x00, // DC3
|
||||
0x00, // DC4
|
||||
0x00, // NAK
|
||||
0x00, // SYN
|
||||
0x00, // ETB
|
||||
0x00, // CAN
|
||||
0x00, // EM
|
||||
0x00, // SUB
|
||||
0x00, // ESC
|
||||
0x00, // FS
|
||||
0x00, // GS
|
||||
0x00, // RS
|
||||
0x00, // US
|
||||
|
||||
0x2c, // ' '
|
||||
0x1e|SHIFT, // !
|
||||
0x34|SHIFT, // "
|
||||
0x20|SHIFT, // #
|
||||
0x21|SHIFT, // $
|
||||
0x22|SHIFT, // %
|
||||
0x24|SHIFT, // &
|
||||
0x34, // '
|
||||
0x26|SHIFT, // (
|
||||
0x27|SHIFT, // )
|
||||
0x25|SHIFT, // *
|
||||
0x2e|SHIFT, // +
|
||||
0x36, // ,
|
||||
0x2d, // -
|
||||
0x37, // .
|
||||
0x38, // /
|
||||
0x27, // 0
|
||||
0x1e, // 1
|
||||
0x1f, // 2
|
||||
0x20, // 3
|
||||
0x21, // 4
|
||||
0x22, // 5
|
||||
0x23, // 6
|
||||
0x24, // 7
|
||||
0x25, // 8
|
||||
0x26, // 9
|
||||
0x33|SHIFT, // :
|
||||
0x33, // ;
|
||||
0x36|SHIFT, // <
|
||||
0x2e, // =
|
||||
0x37|SHIFT, // >
|
||||
0x38|SHIFT, // ?
|
||||
0x1f|SHIFT, // @
|
||||
0x04|SHIFT, // A
|
||||
0x05|SHIFT, // B
|
||||
0x06|SHIFT, // C
|
||||
0x07|SHIFT, // D
|
||||
0x08|SHIFT, // E
|
||||
0x09|SHIFT, // F
|
||||
0x0a|SHIFT, // G
|
||||
0x0b|SHIFT, // H
|
||||
0x0c|SHIFT, // I
|
||||
0x0d|SHIFT, // J
|
||||
0x0e|SHIFT, // K
|
||||
0x0f|SHIFT, // L
|
||||
0x10|SHIFT, // M
|
||||
0x11|SHIFT, // N
|
||||
0x12|SHIFT, // O
|
||||
0x13|SHIFT, // P
|
||||
0x14|SHIFT, // Q
|
||||
0x15|SHIFT, // R
|
||||
0x16|SHIFT, // S
|
||||
0x17|SHIFT, // T
|
||||
0x18|SHIFT, // U
|
||||
0x19|SHIFT, // V
|
||||
0x1a|SHIFT, // W
|
||||
0x1b|SHIFT, // X
|
||||
0x1c|SHIFT, // Y
|
||||
0x1d|SHIFT, // Z
|
||||
0x2f, // [
|
||||
0x31, // bslash
|
||||
0x30, // ]
|
||||
0x23|SHIFT, // ^
|
||||
0x2d|SHIFT, // _
|
||||
0x35, // `
|
||||
0x04, // a
|
||||
0x05, // b
|
||||
0x06, // c
|
||||
0x07, // d
|
||||
0x08, // e
|
||||
0x09, // f
|
||||
0x0a, // g
|
||||
0x0b, // h
|
||||
0x0c, // i
|
||||
0x0d, // j
|
||||
0x0e, // k
|
||||
0x0f, // l
|
||||
0x10, // m
|
||||
0x11, // n
|
||||
0x12, // o
|
||||
0x13, // p
|
||||
0x14, // q
|
||||
0x15, // r
|
||||
0x16, // s
|
||||
0x17, // t
|
||||
0x18, // u
|
||||
0x19, // v
|
||||
0x1a, // w
|
||||
0x1b, // x
|
||||
0x1c, // y
|
||||
0x1d, // z
|
||||
0x2f|SHIFT, //
|
||||
0x31|SHIFT, // |
|
||||
0x30|SHIFT, // }
|
||||
0x35|SHIFT, // ~
|
||||
0 // DEL
|
||||
};
|
||||
|
||||
void HIDKeyboard::begin(void){
|
||||
USBHID.addOutputBuffer(&ledData);
|
||||
}
|
||||
|
||||
void HIDKeyboard::end(void) {
|
||||
}
|
||||
|
||||
// 136: non-printing key
|
||||
// shift -> 0x02
|
||||
// modifiers: 128 --> bit shift
|
||||
|
||||
uint8_t HIDKeyboard::getKeyCode(uint8_t k, uint8_t* modifiersP)
|
||||
{
|
||||
*modifiersP = 0;
|
||||
|
||||
if (adjustForHostCapsLock && (getLEDs() & 0x02)) { // capslock is down on host OS, so host will reverse
|
||||
if ('a' <= k && k <= 'z')
|
||||
k += 'A'-'a';
|
||||
else if ('A' <= k && k <= 'Z')
|
||||
k += 'a'-'A';
|
||||
}
|
||||
|
||||
if (k < 0x80) {
|
||||
k = ascii_to_hid[k];
|
||||
if (k & SHIFT) {
|
||||
k &= 0x7f;
|
||||
*modifiersP = 0x02;
|
||||
}
|
||||
return k;
|
||||
}
|
||||
if (k >= 0x88) { // non-printing key, Arduino format
|
||||
return k - 0x88;
|
||||
}
|
||||
else { // shift key
|
||||
*modifiersP = 1<<(k-0x80);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
size_t HIDKeyboard::press(uint8_t k) {
|
||||
uint8_t modifiers;
|
||||
|
||||
k = getKeyCode(k, &modifiers);
|
||||
|
||||
if (k == 0) {
|
||||
if (modifiers == 0) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (unsigned i = 0; i<HID_KEYBOARD_ROLLOVER; i++) {
|
||||
if (keyReport.keys[i] == k) {
|
||||
goto SEND;
|
||||
}
|
||||
}
|
||||
for (unsigned i = 0; i<HID_KEYBOARD_ROLLOVER; i++) {
|
||||
if (keyReport.keys[i] == 0) {
|
||||
keyReport.keys[i] = k;
|
||||
goto SEND;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
SEND:
|
||||
keyReport.modifiers |= modifiers;
|
||||
sendReport();
|
||||
return 1;
|
||||
}
|
||||
|
||||
// release() takes the specified key out of the persistent key report and
|
||||
// sends the report. This tells the OS the key is no longer pressed and that
|
||||
// it shouldn't be repeated any more.
|
||||
size_t HIDKeyboard::release(uint8_t k)
|
||||
{
|
||||
uint8_t modifiers;
|
||||
k = getKeyCode(k, &modifiers);
|
||||
|
||||
if (k != 0) {
|
||||
for (unsigned i=0; i<HID_KEYBOARD_ROLLOVER; i++) {
|
||||
if (keyReport.keys[i] == k) {
|
||||
keyReport.keys[i] = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (modifiers == 0)
|
||||
return 0;
|
||||
}
|
||||
|
||||
keyReport.modifiers &= ~modifiers;
|
||||
|
||||
sendReport();
|
||||
return 1;
|
||||
}
|
||||
|
||||
void HIDKeyboard::releaseAll(void)
|
||||
{
|
||||
memset(keyReport.keys, 0, HID_KEYBOARD_ROLLOVER);
|
||||
keyReport.modifiers = 0;
|
||||
|
||||
sendReport();
|
||||
}
|
||||
|
||||
size_t HIDKeyboard::write(uint8_t c)
|
||||
{
|
||||
if (press(c)) {
|
||||
release(c); // Keyup
|
||||
return 1;
|
||||
}
|
||||
else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
HIDKeyboard Keyboard;
|
|
@ -0,0 +1,45 @@
|
|||
Copyright (c) 2011, Peter Barrett
|
||||
|
||||
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.
|
||||
|
||||
------------
|
||||
|
||||
The MIT License
|
||||
|
||||
Copyright (c) 2011 LeafLabs LLC.
|
||||
Parts of composite HID+Serial code taken from https://github.com/libarra111/Arduino_STM32
|
||||
USB Mass storage code based on Joe Ferner's code: https://github.com/joeferner/maple-usbMassStorage
|
||||
MIDI code Copyright (c) 2010 Perry Hung, (c) 2013 Magnus Lundin, (c) 2013 Donald Delmar Davis, Suspect Devices,
|
||||
(c) 2003-2008 Tymm Twillman <tymm@booyaka.com>.
|
||||
|
||||
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.
|
||||
|
|
@ -0,0 +1,151 @@
|
|||
/*--------------------------------------------------------------------------MidiSpecs.h
|
||||
*
|
||||
* These defines are based on specs created by the USB and MMA standards organizations.
|
||||
* There are not a lot of other ways to code them so licensing this is rather ludicrous.
|
||||
* However, in order to be able to embed this in client projects, and avoid the stupidity
|
||||
* of enforced open everything I will declare the following about this file.
|
||||
*
|
||||
* Copyright (c) 2011 Donald Delmar Davis, Suspect Devices
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* USB midi commands from
|
||||
* MIDI10.pdf "Universal Serial Bus Device Class Definition for MIDI Devices"
|
||||
* REV 1. (1999)
|
||||
* http://www.usb.org/developers/devclass_docs/midi10.pdf
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __LETS_MIDI_SPECS_H__
|
||||
#define __LETS_MIDI_SPECS_H__
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
// rework this for the different architectures....
|
||||
#if defined(__GNUC__)
|
||||
typedef struct
|
||||
{
|
||||
unsigned cin : 4; // this is the low nibble.
|
||||
unsigned cable : 4;
|
||||
// uint8_t cin;
|
||||
uint8_t midi0;
|
||||
uint8_t midi1;
|
||||
uint8_t midi2;
|
||||
} __attribute__ ((__packed__)) MIDI_EVENT_PACKET_t ;
|
||||
#else
|
||||
typedef struct // may need to be adjusted for other compilers and bitfield order...
|
||||
{
|
||||
unsigned cable : 4;
|
||||
unsigned cin : 4;
|
||||
uint8_t midi0;
|
||||
uint8_t midi1;
|
||||
uint8_t midi2;
|
||||
} MIDI_EVENT_PACKET_t ;
|
||||
#endif
|
||||
|
||||
|
||||
#define CIN_MISC_FUNCTION 0x00 /* Reserved for future extension. */
|
||||
#define CIN_CABLE_EVENT 0x01 /* Reserved for future extension. */
|
||||
#define CIN_2BYTE_SYS_COMMON 0x02 /* 2Bytes -- MTC, SongSelect, etc. */
|
||||
#define CIN_3BYTE_SYS_COMMON 0x03 /* 3Bytes -- SPP, etc. */
|
||||
#define CIN_SYSEX 0x04 /* 3Bytes */
|
||||
#define CIN_SYSEX_ENDS_IN_1 0x05 /* 1Bytes */
|
||||
#define CIN_SYSEX_ENDS_IN_2 0x06 /* 2Bytes */
|
||||
#define CIN_SYSEX_ENDS_IN_3 0x07 /* 3Bytes */
|
||||
#define CIN_NOTE_OFF 0x08 /* 3Bytes */
|
||||
#define CIN_NOTE_ON 0x09 /* 3Bytes */
|
||||
#define CIN_AFTER_TOUCH 0x0A /* 3Bytes */
|
||||
#define CIN_CONTROL_CHANGE 0x0B /* 3Bytes */
|
||||
#define CIN_PROGRAM_CHANGE 0x0C /* 2Bytes */
|
||||
#define CIN_CHANNEL_PRESSURE 0x0D /* 2Bytes */
|
||||
#define CIN_PITCH_WHEEL 0x0E /* 3Bytes */
|
||||
#define CIN_1BYTE 0x0F /* 1Bytes */
|
||||
//#define CIN_IS_SYSEX(cin) ((cin == CIN_SYSEX)||(cin == CIN_SYSEX_ENDS_IN_1)||(cin == CIN_SYSEX_ENDS_IN_2)||(cin == CIN_SYSEX_ENDS_IN_3))
|
||||
#define CIN_IS_SYSEX(cin) ( ((cin) & ~(0x08)) && ((cin) &0x04) )
|
||||
|
||||
/*
|
||||
* MIDI V1 message definitions these are from the MMA document
|
||||
* http://www.midi.org/techspecs/midimessages.php
|
||||
*/
|
||||
#define MIDIv1_BAUD_RATE 31250
|
||||
|
||||
/*
|
||||
* parse midi (v1) message macros
|
||||
*/
|
||||
#define MIDIv1_IS_STATUS(b) ((b) & 0x80)
|
||||
#define MIDIv1_IS_VOICE(b) ((b) <= 0xEF)
|
||||
#define MIDIv1_VOICE_COMMAND(b) ((b) & 0xF0)
|
||||
#define MIDIv1_VOICE_CHANNEL(b) ((b) & 0x0F)
|
||||
#define MIDIv1_IS_SYSCOMMON(b) (((b) >= 0xF0) & ((b) < 0xF8))
|
||||
#define MIDIv1_IS_REALTIME(b) ((b) >= 0xF8)
|
||||
|
||||
/*
|
||||
* Voice category messages
|
||||
*/
|
||||
#define MIDIv1_NOTE_OFF 0x80 /* 2 bytes data -- CIN_NOTE_OFF */
|
||||
#define MIDIv1_NOTE_ON 0x90 /* 2 bytes data -- CIN_NOTE_ON */
|
||||
#define MIDIv1_AFTER_TOUCH 0xA0 /* 2 bytes data -- CIN_AFTER_TOUCH */
|
||||
#define MIDIv1_CONTROL_CHANGE 0xB0 /* 2 bytes data -- CIN_CONTROL_CHANGE */
|
||||
#define MIDIv1_PROGRAM_CHANGE 0xC0 /* 1 byte data -- CIN_PROGRAM_CHANGE */
|
||||
#define MIDIv1_CHANNEL_PRESSURE 0xD0 /* 1 byte data -- CIN_CHANNEL_PRESSURE */
|
||||
#define MIDIv1_PITCH_WHEEL 0xE0 /* 2 bytes data -- CIN_PITCH_WHEEL */
|
||||
|
||||
/*
|
||||
* System common category messages
|
||||
*/
|
||||
#define MIDIv1_SYSEX_START 0xF0
|
||||
#define MIDIv1_SYSEX_END 0xF7
|
||||
#define MIDIv1_MTC_QUARTER_FRAME 0xF1 /* 1 byte data -- CIN_2BYTE_SYS_COMMON */
|
||||
#define MIDIv1_SONG_POSITION_PTR 0xF2 /* 2 bytes data -- CIN_3BYTE_SYS_COMMON */
|
||||
#define MIDIv1_SONG_SELECT 0xF3 /* 1 byte data -- CIN_2BYTE_SYS_COMMON */
|
||||
#define MIDIv1_TUNE_REQUEST 0xF6 /* no data -- CIN_1BYTE */
|
||||
|
||||
/*
|
||||
* Realtime category messages, can be sent anytime
|
||||
*/
|
||||
#define MIDIv1_CLOCK 0xF8 /* no data -- CIN_1BYTE */
|
||||
#define MIDIv1_TICK 0xF9 /* no data -- CIN_1BYTE */
|
||||
#define MIDIv1_START 0xFA /* no data -- CIN_1BYTE */
|
||||
#define MIDIv1_CONTINUE 0xFB /* no data -- CIN_1BYTE */
|
||||
#define MIDIv1_STOP 0xFC /* no data -- CIN_1BYTE */
|
||||
#define MIDIv1_ACTIVE_SENSE 0xFE /* no data -- CIN_1BYTE */
|
||||
#define MIDIv1_RESET 0xFF /* no data -- CIN_1BYTE */
|
||||
|
||||
/*
|
||||
* sysex universal id's
|
||||
*/
|
||||
#define MIDIv1_UNIVERSAL_REALTIME_ID 0x7F
|
||||
#define MIDIv1_UNIVERSAL_NON_REALTIME_ID 0x7E
|
||||
#define MIDIv1_UNIVERSAL_ALL_CHANNELS 0x7F
|
||||
/*
|
||||
* Susbset of universal sysex (general info request)
|
||||
* As described http://www.blitter.com/~russtopia/MIDI/~jglatt/tech/midispec.htm
|
||||
*/
|
||||
#define USYSEX_NON_REAL_TIME 0x7E
|
||||
#define USYSEX_REAL_TIME 0x7F
|
||||
#define USYSEX_ALL_CHANNELS 0x7F
|
||||
#define USYSEX_GENERAL_INFO 0x06
|
||||
#define USYSEX_GI_ID_REQUEST 0x01
|
||||
#define USYSEX_GI_ID_RESPONSE 0x02
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
|
@ -0,0 +1,306 @@
|
|||
//
|
||||
// MinSysex.c
|
||||
// LibMaple4Midi
|
||||
//
|
||||
// Created by Donald D Davis on 4/11/13.
|
||||
// Copyright (c) 2013 Suspect Devices. All rights reserved.
|
||||
// Modified BSD Liscense
|
||||
/*
|
||||
0xF0 SysEx
|
||||
0x7E Non-Realtime
|
||||
0x7F The SysEx channel. Could be from 0x00 to 0x7F.
|
||||
Here we set it to "disregard channel".
|
||||
0x06 Sub-ID -- General Information
|
||||
0x01 Sub-ID2 -- Identity Request
|
||||
0xF7 End of SysEx
|
||||
---- response
|
||||
0xF0 SysEx
|
||||
0x7E Non-Realtime
|
||||
0x7F The SysEx channel. Could be from 0x00 to 0x7F.
|
||||
Here we set it to "disregard channel".
|
||||
0x06 Sub-ID -- General Information
|
||||
0x02 Sub-ID2 -- Identity Reply
|
||||
0xID Manufacturer's ID
|
||||
0xf1 The f1 and f2 bytes make up the family code. Each
|
||||
0xf2 manufacturer assigns different family codes to his products.
|
||||
0xp1 The p1 and p2 bytes make up the model number. Each
|
||||
0xp2 manufacturer assigns different model numbers to his products.
|
||||
0xv1 The v1, v2, v3 and v4 bytes make up the version number.
|
||||
0xv2
|
||||
0xv3
|
||||
0xv4
|
||||
0xF7 End of SysEx
|
||||
*/
|
||||
|
||||
#define USB_MIDI
|
||||
#ifdef USB_MIDI
|
||||
|
||||
// change this to packets
|
||||
#define STANDARD_ID_RESPONSE_LENGTH 7
|
||||
|
||||
#include "usb_midi_device.h"
|
||||
#include <libmaple/nvic.h>
|
||||
#include <libmaple/delay.h>
|
||||
#include <MinSysex.h>
|
||||
//#include <wirish/wirish.h>
|
||||
|
||||
|
||||
#define MAX_SYSEX_SIZE 256
|
||||
|
||||
/********************************* ACHTUNG! ignores usbmidi cable ********************************/
|
||||
/*const MIDI_EVENT_PACKET_t standardIDResponse[]={
|
||||
{ DEFAULT_MIDI_CABLE,
|
||||
CIN_SYSEX,
|
||||
MIDIv1_SYSEX_START,
|
||||
USYSEX_NON_REAL_TIME,
|
||||
USYSEX_ALL_CHANNELS},
|
||||
{ DEFAULT_MIDI_CABLE,
|
||||
CIN_SYSEX,
|
||||
USYSEX_GENERAL_INFO,
|
||||
USYSEX_GI_ID_RESPONSE,
|
||||
LEAFLABS_MMA_VENDOR_1},
|
||||
{ DEFAULT_MIDI_CABLE,
|
||||
CIN_SYSEX,
|
||||
LEAFLABS_MMA_VENDOR_2, // extended ID
|
||||
LEAFLABS_MMA_VENDOR_3, // extended ID
|
||||
1}, // family #1
|
||||
{ DEFAULT_MIDI_CABLE,
|
||||
CIN_SYSEX,
|
||||
2, // family #2
|
||||
1, // part #1
|
||||
2}, // part #2
|
||||
{ DEFAULT_MIDI_CABLE,
|
||||
CIN_SYSEX,
|
||||
0, // version 1
|
||||
0, // version 2
|
||||
1}, // version 3
|
||||
{ DEFAULT_MIDI_CABLE,
|
||||
CIN_SYSEX_ENDS_IN_2,
|
||||
'!', // lgl compatible
|
||||
MIDIv1_SYSEX_END,
|
||||
0}
|
||||
};
|
||||
*/
|
||||
const uint8 standardIDResponse[]={
|
||||
CIN_SYSEX,
|
||||
MIDIv1_SYSEX_START,
|
||||
USYSEX_NON_REAL_TIME,
|
||||
USYSEX_ALL_CHANNELS,
|
||||
CIN_SYSEX,
|
||||
USYSEX_GENERAL_INFO,
|
||||
USYSEX_GI_ID_RESPONSE,
|
||||
LEAFLABS_MMA_VENDOR_1,
|
||||
CIN_SYSEX,
|
||||
LEAFLABS_MMA_VENDOR_2, // extended ID
|
||||
LEAFLABS_MMA_VENDOR_3, // extended ID
|
||||
1, // family #1
|
||||
CIN_SYSEX,
|
||||
2, // family #2
|
||||
1, // part #1
|
||||
2, // part #2
|
||||
CIN_SYSEX,
|
||||
0, // version 1
|
||||
0, // version 2
|
||||
1, // version 3
|
||||
CIN_SYSEX_ENDS_IN_2,
|
||||
'!', // lgl compatible
|
||||
MIDIv1_SYSEX_END,
|
||||
0
|
||||
};
|
||||
//#define STANDARD_ID_RESPONSE_LENGTH (sizeof(standardIDResponse))
|
||||
|
||||
typedef enum {NOT_IN_SYSEX=0,COULD_BE_MY_SYSEX,YUP_ITS_MY_SYSEX,ITS_NOT_MY_SYSEX} sysexStates;
|
||||
volatile uint8 sysexBuffer[MAX_SYSEX_SIZE];
|
||||
volatile sysexStates sysexState;
|
||||
volatile int sysexFinger=0;
|
||||
|
||||
/*
|
||||
0xF0 SysEx
|
||||
0x?? LEAFLABS_MMA_VENDOR_1
|
||||
0x?? LEAFLABS_MMA_VENDOR_2
|
||||
|
||||
0x?? LEAFLABS_MMA_VENDOR_3
|
||||
0x10 LGL_DEVICE_NUMBER
|
||||
0xLE CMD: REBOOT
|
||||
|
||||
0xf7 EOSysEx
|
||||
*/
|
||||
#define STACK_TOP 0x20000800
|
||||
#define EXC_RETURN 0xFFFFFFF9
|
||||
#define DEFAULT_CPSR 0x61000000
|
||||
#define RESET_DELAY 100000
|
||||
#if 0
|
||||
static void wait_reset(void) {
|
||||
delay_us(RESET_DELAY);
|
||||
nvic_sys_reset();
|
||||
}
|
||||
#endif
|
||||
|
||||
/* -----------------------------------------------------------------------------dealWithItQuickly()
|
||||
* Note: at this point we have established that the sysex belongs to us.
|
||||
* So we need to respond to any generic requests like information requests.
|
||||
* We also need to handle requests which are meant for us. At the moment this is just the
|
||||
* reset request.
|
||||
*
|
||||
*/
|
||||
void dealWithItQuickly(){
|
||||
switch (sysexBuffer[1]) {
|
||||
case USYSEX_NON_REAL_TIME:
|
||||
switch (sysexBuffer[3]) {
|
||||
case USYSEX_GENERAL_INFO:
|
||||
if (sysexBuffer[4]==USYSEX_GI_ID_REQUEST) {
|
||||
usb_midi_tx((uint32 *) standardIDResponse, STANDARD_ID_RESPONSE_LENGTH);
|
||||
}
|
||||
}
|
||||
case USYSEX_REAL_TIME:
|
||||
break;
|
||||
#if 0
|
||||
case LEAFLABS_MMA_VENDOR_1:
|
||||
if (sysexBuffer[5]==LGL_RESET_CMD) {
|
||||
uintptr_t target = (uintptr_t)wait_reset | 0x1;
|
||||
asm volatile("mov r0, %[stack_top] \n\t" // Reset stack
|
||||
"mov sp, r0 \n\t"
|
||||
"mov r0, #1 \n\t"
|
||||
"mov r1, %[target_addr] \n\t"
|
||||
"mov r2, %[cpsr] \n\t"
|
||||
"push {r2} \n\t" // Fake xPSR
|
||||
"push {r1} \n\t" // PC target addr
|
||||
"push {r0} \n\t" // Fake LR
|
||||
"push {r0} \n\t" // Fake R12
|
||||
"push {r0} \n\t" // Fake R3
|
||||
"push {r0} \n\t" // Fake R2
|
||||
"push {r0} \n\t" // Fake R1
|
||||
"push {r0} \n\t" // Fake R0
|
||||
"mov lr, %[exc_return] \n\t"
|
||||
"bx lr"
|
||||
:
|
||||
: [stack_top] "r" (STACK_TOP),
|
||||
[target_addr] "r" (target),
|
||||
[exc_return] "r" (EXC_RETURN),
|
||||
[cpsr] "r" (DEFAULT_CPSR)
|
||||
: "r0", "r1", "r2");
|
||||
/* Can't happen. */
|
||||
ASSERT_FAULT(0);
|
||||
|
||||
}
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
}
|
||||
;//turn the led on?
|
||||
}
|
||||
|
||||
/* -----------------------------------------------------------------------------LglSysexHandler()
|
||||
* The idea here is to identify which Sysex's belong to us and deal with them.
|
||||
*/
|
||||
void LglSysexHandler(uint32 *midiBufferRx, uint32 *rx_offset, uint32 *n_unread_packets) {
|
||||
MIDI_EVENT_PACKET_t * midiPackets = (MIDI_EVENT_PACKET_t *) (midiBufferRx+(*rx_offset));
|
||||
uint8 nPackets=((*n_unread_packets)-(*rx_offset));
|
||||
int cPacket;
|
||||
uint8 soPackets=0;
|
||||
/********************************* ACHTUNG! ignores usbmidi cable ********************************/
|
||||
MIDI_EVENT_PACKET_t *packet;
|
||||
for (cPacket=0;cPacket<nPackets;cPacket++){
|
||||
packet=midiPackets+cPacket;
|
||||
if (!CIN_IS_SYSEX(packet->cin)) {
|
||||
continue;
|
||||
} // else {
|
||||
if (!soPackets) {
|
||||
soPackets=cPacket*4;
|
||||
}
|
||||
if ((sysexState==YUP_ITS_MY_SYSEX) && ((sysexFinger+3)>=MAX_SYSEX_SIZE)){
|
||||
sysexState=ITS_NOT_MY_SYSEX; //eisenhower policy. Even if its mine I cant deal with it.
|
||||
}
|
||||
switch (packet->cin) {
|
||||
case CIN_SYSEX:
|
||||
switch (sysexState) {
|
||||
case NOT_IN_SYSEX : // new sysex.
|
||||
sysexFinger=0;
|
||||
if (packet->midi0 == MIDIv1_SYSEX_START) {
|
||||
if (packet->midi1==USYSEX_REAL_TIME
|
||||
||packet->midi1==USYSEX_NON_REAL_TIME) {
|
||||
if ((packet->midi2==myMidiChannel)
|
||||
||(packet->midi2==USYSEX_ALL_CHANNELS)
|
||||
) {
|
||||
sysexState=YUP_ITS_MY_SYSEX;
|
||||
sysexBuffer[sysexFinger++]=MIDIv1_SYSEX_START;
|
||||
sysexBuffer[sysexFinger++]=packet->midi1;
|
||||
sysexBuffer[sysexFinger++]=packet->midi2;
|
||||
break;
|
||||
}
|
||||
} else if ((packet->midi1==myMidiID[0])
|
||||
&& (packet->midi2==myMidiID[1])
|
||||
){
|
||||
sysexState=COULD_BE_MY_SYSEX;
|
||||
sysexBuffer[sysexFinger++]=MIDIv1_SYSEX_START;
|
||||
sysexBuffer[sysexFinger++]=packet->midi1;
|
||||
sysexBuffer[sysexFinger++]=packet->midi2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case COULD_BE_MY_SYSEX:
|
||||
if (packet->midi0==myMidiID[2]) {
|
||||
sysexState=YUP_ITS_MY_SYSEX;
|
||||
sysexBuffer[sysexFinger++]=packet->midi0;
|
||||
sysexBuffer[sysexFinger++]=packet->midi1;
|
||||
sysexBuffer[sysexFinger++]=packet->midi2;
|
||||
} else {
|
||||
sysexState=ITS_NOT_MY_SYSEX;
|
||||
sysexFinger=0;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
break;
|
||||
case CIN_SYSEX_ENDS_IN_1:
|
||||
case CIN_SYSEX_ENDS_IN_2:
|
||||
case CIN_SYSEX_ENDS_IN_3:
|
||||
sysexBuffer[sysexFinger++]=packet->midi0;
|
||||
sysexBuffer[sysexFinger++]=packet->midi1;
|
||||
sysexBuffer[sysexFinger++]=packet->midi2;
|
||||
if (sysexState==YUP_ITS_MY_SYSEX) {
|
||||
if(cPacket>=(int32)(*n_unread_packets)){
|
||||
*n_unread_packets = soPackets;
|
||||
*rx_offset = soPackets;
|
||||
} else {
|
||||
uint8 c = cPacket;
|
||||
uint32 *s;
|
||||
uint32 *d = midiBufferRx + soPackets;
|
||||
for (s = midiBufferRx+c;
|
||||
((*n_unread_packets) && (s <= midiBufferRx+(USB_MIDI_RX_EPSIZE/4)));
|
||||
d++,s++
|
||||
) {
|
||||
(*d)=(*s);
|
||||
(*n_unread_packets)--;
|
||||
(*rx_offset)++;
|
||||
|
||||
}
|
||||
// we need to reset the for loop variables to re process remaining data.
|
||||
nPackets=((*n_unread_packets)-(*rx_offset));
|
||||
cPacket=(*rx_offset);
|
||||
}
|
||||
dealWithItQuickly();
|
||||
|
||||
}
|
||||
sysexFinger=0;
|
||||
sysexState=NOT_IN_SYSEX;
|
||||
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
//}
|
||||
|
||||
|
||||
|
||||
}
|
||||
// its our sysex and we will cry if we want to
|
||||
return;
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,37 @@
|
|||
//
|
||||
// lgl_min_sysex.h
|
||||
// LibMaple4Midi
|
||||
//
|
||||
// Created by Donald D Davis on 4/11/13.
|
||||
// Copyright (c) 2013 Suspect Devices. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef __LGL_MIN_SYSEX_H__
|
||||
#define __LGL_MIN_SYSEX_H__ 1
|
||||
|
||||
|
||||
#include "MidiSpecs.h"
|
||||
//#include "LGL.h"
|
||||
|
||||
#define LEAFLABS_MMA_VENDOR_1 0x7D
|
||||
#define LEAFLABS_MMA_VENDOR_2 0x1E
|
||||
#define LEAFLABS_MMA_VENDOR_3 0x4F
|
||||
|
||||
// move to LGL.h
|
||||
#define LGL_RESET_CMD 0x1e
|
||||
|
||||
#define DEFAULT_MIDI_CHANNEL 0x0A
|
||||
#define DEFAULT_MIDI_DEVICE 0x0A
|
||||
#define DEFAULT_MIDI_CABLE 0x00
|
||||
|
||||
// eventually all of this should be in a place for settings which can be written to flash.
|
||||
extern volatile uint8 myMidiChannel;
|
||||
extern volatile uint8 myMidiDevice;
|
||||
extern volatile uint8 myMidiCable;
|
||||
extern volatile uint8 myMidiID[];
|
||||
|
||||
|
||||
void LglSysexHandler(uint32 *midiBufferRx,uint32 *rx_offset,uint32 *n_unread_bytes);
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
#include "USBHID.h"
|
||||
|
||||
//================================================================================
|
||||
//================================================================================
|
||||
// Mouse
|
||||
|
||||
void HIDMouse::begin(void){
|
||||
}
|
||||
|
||||
void HIDMouse::end(void){
|
||||
}
|
||||
|
||||
void HIDMouse::click(uint8_t b)
|
||||
{
|
||||
_buttons = b;
|
||||
move(0,0,0);
|
||||
_buttons = 0;
|
||||
move(0,0,0);
|
||||
}
|
||||
|
||||
void HIDMouse::move(signed char x, signed char y, signed char wheel)
|
||||
{
|
||||
reportBuffer[1] = _buttons;
|
||||
reportBuffer[2] = x;
|
||||
reportBuffer[3] = y;
|
||||
reportBuffer[4] = wheel;
|
||||
|
||||
sendReport();
|
||||
}
|
||||
|
||||
void HIDMouse::buttons(uint8_t b)
|
||||
{
|
||||
if (b != _buttons)
|
||||
{
|
||||
_buttons = b;
|
||||
move(0,0,0);
|
||||
}
|
||||
}
|
||||
|
||||
void HIDMouse::press(uint8_t b)
|
||||
{
|
||||
buttons(_buttons | b);
|
||||
}
|
||||
|
||||
void HIDMouse::release(uint8_t b)
|
||||
{
|
||||
buttons(_buttons & ~b);
|
||||
}
|
||||
|
||||
bool HIDMouse::isPressed(uint8_t b)
|
||||
{
|
||||
if ((b & _buttons) > 0)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
HIDMouse Mouse;
|
|
@ -0,0 +1,88 @@
|
|||
# USB Composite library for STM32F1
|
||||
|
||||
## Protocols supported
|
||||
|
||||
- standard USB HID, with many built-in profiles, and customizable with more
|
||||
|
||||
- MIDI over USB
|
||||
|
||||
- XBox360 controller (only controller-to-host is currently supported)
|
||||
|
||||
- Mass storage
|
||||
|
||||
## Basic concepts
|
||||
|
||||
Start with:
|
||||
```
|
||||
#include <USBComposite.h>
|
||||
```
|
||||
|
||||
The library defines several crucial objects. The central object is:
|
||||
|
||||
```
|
||||
extern USBCompositeDevice USBComposite;
|
||||
```
|
||||
|
||||
This controls USB device identification as well as registers the plugins that are connected to it.
|
||||
|
||||
Plugin objects included in the library are:
|
||||
|
||||
```
|
||||
extern USBHIDDevice USBHID;
|
||||
extern USBMidi USBMIDI;
|
||||
extern USBXBox360 XBox360;
|
||||
extern USBMassStorageDevice MassStorage;
|
||||
extern USBCompositeSerial CompositeSerial;
|
||||
```
|
||||
|
||||
You can also create your own customized instances of these plugin classes or their subclasses.
|
||||
|
||||
If you want to make a simple (non-composite) USB device, you can just call the plugin's `begin()`
|
||||
method, and it will take care of registering itself with `USBComposite` and starting up
|
||||
`USBComposite`. If you want to make a composite USB device, however,
|
||||
you need to control the device with `USBComposite`:
|
||||
|
||||
```
|
||||
USBComposite.clear(); // clear any plugins previously registered
|
||||
plugin1.registerComponent();
|
||||
plugin2.registerComponent();
|
||||
USBComposite.begin();
|
||||
```
|
||||
|
||||
Of course, you may need to do some further configuring of the plugins or the `USBComposite` device
|
||||
before the `USBComposite.begin()` call.
|
||||
|
||||
Finally, there are a number of objects that implement particular protocols for the `USBHID` plugin:
|
||||
```
|
||||
extern HIDMouse Mouse;
|
||||
extern HIDKeyboard Keyboard;
|
||||
extern HIDJoystick Joystick;
|
||||
extern HIDKeyboard BootKeyboard;
|
||||
```
|
||||
And you can customize with more. Moreover, the `USBHID` plugin itself allows for compositing
|
||||
multiple HID profiles, e.g., Mouse / Keyboard / three joysticks.
|
||||
|
||||
Not all combinations will fit within the constraints of the STM32F1 USB system, and not all
|
||||
combinations will be supported by all operating systems.
|
||||
|
||||
## Simple USB device configuration
|
||||
|
||||
A simple USB device uses a single plugin. You just need to call any setup methods for the plugin
|
||||
and the `begin()` method for the plugin. For instance, to inject keyboard data, you can do:
|
||||
|
||||
```
|
||||
USBHID.begin(HID_KEYBOARD);
|
||||
```
|
||||
|
||||
and then call `Keyboard.print("TextToInject")` to inject keyboard data. Some plugin configurations
|
||||
may require further initialization code or further code that needs to be called inside the Arduino
|
||||
`loop()` function.
|
||||
|
||||
See the `BootKeyboard`, `midiout` and `x360` example code for this procedure.
|
||||
|
||||
Additionally, for backwards compatibility reasons, the `USBHID` plugin has a convenience
|
||||
`USBHID_begin_with_serial()` function which works just like `USBHID.begin()` except that it also
|
||||
composites a `CompositeSerial` plugin.
|
||||
|
||||
However, if you want a USB device using more than one plugin, then you will NOT call the plugin's
|
||||
`begin()` method.
|
|
@ -0,0 +1,147 @@
|
|||
#include "USBComposite.h"
|
||||
|
||||
#define DEFAULT_VENDOR_ID 0x1EAF
|
||||
#define DEFAULT_PRODUCT_ID 0x0004
|
||||
|
||||
static char* putSerialNumber(char* out, int nibbles, uint32 id) {
|
||||
for (int i=0; i<nibbles; i++, id >>= 4) {
|
||||
uint8 nibble = id & 0xF;
|
||||
if (nibble <= 9)
|
||||
*out++ = nibble + '0';
|
||||
else
|
||||
*out++ = nibble - 10 + 'a';
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
const char* getDeviceIDString() {
|
||||
static char string[80/4+1];
|
||||
char* p = string;
|
||||
|
||||
uint32 id = (uint32) *(uint16*) (0x1FFFF7E8+0x02);
|
||||
p = putSerialNumber(p, 4, id);
|
||||
|
||||
id = *(uint32*) (0x1FFFF7E8+0x04);
|
||||
p = putSerialNumber(p, 8, id);
|
||||
|
||||
id = *(uint32*) (0x1FFFF7E8+0x08);
|
||||
p = putSerialNumber(p, 8, id);
|
||||
|
||||
*p = 0;
|
||||
|
||||
return string;
|
||||
}
|
||||
|
||||
USBCompositeDevice::USBCompositeDevice(void) {
|
||||
vendorId = 0;
|
||||
productId = 0;
|
||||
numParts = 0;
|
||||
setManufacturerString(NULL);
|
||||
setProductString(NULL);
|
||||
setSerialString(DEFAULT_SERIAL_STRING);
|
||||
iManufacturer[0] = 0;
|
||||
iProduct[0] = 0;
|
||||
iSerialNumber[0] = 0;
|
||||
}
|
||||
|
||||
void USBCompositeDevice::setVendorId(uint16 _vendorId) {
|
||||
if (_vendorId != 0)
|
||||
vendorId = _vendorId;
|
||||
else
|
||||
vendorId = DEFAULT_VENDOR_ID;
|
||||
}
|
||||
|
||||
void USBCompositeDevice::setProductId(uint16 _productId) {
|
||||
if (_productId != 0)
|
||||
productId = _productId;
|
||||
else
|
||||
productId = DEFAULT_PRODUCT_ID;
|
||||
}
|
||||
|
||||
void setString(uint8* out, const usb_descriptor_string* defaultDescriptor, const char* s, uint32 maxLength) {
|
||||
if (s == NULL) {
|
||||
uint8 n = defaultDescriptor->bLength;
|
||||
uint8 m = USB_DESCRIPTOR_STRING_LEN(maxLength);
|
||||
if (n > m)
|
||||
n = m;
|
||||
memcpy(out, defaultDescriptor, n);
|
||||
out[0] = n;
|
||||
}
|
||||
else {
|
||||
uint32 n = strlen(s);
|
||||
if (n > maxLength)
|
||||
n = maxLength;
|
||||
out[0] = (uint8)USB_DESCRIPTOR_STRING_LEN(n);
|
||||
out[1] = USB_DESCRIPTOR_TYPE_STRING;
|
||||
for (uint32 i=0; i<n; i++) {
|
||||
out[2 + 2*i] = (uint8)s[i];
|
||||
out[2 + 1 + 2*i] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void USBCompositeDevice::setManufacturerString(const char* s) {
|
||||
setString(iManufacturer, &usb_generic_default_iManufacturer, s, USB_MAX_MANUFACTURER_LENGTH);
|
||||
}
|
||||
|
||||
void USBCompositeDevice::setProductString(const char* s) {
|
||||
setString(iProduct, &usb_generic_default_iProduct, s, USB_MAX_PRODUCT_LENGTH);
|
||||
}
|
||||
|
||||
void USBCompositeDevice::setSerialString(const char* s) {
|
||||
if (s == NULL)
|
||||
haveSerialNumber = false;
|
||||
else {
|
||||
haveSerialNumber = true;
|
||||
setString(iSerialNumber, NULL, s, USB_MAX_SERIAL_NUMBER_LENGTH);
|
||||
}
|
||||
}
|
||||
|
||||
bool USBCompositeDevice::begin() {
|
||||
if (enabled)
|
||||
return true;
|
||||
usb_generic_set_info(vendorId, productId, iManufacturer[0] ? iManufacturer : NULL, iProduct[0] ? iProduct : NULL,
|
||||
haveSerialNumber ? iSerialNumber : NULL);
|
||||
for (uint32 i = 0 ; i < numParts ; i++) {
|
||||
if (init[i] != NULL && !init[i](plugin[i]))
|
||||
return false;
|
||||
}
|
||||
if (! usb_generic_set_parts(parts, numParts))
|
||||
return false;
|
||||
usb_generic_enable();
|
||||
enabled = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void USBCompositeDevice::end() {
|
||||
if (!enabled)
|
||||
return;
|
||||
usb_generic_disable();
|
||||
for (uint32 i = 0 ; i < numParts ; i++)
|
||||
if (stop[i] != NULL)
|
||||
stop[i](plugin[i]);
|
||||
enabled = false;
|
||||
}
|
||||
|
||||
void USBCompositeDevice::clear() {
|
||||
numParts = 0;
|
||||
}
|
||||
|
||||
bool USBCompositeDevice::add(USBCompositePart* part, void* _plugin, USBPartInitializer _init, USBPartStopper _stop) {
|
||||
unsigned i;
|
||||
|
||||
for (i = 0; i<numParts; i++)
|
||||
if (plugin[numParts] == _plugin && parts[i] == part)
|
||||
break;
|
||||
if (i >= USB_COMPOSITE_MAX_PARTS)
|
||||
return false;
|
||||
parts[i] = part;
|
||||
init[i] = _init;
|
||||
stop[i] = _stop;
|
||||
plugin[i] = _plugin;
|
||||
if (i >= numParts)
|
||||
numParts++;
|
||||
return true;
|
||||
}
|
||||
|
||||
USBCompositeDevice USBComposite;
|
|
@ -0,0 +1,64 @@
|
|||
#ifndef _USBCOMPOSITE_H_
|
||||
#define _USBCOMPOSITE_H_
|
||||
|
||||
#include <boards.h>
|
||||
#include "Stream.h"
|
||||
#include "usb_generic.h"
|
||||
//#include <libmaple/usb.h>
|
||||
|
||||
#include <USBHID.h>
|
||||
#include <USBXBox360.h>
|
||||
#include <USBMassStorage.h>
|
||||
#include <USBCompositeSerial.h>
|
||||
|
||||
#define USB_MAX_PRODUCT_LENGTH 32
|
||||
#define USB_MAX_MANUFACTURER_LENGTH 32
|
||||
#define USB_MAX_SERIAL_NUMBER_LENGTH 20
|
||||
|
||||
|
||||
// You could use this for a serial number, but you'll be revealing the device ID to the host,
|
||||
// and hence burning it for cryptographic purposes.
|
||||
const char* getDeviceIDString();
|
||||
|
||||
#define USB_COMPOSITE_MAX_PARTS 6
|
||||
|
||||
class USBCompositeDevice;
|
||||
|
||||
#define DEFAULT_SERIAL_STRING "00000000000000000001"
|
||||
|
||||
typedef bool(*USBPartInitializer)(void*);
|
||||
typedef void(*USBPartStopper)(void*);
|
||||
|
||||
class USBCompositeDevice {
|
||||
private:
|
||||
bool enabled = false;
|
||||
bool haveSerialNumber = false;
|
||||
uint8_t iManufacturer[USB_DESCRIPTOR_STRING_LEN(USB_MAX_MANUFACTURER_LENGTH)];
|
||||
uint8_t iProduct[USB_DESCRIPTOR_STRING_LEN(USB_MAX_PRODUCT_LENGTH)];
|
||||
uint8_t iSerialNumber[USB_DESCRIPTOR_STRING_LEN(USB_MAX_SERIAL_NUMBER_LENGTH)];
|
||||
uint16 vendorId;
|
||||
uint16 productId;
|
||||
USBCompositePart* parts[USB_COMPOSITE_MAX_PARTS];
|
||||
USBPartInitializer init[USB_COMPOSITE_MAX_PARTS];
|
||||
USBPartStopper stop[USB_COMPOSITE_MAX_PARTS];
|
||||
void* plugin[USB_COMPOSITE_MAX_PARTS];
|
||||
uint32 numParts;
|
||||
public:
|
||||
USBCompositeDevice(void);
|
||||
void setVendorId(uint16 vendor=0);
|
||||
void setProductId(uint16 product=0);
|
||||
void setManufacturerString(const char* manufacturer=NULL);
|
||||
void setProductString(const char* product=NULL);
|
||||
void setSerialString(const char* serialNumber=DEFAULT_SERIAL_STRING);
|
||||
bool begin(void);
|
||||
void end(void);
|
||||
void clear();
|
||||
bool isReady() {
|
||||
return enabled && usb_is_connected(USBLIB) && usb_is_configured(USBLIB);
|
||||
}
|
||||
bool add(USBCompositePart* part, void* plugin, USBPartInitializer init = NULL, USBPartStopper stop = NULL);
|
||||
};
|
||||
|
||||
extern USBCompositeDevice USBComposite;
|
||||
#endif
|
||||
|
|
@ -0,0 +1,274 @@
|
|||
/* Copyright (c) 2011, Peter Barrett
|
||||
**
|
||||
** 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 <USBCompositeSerial.h>
|
||||
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <libmaple/nvic.h>
|
||||
#include <libmaple/usb.h>
|
||||
#include <string.h>
|
||||
#include <libmaple/iwdg.h>
|
||||
|
||||
#include "usb_serial.h"
|
||||
|
||||
#define USB_TIMEOUT 50
|
||||
|
||||
#if defined(SERIAL_USB)
|
||||
static void rxHook(unsigned, void*);
|
||||
static void ifaceSetupHook(unsigned, void*);
|
||||
#endif
|
||||
|
||||
bool USBCompositeSerial::init(USBCompositeSerial* me) {
|
||||
(void)me;
|
||||
#if defined(SERIAL_USB)
|
||||
composite_cdcacm_set_hooks(USBHID_CDCACM_HOOK_RX, rxHook);
|
||||
composite_cdcacm_set_hooks(USBHID_CDCACM_HOOK_IFACE_SETUP, ifaceSetupHook);
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
void USBCompositeSerial::begin(long speed) {
|
||||
(void)speed;
|
||||
if (!enabled) {
|
||||
USBComposite.clear();
|
||||
registerComponent();
|
||||
USBComposite.begin();
|
||||
enabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
void USBCompositeSerial::end() {
|
||||
if (enabled) {
|
||||
USBComposite.end();
|
||||
enabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
size_t USBCompositeSerial::write(uint8 ch) {
|
||||
size_t n = 0;
|
||||
this->write(&ch, 1);
|
||||
return n;
|
||||
}
|
||||
|
||||
size_t USBCompositeSerial::write(const char *str) {
|
||||
size_t n = 0;
|
||||
this->write((const uint8*)str, strlen(str));
|
||||
return n;
|
||||
}
|
||||
|
||||
size_t USBCompositeSerial::write(const uint8 *buf, uint32 len)
|
||||
{
|
||||
size_t n = 0;
|
||||
|
||||
if (!this->isConnected() || !buf) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32 txed = 0;
|
||||
while (txed < len) {
|
||||
txed += composite_cdcacm_tx((const uint8*)buf + txed, len - txed);
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
int USBCompositeSerial::available(void) {
|
||||
return composite_cdcacm_data_available();
|
||||
}
|
||||
|
||||
int USBCompositeSerial::peek(void)
|
||||
{
|
||||
uint8 b;
|
||||
if (composite_cdcacm_peek(&b, 1)==1)
|
||||
{
|
||||
return b;
|
||||
}
|
||||
else
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
bool USBCompositeSerial::registerComponent() {
|
||||
return USBComposite.add(&usbSerialPart, this, (USBPartInitializer)&USBCompositeSerial::init);
|
||||
}
|
||||
|
||||
void USBCompositeSerial::flush(void)
|
||||
{
|
||||
/*Roger Clark. Rather slow method. Need to improve this */
|
||||
uint8 b;
|
||||
while(composite_cdcacm_data_available())
|
||||
{
|
||||
this->read(&b, 1);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
uint32 USBCompositeSerial::read(uint8 * buf, uint32 len) {
|
||||
uint32 rxed = 0;
|
||||
while (rxed < len) {
|
||||
rxed += composite_cdcacm_rx(buf + rxed, len - rxed);
|
||||
}
|
||||
|
||||
return rxed;
|
||||
}
|
||||
|
||||
/* Blocks forever until 1 byte is received */
|
||||
int USBCompositeSerial::read(void) {
|
||||
uint8 b;
|
||||
/*
|
||||
this->read(&b, 1);
|
||||
return b;
|
||||
*/
|
||||
|
||||
if (composite_cdcacm_rx(&b, 1)==0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
return b;
|
||||
}
|
||||
}
|
||||
|
||||
uint8 USBCompositeSerial::pending(void) {
|
||||
return composite_cdcacm_get_pending();
|
||||
}
|
||||
|
||||
uint8 USBCompositeSerial::isConnected(void) {
|
||||
return usb_is_connected(USBLIB) && usb_is_configured(USBLIB) && composite_cdcacm_get_dtr();
|
||||
}
|
||||
|
||||
uint8 USBCompositeSerial::getDTR(void) {
|
||||
return composite_cdcacm_get_dtr();
|
||||
}
|
||||
|
||||
uint8 USBCompositeSerial::getRTS(void) {
|
||||
return composite_cdcacm_get_rts();
|
||||
}
|
||||
|
||||
#if defined(SERIAL_USB)
|
||||
|
||||
enum reset_state_t {
|
||||
DTR_UNSET,
|
||||
DTR_HIGH,
|
||||
DTR_NEGEDGE,
|
||||
DTR_LOW
|
||||
};
|
||||
|
||||
static reset_state_t reset_state = DTR_UNSET;
|
||||
|
||||
static void ifaceSetupHook(unsigned hook, void *requestvp) {
|
||||
(void)hook;
|
||||
uint8 request = *(uint8*)requestvp;
|
||||
|
||||
// Ignore requests we're not interested in.
|
||||
if (request != USBHID_CDCACM_SET_CONTROL_LINE_STATE) {
|
||||
return;
|
||||
}
|
||||
|
||||
// We need to see a negative edge on DTR before we start looking
|
||||
// for the in-band magic reset byte sequence.
|
||||
uint8 dtr = composite_cdcacm_get_dtr();
|
||||
switch (reset_state) {
|
||||
case DTR_UNSET:
|
||||
reset_state = dtr ? DTR_HIGH : DTR_LOW;
|
||||
break;
|
||||
case DTR_HIGH:
|
||||
reset_state = dtr ? DTR_HIGH : DTR_NEGEDGE;
|
||||
break;
|
||||
case DTR_NEGEDGE:
|
||||
reset_state = dtr ? DTR_HIGH : DTR_LOW;
|
||||
break;
|
||||
case DTR_LOW:
|
||||
reset_state = dtr ? DTR_HIGH : DTR_LOW;
|
||||
break;
|
||||
}
|
||||
|
||||
if ((composite_cdcacm_get_baud() == 1200) && (reset_state == DTR_NEGEDGE)) {
|
||||
iwdg_init(IWDG_PRE_4, 10);
|
||||
while (1);
|
||||
}
|
||||
}
|
||||
|
||||
#define RESET_DELAY 100000
|
||||
static void wait_reset(void) {
|
||||
delay_us(RESET_DELAY);
|
||||
nvic_sys_reset();
|
||||
}
|
||||
|
||||
#define STACK_TOP 0x20000800
|
||||
#define EXC_RETURN 0xFFFFFFF9
|
||||
#define DEFAULT_CPSR 0x61000000
|
||||
static void rxHook(unsigned hook, void *ignored) {
|
||||
(void)hook;
|
||||
(void)ignored;
|
||||
/* FIXME this is mad buggy; we need a new reset sequence. E.g. NAK
|
||||
* after each RX means you can't reset if any bytes are waiting. */
|
||||
if (reset_state == DTR_NEGEDGE) {
|
||||
reset_state = DTR_LOW;
|
||||
|
||||
if (composite_cdcacm_data_available() >= 4) {
|
||||
// The magic reset sequence is "1EAF".
|
||||
static const uint8 magic[4] = {'1', 'E', 'A', 'F'};
|
||||
|
||||
uint8 chkBuf[4];
|
||||
|
||||
// Peek at the waiting bytes, looking for reset sequence,
|
||||
// bailing on mismatch.
|
||||
composite_cdcacm_peek_ex(chkBuf, composite_cdcacm_data_available() - 4, 4);
|
||||
for (unsigned i = 0; i < sizeof(magic); i++) {
|
||||
if (chkBuf[i] != magic[i]) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Got the magic sequence -> reset, presumably into the bootloader.
|
||||
// Return address is wait_reset, but we must set the thumb bit.
|
||||
uintptr_t target = (uintptr_t)wait_reset | 0x1;
|
||||
asm volatile("mov r0, %[stack_top] \n\t" // Reset stack
|
||||
"mov sp, r0 \n\t"
|
||||
"mov r0, #1 \n\t"
|
||||
"mov r1, %[target_addr] \n\t"
|
||||
"mov r2, %[cpsr] \n\t"
|
||||
"push {r2} \n\t" // Fake xPSR
|
||||
"push {r1} \n\t" // PC target addr
|
||||
"push {r0} \n\t" // Fake LR
|
||||
"push {r0} \n\t" // Fake R12
|
||||
"push {r0} \n\t" // Fake R3
|
||||
"push {r0} \n\t" // Fake R2
|
||||
"push {r0} \n\t" // Fake R1
|
||||
"push {r0} \n\t" // Fake R0
|
||||
"mov lr, %[exc_return] \n\t"
|
||||
"bx lr"
|
||||
:
|
||||
: [stack_top] "r" (STACK_TOP),
|
||||
[target_addr] "r" (target),
|
||||
[exc_return] "r" (EXC_RETURN),
|
||||
[cpsr] "r" (DEFAULT_CPSR)
|
||||
: "r0", "r1", "r2");
|
||||
|
||||
/* Can't happen. */
|
||||
ASSERT_FAULT(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
USBCompositeSerial CompositeSerial;
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
#ifndef _COMPOSITE_SERIAL_H_
|
||||
#define _COMPOSITE_SERIAL_H_
|
||||
|
||||
#include "USBComposite.h"
|
||||
#include "usb_serial.h"
|
||||
|
||||
class USBCompositeSerial : public Stream {
|
||||
private:
|
||||
bool enabled = false;
|
||||
public:
|
||||
void begin(long speed=9600);
|
||||
void end();
|
||||
static bool init(USBCompositeSerial* me);
|
||||
bool registerComponent();
|
||||
|
||||
operator bool() { return true; } // Roger Clark. This is needed because in cardinfo.ino it does if (!Serial) . It seems to be a work around for the Leonardo that we needed to implement just to be compliant with the API
|
||||
|
||||
virtual int available(void);// Changed to virtual
|
||||
|
||||
uint32 read(uint8 * buf, uint32 len);
|
||||
// uint8 read(void);
|
||||
|
||||
// Roger Clark. added functions to support Arduino 1.0 API
|
||||
virtual int peek(void);
|
||||
virtual int read(void);
|
||||
int availableForWrite(void);
|
||||
virtual void flush(void);
|
||||
|
||||
size_t write(uint8);
|
||||
size_t write(const char *str);
|
||||
size_t write(const uint8*, uint32);
|
||||
|
||||
uint8 getRTS();
|
||||
uint8 getDTR();
|
||||
uint8 isConnected();
|
||||
uint8 pending();
|
||||
};
|
||||
|
||||
extern USBCompositeSerial CompositeSerial;
|
||||
#endif
|
|
@ -0,0 +1,180 @@
|
|||
/* Copyright (c) 2011, Peter Barrett
|
||||
**
|
||||
** 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 "USBHID.h"
|
||||
#include "USBCompositeSerial.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <libmaple/nvic.h>
|
||||
#include "usb_hid.h"
|
||||
#include "usb_serial.h"
|
||||
#include "usb_generic.h"
|
||||
#include <libmaple/usb.h>
|
||||
#include <string.h>
|
||||
#include <libmaple/iwdg.h>
|
||||
|
||||
#include <wirish.h>
|
||||
|
||||
/*
|
||||
* USB HID interface
|
||||
*/
|
||||
|
||||
bool USBHIDDevice::registerComponent() {
|
||||
return USBComposite.add(&usbHIDPart, this);
|
||||
}
|
||||
|
||||
void USBHIDDevice::setReportDescriptor(const uint8_t* report_descriptor, uint16_t report_descriptor_length) {
|
||||
usb_hid_set_report_descriptor(report_descriptor, report_descriptor_length);
|
||||
}
|
||||
|
||||
void USBHIDDevice::setReportDescriptor(const HIDReportDescriptor* report) {
|
||||
setReportDescriptor(report->descriptor, report->length);
|
||||
}
|
||||
|
||||
void USBHIDDevice::begin(const uint8_t* report_descriptor, uint16_t report_descriptor_length, uint16_t idVendor, uint16_t idProduct,
|
||||
const char* manufacturer, const char* product, const char* serialNumber) {
|
||||
|
||||
if (enabledHID)
|
||||
return;
|
||||
|
||||
setReportDescriptor(report_descriptor, report_descriptor_length);
|
||||
|
||||
USBComposite.clear();
|
||||
USBComposite.setVendorId(idVendor);
|
||||
USBComposite.setProductId(idProduct);
|
||||
USBComposite.setManufacturerString(manufacturer);
|
||||
USBComposite.setProductString(product);
|
||||
USBComposite.setSerialString(serialNumber);
|
||||
registerComponent();
|
||||
|
||||
USBComposite.begin();
|
||||
|
||||
enabledHID = true;
|
||||
}
|
||||
|
||||
void USBHIDDevice::begin(const HIDReportDescriptor* report, uint16_t idVendor, uint16_t idProduct,
|
||||
const char* manufacturer, const char* product, const char* serialNumber) {
|
||||
begin(report->descriptor, report->length, idVendor, idProduct, manufacturer, product, serialNumber);
|
||||
}
|
||||
|
||||
void USBHIDDevice::setBuffers(uint8_t type, volatile HIDBuffer_t* fb, int count) {
|
||||
usb_hid_set_buffers(type, fb, count);
|
||||
}
|
||||
|
||||
bool USBHIDDevice::addBuffer(uint8_t type, volatile HIDBuffer_t* buffer) {
|
||||
return 0 != usb_hid_add_buffer(type, buffer);
|
||||
}
|
||||
|
||||
void USBHIDDevice::clearBuffers(uint8_t type) {
|
||||
usb_hid_clear_buffers(type);
|
||||
}
|
||||
|
||||
void USBHIDDevice::clearBuffers() {
|
||||
clearBuffers(HID_REPORT_TYPE_OUTPUT);
|
||||
clearBuffers(HID_REPORT_TYPE_FEATURE);
|
||||
}
|
||||
|
||||
void USBHIDDevice::end(void){
|
||||
if(enabledHID){
|
||||
USBComposite.end();
|
||||
enabledHID = false;
|
||||
}
|
||||
}
|
||||
|
||||
void HIDReporter::sendReport() {
|
||||
// while (usb_is_transmitting() != 0) {
|
||||
// }
|
||||
|
||||
unsigned toSend = bufferSize;
|
||||
uint8* b = buffer;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
HIDReporter::HIDReporter(uint8_t* _buffer, unsigned _size, uint8_t _reportID) {
|
||||
if (_reportID == 0) {
|
||||
buffer = _buffer+1;
|
||||
bufferSize = _size-1;
|
||||
}
|
||||
else {
|
||||
buffer = _buffer;
|
||||
bufferSize = _size;
|
||||
}
|
||||
memset(buffer, 0, bufferSize);
|
||||
reportID = _reportID;
|
||||
if (_size > 0 && reportID != 0)
|
||||
buffer[0] = _reportID;
|
||||
}
|
||||
|
||||
HIDReporter::HIDReporter(uint8_t* _buffer, unsigned _size) {
|
||||
buffer = _buffer;
|
||||
bufferSize = _size;
|
||||
memset(buffer, 0, _size);
|
||||
reportID = 0;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
USBHIDDevice USBHID;
|
||||
|
||||
void USBHID_begin_with_serial(const uint8_t* report_descriptor, uint16_t report_descriptor_length, uint16_t idVendor, uint16_t idProduct,
|
||||
const char* manufacturer, const char* product, const char* serialNumber) {
|
||||
|
||||
USBComposite.clear();
|
||||
USBComposite.setVendorId(idVendor);
|
||||
USBComposite.setProductId(idProduct);
|
||||
USBComposite.setManufacturerString(manufacturer);
|
||||
USBComposite.setProductString(product);
|
||||
USBComposite.setSerialString(serialNumber);
|
||||
|
||||
USBHID.setReportDescriptor(report_descriptor, report_descriptor_length);
|
||||
USBHID.registerComponent();
|
||||
|
||||
CompositeSerial.registerComponent();
|
||||
|
||||
USBComposite.begin();
|
||||
}
|
||||
|
||||
void USBHID_begin_with_serial(const HIDReportDescriptor* report, uint16_t idVendor, uint16_t idProduct,
|
||||
const char* manufacturer, const char* product, const char* serialNumber) {
|
||||
USBHID_begin_with_serial(report->descriptor, report->length, idVendor, idProduct, manufacturer, product, serialNumber);
|
||||
}
|
||||
|
|
@ -0,0 +1,608 @@
|
|||
/* Copyright (c) 2011, Peter Barrett
|
||||
**
|
||||
** 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.
|
||||
*/
|
||||
|
||||
#ifndef _USBHID_H_
|
||||
#define _USBHID_H_
|
||||
|
||||
#include <USBComposite.h>
|
||||
#include <USBCompositeSerial.h>
|
||||
#include <Print.h>
|
||||
#include <boards.h>
|
||||
#include "Stream.h"
|
||||
#include "usb_hid.h"
|
||||
|
||||
#define USB_HID_MAX_PRODUCT_LENGTH 32
|
||||
#define USB_HID_MAX_MANUFACTURER_LENGTH 32
|
||||
#define USB_HID_MAX_SERIAL_NUMBER_LENGTH 20
|
||||
|
||||
#define HID_MOUSE_REPORT_ID 1
|
||||
#define HID_KEYBOARD_REPORT_ID 2
|
||||
#define HID_CONSUMER_REPORT_ID 3
|
||||
#define HID_JOYSTICK_REPORT_ID 20
|
||||
|
||||
#define HID_KEYBOARD_ROLLOVER 6
|
||||
|
||||
#define MACRO_GET_ARGUMENT_2(x, y, ...) y
|
||||
#define MACRO_GET_ARGUMENT_1_WITH_DEFAULT(default, ...) MACRO_GET_ARGUMENT_2(placeholder, ## __VA_ARGS__, default)
|
||||
#define MACRO_ARGUMENT_2_TO_END(skip, ...) __VA_ARGS__
|
||||
|
||||
// HIDBuffer_t data buffers must have even memory length because of how PMA transfers work
|
||||
#define HID_DATA_BUFFER_SIZE(n) (((n)+1)/2*2)
|
||||
|
||||
/* note that featureSize must be 1 less than the buffer size for the feature,
|
||||
since the latter must include the reportId */
|
||||
/* this only works in a collection with a report_id */
|
||||
#define HID_FEATURE_REPORT_DESCRIPTOR(dataSize) \
|
||||
0x06, 0x00, 0xFF, /* USAGE_PAGE (Vendor Defined Page 1) */ \
|
||||
0x09, 0x01, /* USAGE (Vendor Usage 1) */ \
|
||||
0x15, 0x00, /* LOGICAL_MINIMUM (0) */ \
|
||||
0x26, 0xff, 0x00, /* LOGICAL_MAXIMUM (255) */ \
|
||||
0x75, 0x08, /* REPORT_SIZE (8) */ \
|
||||
0x95, dataSize, /* REPORT_COUNT (xx) */ \
|
||||
0xB1, 0x02, /* FEATURE (Data,Var,Abs) */ \
|
||||
|
||||
#define HID_OUTPUT_REPORT_DESCRIPTOR(dataSize) \
|
||||
0x06, 0x00, 0xFF, /* USAGE_PAGE (Vendor Defined Page 1) */ \
|
||||
0x09, 0x01, /* USAGE (Vendor Usage 1) */ \
|
||||
0x15, 0x00, /* LOGICAL_MINIMUM (0) */ \
|
||||
0x26, 0xff, 0x00, /* LOGICAL_MAXIMUM (255) */ \
|
||||
0x75, 0x08, /* REPORT_SIZE (8) */ \
|
||||
0x95, dataSize, /* REPORT_COUNT (32) */ \
|
||||
0x91, 0x02, /* OUTPUT (Data,Var,Abs) */ \
|
||||
|
||||
#define HID_CONSUMER_REPORT_DESCRIPTOR(...) \
|
||||
0x05, 0x0C, /* usage page (consumer device) */ \
|
||||
0x09, 0x01, /* usage -- consumer control */ \
|
||||
0xA1, 0x01, /* collection (application) */ \
|
||||
0x85, MACRO_GET_ARGUMENT_1_WITH_DEFAULT(HID_CONSUMER_REPORT_ID, ## __VA_ARGS__), /* REPORT_ID */ \
|
||||
0x15, 0x00, /* logical minimum */ \
|
||||
0x26, 0xFF, 0x03, /* logical maximum (3ff) */ \
|
||||
0x19, 0x00, /* usage minimum (0) */ \
|
||||
0x2A, 0xFF, 0x03, /* usage maximum (3ff) */ \
|
||||
0x75, 0x10, /* report size (16) */ \
|
||||
0x95, 0x01, /* report count (1) */ \
|
||||
0x81, 0x00, /* input */ \
|
||||
MACRO_ARGUMENT_2_TO_END(__VA_ARGS__) \
|
||||
0xC0 /* end collection */
|
||||
|
||||
#define HID_MOUSE_REPORT_DESCRIPTOR(...) \
|
||||
0x05, 0x01, /* USAGE_PAGE (Generic Desktop) // 54 */ \
|
||||
0x09, 0x02, /* USAGE (Mouse) */ \
|
||||
0xa1, 0x01, /* COLLECTION (Application) */ \
|
||||
0x85, MACRO_GET_ARGUMENT_1_WITH_DEFAULT(HID_MOUSE_REPORT_ID, ## __VA_ARGS__), /* REPORT_ID */ \
|
||||
0x09, 0x01, /* USAGE (Pointer) */ \
|
||||
0xa1, 0x00, /* COLLECTION (Physical) */ \
|
||||
0x05, 0x09, /* USAGE_PAGE (Button) */ \
|
||||
0x19, 0x01, /* USAGE_MINIMUM (Button 1) */ \
|
||||
0x29, 0x08, /* USAGE_MAXIMUM (Button 8) */ \
|
||||
0x15, 0x00, /* LOGICAL_MINIMUM (0) */ \
|
||||
0x25, 0x01, /* LOGICAL_MAXIMUM (1) */ \
|
||||
0x95, 0x08, /* REPORT_COUNT (8) */ \
|
||||
0x75, 0x01, /* REPORT_SIZE (1) */ \
|
||||
0x81, 0x02, /* INPUT (Data,Var,Abs) */ \
|
||||
0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */ \
|
||||
0x09, 0x30, /* USAGE (X) */ \
|
||||
0x09, 0x31, /* USAGE (Y) */ \
|
||||
0x09, 0x38, /* USAGE (Wheel) */ \
|
||||
0x15, 0x81, /* LOGICAL_MINIMUM (-127) */ \
|
||||
0x25, 0x7f, /* LOGICAL_MAXIMUM (127) */ \
|
||||
0x75, 0x08, /* REPORT_SIZE (8) */ \
|
||||
0x95, 0x03, /* REPORT_COUNT (3) */ \
|
||||
0x81, 0x06, /* INPUT (Data,Var,Rel) */ \
|
||||
0xc0, /* END_COLLECTION */ \
|
||||
MACRO_ARGUMENT_2_TO_END(__VA_ARGS__) \
|
||||
0xc0 /* END_COLLECTION */
|
||||
|
||||
#define HID_ABS_MOUSE_REPORT_DESCRIPTOR(...) \
|
||||
0x05, 0x01, /* USAGE_PAGE (Generic Desktop) // 54 */ \
|
||||
0x09, 0x02, /* USAGE (Mouse) */ \
|
||||
0xa1, 0x01, /* COLLECTION (Application) */ \
|
||||
0x85, MACRO_GET_ARGUMENT_1_WITH_DEFAULT(HID_MOUSE_REPORT_ID, ## __VA_ARGS__), /* REPORT_ID */ \
|
||||
0x09, 0x01, /* USAGE (Pointer) */ \
|
||||
0xa1, 0x00, /* COLLECTION (Physical) */ \
|
||||
0x05, 0x09, /* USAGE_PAGE (Button) */ \
|
||||
0x19, 0x01, /* USAGE_MINIMUM (Button 1) */ \
|
||||
0x29, 0x08, /* USAGE_MAXIMUM (Button 8) */ \
|
||||
0x15, 0x00, /* LOGICAL_MINIMUM (0) */ \
|
||||
0x25, 0x01, /* LOGICAL_MAXIMUM (1) */ \
|
||||
0x95, 0x08, /* REPORT_COUNT (8) */ \
|
||||
0x75, 0x01, /* REPORT_SIZE (1) */ \
|
||||
0x81, 0x02, /* INPUT (Data,Var,Abs) */ \
|
||||
0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */ \
|
||||
0x09, 0x30, /* USAGE (X) */ \
|
||||
0x09, 0x31, /* USAGE (Y) */ \
|
||||
0x16, 0x00, 0x80, /* LOGICAL_MINIMUM (-32768) */ \
|
||||
0x26, 0xFF, 0x7f, /* LOGICAL_MAXIMUM (32767) */ \
|
||||
0x75, 0x10, /* REPORT_SIZE (16) */ \
|
||||
0x95, 0x02, /* REPORT_COUNT (2) */ \
|
||||
0x81, 0x02, /* INPUT (Data,Var,Abs) */ \
|
||||
0x09, 0x38, /* USAGE (Wheel) */ \
|
||||
0x15, 0x81, /* LOGICAL_MINIMUM (-127) */ \
|
||||
0x25, 0x7f, /* LOGICAL_MAXIMUM (127) */ \
|
||||
0x75, 0x08, /* REPORT_SIZE (8) */ \
|
||||
0x95, 0x01, /* REPORT_COUNT (1) */ \
|
||||
0x81, 0x06, /* INPUT (Data,Var,Rel) */ \
|
||||
0xc0, /* END_COLLECTION */ \
|
||||
MACRO_ARGUMENT_2_TO_END(__VA_ARGS__) \
|
||||
0xc0 /* END_COLLECTION */
|
||||
|
||||
#define HID_KEYBOARD_REPORT_DESCRIPTOR(...) \
|
||||
0x05, 0x01, /* USAGE_PAGE (Generic Desktop) // 47 */ \
|
||||
0x09, 0x06, /* USAGE (Keyboard) */ \
|
||||
0xa1, 0x01, /* COLLECTION (Application) */ \
|
||||
0x85, MACRO_GET_ARGUMENT_1_WITH_DEFAULT(HID_KEYBOARD_REPORT_ID, ## __VA_ARGS__), /* REPORT_ID */ \
|
||||
0x05, 0x07, /* USAGE_PAGE (Keyboard) */ \
|
||||
0x19, 0xe0, /* USAGE_MINIMUM (Keyboard LeftControl) */ \
|
||||
0x29, 0xe7, /* USAGE_MAXIMUM (Keyboard Right GUI) */ \
|
||||
0x15, 0x00, /* LOGICAL_MINIMUM (0) */ \
|
||||
0x25, 0x01, /* LOGICAL_MAXIMUM (1) */ \
|
||||
0x75, 0x01, /* REPORT_SIZE (1) */ \
|
||||
0x95, 0x08, /* REPORT_COUNT (8) */ \
|
||||
0x81, 0x02, /* INPUT (Data,Var,Abs) */ \
|
||||
\
|
||||
0x95, 0x01, /* REPORT_COUNT (1) */ \
|
||||
0x75, 0x08, /* REPORT_SIZE (8) */ \
|
||||
0x81, 0x03, /* INPUT (Cnst,Var,Abs) */ \
|
||||
\
|
||||
0x95, HID_KEYBOARD_ROLLOVER, /* REPORT_COUNT (6) */ \
|
||||
0x75, 0x08, /* REPORT_SIZE (8) */ \
|
||||
0x15, 0x00, /* LOGICAL_MINIMUM (0) */ \
|
||||
0x25, 0x65, /* LOGICAL_MAXIMUM (101) */ \
|
||||
0x05, 0x07, /* USAGE_PAGE (Keyboard) */ \
|
||||
\
|
||||
0x19, 0x00, /* USAGE_MINIMUM (Reserved (no event indicated)) */ \
|
||||
0x29, 0x65, /* USAGE_MAXIMUM (Keyboard Application) */ \
|
||||
0x81, 0x00, /* INPUT (Data,Ary,Abs) */ \
|
||||
\
|
||||
0x05, 0x08, /* USAGE_PAGE (LEDs) */ \
|
||||
0x19, 0x01, /* USAGE_MINIMUM (Num Lock) */ \
|
||||
0x29, 0x08, /* USAGE_MAXIMUM (Kana + 3 custom)*/ \
|
||||
0x95, 0x08, /* REPORT_COUNT (8) */ \
|
||||
0x75, 0x01, /* REPORT_SIZE (1) */ \
|
||||
0x91, 0x02, /* OUTPUT (Data,Var,Abs) */ \
|
||||
MACRO_ARGUMENT_2_TO_END(__VA_ARGS__) \
|
||||
0xc0 /* END_COLLECTION */
|
||||
|
||||
#define HID_BOOT_KEYBOARD_REPORT_DESCRIPTOR(...) \
|
||||
0x05, 0x01, /* USAGE_PAGE (Generic Desktop) // 47 */ \
|
||||
0x09, 0x06, /* USAGE (Keyboard) */ \
|
||||
0xa1, 0x01, /* COLLECTION (Application) */ \
|
||||
0x05, 0x07, /* USAGE_PAGE (Keyboard) */ \
|
||||
0x19, 0xe0, /* USAGE_MINIMUM (Keyboard LeftControl) */ \
|
||||
0x29, 0xe7, /* USAGE_MAXIMUM (Keyboard Right GUI) */ \
|
||||
0x15, 0x00, /* LOGICAL_MINIMUM (0) */ \
|
||||
0x25, 0x01, /* LOGICAL_MAXIMUM (1) */ \
|
||||
0x75, 0x01, /* REPORT_SIZE (1) */ \
|
||||
0x95, 0x08, /* REPORT_COUNT (8) */ \
|
||||
0x81, 0x02, /* INPUT (Data,Var,Abs) */ \
|
||||
\
|
||||
0x95, 0x01, /* REPORT_COUNT (1) */ \
|
||||
0x75, 0x08, /* REPORT_SIZE (8) */ \
|
||||
0x81, 0x03, /* INPUT (Cnst,Var,Abs) */ \
|
||||
\
|
||||
0x95, 0x06, /* REPORT_COUNT (6) */ \
|
||||
0x75, 0x08, /* REPORT_SIZE (8) */ \
|
||||
0x15, 0x00, /* LOGICAL_MINIMUM (0) */ \
|
||||
0x25, 0x65, /* LOGICAL_MAXIMUM (101) */ \
|
||||
0x05, 0x07, /* USAGE_PAGE (Keyboard) */ \
|
||||
\
|
||||
0x19, 0x00, /* USAGE_MINIMUM (Reserved (no event indicated)) */ \
|
||||
0x29, 0x65, /* USAGE_MAXIMUM (Keyboard Application) */ \
|
||||
0x81, 0x00, /* INPUT (Data,Ary,Abs) */ \
|
||||
\
|
||||
0x05, 0x08, /* USAGE_PAGE (LEDs) */ \
|
||||
0x19, 0x01, /* USAGE_MINIMUM (Num Lock) */ \
|
||||
0x29, 0x08, /* USAGE_MAXIMUM (Kana + 3 custom)*/ \
|
||||
0x95, 0x08, /* REPORT_COUNT (8) */ \
|
||||
0x75, 0x01, /* REPORT_SIZE (1) */ \
|
||||
0x91, 0x02, /* OUTPUT (Data,Var,Abs) */ \
|
||||
__VA_ARGS__ \
|
||||
0xc0 /* END_COLLECTION */
|
||||
|
||||
#define HID_JOYSTICK_REPORT_DESCRIPTOR(...) \
|
||||
0x05, 0x01, /* Usage Page (Generic Desktop) */ \
|
||||
0x09, 0x04, /* Usage (Joystick) */ \
|
||||
0xA1, 0x01, /* Collection (Application) */ \
|
||||
0x85, MACRO_GET_ARGUMENT_1_WITH_DEFAULT(HID_JOYSTICK_REPORT_ID, ## __VA_ARGS__), /* REPORT_ID */ \
|
||||
0x15, 0x00, /* Logical Minimum (0) */ \
|
||||
0x25, 0x01, /* Logical Maximum (1) */ \
|
||||
0x75, 0x01, /* Report Size (1) */ \
|
||||
0x95, 0x20, /* Report Count (32) */ \
|
||||
0x05, 0x09, /* Usage Page (Button) */ \
|
||||
0x19, 0x01, /* Usage Minimum (Button #1) */ \
|
||||
0x29, 0x20, /* Usage Maximum (Button #32) */ \
|
||||
0x81, 0x02, /* Input (variable,absolute) */ \
|
||||
0x15, 0x00, /* Logical Minimum (0) */ \
|
||||
0x25, 0x07, /* Logical Maximum (7) */ \
|
||||
0x35, 0x00, /* Physical Minimum (0) */ \
|
||||
0x46, 0x3B, 0x01, /* Physical Maximum (315) */ \
|
||||
0x75, 0x04, /* Report Size (4) */ \
|
||||
0x95, 0x01, /* Report Count (1) */ \
|
||||
0x65, 0x14, /* Unit (20) */ \
|
||||
0x05, 0x01, /* Usage Page (Generic Desktop) */ \
|
||||
0x09, 0x39, /* Usage (Hat switch) */ \
|
||||
0x81, 0x42, /* Input (variable,absolute,null_state) */ \
|
||||
0x05, 0x01, /* Usage Page (Generic Desktop) */ \
|
||||
0x09, 0x01, /* Usage (Pointer) */ \
|
||||
0xA1, 0x00, /* Collection () */ \
|
||||
0x15, 0x00, /* Logical Minimum (0) */ \
|
||||
0x26, 0xFF, 0x03, /* Logical Maximum (1023) */ \
|
||||
0x75, 0x0A, /* Report Size (10) */ \
|
||||
0x95, 0x04, /* Report Count (4) */ \
|
||||
0x09, 0x30, /* Usage (X) */ \
|
||||
0x09, 0x31, /* Usage (Y) */ \
|
||||
0x09, 0x33, /* Usage (Rx) */ \
|
||||
0x09, 0x34, /* Usage (Ry) */ \
|
||||
0x81, 0x02, /* Input (variable,absolute) */ \
|
||||
0xC0, /* End Collection */ \
|
||||
0x15, 0x00, /* Logical Minimum (0) */ \
|
||||
0x26, 0xFF, 0x03, /* Logical Maximum (1023) */ \
|
||||
0x75, 0x0A, /* Report Size (10) */ \
|
||||
0x95, 0x02, /* Report Count (2) */ \
|
||||
0x09, 0x36, /* Usage (Slider) */ \
|
||||
0x09, 0x36, /* Usage (Slider) */ \
|
||||
0x81, 0x02, /* Input (variable,absolute) */ \
|
||||
MACRO_ARGUMENT_2_TO_END(__VA_ARGS__) \
|
||||
0xC0
|
||||
|
||||
#define RAWHID_USAGE_PAGE 0xFFC0 // recommended: 0xFF00 to 0xFFFF
|
||||
#define RAWHID_USAGE 0x0C00 // recommended: 0x0100 to 0xFFFF
|
||||
|
||||
#define LSB(x) ((x) & 0xFF)
|
||||
#define MSB(x) (((x) & 0xFF00) >> 8)
|
||||
// TODO: make this work for txSize > 255
|
||||
#define HID_RAW_REPORT_DESCRIPTOR(txSize, rxSize) \
|
||||
0x06, LSB(RAWHID_USAGE_PAGE), MSB(RAWHID_USAGE_PAGE), \
|
||||
0x0A, LSB(RAWHID_USAGE), MSB(RAWHID_USAGE), \
|
||||
0xA1, 0x01, /* Collection 0x01 */ \
|
||||
/* 0x85, 10, */ /* REPORT_ID (1) */ \
|
||||
0x75, 0x08, /* report size = 8 bits */ \
|
||||
0x15, 0x00, /* logical minimum = 0 */ \
|
||||
0x26, 0xFF, 0x00, /* logical maximum = 255 */ \
|
||||
\
|
||||
0x96, LSB(txSize), MSB(txSize), /* report count TX */ \
|
||||
0x09, 0x01, /* usage */ \
|
||||
0x81, 0x02, /* Input (array) */ \
|
||||
\
|
||||
0x75, 0x08, /* report size = 8 bits */ \
|
||||
0x15, 0x00, /* logical minimum = 0 */ \
|
||||
0x26, 0xFF, 0x00, /* logical maximum = 255 */ \
|
||||
0x96, LSB(rxSize), MSB(rxSize), /* report count RX */ \
|
||||
0x09, 0x02, /* usage */ \
|
||||
0x91, 0x02, /* OUTPUT (0x91) */ \
|
||||
0xC0 /* end collection */
|
||||
|
||||
typedef struct {
|
||||
uint8_t* descriptor;
|
||||
uint16_t length;
|
||||
} HIDReportDescriptor;
|
||||
|
||||
class USBHIDDevice {
|
||||
private:
|
||||
bool enabledHID = false;
|
||||
public:
|
||||
bool registerComponent();
|
||||
void setReportDescriptor(const uint8_t* report_descriptor, uint16_t report_descriptor_length);
|
||||
void setReportDescriptor(const HIDReportDescriptor* reportDescriptor);
|
||||
// All the strings are zero-terminated ASCII strings. Use NULL for defaults.
|
||||
void begin(const uint8_t* report_descriptor, uint16_t length, uint16_t idVendor=0, uint16_t idProduct=0,
|
||||
const char* manufacturer=NULL, const char* product=NULL, const char* serialNumber="00000000000000000001");
|
||||
void begin(const HIDReportDescriptor* reportDescriptor, uint16_t idVendor=0, uint16_t idProduct=0,
|
||||
const char* manufacturer=NULL, const char* product=NULL, const char* serialNumber="00000000000000000001");
|
||||
void setSerial(uint8 serialSupport=true);
|
||||
void setBuffers(uint8_t buffers, volatile HIDBuffer_t* fb=NULL, int count=0); // type = HID_REPORT_TYPE_FEATURE or HID_REPORT_TYPE_OUTPUT
|
||||
bool addBuffer(uint8_t type, volatile HIDBuffer_t* buffer);
|
||||
void clearBuffers(uint8_t type);
|
||||
void clearBuffers();
|
||||
inline bool addFeatureBuffer(volatile HIDBuffer_t* buffer) {
|
||||
return addBuffer(HID_REPORT_TYPE_FEATURE, buffer);
|
||||
}
|
||||
inline bool addOutputBuffer(volatile HIDBuffer_t* buffer) {
|
||||
return addBuffer(HID_REPORT_TYPE_OUTPUT, buffer);
|
||||
}
|
||||
inline void setFeatureBuffers(volatile HIDBuffer_t* fb=NULL, int count=0) {
|
||||
setBuffers(HID_REPORT_TYPE_FEATURE, fb, count);
|
||||
}
|
||||
inline void setOutputBuffers(volatile HIDBuffer_t* fb=NULL, int count=0) {
|
||||
setBuffers(HID_REPORT_TYPE_OUTPUT, fb, count);
|
||||
}
|
||||
void end(void);
|
||||
};
|
||||
|
||||
void USBHID_begin_with_serial(const uint8_t* report_descriptor, uint16_t length, uint16_t idVendor=0, uint16_t idProduct=0,
|
||||
const char* manufacturer=NULL, const char* product=NULL, const char* serialNumber="00000000000000000001");
|
||||
void USBHID_begin_with_serial(const HIDReportDescriptor* reportDescriptor, uint16_t idVendor=0, uint16_t idProduct=0,
|
||||
const char* manufacturer=NULL, const char* product=NULL, const char* serialNumber="00000000000000000001");
|
||||
|
||||
class HIDReporter {
|
||||
private:
|
||||
uint8_t* buffer;
|
||||
unsigned bufferSize;
|
||||
uint8_t reportID;
|
||||
|
||||
public:
|
||||
void sendReport();
|
||||
|
||||
public:
|
||||
// if you use this init function, the buffer starts with a reportID, even if the reportID is zero,
|
||||
// and bufferSize includes the reportID; if reportID is zero, sendReport() will skip the initial
|
||||
// reportID byte
|
||||
HIDReporter(uint8_t* _buffer, unsigned _size, uint8_t _reportID);
|
||||
// if you use this init function, the buffer has no reportID byte in it
|
||||
HIDReporter(uint8_t* _buffer, unsigned _size);
|
||||
uint16_t getFeature(uint8_t* out=NULL, uint8_t poll=1);
|
||||
uint16_t getOutput(uint8_t* out=NULL, uint8_t poll=1);
|
||||
uint16_t getData(uint8_t type, uint8_t* out, uint8_t poll=1); // type = HID_REPORT_TYPE_FEATURE or HID_REPORT_TYPE_OUTPUT
|
||||
void setFeature(uint8_t* feature);
|
||||
};
|
||||
|
||||
//================================================================================
|
||||
//================================================================================
|
||||
// Mouse
|
||||
|
||||
#define MOUSE_LEFT 1
|
||||
#define MOUSE_RIGHT 2
|
||||
#define MOUSE_MIDDLE 4
|
||||
#define MOUSE_ALL (MOUSE_LEFT | MOUSE_RIGHT | MOUSE_MIDDLE)
|
||||
|
||||
class HIDMouse : public HIDReporter {
|
||||
protected:
|
||||
uint8_t _buttons;
|
||||
void buttons(uint8_t b);
|
||||
uint8_t reportBuffer[5];
|
||||
public:
|
||||
HIDMouse(uint8_t reportID=HID_MOUSE_REPORT_ID) : HIDReporter(reportBuffer, sizeof(reportBuffer), reportID), _buttons(0) {}
|
||||
void begin(void);
|
||||
void end(void);
|
||||
void click(uint8_t b = MOUSE_LEFT);
|
||||
void move(signed char x, signed char y, signed char wheel = 0);
|
||||
void press(uint8_t b = MOUSE_LEFT); // press LEFT by default
|
||||
void release(uint8_t b = MOUSE_LEFT); // release LEFT by default
|
||||
bool isPressed(uint8_t b = MOUSE_ALL); // check all buttons by default
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
uint8_t reportID;
|
||||
uint8_t buttons;
|
||||
int16_t x;
|
||||
int16_t y;
|
||||
uint8_t wheel;
|
||||
} __packed AbsMouseReport_t;
|
||||
|
||||
class HIDAbsMouse : public HIDReporter {
|
||||
protected:
|
||||
void buttons(uint8_t b);
|
||||
AbsMouseReport_t report;
|
||||
public:
|
||||
HIDAbsMouse(uint8_t reportID=HID_MOUSE_REPORT_ID) : HIDReporter((uint8_t*)&report, sizeof(report), reportID) {
|
||||
report.buttons = 0;
|
||||
report.x = 0;
|
||||
report.y = 0;
|
||||
report.wheel = 0;
|
||||
}
|
||||
void begin(void);
|
||||
void end(void);
|
||||
void click(uint8_t b = MOUSE_LEFT);
|
||||
void move(int16_t x, int16_t y, int8_t wheel = 0);
|
||||
void press(uint8_t b = MOUSE_LEFT); // press LEFT by default
|
||||
void release(uint8_t b = MOUSE_LEFT); // release LEFT by default
|
||||
bool isPressed(uint8_t b = MOUSE_ALL); // check all buttons by default
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
uint8_t reportID;
|
||||
uint16_t button;
|
||||
} __packed ConsumerReport_t;
|
||||
|
||||
class HIDConsumer : public HIDReporter {
|
||||
protected:
|
||||
ConsumerReport_t report;
|
||||
public:
|
||||
enum {
|
||||
BRIGHTNESS_UP = 0x6F,
|
||||
BRIGHTNESS_DOWN = 0x70,
|
||||
VOLUME_UP = 0xE9,
|
||||
VOLUME_DOWN = 0xEA,
|
||||
MUTE = 0xE2,
|
||||
PLAY_OR_PAUSE = 0xCD
|
||||
// see pages 75ff of http://www.usb.org/developers/hidpage/Hut1_12v2.pdf
|
||||
};
|
||||
HIDConsumer(uint8_t reportID=HID_CONSUMER_REPORT_ID) : HIDReporter((uint8_t*)&report, sizeof(report), reportID) {
|
||||
report.button = 0;
|
||||
}
|
||||
void begin(void);
|
||||
void end(void);
|
||||
void press(uint16_t button);
|
||||
void release();
|
||||
};
|
||||
|
||||
#define KEY_LEFT_CTRL 0x80
|
||||
#define KEY_LEFT_SHIFT 0x81
|
||||
#define KEY_LEFT_ALT 0x82
|
||||
#define KEY_LEFT_GUI 0x83
|
||||
#define KEY_RIGHT_CTRL 0x84
|
||||
#define KEY_RIGHT_SHIFT 0x85
|
||||
#define KEY_RIGHT_ALT 0x86
|
||||
#define KEY_RIGHT_GUI 0x87
|
||||
|
||||
#define KEY_UP_ARROW 0xDA
|
||||
#define KEY_DOWN_ARROW 0xD9
|
||||
#define KEY_LEFT_ARROW 0xD8
|
||||
#define KEY_RIGHT_ARROW 0xD7
|
||||
#define KEY_BACKSPACE 0xB2
|
||||
#define KEY_TAB 0xB3
|
||||
#define KEY_RETURN 0xB0
|
||||
#define KEY_ESC 0xB1
|
||||
#define KEY_INSERT 0xD1
|
||||
#define KEY_DELETE 0xD4
|
||||
#define KEY_PAGE_UP 0xD3
|
||||
#define KEY_PAGE_DOWN 0xD6
|
||||
#define KEY_HOME 0xD2
|
||||
#define KEY_END 0xD5
|
||||
#define KEY_CAPS_LOCK 0xC1
|
||||
#define KEY_F1 0xC2
|
||||
#define KEY_F2 0xC3
|
||||
#define KEY_F3 0xC4
|
||||
#define KEY_F4 0xC5
|
||||
#define KEY_F5 0xC6
|
||||
#define KEY_F6 0xC7
|
||||
#define KEY_F7 0xC8
|
||||
#define KEY_F8 0xC9
|
||||
#define KEY_F9 0xCA
|
||||
#define KEY_F10 0xCB
|
||||
#define KEY_F11 0xCC
|
||||
#define KEY_F12 0xCD
|
||||
|
||||
typedef struct{
|
||||
uint8_t reportID;
|
||||
uint8_t modifiers;
|
||||
uint8_t reserved;
|
||||
uint8_t keys[HID_KEYBOARD_ROLLOVER];
|
||||
} __packed KeyReport_t;
|
||||
|
||||
class HIDKeyboard : public Print, public HIDReporter {
|
||||
public:
|
||||
KeyReport_t keyReport;
|
||||
|
||||
protected:
|
||||
uint8_t leds[HID_BUFFER_ALLOCATE_SIZE(1,1)];
|
||||
HIDBuffer_t ledData;
|
||||
uint8_t reportID;
|
||||
uint8_t getKeyCode(uint8_t k, uint8_t* modifiersP);
|
||||
bool adjustForHostCapsLock = true;
|
||||
|
||||
public:
|
||||
HIDKeyboard(uint8_t _reportID=HID_KEYBOARD_REPORT_ID) :
|
||||
HIDReporter((uint8*)&keyReport, sizeof(KeyReport_t), _reportID),
|
||||
ledData(leds, HID_BUFFER_SIZE(1,_reportID), _reportID, HID_BUFFER_MODE_NO_WAIT),
|
||||
reportID(_reportID)
|
||||
{}
|
||||
void begin(void);
|
||||
void end(void);
|
||||
void setAdjustForHostCapsLock(bool state) {
|
||||
adjustForHostCapsLock = state;
|
||||
}
|
||||
inline uint8 getLEDs(void) {
|
||||
return leds[reportID != 0 ? 1 : 0];
|
||||
}
|
||||
virtual size_t write(uint8_t k);
|
||||
virtual size_t press(uint8_t k);
|
||||
virtual size_t release(uint8_t k);
|
||||
virtual void releaseAll(void);
|
||||
};
|
||||
|
||||
|
||||
//================================================================================
|
||||
//================================================================================
|
||||
// Joystick
|
||||
|
||||
// only works for little-endian machines, but makes the code so much more
|
||||
// readable
|
||||
typedef struct {
|
||||
uint8_t reportID;
|
||||
uint32_t buttons;
|
||||
unsigned hat:4;
|
||||
unsigned x:10;
|
||||
unsigned y:10;
|
||||
unsigned rx:10;
|
||||
unsigned ry:10;
|
||||
unsigned sliderLeft:10;
|
||||
unsigned sliderRight:10;
|
||||
} __packed JoystickReport_t;
|
||||
|
||||
static_assert(sizeof(JoystickReport_t)==13, "Wrong endianness/packing!");
|
||||
|
||||
class HIDJoystick : public HIDReporter {
|
||||
protected:
|
||||
JoystickReport_t joyReport;
|
||||
bool manualReport = false;
|
||||
void safeSendReport(void);
|
||||
public:
|
||||
inline void send(void) {
|
||||
sendReport();
|
||||
}
|
||||
void setManualReportMode(bool manualReport); // in manual report mode, reports only sent when send() is called
|
||||
bool getManualReportMode();
|
||||
void begin(void);
|
||||
void end(void);
|
||||
void button(uint8_t button, bool val);
|
||||
void X(uint16_t val);
|
||||
void Y(uint16_t val);
|
||||
void position(uint16_t x, uint16_t y);
|
||||
void Xrotate(uint16_t val);
|
||||
void Yrotate(uint16_t val);
|
||||
void sliderLeft(uint16_t val);
|
||||
void sliderRight(uint16_t val);
|
||||
void slider(uint16_t val);
|
||||
void hat(int16_t dir);
|
||||
HIDJoystick(uint8_t reportID=HID_JOYSTICK_REPORT_ID) : HIDReporter((uint8_t*)&joyReport, sizeof(joyReport), reportID) {
|
||||
joyReport.buttons = 0;
|
||||
joyReport.hat = 15;
|
||||
joyReport.x = 512;
|
||||
joyReport.y = 512;
|
||||
joyReport.rx = 512;
|
||||
joyReport.ry = 512;
|
||||
joyReport.sliderLeft = 0;
|
||||
joyReport.sliderRight = 0;
|
||||
}
|
||||
};
|
||||
|
||||
extern USBHIDDevice USBHID;
|
||||
|
||||
template<unsigned txSize,unsigned rxSize>class HIDRaw : public HIDReporter {
|
||||
private:
|
||||
uint8_t txBuffer[txSize];
|
||||
uint8_t rxBuffer[HID_BUFFER_ALLOCATE_SIZE(rxSize,0)];
|
||||
HIDBuffer_t buf;
|
||||
public:
|
||||
HIDRaw() : HIDReporter(txBuffer, sizeof(txBuffer)) {}
|
||||
void begin(void) {
|
||||
buf.buffer = rxBuffer;
|
||||
buf.bufferSize = HID_BUFFER_SIZE(rxSize,0);
|
||||
buf.reportID = 0;
|
||||
USBHID.addOutputBuffer(&buf);
|
||||
}
|
||||
void end(void);
|
||||
void send(const uint8_t* data, unsigned n=sizeof(txBuffer)) {
|
||||
memset(txBuffer, 0, sizeof(txBuffer));
|
||||
memcpy(txBuffer, data, n>sizeof(txBuffer)?sizeof(txBuffer):n);
|
||||
sendReport();
|
||||
}
|
||||
};
|
||||
|
||||
extern HIDMouse Mouse;
|
||||
extern HIDKeyboard Keyboard;
|
||||
extern HIDJoystick Joystick;
|
||||
extern HIDKeyboard BootKeyboard;
|
||||
|
||||
extern const HIDReportDescriptor* hidReportMouse;
|
||||
extern const HIDReportDescriptor* hidReportKeyboard;
|
||||
extern const HIDReportDescriptor* hidReportJoystick;
|
||||
extern const HIDReportDescriptor* hidReportKeyboardMouse;
|
||||
extern const HIDReportDescriptor* hidReportKeyboardJoystick;
|
||||
extern const HIDReportDescriptor* hidReportKeyboardMouseJoystick;
|
||||
extern const HIDReportDescriptor* hidReportBootKeyboard;
|
||||
|
||||
#define HID_MOUSE hidReportMouse
|
||||
#define HID_KEYBOARD hidReportKeyboard
|
||||
#define HID_JOYSTICK hidReportJoystick
|
||||
#define HID_KEYBOARD_MOUSE hidReportKeyboardMouse
|
||||
#define HID_KEYBOARD_JOYSTICK hidReportKeyboardJoystick
|
||||
#define HID_KEYBOARD_MOUSE_JOYSTICK hidReportKeyboardMouseJoystick
|
||||
#define HID_BOOT_KEYBOARD hidReportBootKeyboard
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,469 @@
|
|||
/******************************************************************************
|
||||
* 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. Tymm Twillman kindly gave Alexander Pruss permission to relicense his code under the MIT
|
||||
* license, which fixed a nasty licensing mess.
|
||||
*
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2010 Perry Hung.
|
||||
* Copyright (c) 2013 Magnus Lundin.
|
||||
* Copyright (c) 2013 Donald Delmar Davis, Suspect Devices.
|
||||
* (c) 2003-2008 Tymm Twillman <tymm@booyaka.com>
|
||||
*
|
||||
* 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 "USBMIDI.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <libmaple/nvic.h>
|
||||
#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) {
|
||||
return true;
|
||||
}*/
|
||||
|
||||
bool USBMidi::registerComponent() {
|
||||
return USBComposite.add(&usbMIDIPart, this);
|
||||
}
|
||||
|
||||
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_TX_EPSIZE) {
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
USBMidi USBMIDI;
|
||||
|
||||
|
||||
// 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_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) {}
|
||||
#pragma GCC diagnostic pop
|
|
@ -0,0 +1,186 @@
|
|||
/******************************************************************************
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2010 Perry Hung.
|
||||
* Copyright (c) 2013 Magnus Lundin.
|
||||
*
|
||||
* 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_
|
||||
|
||||
//#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);
|
||||
|
||||
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_;
|
||||
}
|
||||
|
||||
// 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);
|
||||
void sendNoteOn(unsigned int channel, unsigned int note, unsigned int velocity);
|
||||
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);
|
||||
};
|
||||
|
||||
extern USBMidi USBMIDI;
|
||||
|
||||
extern const uint32 midiNoteFrequency_10ths[128];
|
||||
|
||||
#endif
|
|
@ -0,0 +1,45 @@
|
|||
#include "USBMassStorage.h"
|
||||
#include "usb_mass.h"
|
||||
#include "usb_mass_mal.h"
|
||||
#include "usb_serial.h"
|
||||
#include <string.h>
|
||||
|
||||
void USBMassStorageDevice::begin() {
|
||||
if(!enabled) {
|
||||
USBComposite.clear();
|
||||
registerComponent();
|
||||
USBComposite.begin();
|
||||
|
||||
enabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
void USBMassStorageDevice::end() {
|
||||
USBComposite.end();
|
||||
}
|
||||
|
||||
void USBMassStorageDevice::loop() {
|
||||
usb_mass_loop();
|
||||
}
|
||||
|
||||
bool USBMassStorageDevice::registerComponent() {
|
||||
return USBComposite.add(&usbMassPart, this);
|
||||
}
|
||||
|
||||
void USBMassStorageDevice::setDrive(uint32 driveNumber, uint32 byteSize, MassStorageReader reader,
|
||||
MassStorageWriter writer, MassStorageStatuser statuser, MassStorageInitializer initializer) {
|
||||
if (driveNumber >= USB_MASS_MAX_DRIVES)
|
||||
return;
|
||||
usb_mass_drives[driveNumber].blockCount = byteSize/512;
|
||||
usb_mass_drives[driveNumber].read = reader;
|
||||
usb_mass_drives[driveNumber].write = writer;
|
||||
usb_mass_drives[driveNumber].status = statuser;
|
||||
usb_mass_drives[driveNumber].init = initializer;
|
||||
usb_mass_drives[driveNumber].format = initializer;
|
||||
}
|
||||
|
||||
void USBMassStorageDevice::clearDrives() {
|
||||
memset(usb_mass_drives, 0, sizeof(usb_mass_drives));
|
||||
}
|
||||
|
||||
USBMassStorageDevice MassStorage;
|
|
@ -0,0 +1,25 @@
|
|||
#ifndef USBMASSSTORAGE_H
|
||||
#define USBMASSSTORAGE_H
|
||||
|
||||
#include <boards.h>
|
||||
#include "USBComposite.h"
|
||||
#include "usb_generic.h"
|
||||
#include "usb_mass_mal.h"
|
||||
|
||||
class USBMassStorageDevice {
|
||||
private:
|
||||
bool enabled = false;
|
||||
public:
|
||||
void begin();
|
||||
void end();
|
||||
void loop();
|
||||
void clearDrives(void);
|
||||
bool registerComponent();
|
||||
void setDrive(uint32 driveNumber, uint32 byteSize, MassStorageReader reader,
|
||||
MassStorageWriter writer = NULL, MassStorageStatuser = NULL, MassStorageInitializer = NULL);
|
||||
};
|
||||
|
||||
extern USBMassStorageDevice MassStorage;
|
||||
|
||||
#endif /* USBMASSSTORAGE_H */
|
||||
|
|
@ -0,0 +1,173 @@
|
|||
/* Copyright (c) 2011, Peter Barrett
|
||||
**
|
||||
** 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief USB HID Keyboard device
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <libmaple/nvic.h>
|
||||
#include <libmaple/usb.h>
|
||||
#include "USBXBox360.h"
|
||||
#include "usb_x360.h"
|
||||
|
||||
void USBXBox360::sendReport(void){
|
||||
x360_tx(xbox360_Report, sizeof(xbox360_Report));
|
||||
|
||||
while (x360_is_transmitting() != 0) {
|
||||
}
|
||||
/* flush out to avoid having the pc wait for more data */
|
||||
x360_tx(NULL, 0);
|
||||
}
|
||||
|
||||
void USBXBox360::setRumbleCallback(void (*callback)(uint8 left, uint8 right)) {
|
||||
x360_set_rumble_callback(callback);
|
||||
}
|
||||
|
||||
void USBXBox360::setLEDCallback(void (*callback)(uint8 pattern)) {
|
||||
x360_set_led_callback(callback);
|
||||
}
|
||||
|
||||
|
||||
bool USBXBox360::init(void* ignore) {
|
||||
(void)ignore;
|
||||
usb_generic_set_info(0x045e, 0x028e, NULL, NULL, NULL);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool USBXBox360::registerComponent() {
|
||||
return USBComposite.add(&usbX360Part, this, init);
|
||||
}
|
||||
|
||||
void USBXBox360::begin(void){
|
||||
if(!enabled){
|
||||
USBComposite.clear();
|
||||
registerComponent();
|
||||
USBComposite.begin();
|
||||
|
||||
enabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
void USBXBox360::end() {
|
||||
if (enabled) {
|
||||
enabled = false;
|
||||
USBComposite.end();
|
||||
}
|
||||
}
|
||||
|
||||
void USBXBox360::stop(void){
|
||||
setRumbleCallback(NULL);
|
||||
setLEDCallback(NULL);
|
||||
}
|
||||
|
||||
void USBXBox360::setManualReportMode(bool mode) {
|
||||
manualReport = mode;
|
||||
}
|
||||
|
||||
bool USBXBox360::getManualReportMode() {
|
||||
return manualReport;
|
||||
}
|
||||
|
||||
void USBXBox360::safeSendReport() {
|
||||
if (!manualReport) {
|
||||
while (x360_is_transmitting() != 0) {
|
||||
}
|
||||
sendReport();
|
||||
}
|
||||
}
|
||||
|
||||
void USBXBox360::send() {
|
||||
while (x360_is_transmitting() != 0) {
|
||||
}
|
||||
sendReport();
|
||||
}
|
||||
|
||||
void USBXBox360::button(uint8_t button, bool val){
|
||||
button--;
|
||||
uint8_t mask = (1 << (button & 7));
|
||||
if (val) {
|
||||
if (button < 8) xbox360_Report[2] |= mask;
|
||||
else if (button < 16) xbox360_Report[3] |= mask;
|
||||
} else {
|
||||
mask = ~mask;
|
||||
if (button < 8) xbox360_Report[2] &= mask;
|
||||
else if (button < 16) xbox360_Report[3] &= mask;
|
||||
}
|
||||
|
||||
safeSendReport();
|
||||
}
|
||||
|
||||
void USBXBox360::X(int16_t val){
|
||||
xbox360_Report[6] = val;
|
||||
xbox360_Report[7] = (uint16)val >> 8;
|
||||
|
||||
safeSendReport();
|
||||
}
|
||||
|
||||
void USBXBox360::Y(int16_t val){
|
||||
xbox360_Report[8] = val;
|
||||
xbox360_Report[9] = (uint16)val >> 8;
|
||||
|
||||
safeSendReport();
|
||||
}
|
||||
|
||||
void USBXBox360::XRight(int16_t val){
|
||||
xbox360_Report[0xA] = val;
|
||||
xbox360_Report[0xB] = (uint16)val >> 8;
|
||||
|
||||
safeSendReport();
|
||||
}
|
||||
|
||||
void USBXBox360::YRight(int16_t val){
|
||||
xbox360_Report[0xC] = val;
|
||||
xbox360_Report[0xD] = (uint16)val >> 8;
|
||||
|
||||
safeSendReport();
|
||||
}
|
||||
|
||||
void USBXBox360::position(int16_t x, int16_t y){
|
||||
xbox360_Report[6] = x;
|
||||
xbox360_Report[7] = (uint16)x >> 8;
|
||||
xbox360_Report[8] = y;
|
||||
xbox360_Report[9] = (uint16)y >> 8;
|
||||
|
||||
safeSendReport();
|
||||
}
|
||||
|
||||
void USBXBox360::positionRight(int16_t x, int16_t y){
|
||||
xbox360_Report[0xA] = x;
|
||||
xbox360_Report[0xB] = (uint16)x >> 8;
|
||||
xbox360_Report[0xC] = y;
|
||||
xbox360_Report[0xD] = (uint16)y >> 8;
|
||||
|
||||
safeSendReport();
|
||||
}
|
||||
|
||||
void USBXBox360::sliderLeft(uint8_t val){
|
||||
xbox360_Report[4] = val;
|
||||
|
||||
safeSendReport();
|
||||
}
|
||||
|
||||
void USBXBox360::sliderRight(uint8_t val){
|
||||
xbox360_Report[5] = val;
|
||||
|
||||
safeSendReport();
|
||||
}
|
||||
|
||||
USBXBox360 XBox360;
|
|
@ -0,0 +1,58 @@
|
|||
/* Copyright (c) 2011, Peter Barrett
|
||||
**
|
||||
** 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.
|
||||
*/
|
||||
|
||||
#ifndef _USBXBox360_H
|
||||
#define _USBXBox360_H
|
||||
|
||||
#include <Print.h>
|
||||
#include <boards.h>
|
||||
#include "USBComposite.h"
|
||||
#include "usb_generic.h"
|
||||
|
||||
class USBXBox360 {
|
||||
private:
|
||||
uint8_t xbox360_Report[20] = {0,0x14};// 3,0,0,0,0,0x0F,0x20,0x80,0x00,0x02,0x08,0x20,0x80};
|
||||
bool manualReport = false;
|
||||
bool enabled;
|
||||
void safeSendReport(void);
|
||||
void sendReport(void);
|
||||
public:
|
||||
void send(void);
|
||||
static bool init(void* ignore);
|
||||
bool registerComponent();
|
||||
void stop();
|
||||
void setManualReportMode(bool manualReport);
|
||||
bool getManualReportMode();
|
||||
void begin(void);
|
||||
void end(void);
|
||||
void button(uint8_t button, bool val);
|
||||
void X(int16_t val);
|
||||
void Y(int16_t val);
|
||||
void position(int16_t x, int16_t y);
|
||||
void positionRight(int16_t x, int16_t y);
|
||||
void XRight(int16_t val);
|
||||
void YRight(int16_t val);
|
||||
void sliderLeft(uint8_t val);
|
||||
void sliderRight(uint8_t val);
|
||||
void hat(int16_t dir);
|
||||
void setLEDCallback(void (*callback)(uint8 pattern));
|
||||
void setRumbleCallback(void (*callback)(uint8 left, uint8 right));
|
||||
};
|
||||
|
||||
extern USBXBox360 XBox360;
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
#include <USBHID.h>
|
||||
|
||||
void setup()
|
||||
{
|
||||
USBHID_begin_with_serial(HID_BOOT_KEYBOARD);
|
||||
BootKeyboard.begin(); // needed just in case you need LED support
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
BootKeyboard.press(KEY_F12);
|
||||
delay(100);
|
||||
BootKeyboard.release(KEY_F12);
|
||||
delay(1000);
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
#include <USBHID.h>
|
||||
|
||||
const uint8_t reportDescription[] = {
|
||||
HID_ABS_MOUSE_REPORT_DESCRIPTOR(HID_MOUSE_REPORT_ID)
|
||||
};
|
||||
|
||||
HIDAbsMouse mouse;
|
||||
|
||||
void setup(){
|
||||
USBHID_begin_with_serial(reportDescription, sizeof(reportDescription));
|
||||
delay(1000);
|
||||
mouse.move(0,0);
|
||||
delay(1000);
|
||||
mouse.press(MOUSE_LEFT);
|
||||
mouse.move(500,500);
|
||||
mouse.release(MOUSE_ALL);
|
||||
mouse.click(MOUSE_RIGHT);
|
||||
}
|
||||
|
||||
void loop(){
|
||||
mouse.move(0,0);
|
||||
delay(1000);
|
||||
mouse.move(16384,16384);
|
||||
delay(1000);
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
#include <USBComposite.h>
|
||||
|
||||
const uint8_t reportDescription[] = {
|
||||
HID_CONSUMER_REPORT_DESCRIPTOR()
|
||||
};
|
||||
|
||||
HIDConsumer Consumer;
|
||||
|
||||
void setup(){
|
||||
USBHID_begin_with_serial(reportDescription, sizeof(reportDescription));
|
||||
}
|
||||
|
||||
void loop() {
|
||||
Consumer.press(HIDConsumer::BRIGHTNESS_DOWN);
|
||||
Consumer.release();
|
||||
delay(500);
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
#include <USBComposite.h>
|
||||
|
||||
|
||||
void setup() {
|
||||
USBHID_begin_with_serial(HID_KEYBOARD);
|
||||
Keyboard.begin(); // needed for LED support
|
||||
delay(1000);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
CompositeSerial.println(Keyboard.getLEDs());
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
uint8 image[11776] = {
|
||||
0xEB,0x3C,0x90,0x6D,0x6B,0x64,0x6F,0x73,0x66,0x73,0x00,0x00,0x02,0x04,0x01,0x00,
|
||||
0x02,0x10,0x00,0x17,0x00,0xF8,0x01,0x00,0x01,0x00,0x01,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x29,0xDB,0x32,0x73,0x5A,0x20,0x20,0x20,0x20,0x20,
|
||||
0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x41,0x54,0x31,0x32,0x20,0x20,0x20,0x0E,0x1F,
|
||||
0xBE,0x5B,0x7C,0xAC,0x22,0xC0,0x74,0x0B,0x56,0xB4,0x0E,0xBB,0x07,0x00,0xCD,0x10,
|
||||
0x5E,0xEB,0xF0,0x32,0xE4,0xCD,0x16,0xCD,0x19,0xEB,0xFE,0x54,0x68,0x69,0x73,0x20,
|
||||
0x69,0x73,0x20,0x6E,0x6F,0x74,0x20,0x61,0x20,0x62,0x6F,0x6F,0x74,0x61,0x62,0x6C,
|
||||
0x65,0x20,0x64,0x69,0x73,0x6B,0x2E,0x20,0x20,0x50,0x6C,0x65,0x61,0x73,0x65,0x20,
|
||||
0x69,0x6E,0x73,0x65,0x72,0x74,0x20,0x61,0x20,0x62,0x6F,0x6F,0x74,0x61,0x62,0x6C,
|
||||
0x65,0x20,0x66,0x6C,0x6F,0x70,0x70,0x79,0x20,0x61,0x6E,0x64,0x0D,0x0A,0x70,0x72,
|
||||
0x65,0x73,0x73,0x20,0x61,0x6E,0x79,0x20,0x6B,0x65,0x79,0x20,0x74,0x6F,0x20,0x74,
|
||||
0x72,0x79,0x20,0x61,0x67,0x61,0x69,0x6E,0x20,0x2E,0x2E,0x2E,0x20,0x0D,0x0A,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x55,0xAA,
|
||||
0xF8,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0xF8,0xFF,0xFF,
|
||||
};
|
|
@ -0,0 +1,65 @@
|
|||
#include <USBComposite.h>
|
||||
|
||||
#include "image.h"
|
||||
|
||||
bool write(uint32_t memoryOffset, const uint8_t *writebuff, uint16_t transferLength) {
|
||||
memcpy(image+memoryOffset, writebuff, transferLength);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool read(uint32_t memoryOffset, uint8_t *readbuff, uint16_t transferLength) {
|
||||
memcpy(readbuff, image+memoryOffset, transferLength);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
char hexNibble(uint8 x) {
|
||||
return x < 10 ? x + '0' : x + 'A' - 10;
|
||||
}
|
||||
|
||||
char* format16(uint16 c) {
|
||||
static char str[6];
|
||||
str[5] = 0;
|
||||
char *p = str+5;
|
||||
do {
|
||||
*--p = (c % 10) + '0';
|
||||
c /= 10;
|
||||
} while(c);
|
||||
return p;
|
||||
}
|
||||
|
||||
void dumpDrive() {
|
||||
char hex[7] = "0x11,";
|
||||
CompositeSerial.print("uint8 image[");
|
||||
CompositeSerial.print(format16(sizeof(image)));
|
||||
CompositeSerial.println("] = {");
|
||||
int last;
|
||||
for (last=sizeof(image)-1;last>=0 && image[last] == 0;last--);
|
||||
if (last<0) last=0;
|
||||
|
||||
for (int i=0; i<=last; i++) {
|
||||
if (i && i % 16 == 0)
|
||||
CompositeSerial.println("");
|
||||
hex[2] = hexNibble(image[i]>>4);
|
||||
hex[3] = hexNibble(image[i]&0xF);
|
||||
CompositeSerial.print(hex);
|
||||
}
|
||||
CompositeSerial.println("\n};\n");
|
||||
}
|
||||
|
||||
void setup() {
|
||||
MassStorage.setDrive(0, sizeof(image), read, write);
|
||||
MassStorage.registerComponent();
|
||||
CompositeSerial.registerComponent();
|
||||
USBComposite.begin();
|
||||
delay(2000);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
MassStorage.loop();
|
||||
if (CompositeSerial.available() && 'd' == CompositeSerial.read()) {
|
||||
dumpDrive();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
#include <USBMIDI.h>
|
||||
|
||||
#define SPEAKER_PIN PA0
|
||||
|
||||
class myMidi : public USBMidi {
|
||||
virtual void handleNoteOff(unsigned int channel, unsigned int note, unsigned int velocity) {
|
||||
noTone(SPEAKER_PIN);
|
||||
}
|
||||
virtual void handleNoteOn(unsigned int channel, unsigned int note, unsigned int velocity) {
|
||||
tone(SPEAKER_PIN, (midiNoteFrequency_10ths[note]+5)/10);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
myMidi midi;
|
||||
|
||||
void setup() {
|
||||
pinMode(SPEAKER_PIN, OUTPUT);
|
||||
midi.registerComponent();
|
||||
CompositeSerial.registerComponent();
|
||||
USBComposite.begin();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
midi.poll();
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
#include <USBMIDI.h>
|
||||
|
||||
const uint8_t notes[] = {60, 62, 64, 65, 67, 69, 71, 72, 61, 63, 66, 68, 70};
|
||||
const int numNotes = sizeof(notes)/sizeof(*notes);
|
||||
|
||||
void setup() {
|
||||
USBMIDI.begin();
|
||||
delay(1000);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
for (int i=0;i<numNotes;i++) {
|
||||
USBMIDI.sendNoteOn(0, notes[i], 127);
|
||||
delay(200);
|
||||
USBMIDI.sendNoteOff(0, notes[i], 127);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
#include <USBComposite.h>
|
||||
|
||||
#define TXSIZE 256
|
||||
#define RXSIZE 300
|
||||
|
||||
HIDRaw<TXSIZE,RXSIZE> raw;
|
||||
uint8 buf[RXSIZE];
|
||||
|
||||
const uint8_t reportDescription[] = {
|
||||
HID_RAW_REPORT_DESCRIPTOR(TXSIZE,RXSIZE)
|
||||
};
|
||||
|
||||
void setup(){
|
||||
USBHID_begin_with_serial(reportDescription, sizeof(reportDescription));
|
||||
raw.begin();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
if (raw.getOutput(buf)) {
|
||||
for (int i=0;i<RXSIZE;i++) buf[i]++;
|
||||
raw.send(buf+RXSIZE-min(RXSIZE,TXSIZE),min(RXSIZE,TXSIZE));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
from pywinusb import hid
|
||||
from time import sleep
|
||||
|
||||
SIZE=300
|
||||
|
||||
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:
|
||||
for out_report in device.find_output_reports():
|
||||
buffer=[i for i in range(SIZE+1)]
|
||||
buffer[0]=0x0 # report id
|
||||
buffer[-1] = n
|
||||
out_report.set_raw_data(buffer)
|
||||
print("sending")
|
||||
if out_report.send():
|
||||
n = (n+1)&0xFF
|
||||
sleep(0.1)
|
||||
#sleep(0.005)
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
// This uses the greiman sdfat library.
|
||||
// To use SdFatEX, set ENABLE_EXTENDED_TRANSFER_CLASS to 1 in the library's
|
||||
// src/SdFatConfig.h
|
||||
#include <USBComposite.h>
|
||||
#include <SPI.h>
|
||||
#include "SdFat.h"
|
||||
|
||||
#define LED_PIN PB12
|
||||
|
||||
SdFatEX sd;
|
||||
const uint32_t speed = SPI_CLOCK_DIV2 ;
|
||||
const uint8_t SD_CHIP_SELECT = SS;
|
||||
bool enabled = false;
|
||||
uint32 cardSize;
|
||||
|
||||
bool write(uint32_t memoryOffset, const uint8_t *writebuff, uint16_t transferLength) {
|
||||
return sd.card()->writeBlocks(memoryOffset/512, writebuff, transferLength/512);
|
||||
}
|
||||
|
||||
bool read(uint32_t memoryOffset, uint8_t *readbuff, uint16_t transferLength) {
|
||||
return sd.card()->readBlocks(memoryOffset/512, readbuff, transferLength/512);
|
||||
}
|
||||
|
||||
void setup() {
|
||||
pinMode(LED_PIN,OUTPUT);
|
||||
digitalWrite(LED_PIN,1);
|
||||
}
|
||||
|
||||
void initReader() {
|
||||
digitalWrite(LED_PIN,0);
|
||||
cardSize = sd.card()->cardSize();
|
||||
MassStorage.setDrive(0, cardSize*512, read, write);
|
||||
MassStorage.registerComponent();
|
||||
CompositeSerial.registerComponent();
|
||||
USBComposite.begin();
|
||||
enabled=true;
|
||||
}
|
||||
|
||||
void loop() {
|
||||
if (!enabled) {
|
||||
if (sd.begin(SD_CHIP_SELECT)) {
|
||||
initReader();
|
||||
}
|
||||
else {
|
||||
delay(50);
|
||||
}
|
||||
}
|
||||
else {
|
||||
MassStorage.loop();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
#include <USBComposite.h>
|
||||
|
||||
void setup() {
|
||||
USBHID_begin_with_serial(HID_JOYSTICK);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
Joystick.X(0);
|
||||
delay(500);
|
||||
Joystick.X(1023);
|
||||
delay(500);
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
#include <USBComposite.h>
|
||||
|
||||
void setup() {
|
||||
USBHID_begin_with_serial(HID_KEYBOARD);
|
||||
Keyboard.begin(); // useful to detect host capslock state and LEDs
|
||||
delay(1000);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
Keyboard.println("Hello world");
|
||||
delay(10000);
|
||||
}
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
from pywinusb import hid
|
||||
from time import sleep
|
||||
|
||||
REPORT_ID = 20
|
||||
HID_REPORT_FEATURE = 3
|
||||
|
||||
device = hid.HidDeviceFilter(vendor_id = 0x1EAF).get_devices()[0] # , product_id = 0x0024
|
||||
print(device)
|
||||
device.open()
|
||||
|
||||
"""
|
||||
uint8_t reportID;
|
||||
uint32_t buttons;
|
||||
unsigned hat:4;
|
||||
unsigned x:10;
|
||||
unsigned y:10;
|
||||
unsigned rx:10;
|
||||
unsigned ry:10;
|
||||
unsigned sliderLeft:10;
|
||||
unsigned sliderRight:10;
|
||||
"""
|
||||
|
||||
def toBits(n,bits):
|
||||
return tuple((n>>i)&1 for i in range(bits))
|
||||
|
||||
def getByteFromBits(bits,n):
|
||||
out = 0
|
||||
for i in range(8):
|
||||
out += bits[8*n+i] << i
|
||||
return out
|
||||
|
||||
def joystickData(reportID=REPORT_ID, buttons=0, hat=15, x=512, y=512, rx=512, ry=512, sliderLeft=512, sliderRight=512):
|
||||
joyData = ( toBits(reportID,8) + toBits(buttons,32) + toBits(hat,4) + toBits(x,10) + toBits(y,10) +
|
||||
toBits(rx,10) + toBits(ry,10) + toBits(sliderLeft,10) + toBits(sliderRight,10) )
|
||||
out = [getByteFromBits(joyData,n) for n in range(13)]
|
||||
print(out)
|
||||
return out
|
||||
|
||||
myReport = None
|
||||
|
||||
for report in device.find_feature_reports():
|
||||
if report.report_id == REPORT_ID and report.report_type == "Feature":
|
||||
myReport = report
|
||||
break
|
||||
if myReport is None:
|
||||
for report in device.find_output_reports():
|
||||
if report.report_id == REPORT_ID and report.report_type == "Output":
|
||||
myReport = report
|
||||
break
|
||||
|
||||
assert myReport is not None
|
||||
|
||||
while True:
|
||||
myReport.set_raw_data(joystickData(buttons=7,x=0,y=0))
|
||||
|
||||
myReport.send()
|
||||
sleep(0.5)
|
||||
myReport.set_raw_data(joystickData(buttons=0,x=1023,y=1023))
|
||||
myReport.send()
|
||||
sleep(0.5)
|
|
@ -0,0 +1,47 @@
|
|||
// This is a silly project: you send a feature
|
||||
// report from the PC, and it becomes a joystick setting.
|
||||
// I guess it's not completely useless as it lets you
|
||||
// get vJoy functionality without special vJoy drivers.
|
||||
|
||||
#include <USBComposite.h>
|
||||
|
||||
#define DATA_SIZE (sizeof(JoystickReport_t)-1)
|
||||
|
||||
class HIDJoystickRawData : public HIDJoystick {
|
||||
private:
|
||||
uint8_t featureData[HID_BUFFER_ALLOCATE_SIZE(DATA_SIZE,1)];
|
||||
HIDBuffer_t fb { featureData, HID_BUFFER_SIZE(DATA_SIZE,1), HID_JOYSTICK_REPORT_ID };
|
||||
public:
|
||||
HIDJoystickRawData(uint8_t reportID=HID_JOYSTICK_REPORT_ID) : HIDJoystick(reportID) {}
|
||||
|
||||
void begin() {
|
||||
USBHID.setFeatureBuffers(&fb, 1);
|
||||
}
|
||||
|
||||
void setRawData(JoystickReport_t* p) {
|
||||
joyReport = *p;
|
||||
send();
|
||||
}
|
||||
};
|
||||
|
||||
HIDJoystickRawData joy;
|
||||
JoystickReport_t report = {HID_JOYSTICK_REPORT_ID};
|
||||
|
||||
const uint8_t reportDescription[] = {
|
||||
HID_JOYSTICK_REPORT_DESCRIPTOR(HID_JOYSTICK_REPORT_ID,
|
||||
HID_FEATURE_REPORT_DESCRIPTOR(DATA_SIZE))
|
||||
};
|
||||
|
||||
void setup() {
|
||||
USBHID_begin_with_serial(reportDescription, sizeof(reportDescription));
|
||||
joy.begin();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
if (joy.getFeature(1+(uint8_t*)&report)) {
|
||||
joy.setRawData(&report);
|
||||
}
|
||||
|
||||
delay(5);
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
#include <USBComposite.h>
|
||||
|
||||
const uint8_t reportDescription[] = {
|
||||
HID_MOUSE_REPORT_DESCRIPTOR(),
|
||||
HID_KEYBOARD_REPORT_DESCRIPTOR(),
|
||||
HID_JOYSTICK_REPORT_DESCRIPTOR(),
|
||||
HID_JOYSTICK_REPORT_DESCRIPTOR(HID_JOYSTICK_REPORT_ID+1),
|
||||
};
|
||||
|
||||
HIDJoystick Joystick2(HID_JOYSTICK_REPORT_ID+1);
|
||||
|
||||
void setup(){
|
||||
USBHID_begin_with_serial(reportDescription, sizeof(reportDescription));
|
||||
Joystick.setManualReportMode(true);
|
||||
Joystick2.setManualReportMode(true);
|
||||
}
|
||||
|
||||
void loop(){
|
||||
Joystick.X(0);
|
||||
Joystick.Y(0);
|
||||
Joystick.sliderRight(1023);
|
||||
Joystick.send();
|
||||
delay(400);
|
||||
Joystick.X(1023);
|
||||
Joystick.Y(1023);
|
||||
Joystick.sliderRight(0);
|
||||
Joystick.send();
|
||||
delay(400);
|
||||
Joystick2.X(0);
|
||||
Joystick2.Y(0);
|
||||
Joystick2.sliderRight(1023);
|
||||
Joystick2.send();
|
||||
delay(400);
|
||||
Joystick2.X(1023);
|
||||
Joystick2.Y(1023);
|
||||
Joystick2.sliderRight(0);
|
||||
Joystick2.send();
|
||||
delay(400);
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
#include <USBComposite.h>
|
||||
|
||||
void setup() {
|
||||
XBox360.begin();
|
||||
delay(1000);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
XBox360.X(-32767);
|
||||
XBox360.Y(-32767);
|
||||
delay(1000);
|
||||
XBox360.X(32767);
|
||||
XBox360.Y(32767);
|
||||
delay(1000);
|
||||
}
|
|
@ -0,0 +1,89 @@
|
|||
#######################################
|
||||
# Syntax Coloring Map USBHID
|
||||
#######################################
|
||||
|
||||
#######################################
|
||||
# Datatypes (KEYWORD1)
|
||||
#######################################
|
||||
|
||||
HID KEYWORD1
|
||||
Joystick KEYWORD1
|
||||
Keyboard KEYWORD1
|
||||
Mouse KEYWORD1
|
||||
CompositeSerial KEYWORD1
|
||||
XBox360 KEYWORD1
|
||||
|
||||
#######################################
|
||||
# Methods and Functions (KEYWORD2)
|
||||
#######################################
|
||||
begin KEYWORD2
|
||||
end KEYWORD2
|
||||
XRight KEYWORD2
|
||||
YRight KEYWORD2
|
||||
Yrotate KEYWORD2
|
||||
Y KEYWORD2
|
||||
Xrotate KEYWORD2
|
||||
X KEYWORD2
|
||||
sliderLeft KEYWORD2
|
||||
sliderRight KEYWORD2
|
||||
button KEYWORD2
|
||||
hat KEYWORD2
|
||||
setManualReportMode KEYWORD2
|
||||
getManualReportMode KEYWORD2
|
||||
release KEYWORD2
|
||||
press KEYWORD2
|
||||
releaseAll KEYWORD2
|
||||
move KEYWORD2
|
||||
send KEYWORD2
|
||||
click KEYWORD2
|
||||
|
||||
|
||||
#######################################
|
||||
# Constants (LITERAL1)
|
||||
#######################################
|
||||
USB_HID_MOUSE LITERAL1
|
||||
USB_HID_KEYBOARD LITERAL1
|
||||
USB_HID_JOYSTICK LITERAL1
|
||||
USB_HID_KEYBOARD_MOUSE LITERAL1
|
||||
USB_HID_KEYBOARD_MOUSE_JOYSTICK LITERAL1
|
||||
USB_HID_KEYBOARD_JOYSTICK LITERAL1
|
||||
KEY_LEFT_CTRL LITERAL1
|
||||
KEY_LEFT_SHIFT LITERAL1
|
||||
KEY_LEFT_ALT LITERAL1
|
||||
KEY_LEFT_GUI LITERAL1
|
||||
KEY_RIGHT_CTRL LITERAL1
|
||||
KEY_RIGHT_SHIFT LITERAL1
|
||||
KEY_RIGHT_ALT LITERAL1
|
||||
KEY_RIGHT_GUI LITERAL1
|
||||
KEY_UP_ARROW LITERAL1
|
||||
KEY_DOWN_ARROW LITERAL1
|
||||
KEY_LEFT_ARROW LITERAL1
|
||||
KEY_RIGHT_ARROW LITERAL1
|
||||
KEY_BACKSPACE LITERAL1
|
||||
KEY_TAB LITERAL1
|
||||
KEY_RETURN LITERAL1
|
||||
KEY_ESC LITERAL1
|
||||
KEY_INSERT LITERAL1
|
||||
KEY_DELETE LITERAL1
|
||||
KEY_PAGE_UP LITERAL1
|
||||
KEY_PAGE_DOWN LITERAL1
|
||||
KEY_HOME LITERAL1
|
||||
KEY_END LITERAL1
|
||||
KEY_CAPS_LOCK LITERAL1
|
||||
KEY_F1 LITERAL1
|
||||
KEY_F2 LITERAL1
|
||||
KEY_F3 LITERAL1
|
||||
KEY_F4 LITERAL1
|
||||
KEY_F5 LITERAL1
|
||||
KEY_F6 LITERAL1
|
||||
KEY_F7 LITERAL1
|
||||
KEY_F8 LITERAL1
|
||||
KEY_F9 LITERAL1
|
||||
KEY_F10 LITERAL1
|
||||
KEY_F11 LITERAL1
|
||||
KEY_F12 LITERAL1
|
||||
MOUSE_LEFT LITERAL1
|
||||
MOUSE_MIDDLE LITERAL1
|
||||
MOUSE_RIGHT LITERAL1
|
||||
MOUSE_ALL LITERAL1
|
||||
HIDConsumer LITERAL1
|
|
@ -0,0 +1,10 @@
|
|||
name=USBComposite for STM32F1
|
||||
version=0.64
|
||||
author=Various
|
||||
email=arpruss@gmail.com
|
||||
sentence=USB HID / MIDI / mass storage library for STM32F1
|
||||
paragraph=USB HID / MIDI / mass storage library for STM32F1
|
||||
url=https://github.com/arpruss/USBHID_stm32f1
|
||||
architectures=STM32F1
|
||||
maintainer=arpruss@gmail.com
|
||||
category=Communication
|
|
@ -0,0 +1,531 @@
|
|||
/******************************************************************************
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <libmaple/libmaple_types.h>
|
||||
#include <libmaple/usb.h>
|
||||
#include <libmaple/nvic.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>
|
||||
|
||||
|
||||
//uint16 GetEPTxAddr(uint8 /*bEpNum*/);
|
||||
|
||||
/* usb_lib headers */
|
||||
#include "usb_type.h"
|
||||
#include "usb_core.h"
|
||||
#include "usb_def.h"
|
||||
|
||||
#include "usb_generic.h"
|
||||
|
||||
// Are we currently sending an IN packet?
|
||||
volatile int8 usbGenericTransmitting = -1;
|
||||
|
||||
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);
|
||||
|
||||
#define LEAFLABS_ID_VENDOR 0x1EAF
|
||||
#define MAPLE_ID_PRODUCT 0x0004 // was 0x0024
|
||||
#define USB_DEVICE_CLASS 0x00
|
||||
#define USB_DEVICE_SUBCLASS 0x00
|
||||
#define DEVICE_PROTOCOL 0x01
|
||||
#define HID_DESCRIPTOR_TYPE 0x21
|
||||
|
||||
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 */
|
||||
/* FIXME move to Wirish */
|
||||
static const usb_descriptor_string usbHIDDescriptor_LangID = {
|
||||
.bLength = USB_DESCRIPTOR_STRING_LEN(1),
|
||||
.bDescriptorType = USB_DESCRIPTOR_TYPE_STRING,
|
||||
.bString = {0x09, 0x04},
|
||||
};
|
||||
|
||||
#define default_iManufacturer_length 8
|
||||
const usb_descriptor_string usb_generic_default_iManufacturer = {
|
||||
.bLength = USB_DESCRIPTOR_STRING_LEN(default_iManufacturer_length),
|
||||
.bDescriptorType = USB_DESCRIPTOR_TYPE_STRING,
|
||||
.bString = {'L', 0, 'e', 0, 'a', 0, 'f', 0, 'L', 0, 'a', 0, 'b', 0, 's', 0},
|
||||
};
|
||||
|
||||
#define default_iProduct_length 5
|
||||
const usb_descriptor_string usb_generic_default_iProduct = {
|
||||
.bLength = USB_DESCRIPTOR_STRING_LEN(default_iProduct_length),
|
||||
.bDescriptorType = USB_DESCRIPTOR_TYPE_STRING,
|
||||
.bString = {'M', 0, 'a', 0, 'p', 0, 'l', 0, 'e', 0},
|
||||
};
|
||||
|
||||
|
||||
#define MAX_PACKET_SIZE 0x40 /* 64B, maximum for USB FS Devices */
|
||||
static 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*)&usbHIDDescriptor_LangID, USB_DESCRIPTOR_STRING_LEN(1)},
|
||||
{(uint8*)&usb_generic_default_iManufacturer, USB_DESCRIPTOR_STRING_LEN(default_iManufacturer_length)},
|
||||
{(uint8*)&usb_generic_default_iProduct, USB_DESCRIPTOR_STRING_LEN(default_iProduct_length)},
|
||||
{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);
|
||||
|
||||
uint8 usb_generic_set_parts(USBCompositePart** _parts, unsigned _numParts) {
|
||||
parts = _parts;
|
||||
numParts = _numParts;
|
||||
unsigned numInterfaces = 0;
|
||||
unsigned numEndpoints = 1;
|
||||
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++ ) {
|
||||
parts[i]->startInterface = numInterfaces;
|
||||
numInterfaces += parts[i]->numInterfaces;
|
||||
if (numEndpoints + parts[i]->numEndpoints > 8) {
|
||||
return 0;
|
||||
}
|
||||
if (usbDescriptorSize + parts[i]->descriptorSize > MAX_USB_DESCRIPTOR_DATA_SIZE) {
|
||||
return 0;
|
||||
}
|
||||
parts[i]->startEndpoint = numEndpoints;
|
||||
USBEndpointInfo* ep = parts[i]->endpoints;
|
||||
for (unsigned j = 0 ; j < parts[i]->numEndpoints ; j++) {
|
||||
if (ep[j].bufferSize + pmaOffset > PMA_MEMORY_SIZE) {
|
||||
return 0;
|
||||
}
|
||||
ep[j].pmaAddress = pmaOffset;
|
||||
pmaOffset += ep[j].bufferSize;
|
||||
ep[j].address = numEndpoints;
|
||||
if (ep[j].callback == NULL)
|
||||
ep[j].callback = NOP_Process;
|
||||
if (ep[j].tx) {
|
||||
ep_int_in[numEndpoints - 1] = ep[j].callback;
|
||||
}
|
||||
else {
|
||||
ep_int_out[numEndpoints - 1] = ep[j].callback;
|
||||
}
|
||||
numEndpoints++;
|
||||
}
|
||||
parts[i]->getPartDescriptor(usbConfig.descriptorData + usbDescriptorSize);
|
||||
usbDescriptorSize += parts[i]->descriptorSize;
|
||||
}
|
||||
|
||||
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 = numEndpoints;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void usb_generic_set_info( uint16 idVendor, uint16 idProduct, const uint8* iManufacturer, const uint8* iProduct, const uint8* 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 = (uint8*)&usb_generic_default_iManufacturer;
|
||||
}
|
||||
|
||||
String_Descriptor[1].Descriptor = (uint8*)iManufacturer;
|
||||
String_Descriptor[1].Descriptor_Size = iManufacturer[0];
|
||||
|
||||
if (iProduct == NULL) {
|
||||
iProduct = (uint8*)&usb_generic_default_iProduct;
|
||||
}
|
||||
|
||||
String_Descriptor[2].Descriptor = (uint8*)iProduct;
|
||||
String_Descriptor[2].Descriptor_Size = iProduct[0];
|
||||
|
||||
if (iSerialNumber == NULL) {
|
||||
numStringDescriptors = 3;
|
||||
usbGenericDescriptor_Device.iSerialNumber = 0;
|
||||
}
|
||||
else {
|
||||
String_Descriptor[3].Descriptor = (uint8*)iSerialNumber;
|
||||
String_Descriptor[3].Descriptor_Size = iSerialNumber[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 Victor PV
|
||||
gpio_set_mode(GPIOA, 12, GPIO_OUTPUT_PP);
|
||||
gpio_write_bit(GPIOA, 12, 0);
|
||||
|
||||
for(volatile unsigned int i=0;i<512;i++);// Only small delay seems to be needed
|
||||
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);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
nvic_irq_enable(NVIC_USB_LP_CAN_RX0);
|
||||
|
||||
for (unsigned i = 0 ; i < numParts ; i++)
|
||||
if(parts[i]->usbInit != NULL)
|
||||
parts[i]->usbInit();
|
||||
|
||||
USBLIB->state = USB_UNCONNECTED;
|
||||
}
|
||||
|
||||
#define BTABLE_ADDRESS 0x00
|
||||
|
||||
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 = 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, e->type);
|
||||
if (parts[i]->endpoints[j].tx) {
|
||||
usb_set_ep_tx_addr(address, e->pmaAddress);
|
||||
usb_set_ep_tx_stat(address, USB_EP_STAT_TX_NAK);
|
||||
usb_set_ep_rx_stat(address, USB_EP_STAT_RX_DISABLED);
|
||||
}
|
||||
else {
|
||||
usb_set_ep_rx_addr(address, e->pmaAddress);
|
||||
usb_set_ep_rx_count(address, e->bufferSize);
|
||||
usb_set_ep_rx_stat(address, USB_EP_STAT_RX_VALID);
|
||||
}
|
||||
}
|
||||
if (parts[i]->usbReset != NULL)
|
||||
parts[i]->usbReset();
|
||||
}
|
||||
|
||||
usbGenericTransmitting = -1;
|
||||
|
||||
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). */
|
||||
nvic_irq_disable(NVIC_USB_LP_CAN_RX0);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
static RESULT usbDataSetup(uint8 request) {
|
||||
uint8* (*CopyRoutine)(uint16) = 0;
|
||||
|
||||
if(Type_Recipient == (STANDARD_REQUEST | INTERFACE_RECIPIENT) && request == GET_DESCRIPTOR &&
|
||||
pInformation->USBwValue1 == HID_DESCRIPTOR_TYPE){
|
||||
CopyRoutine = usbGetConfigDescriptor;
|
||||
}
|
||||
|
||||
if (CopyRoutine == NULL){
|
||||
for (unsigned i = 0 ; i < numParts ; i++) {
|
||||
RESULT r = parts[i]->usbDataSetup(request);
|
||||
if (USB_UNSUPPORT != r)
|
||||
return r;
|
||||
}
|
||||
return USB_UNSUPPORT;
|
||||
}
|
||||
|
||||
pInformation->Ctrl_Info.CopyData = CopyRoutine;
|
||||
pInformation->Ctrl_Info.Usb_wOffset = 0;
|
||||
(*CopyRoutine)(0);
|
||||
return USB_SUCCESS;
|
||||
}
|
||||
|
||||
static RESULT usbNoDataSetup(uint8 request) {
|
||||
for (unsigned i = 0 ; i < numParts ; i++) {
|
||||
RESULT r = parts[i]->usbNoDataSetup(request);
|
||||
if (USB_UNSUPPORT != r)
|
||||
return r;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
return Standard_GetDescriptorData(length, &String_Descriptor[wValue0]);
|
||||
}
|
||||
|
||||
|
||||
static RESULT usbGetInterfaceSetting(uint8 interface, uint8 alt_setting) {
|
||||
if (alt_setting > 0) {
|
||||
return USB_UNSUPPORT;
|
||||
} else if (interface >= usbConfig.Config_Header.bNumInterfaces) {
|
||||
return USB_UNSUPPORT;
|
||||
}
|
||||
|
||||
return USB_SUCCESS;
|
||||
}
|
||||
|
||||
void usb_copy_to_pma(const uint8 *buf, uint16 len, uint16 pma_offset) {
|
||||
uint16 *dst = (uint16*)usb_pma_ptr(pma_offset);
|
||||
uint16 n = len >> 1;
|
||||
uint16 i;
|
||||
for (i = 0; i < n; i++) {
|
||||
*dst = (uint16)(*buf) | *(buf + 1) << 8;
|
||||
buf += 2;
|
||||
dst += 2;
|
||||
}
|
||||
if (len & 1) {
|
||||
*dst = *buf;
|
||||
}
|
||||
}
|
||||
|
||||
void usb_copy_from_pma(uint8 *buf, uint16 len, uint16 pma_offset) {
|
||||
uint32 *src = (uint32*)usb_pma_ptr(pma_offset);
|
||||
uint16 *dst = (uint16*)buf;
|
||||
uint16 n = len >> 1;
|
||||
uint16 i;
|
||||
for (i = 0; i < n; i++) {
|
||||
*dst++ = *src++;
|
||||
}
|
||||
if (len & 1) {
|
||||
*dst = *src & 0xFF;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
#ifndef _USB_GENERIC_H
|
||||
#define _USB_GENERIC_H
|
||||
#include <libmaple/libmaple_types.h>
|
||||
typedef unsigned short u16;
|
||||
typedef unsigned char u8;
|
||||
#include <usb_core.h>
|
||||
#include <libmaple/usb.h>
|
||||
|
||||
#define PMA_MEMORY_SIZE 512
|
||||
#define MAX_USB_DESCRIPTOR_DATA_SIZE 200
|
||||
|
||||
#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
|
||||
|
||||
extern const usb_descriptor_string usb_generic_default_iManufacturer;
|
||||
extern const usb_descriptor_string usb_generic_default_iProduct;
|
||||
|
||||
typedef struct USBEndpointInfo {
|
||||
void (*callback)(void);
|
||||
uint16 bufferSize;
|
||||
uint16 type; // bulk, interrupt, etc.
|
||||
uint8 tx; // 1 if TX, 0 if RX
|
||||
uint8 address;
|
||||
uint16 pmaAddress;
|
||||
} USBEndpointInfo;
|
||||
|
||||
typedef struct USBCompositePart {
|
||||
uint8 numInterfaces;
|
||||
uint8 numEndpoints;
|
||||
uint8 startInterface;
|
||||
uint8 startEndpoint;
|
||||
uint16 descriptorSize;
|
||||
void (*getPartDescriptor)(uint8* out);
|
||||
void (*usbInit)(void);
|
||||
void (*usbReset)(void);
|
||||
void (*usbSetConfiguration)(void);
|
||||
void (*usbClearFeature)(void);
|
||||
RESULT (*usbDataSetup)(uint8 request);
|
||||
RESULT (*usbNoDataSetup)(uint8 request);
|
||||
USBEndpointInfo* endpoints;
|
||||
} USBCompositePart;
|
||||
|
||||
void usb_generic_set_info(uint16 idVendor, uint16 idProduct, const uint8* iManufacturer, const uint8* iProduct, const uint8* iSerialNumber);
|
||||
uint8 usb_generic_set_parts(USBCompositePart** _parts, unsigned _numParts);
|
||||
void usb_generic_disable(void);
|
||||
void usb_generic_enable(void);
|
||||
extern volatile int8 usbGenericTransmitting;
|
||||
void usb_copy_from_pma(uint8 *buf, uint16 len, uint16 pma_offset);
|
||||
void usb_copy_to_pma(const uint8 *buf, uint16 len, uint16 pma_offset);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -0,0 +1,578 @@
|
|||
/******************************************************************************
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "usb_hid.h"
|
||||
#include <string.h>
|
||||
#include <libmaple/usb.h>
|
||||
#include <libmaple/nvic.h>
|
||||
#include <libmaple/delay.h>
|
||||
|
||||
/* Private headers */
|
||||
#include "usb_lib_globals.h"
|
||||
#include "usb_reg_map.h"
|
||||
|
||||
uint16 GetEPTxAddr(uint8 /*bEpNum*/);
|
||||
|
||||
/* usb_lib headers */
|
||||
#include "usb_type.h"
|
||||
#include "usb_core.h"
|
||||
#include "usb_def.h"
|
||||
|
||||
static uint32 ProtocolValue = 0;
|
||||
|
||||
static void hidDataTxCb(void);
|
||||
static void hidUSBReset(void);
|
||||
static RESULT hidUSBDataSetup(uint8 request);
|
||||
static RESULT hidUSBNoDataSetup(uint8 request);
|
||||
//static RESULT usbGetInterfaceSetting(uint8 interface, uint8 alt_setting);
|
||||
static uint8* HID_GetReportDescriptor(uint16 Length);
|
||||
static uint8* HID_GetProtocolValue(uint16 Length);
|
||||
|
||||
static volatile HIDBuffer_t hidBuffers[MAX_HID_BUFFERS] = {{ 0 }};
|
||||
static volatile HIDBuffer_t* currentHIDBuffer = NULL;
|
||||
|
||||
//#define DUMMY_BUFFER_SIZE 0x40 // at least as big as a buffer size
|
||||
|
||||
#define HID_INTERFACE_OFFSET 0x00
|
||||
#define NUM_HID_ENDPOINTS 1
|
||||
#define HID_INTERFACE_NUMBER (HID_INTERFACE_OFFSET+usbHIDPart.startInterface)
|
||||
|
||||
/*
|
||||
* Descriptors
|
||||
*/
|
||||
|
||||
static ONE_DESCRIPTOR HID_Report_Descriptor = {
|
||||
(uint8*)NULL,
|
||||
0
|
||||
};
|
||||
|
||||
|
||||
#define HID_ENDPOINT_TX 0
|
||||
|
||||
typedef struct {
|
||||
//HID
|
||||
usb_descriptor_interface HID_Interface;
|
||||
HIDDescriptor HID_Descriptor;
|
||||
usb_descriptor_endpoint HIDDataInEndpoint;
|
||||
} __packed hid_part_config;
|
||||
|
||||
static const hid_part_config hidPartConfigData = {
|
||||
.HID_Interface = {
|
||||
.bLength = sizeof(usb_descriptor_interface),
|
||||
.bDescriptorType = USB_DESCRIPTOR_TYPE_INTERFACE,
|
||||
.bInterfaceNumber = HID_INTERFACE_OFFSET, // PATCH
|
||||
.bAlternateSetting = 0x00,
|
||||
.bNumEndpoints = NUM_HID_ENDPOINTS,
|
||||
.bInterfaceClass = USB_INTERFACE_CLASS_HID,
|
||||
.bInterfaceSubClass = USB_INTERFACE_SUBCLASS_HID,
|
||||
.bInterfaceProtocol = 0x00, /* Common AT Commands */
|
||||
.iInterface = 0x00,
|
||||
},
|
||||
.HID_Descriptor = {
|
||||
.len = 9,//sizeof(HIDDescDescriptor),
|
||||
.dtype = HID_DESCRIPTOR_TYPE,
|
||||
.versionL = 0x10,
|
||||
.versionH = 0x01,
|
||||
.country = 0x00,
|
||||
.numDesc = 0x01,
|
||||
.desctype = REPORT_DESCRIPTOR,//0x22,
|
||||
.descLenL = 0x00, //PATCH
|
||||
.descLenH = 0x00, //PATCH
|
||||
},
|
||||
.HIDDataInEndpoint = {
|
||||
.bLength = sizeof(usb_descriptor_endpoint),
|
||||
.bDescriptorType = USB_DESCRIPTOR_TYPE_ENDPOINT,
|
||||
.bEndpointAddress = USB_DESCRIPTOR_ENDPOINT_IN | HID_ENDPOINT_TX, // PATCH
|
||||
.bmAttributes = USB_ENDPOINT_TYPE_INTERRUPT,
|
||||
.wMaxPacketSize = USB_HID_TX_EPSIZE,//0x40,//big enough for a keyboard 9 byte packet and for a mouse 5 byte packet
|
||||
.bInterval = 0x0A,
|
||||
}
|
||||
};
|
||||
|
||||
static USBEndpointInfo hidEndpoints[1] = {
|
||||
{
|
||||
.callback = hidDataTxCb,
|
||||
.bufferSize = USB_HID_TX_EPSIZE,
|
||||
.type = USB_EP_EP_TYPE_INTERRUPT, // TODO: interrupt???
|
||||
.tx = 1,
|
||||
}
|
||||
};
|
||||
|
||||
#define OUT_BYTE(s,v) out[(uint8*)&(s.v)-(uint8*)&s]
|
||||
|
||||
static void getHIDPartDescriptor(uint8* out) {
|
||||
memcpy(out, &hidPartConfigData, sizeof(hid_part_config));
|
||||
// patch to reflect where the part goes in the descriptor
|
||||
OUT_BYTE(hidPartConfigData, HID_Interface.bInterfaceNumber) += usbHIDPart.startInterface;
|
||||
OUT_BYTE(hidPartConfigData, HIDDataInEndpoint.bEndpointAddress) += usbHIDPart.startEndpoint;
|
||||
OUT_BYTE(hidPartConfigData, HID_Descriptor.descLenL) = (uint8)HID_Report_Descriptor.Descriptor_Size;
|
||||
OUT_BYTE(hidPartConfigData, HID_Descriptor.descLenH) = (uint8)(HID_Report_Descriptor.Descriptor_Size>>8);
|
||||
}
|
||||
|
||||
USBCompositePart usbHIDPart = {
|
||||
.numInterfaces = 1,
|
||||
.numEndpoints = sizeof(hidEndpoints)/sizeof(*hidEndpoints),
|
||||
.descriptorSize = sizeof(hid_part_config),
|
||||
.getPartDescriptor = getHIDPartDescriptor,
|
||||
.usbInit = NULL,
|
||||
.usbReset = hidUSBReset,
|
||||
.usbDataSetup = hidUSBDataSetup,
|
||||
.usbNoDataSetup = hidUSBNoDataSetup,
|
||||
.usbClearFeature = NULL,
|
||||
.usbSetConfiguration = NULL,
|
||||
.endpoints = hidEndpoints
|
||||
};
|
||||
|
||||
|
||||
#define HID_TX_BUFFER_SIZE 256 // must be power of 2
|
||||
#define HID_TX_BUFFER_SIZE_MASK (HID_TX_BUFFER_SIZE-1)
|
||||
// Tx data
|
||||
static volatile uint8 hidBufferTx[HID_TX_BUFFER_SIZE];
|
||||
// Write index to hidBufferTx
|
||||
static volatile uint32 hid_tx_head = 0;
|
||||
// Read index from hidBufferTx
|
||||
static volatile uint32 hid_tx_tail = 0;
|
||||
|
||||
#define CDC_SERIAL_RX_BUFFER_SIZE 256 // must be power of 2
|
||||
#define CDC_SERIAL_RX_BUFFER_SIZE_MASK (CDC_SERIAL_RX_BUFFER_SIZE-1)
|
||||
|
||||
|
||||
|
||||
|
||||
void usb_hid_putc(char ch) {
|
||||
while (!usb_hid_tx((uint8*)&ch, 1))
|
||||
;
|
||||
}
|
||||
|
||||
/*
|
||||
static void hidStatusIn() {
|
||||
if (pInformation->ControlState == WAIT_STATUS_IN) {
|
||||
if (currentInFeature >= 0) {
|
||||
if (featureBuffers[currentInFeature].bufferSize == featureBuffers[currentInFeature].currentDataSize)
|
||||
featureBuffers[currentInFeature].state = HID_BUFFER_UNREAD;
|
||||
currentInFeature = -1;
|
||||
}
|
||||
if (currentOutput >= 0) {
|
||||
if (outputBuffers[currentOutput].bufferSize == outputBuffers[currentOutput].currentDataSize)
|
||||
outputBuffers[currentOutput].state = HID_BUFFER_UNREAD;
|
||||
currentOutput = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
void usb_hid_set_report_descriptor(const uint8* report_descriptor, uint16 report_descriptor_length) {
|
||||
HID_Report_Descriptor.Descriptor = (uint8*)report_descriptor;
|
||||
HID_Report_Descriptor.Descriptor_Size = report_descriptor_length;
|
||||
}
|
||||
|
||||
|
||||
static volatile HIDBuffer_t* usb_hid_find_buffer(uint8 type, uint8 reportID) {
|
||||
uint8 typeTest = type == HID_REPORT_TYPE_OUTPUT ? HID_BUFFER_MODE_OUTPUT : 0;
|
||||
for (int i=0; i<MAX_HID_BUFFERS; i++) {
|
||||
if ( hidBuffers[i].buffer != NULL &&
|
||||
( hidBuffers[i].mode & HID_BUFFER_MODE_OUTPUT ) == typeTest &&
|
||||
hidBuffers[i].reportID == reportID) {
|
||||
return hidBuffers+i;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void usb_hid_set_feature(uint8 reportID, uint8* data) {
|
||||
volatile HIDBuffer_t* buffer = usb_hid_find_buffer(HID_REPORT_TYPE_FEATURE, reportID);
|
||||
if (buffer != NULL) {
|
||||
usb_set_ep_rx_stat(USB_EP0, USB_EP_STAT_RX_NAK);
|
||||
unsigned delta = reportID != 0;
|
||||
memcpy((uint8*)buffer->buffer+delta, data, buffer->bufferSize-delta);
|
||||
if (reportID)
|
||||
buffer->buffer[0] = reportID;
|
||||
buffer->currentDataSize = buffer->bufferSize;
|
||||
buffer->state = HID_BUFFER_READ;
|
||||
usb_set_ep_rx_stat(USB_EP0, USB_EP_STAT_RX_VALID);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static uint8 have_unread_data_in_hid_buffer() {
|
||||
for (int i=0;i<MAX_HID_BUFFERS; i++) {
|
||||
if (hidBuffers[i].buffer != NULL && hidBuffers[i].state == HID_BUFFER_UNREAD)
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint16_t usb_hid_get_data(uint8 type, uint8 reportID, uint8* out, uint8 poll) {
|
||||
volatile HIDBuffer_t* buffer;
|
||||
unsigned ret = 0;
|
||||
|
||||
buffer = usb_hid_find_buffer(type, reportID);
|
||||
|
||||
if (buffer == NULL)
|
||||
return 0;
|
||||
|
||||
nvic_irq_disable(NVIC_USB_LP_CAN_RX0);
|
||||
|
||||
if (buffer->reportID == reportID && buffer->state != HID_BUFFER_EMPTY && !(poll && buffer->state == HID_BUFFER_READ)) {
|
||||
if (buffer->bufferSize != buffer->currentDataSize) {
|
||||
buffer->state = HID_BUFFER_EMPTY;
|
||||
ret = 0;
|
||||
}
|
||||
else {
|
||||
unsigned delta = reportID != 0;
|
||||
if (out != NULL)
|
||||
memcpy(out, (uint8*)buffer->buffer+delta, buffer->bufferSize-delta);
|
||||
|
||||
if (poll) {
|
||||
buffer->state = HID_BUFFER_READ;
|
||||
}
|
||||
|
||||
ret = buffer->bufferSize-delta;
|
||||
}
|
||||
}
|
||||
|
||||
if (! have_unread_data_in_hid_buffer() ) {
|
||||
usb_set_ep_rx_stat(USB_EP0, USB_EP_STAT_RX_VALID);
|
||||
}
|
||||
|
||||
nvic_irq_enable(NVIC_USB_LP_CAN_RX0);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void usb_hid_clear_buffers(uint8 type) {
|
||||
uint8 typeTest = type == HID_REPORT_TYPE_OUTPUT ? HID_BUFFER_MODE_OUTPUT : 0;
|
||||
for (int i=0; i<MAX_HID_BUFFERS; i++) {
|
||||
if (( hidBuffers[i].mode & HID_BUFFER_MODE_OUTPUT ) == typeTest) {
|
||||
hidBuffers[i].buffer = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint8 usb_hid_add_buffer(uint8 type, volatile HIDBuffer_t* buf) {
|
||||
if (type == HID_BUFFER_MODE_OUTPUT)
|
||||
buf->mode |= HID_BUFFER_MODE_OUTPUT;
|
||||
else
|
||||
buf->mode &= ~HID_BUFFER_MODE_OUTPUT;
|
||||
memset((void*)buf->buffer, 0, buf->bufferSize);
|
||||
buf->buffer[0] = buf->reportID;
|
||||
|
||||
volatile HIDBuffer_t* buffer = usb_hid_find_buffer(type, buf->reportID);
|
||||
|
||||
if (buffer != NULL) {
|
||||
*buffer = *buf;
|
||||
return 1;
|
||||
}
|
||||
else {
|
||||
for (int i=0; i<MAX_HID_BUFFERS; i++) {
|
||||
if (hidBuffers[i].buffer == NULL) {
|
||||
hidBuffers[i] = *buf;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void usb_hid_set_buffers(uint8 type, volatile HIDBuffer_t* bufs, int n) {
|
||||
uint8 typeMask = type == HID_REPORT_TYPE_OUTPUT ? HID_BUFFER_MODE_OUTPUT : 0;
|
||||
usb_hid_clear_buffers(type);
|
||||
for (int i=0; i<n; i++) {
|
||||
bufs[i].mode &= ~HID_REPORT_TYPE_OUTPUT;
|
||||
bufs[i].mode |= typeMask;
|
||||
usb_hid_add_buffer(type, bufs+i);
|
||||
}
|
||||
currentHIDBuffer = NULL;
|
||||
}
|
||||
|
||||
/* This function is non-blocking.
|
||||
*
|
||||
* It copies data from a user buffer into the USB peripheral TX
|
||||
* buffer, and returns the number of bytes copied. */
|
||||
uint32 usb_hid_tx(const uint8* buf, uint32 len)
|
||||
{
|
||||
if (len==0) return 0; // no data to send
|
||||
|
||||
uint32 head = hid_tx_head; // load volatile variable
|
||||
uint32 tx_unsent = (head - hid_tx_tail) & HID_TX_BUFFER_SIZE_MASK;
|
||||
|
||||
// We can only put bytes in the buffer if there is place
|
||||
if (len > (HID_TX_BUFFER_SIZE-tx_unsent-1) ) {
|
||||
len = (HID_TX_BUFFER_SIZE-tx_unsent-1);
|
||||
}
|
||||
if (len==0) return 0; // buffer full
|
||||
|
||||
uint16 i;
|
||||
// copy data from user buffer to USB Tx buffer
|
||||
for (i=0; i<len; i++) {
|
||||
hidBufferTx[head] = buf[i];
|
||||
head = (head+1) & HID_TX_BUFFER_SIZE_MASK;
|
||||
}
|
||||
hid_tx_head = head; // store volatile variable
|
||||
|
||||
while(usbGenericTransmitting >= 0);
|
||||
|
||||
if (usbGenericTransmitting<0) {
|
||||
hidDataTxCb(); // initiate data transmission
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
|
||||
uint16 usb_hid_get_pending(void) {
|
||||
return (hid_tx_head - hid_tx_tail) & HID_TX_BUFFER_SIZE_MASK;
|
||||
}
|
||||
|
||||
static void hidDataTxCb(void)
|
||||
{
|
||||
uint32 tail = hid_tx_tail; // load volatile variable
|
||||
uint32 tx_unsent = (hid_tx_head - tail) & HID_TX_BUFFER_SIZE_MASK;
|
||||
if (tx_unsent==0) {
|
||||
if ( (--usbGenericTransmitting)==0) goto flush_hid; // no more data to send
|
||||
return; // it was already flushed, keep Tx endpoint disabled
|
||||
}
|
||||
usbGenericTransmitting = 1;
|
||||
// We can only send up to USBHID_CDCACM_TX_EPSIZE bytes in the endpoint.
|
||||
if (tx_unsent > USB_HID_TX_EPSIZE) {
|
||||
tx_unsent = USB_HID_TX_EPSIZE;
|
||||
}
|
||||
// copy the bytes from USB Tx buffer to PMA buffer
|
||||
uint32 *dst = usb_pma_ptr(usbHIDPart.endpoints[HID_ENDPOINT_TX].pmaAddress);
|
||||
uint16 tmp = 0;
|
||||
uint16 val;
|
||||
unsigned i;
|
||||
for (i = 0; i < tx_unsent; i++) {
|
||||
val = hidBufferTx[tail];
|
||||
tail = (tail + 1) & HID_TX_BUFFER_SIZE_MASK;
|
||||
if (i&1) {
|
||||
*dst++ = tmp | (val<<8);
|
||||
} else {
|
||||
tmp = val;
|
||||
}
|
||||
}
|
||||
if ( tx_unsent&1 ) {
|
||||
*dst = tmp;
|
||||
}
|
||||
hid_tx_tail = tail; // store volatile variable
|
||||
|
||||
flush_hid:
|
||||
// enable Tx endpoint
|
||||
usb_set_ep_tx_count(usbHIDPart.endpoints[HID_ENDPOINT_TX].address, tx_unsent);
|
||||
usb_set_ep_tx_stat(usbHIDPart.endpoints[HID_ENDPOINT_TX].address, USB_EP_STAT_TX_VALID);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void hidUSBReset(void) {
|
||||
/* Reset the RX/TX state */
|
||||
hid_tx_head = 0;
|
||||
hid_tx_tail = 0;
|
||||
|
||||
currentHIDBuffer = NULL;
|
||||
}
|
||||
|
||||
static uint8* HID_Set(uint16 length) {
|
||||
if (currentHIDBuffer == NULL)
|
||||
return NULL;
|
||||
|
||||
if (length ==0) {
|
||||
if ( (0 == (currentHIDBuffer->mode & HID_BUFFER_MODE_NO_WAIT)) &&
|
||||
currentHIDBuffer->state == HID_BUFFER_UNREAD &&
|
||||
pInformation->Ctrl_Info.Usb_wOffset < pInformation->USBwLengths.w) {
|
||||
pInformation->Ctrl_Info.Usb_wLength = 0xFFFF;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uint16 len = pInformation->USBwLengths.w;
|
||||
if (len > currentHIDBuffer->bufferSize)
|
||||
len = currentHIDBuffer->bufferSize;
|
||||
|
||||
currentHIDBuffer->currentDataSize = len;
|
||||
|
||||
currentHIDBuffer->state = HID_BUFFER_EMPTY;
|
||||
|
||||
if (pInformation->Ctrl_Info.Usb_wOffset < len) {
|
||||
pInformation->Ctrl_Info.Usb_wLength = len - pInformation->Ctrl_Info.Usb_wOffset;
|
||||
}
|
||||
else {
|
||||
pInformation->Ctrl_Info.Usb_wLength = 0;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (pInformation->USBwLengths.w <= pInformation->Ctrl_Info.Usb_wOffset + pInformation->Ctrl_Info.PacketSize) {
|
||||
currentHIDBuffer->state = HID_BUFFER_UNREAD;
|
||||
}
|
||||
|
||||
return (uint8*)currentHIDBuffer->buffer + pInformation->Ctrl_Info.Usb_wOffset;
|
||||
}
|
||||
|
||||
static uint8* HID_GetFeature(uint16 length) {
|
||||
if (currentHIDBuffer == NULL)
|
||||
return NULL;
|
||||
|
||||
unsigned wOffset = pInformation->Ctrl_Info.Usb_wOffset;
|
||||
|
||||
if (length == 0)
|
||||
{
|
||||
pInformation->Ctrl_Info.Usb_wLength = currentHIDBuffer->bufferSize - wOffset;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return (uint8*)currentHIDBuffer->buffer + wOffset;
|
||||
}
|
||||
|
||||
static RESULT hidUSBDataSetup(uint8 request) {
|
||||
uint8* (*CopyRoutine)(uint16) = 0;
|
||||
|
||||
if (pInformation->USBwIndex0 != HID_INTERFACE_NUMBER)
|
||||
return USB_UNSUPPORT;
|
||||
|
||||
if (Type_Recipient == (CLASS_REQUEST | INTERFACE_RECIPIENT)) {
|
||||
switch (request) {
|
||||
case SET_REPORT:
|
||||
if (pInformation->USBwValue1 == HID_REPORT_TYPE_FEATURE) {
|
||||
volatile HIDBuffer_t* buffer = usb_hid_find_buffer(HID_REPORT_TYPE_FEATURE, pInformation->USBwValue0);
|
||||
|
||||
if (buffer == NULL) {
|
||||
return USB_UNSUPPORT;
|
||||
}
|
||||
|
||||
if (0 == (buffer->mode & HID_BUFFER_MODE_NO_WAIT) && buffer->state == HID_BUFFER_UNREAD) {
|
||||
return USB_NOT_READY;
|
||||
}
|
||||
else
|
||||
{
|
||||
currentHIDBuffer = buffer;
|
||||
CopyRoutine = HID_Set;
|
||||
}
|
||||
}
|
||||
else if (pInformation->USBwValue1 == HID_REPORT_TYPE_OUTPUT) {
|
||||
volatile HIDBuffer_t* buffer = usb_hid_find_buffer(HID_REPORT_TYPE_OUTPUT, pInformation->USBwValue0);
|
||||
|
||||
if (buffer == NULL) {
|
||||
return USB_UNSUPPORT;
|
||||
}
|
||||
|
||||
if (0 == (buffer->mode & HID_BUFFER_MODE_NO_WAIT) && buffer->state == HID_BUFFER_UNREAD) {
|
||||
return USB_NOT_READY;
|
||||
}
|
||||
else
|
||||
{
|
||||
currentHIDBuffer = buffer;
|
||||
CopyRoutine = HID_Set;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case GET_REPORT:
|
||||
if (pInformation->USBwValue1 == HID_REPORT_TYPE_FEATURE) {
|
||||
volatile HIDBuffer_t* buffer = usb_hid_find_buffer(HID_REPORT_TYPE_FEATURE, pInformation->USBwValue0);
|
||||
|
||||
if (buffer == NULL || buffer->state == HID_BUFFER_EMPTY) {
|
||||
return USB_UNSUPPORT;
|
||||
}
|
||||
|
||||
currentHIDBuffer = buffer;
|
||||
CopyRoutine = HID_GetFeature;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(Type_Recipient == (STANDARD_REQUEST | INTERFACE_RECIPIENT)){
|
||||
switch (request){
|
||||
case GET_DESCRIPTOR:
|
||||
if (pInformation->USBwValue1 == REPORT_DESCRIPTOR){
|
||||
CopyRoutine = HID_GetReportDescriptor;
|
||||
}
|
||||
break;
|
||||
case GET_PROTOCOL: // TODO: check for interface number?
|
||||
CopyRoutine = HID_GetProtocolValue;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (CopyRoutine == NULL){
|
||||
return USB_UNSUPPORT;
|
||||
}
|
||||
|
||||
pInformation->Ctrl_Info.CopyData = CopyRoutine;
|
||||
pInformation->Ctrl_Info.Usb_wOffset = 0;
|
||||
(*CopyRoutine)(0);
|
||||
return USB_SUCCESS;
|
||||
}
|
||||
|
||||
static RESULT hidUSBNoDataSetup(uint8 request) {
|
||||
if (pInformation->USBwIndex0 != HID_INTERFACE_NUMBER)
|
||||
return USB_UNSUPPORT;
|
||||
|
||||
RESULT ret = USB_UNSUPPORT;
|
||||
|
||||
if (Type_Recipient == (CLASS_REQUEST | INTERFACE_RECIPIENT)) {
|
||||
switch(request) {
|
||||
case SET_PROTOCOL:
|
||||
ProtocolValue = pInformation->USBwValue0;
|
||||
ret = USB_SUCCESS;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
static RESULT HID_SetProtocol(void){
|
||||
uint8 wValue0 = pInformation->USBwValue0;
|
||||
ProtocolValue = wValue0;
|
||||
return USB_SUCCESS;
|
||||
}
|
||||
*/
|
||||
static uint8* HID_GetProtocolValue(uint16 Length){
|
||||
if (Length == 0){
|
||||
pInformation->Ctrl_Info.Usb_wLength = 1;
|
||||
return NULL;
|
||||
} else {
|
||||
return (uint8 *)(&ProtocolValue);
|
||||
}
|
||||
}
|
||||
|
||||
static uint8* HID_GetReportDescriptor(uint16 Length){
|
||||
return Standard_GetDescriptorData(Length, &HID_Report_Descriptor);
|
||||
}
|
||||
|
|
@ -0,0 +1,153 @@
|
|||
/******************************************************************************
|
||||
* 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 1
|
||||
#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;
|
||||
uint16_t currentDataSize;
|
||||
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
|
||||
|
||||
#define USB_HID_TX_EPSIZE 0x40
|
||||
|
||||
void usb_hid_set_report_descriptor(const uint8* report_descriptor, uint16 report_descriptor_length);
|
||||
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);
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* 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_ENDPOINT_TYPE_INTERRUPT 0x03
|
||||
|
||||
#define USB_INTERFACE_CLASS_HID 0x03
|
||||
#define USB_INTERFACE_SUBCLASS_HID 0x01
|
||||
|
||||
/*
|
||||
* HID interface
|
||||
*/
|
||||
|
||||
void usb_hid_putc(char ch);
|
||||
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 */
|
||||
uint16 usb_hid_get_pending(void);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -0,0 +1,464 @@
|
|||
#include <string.h>
|
||||
|
||||
#include "usb_generic.h"
|
||||
#include "usb_mass.h"
|
||||
#include "usb_scsi.h"
|
||||
#include "usb_mass_internal.h"
|
||||
|
||||
#include <libmaple/usb.h>
|
||||
#include <libmaple/nvic.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"
|
||||
|
||||
static void usb_mass_bot_cbw_decode();
|
||||
|
||||
static void usb_mass_set_configuration();
|
||||
static void usb_mass_clear_feature();
|
||||
static RESULT usb_mass_data_setup(uint8 request);
|
||||
static RESULT usb_mass_no_data_setup(uint8 request);
|
||||
static void usb_mass_reset();
|
||||
static uint8_t* usb_mass_get_max_lun(uint16_t Length);
|
||||
static void usb_mass_in(void);
|
||||
static void usb_mass_out(void);
|
||||
uint32_t usb_mass_sil_write(uint8_t* pBufferPointer, uint32_t wBufferSize);
|
||||
uint32_t usb_mass_sil_read(uint8_t* pBufferPointer);
|
||||
|
||||
#define MASS_INTERFACE_OFFSET 0x00
|
||||
#define MASS_INTERFACE_NUMBER (MASS_INTERFACE_OFFSET+usbMassPart.startInterface)
|
||||
|
||||
|
||||
#define LUN_DATA_LENGTH 1
|
||||
|
||||
static uint32_t maxLun = 0;
|
||||
static uint32_t deviceState = DEVICE_STATE_UNCONNECTED;
|
||||
uint8_t usb_mass_botState = BOT_STATE_IDLE;
|
||||
BulkOnlyCBW usb_mass_CBW;
|
||||
BulkOnlyCSW usb_mass_CSW;
|
||||
uint8_t usb_mass_bulkDataBuff[MAX_BULK_PACKET_SIZE];
|
||||
uint16_t usb_mass_dataLength;
|
||||
static uint8_t inRequestPending;
|
||||
static uint8_t outRequestPending;
|
||||
|
||||
typedef struct mass_descriptor_config {
|
||||
// usb_descriptor_config_header Config_Header;
|
||||
usb_descriptor_interface MASS_Interface;
|
||||
usb_descriptor_endpoint DataInEndpoint;
|
||||
usb_descriptor_endpoint DataOutEndpoint;
|
||||
} __packed mass_descriptor_config;
|
||||
|
||||
|
||||
#define MAX_POWER (500 >> 1)
|
||||
const mass_descriptor_config usbMassConfigDescriptor = {
|
||||
/*.Config_Header =
|
||||
{
|
||||
.bLength = sizeof (usb_descriptor_config_header),
|
||||
.bDescriptorType = USB_DESCRIPTOR_TYPE_CONFIGURATION,
|
||||
.wTotalLength = sizeof (usb_descriptor_config),
|
||||
.bNumInterfaces = 0x01,
|
||||
.bConfigurationValue = 0x01,
|
||||
.iConfiguration = 0x00,
|
||||
.bmAttributes = (USB_CONFIG_ATTR_BUSPOWERED | USB_CONFIG_ATTR_SELF_POWERED),
|
||||
.bMaxPower = MAX_POWER,
|
||||
}, */
|
||||
|
||||
.MASS_Interface =
|
||||
{
|
||||
.bLength = sizeof (usb_descriptor_interface),
|
||||
.bDescriptorType = USB_DESCRIPTOR_TYPE_INTERFACE,
|
||||
.bInterfaceNumber = 0x00, // PATCH
|
||||
.bAlternateSetting = 0x00,
|
||||
.bNumEndpoints = 0x02,
|
||||
.bInterfaceClass = 8, // mass storage
|
||||
.bInterfaceSubClass = 6, // SCSI
|
||||
.bInterfaceProtocol = 0x50, // Bulk-Only
|
||||
.iInterface = 0,
|
||||
},
|
||||
|
||||
.DataInEndpoint =
|
||||
{
|
||||
.bLength = sizeof (usb_descriptor_endpoint),
|
||||
.bDescriptorType = USB_DESCRIPTOR_TYPE_ENDPOINT,
|
||||
.bEndpointAddress = (USB_DESCRIPTOR_ENDPOINT_IN | MASS_ENDPOINT_TX), // PATCH
|
||||
.bmAttributes = USB_EP_TYPE_BULK,
|
||||
.wMaxPacketSize = MAX_BULK_PACKET_SIZE,
|
||||
.bInterval = 0,
|
||||
},
|
||||
|
||||
.DataOutEndpoint =
|
||||
{
|
||||
.bLength = sizeof (usb_descriptor_endpoint),
|
||||
.bDescriptorType = USB_DESCRIPTOR_TYPE_ENDPOINT,
|
||||
.bEndpointAddress = (USB_DESCRIPTOR_ENDPOINT_OUT | MASS_ENDPOINT_RX), // PATCH
|
||||
.bmAttributes = USB_EP_TYPE_BULK,
|
||||
.wMaxPacketSize = MAX_BULK_PACKET_SIZE,
|
||||
.bInterval = 1,
|
||||
}
|
||||
};
|
||||
|
||||
USBEndpointInfo usbMassEndpoints[2] = {
|
||||
{
|
||||
.callback = usb_mass_in,
|
||||
.bufferSize = MAX_BULK_PACKET_SIZE,
|
||||
.type = USB_EP_EP_TYPE_BULK,
|
||||
.tx = 1,
|
||||
},
|
||||
{
|
||||
.callback = usb_mass_out,
|
||||
.bufferSize = MAX_BULK_PACKET_SIZE,
|
||||
.type = USB_EP_EP_TYPE_BULK,
|
||||
.tx = 0,
|
||||
},
|
||||
};
|
||||
|
||||
#define OUT_BYTE(s,v) out[(uint8*)&(s.v)-(uint8*)&s]
|
||||
|
||||
static void getMassPartDescriptor(uint8* out) {
|
||||
memcpy(out, &usbMassConfigDescriptor, sizeof(mass_descriptor_config));
|
||||
// patch to reflect where the part goes in the descriptor
|
||||
OUT_BYTE(usbMassConfigDescriptor, MASS_Interface.bInterfaceNumber) += usbMassPart.startInterface;
|
||||
OUT_BYTE(usbMassConfigDescriptor, DataInEndpoint.bEndpointAddress) += usbMassPart.startEndpoint;
|
||||
OUT_BYTE(usbMassConfigDescriptor, DataOutEndpoint.bEndpointAddress) += usbMassPart.startEndpoint;
|
||||
}
|
||||
|
||||
|
||||
|
||||
USBCompositePart usbMassPart = {
|
||||
.numInterfaces = 1,
|
||||
.numEndpoints = sizeof(usbMassEndpoints)/sizeof(*usbMassEndpoints),
|
||||
.descriptorSize = sizeof(mass_descriptor_config),
|
||||
.getPartDescriptor = getMassPartDescriptor,
|
||||
.usbInit = NULL,
|
||||
.usbReset = usb_mass_reset,
|
||||
.usbDataSetup = usb_mass_data_setup,
|
||||
.usbNoDataSetup = usb_mass_no_data_setup,
|
||||
.usbClearFeature = usb_mass_clear_feature,
|
||||
.usbSetConfiguration = usb_mass_set_configuration,
|
||||
.endpoints = usbMassEndpoints
|
||||
};
|
||||
|
||||
static void usb_mass_reset(void) {
|
||||
usb_mass_mal_init(0);
|
||||
|
||||
pInformation->Current_Configuration = 0; // TODO: remove?
|
||||
|
||||
/* current feature is current bmAttributes */
|
||||
pInformation->Current_Feature = (USB_CONFIG_ATTR_BUSPOWERED | USB_CONFIG_ATTR_SELF_POWERED); // usbMassConfigDescriptor.Config_Header.bmAttributes; // TODO: remove?
|
||||
|
||||
deviceState = DEVICE_STATE_ATTACHED;
|
||||
usb_mass_CBW.dSignature = BOT_CBW_SIGNATURE;
|
||||
usb_mass_botState = BOT_STATE_IDLE;
|
||||
}
|
||||
|
||||
static void usb_mass_set_configuration(void) {
|
||||
if (pInformation->Current_Configuration != 0) {
|
||||
deviceState = USB_CONFIGURED;
|
||||
ClearDTOG_TX(USB_MASS_TX_ENDP);
|
||||
ClearDTOG_RX(USB_MASS_RX_ENDP);
|
||||
usb_mass_botState = BOT_STATE_IDLE;
|
||||
}
|
||||
}
|
||||
|
||||
static void usb_mass_clear_feature(void) {
|
||||
/* when the host send a usb_mass_CBW with invalid signature or invalid length the two
|
||||
Endpoints (IN & OUT) shall stall until receiving a Mass Storage Reset */
|
||||
if (usb_mass_CBW.dSignature != BOT_CBW_SIGNATURE) {
|
||||
usb_mass_bot_abort(BOT_DIR_BOTH);
|
||||
}
|
||||
}
|
||||
|
||||
static RESULT usb_mass_data_setup(uint8 request) {
|
||||
uint8_t * (*copy_routine)(uint16_t);
|
||||
|
||||
copy_routine = NULL;
|
||||
if ((Type_Recipient == (CLASS_REQUEST | INTERFACE_RECIPIENT))
|
||||
&& (request == REQUEST_GET_MAX_LUN) && (pInformation->USBwValue == 0)
|
||||
&& (pInformation->USBwIndex == MASS_INTERFACE_NUMBER) && (pInformation->USBwLength == 0x01)) {
|
||||
copy_routine = usb_mass_get_max_lun;
|
||||
} else {
|
||||
return USB_UNSUPPORT;
|
||||
}
|
||||
|
||||
if (copy_routine == NULL) {
|
||||
return USB_UNSUPPORT;
|
||||
}
|
||||
|
||||
pInformation->Ctrl_Info.CopyData = copy_routine;
|
||||
pInformation->Ctrl_Info.Usb_wOffset = 0;
|
||||
(*copy_routine)(0);
|
||||
|
||||
return USB_SUCCESS;
|
||||
}
|
||||
|
||||
static uint8_t* usb_mass_get_max_lun(uint16_t length) {
|
||||
if (length == 0) {
|
||||
pInformation->Ctrl_Info.Usb_wLength = LUN_DATA_LENGTH;
|
||||
return 0;
|
||||
} else {
|
||||
return ((uint8_t*) (&maxLun));
|
||||
}
|
||||
}
|
||||
|
||||
static RESULT usb_mass_no_data_setup(uint8 request) {
|
||||
if ((Type_Recipient == (CLASS_REQUEST | INTERFACE_RECIPIENT))
|
||||
&& (request == REQUEST_MASS_STORAGE_RESET) && (pInformation->USBwValue == 0)
|
||||
&& (pInformation->USBwIndex == MASS_INTERFACE_NUMBER) && (pInformation->USBwLength == 0x00)) {
|
||||
|
||||
/* Initialize Endpoint 1 */
|
||||
ClearDTOG_TX(USB_MASS_TX_ENDP);
|
||||
|
||||
/* Initialize Endpoint 2 */
|
||||
ClearDTOG_RX(USB_MASS_RX_ENDP);
|
||||
|
||||
/*initialize the usb_mass_CBW signature to enable the clear feature*/
|
||||
usb_mass_CBW.dSignature = BOT_CBW_SIGNATURE;
|
||||
usb_mass_botState = BOT_STATE_IDLE;
|
||||
|
||||
return USB_SUCCESS;
|
||||
}
|
||||
return USB_UNSUPPORT;
|
||||
}
|
||||
|
||||
|
||||
void usb_mass_loop() {
|
||||
if (inRequestPending) {
|
||||
inRequestPending = 0;
|
||||
|
||||
switch (usb_mass_botState) {
|
||||
case BOT_STATE_CSW_Send:
|
||||
case BOT_STATE_ERROR:
|
||||
usb_mass_botState = BOT_STATE_IDLE;
|
||||
SetEPRxStatus(USB_MASS_RX_ENDP, USB_EP_ST_RX_VAL); /* enable the Endpoint to receive the next cmd*/
|
||||
break;
|
||||
case BOT_STATE_DATA_IN:
|
||||
switch (usb_mass_CBW.CB[0]) {
|
||||
case SCSI_READ10:
|
||||
scsi_read10_cmd(usb_mass_CBW.bLUN, SCSI_lba, SCSI_blkLen);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case BOT_STATE_DATA_IN_LAST:
|
||||
usb_mass_bot_set_csw(BOT_CSW_CMD_PASSED, BOT_SEND_CSW_ENABLE);
|
||||
SetEPRxStatus(USB_MASS_RX_ENDP, USB_EP_ST_RX_VAL);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (outRequestPending) {
|
||||
outRequestPending = 0;
|
||||
|
||||
uint8_t CMD;
|
||||
CMD = usb_mass_CBW.CB[0];
|
||||
|
||||
switch (usb_mass_botState) {
|
||||
case BOT_STATE_IDLE:
|
||||
usb_mass_bot_cbw_decode();
|
||||
break;
|
||||
case BOT_STATE_DATA_OUT:
|
||||
if (CMD == SCSI_WRITE10) {
|
||||
scsi_write10_cmd(usb_mass_CBW.bLUN, SCSI_lba, SCSI_blkLen);
|
||||
break;
|
||||
}
|
||||
usb_mass_bot_abort(BOT_DIR_OUT);
|
||||
scsi_set_sense_data(usb_mass_CBW.bLUN, SCSI_ILLEGAL_REQUEST, SCSI_INVALID_FIELED_IN_COMMAND);
|
||||
usb_mass_bot_set_csw(BOT_CSW_PHASE_ERROR, BOT_SEND_CSW_DISABLE);
|
||||
break;
|
||||
default:
|
||||
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_PHASE_ERROR, BOT_SEND_CSW_DISABLE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* IN
|
||||
*/
|
||||
static void usb_mass_in(void) {
|
||||
inRequestPending = 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* OUT
|
||||
*/
|
||||
static void usb_mass_out(void) {
|
||||
usb_mass_dataLength = usb_mass_sil_read(usb_mass_bulkDataBuff);
|
||||
outRequestPending = 1;
|
||||
}
|
||||
|
||||
static void usb_mass_bot_cbw_decode() {
|
||||
uint32_t counter;
|
||||
|
||||
for (counter = 0; counter < usb_mass_dataLength; counter++) {
|
||||
*((uint8_t *) & usb_mass_CBW + counter) = usb_mass_bulkDataBuff[counter];
|
||||
}
|
||||
usb_mass_CSW.dTag = usb_mass_CBW.dTag;
|
||||
usb_mass_CSW.dDataResidue = usb_mass_CBW.dDataLength;
|
||||
if (usb_mass_dataLength != BOT_CBW_PACKET_LENGTH) {
|
||||
usb_mass_bot_abort(BOT_DIR_BOTH);
|
||||
/* reset the usb_mass_CBW.dSignature to disable the clear feature until receiving a Mass storage reset*/
|
||||
usb_mass_CBW.dSignature = 0;
|
||||
scsi_set_sense_data(usb_mass_CBW.bLUN, SCSI_ILLEGAL_REQUEST, SCSI_PARAMETER_LIST_LENGTH_ERROR);
|
||||
usb_mass_bot_set_csw(BOT_CSW_CMD_FAILED, BOT_SEND_CSW_DISABLE);
|
||||
return;
|
||||
}
|
||||
|
||||
if ((usb_mass_CBW.CB[0] == SCSI_READ10) || (usb_mass_CBW.CB[0] == SCSI_WRITE10)) {
|
||||
/* Calculate Logical Block Address */
|
||||
SCSI_lba = (usb_mass_CBW.CB[2] << 24) | (usb_mass_CBW.CB[3] << 16) | (usb_mass_CBW.CB[4] << 8) | usb_mass_CBW.CB[5];
|
||||
/* Calculate the Number of Blocks to transfer */
|
||||
SCSI_blkLen = (usb_mass_CBW.CB[7] << 8) | usb_mass_CBW.CB[8];
|
||||
}
|
||||
|
||||
if (usb_mass_CBW.dSignature == BOT_CBW_SIGNATURE) {
|
||||
/* Valid usb_mass_CBW */
|
||||
if ((usb_mass_CBW.bLUN > maxLun) || (usb_mass_CBW.bCBLength < 1) || (usb_mass_CBW.bCBLength > 16)) {
|
||||
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);
|
||||
} else {
|
||||
switch (usb_mass_CBW.CB[0]) {
|
||||
case SCSI_REQUEST_SENSE:
|
||||
scsi_request_sense_cmd(usb_mass_CBW.bLUN);
|
||||
break;
|
||||
case SCSI_INQUIRY:
|
||||
scsi_inquiry_cmd(usb_mass_CBW.bLUN);
|
||||
break;
|
||||
case SCSI_START_STOP_UNIT:
|
||||
scsi_start_stop_unit_cmd(usb_mass_CBW.bLUN);
|
||||
break;
|
||||
case SCSI_ALLOW_MEDIUM_REMOVAL:
|
||||
scsi_start_stop_unit_cmd(usb_mass_CBW.bLUN);
|
||||
break;
|
||||
case SCSI_MODE_SENSE6:
|
||||
scsi_mode_sense6_cmd(usb_mass_CBW.bLUN);
|
||||
break;
|
||||
case SCSI_MODE_SENSE10:
|
||||
scsi_mode_sense10_cmd(usb_mass_CBW.bLUN);
|
||||
break;
|
||||
case SCSI_READ_FORMAT_CAPACITIES:
|
||||
scsi_read_format_capacity_cmd(usb_mass_CBW.bLUN);
|
||||
break;
|
||||
case SCSI_READ_CAPACITY10:
|
||||
scsi_read_capacity10_cmd(usb_mass_CBW.bLUN);
|
||||
break;
|
||||
case SCSI_TEST_UNIT_READY:
|
||||
scsi_test_unit_ready_cmd(usb_mass_CBW.bLUN);
|
||||
break;
|
||||
case SCSI_READ10:
|
||||
scsi_read10_cmd(usb_mass_CBW.bLUN, SCSI_lba, SCSI_blkLen);
|
||||
break;
|
||||
case SCSI_WRITE10:
|
||||
scsi_write10_cmd(usb_mass_CBW.bLUN, SCSI_lba, SCSI_blkLen);
|
||||
break;
|
||||
case SCSI_VERIFY10:
|
||||
scsi_verify10_cmd(usb_mass_CBW.bLUN);
|
||||
break;
|
||||
case SCSI_FORMAT_UNIT:
|
||||
scsi_format_cmd(usb_mass_CBW.bLUN);
|
||||
break;
|
||||
|
||||
case SCSI_MODE_SELECT10:
|
||||
case SCSI_MODE_SELECT6:
|
||||
case SCSI_SEND_DIAGNOSTIC:
|
||||
case SCSI_READ6:
|
||||
case SCSI_READ12:
|
||||
case SCSI_READ16:
|
||||
case SCSI_READ_CAPACITY16:
|
||||
case SCSI_WRITE6:
|
||||
case SCSI_WRITE12:
|
||||
case SCSI_VERIFY12:
|
||||
case SCSI_VERIFY16:
|
||||
case SCSI_WRITE16:
|
||||
scsi_invalid_cmd(usb_mass_CBW.bLUN);
|
||||
break;
|
||||
|
||||
default:
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* Invalid usb_mass_CBW */
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
void usb_mass_bot_abort(uint8_t direction) {
|
||||
switch (direction) {
|
||||
case BOT_DIR_IN:
|
||||
SetEPTxStatus(USB_MASS_TX_ENDP, USB_EP_ST_TX_STL);
|
||||
break;
|
||||
case BOT_DIR_OUT:
|
||||
SetEPRxStatus(USB_MASS_RX_ENDP, USB_EP_ST_RX_STL);
|
||||
break;
|
||||
case BOT_DIR_BOTH:
|
||||
SetEPTxStatus(USB_MASS_TX_ENDP, USB_EP_ST_TX_STL);
|
||||
SetEPRxStatus(USB_MASS_RX_ENDP, USB_EP_ST_RX_STL);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void usb_mass_transfer_data_request(uint8_t* dataPointer, uint16_t dataLen) {
|
||||
usb_mass_sil_write(dataPointer, dataLen);
|
||||
|
||||
SetEPTxStatus(USB_MASS_TX_ENDP, USB_EP_ST_TX_VAL);
|
||||
usb_mass_botState = BOT_STATE_DATA_IN_LAST;
|
||||
usb_mass_CSW.dDataResidue -= dataLen;
|
||||
usb_mass_CSW.bStatus = BOT_CSW_CMD_PASSED;
|
||||
}
|
||||
|
||||
void usb_mass_bot_set_csw(uint8_t status, uint8_t sendPermission) {
|
||||
usb_mass_CSW.dSignature = BOT_CSW_SIGNATURE;
|
||||
usb_mass_CSW.bStatus = status;
|
||||
|
||||
usb_mass_sil_write(((uint8_t *) & usb_mass_CSW), BOT_CSW_DATA_LENGTH);
|
||||
|
||||
usb_mass_botState = BOT_STATE_ERROR;
|
||||
if (sendPermission) {
|
||||
usb_mass_botState = BOT_STATE_CSW_Send;
|
||||
SetEPTxStatus(USB_MASS_TX_ENDP, USB_EP_ST_TX_VAL);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t usb_mass_sil_write(uint8_t* pBufferPointer, uint32_t wBufferSize) {
|
||||
/* Use the memory interface function to write to the selected endpoint */
|
||||
usb_copy_to_pma(pBufferPointer, wBufferSize, USB_MASS_TX_ADDR);
|
||||
|
||||
/* Update the data length in the control register */
|
||||
SetEPTxCount(USB_MASS_TX_ENDP, wBufferSize);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t usb_mass_sil_read(uint8_t* pBufferPointer) {
|
||||
uint32_t usb_mass_dataLength = 0;
|
||||
|
||||
/* Get the number of received data on the selected Endpoint */
|
||||
usb_mass_dataLength = GetEPRxCount(USB_MASS_RX_ENDP);
|
||||
|
||||
/* Use the memory interface function to write to the selected endpoint */
|
||||
usb_copy_from_pma(pBufferPointer, usb_mass_dataLength, USB_MASS_RX_ADDR);
|
||||
|
||||
/* Return the number of received data */
|
||||
return usb_mass_dataLength;
|
||||
}
|
|
@ -0,0 +1,112 @@
|
|||
|
||||
#ifndef _LIBMAPLE_USB_MASS_H_
|
||||
#define _LIBMAPLE_USB_MASS_H_
|
||||
|
||||
#include <libmaple/libmaple_types.h>
|
||||
#include <libmaple/gpio.h>
|
||||
#include <libmaple/usb.h>
|
||||
#include <inttypes.h>
|
||||
#include "usb_generic.h"
|
||||
#include "usb_mass_mal.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define N_STRING_DESCRIPTORS 4
|
||||
|
||||
#define MAX_PACKET_SIZE 0x40 /* 64B, maximum for USB FS Devices */
|
||||
#define MAX_BULK_PACKET_SIZE 0x40 /* 64B, max bulk Can't use 512 because the internal buffers for USB is only 512B */
|
||||
|
||||
|
||||
/* MASS Storage Requests */
|
||||
#define REQUEST_GET_MAX_LUN 0xFE
|
||||
#define REQUEST_MASS_STORAGE_RESET 0xFF
|
||||
|
||||
/* USB device state */
|
||||
typedef enum _DEVICE_STATE {
|
||||
DEVICE_STATE_UNCONNECTED,
|
||||
DEVICE_STATE_ATTACHED,
|
||||
DEVICE_STATE_POWERED,
|
||||
DEVICE_STATE_SUSPENDED,
|
||||
DEVICE_STATE_ADDRESSED,
|
||||
DEVICE_STATE_CONFIGURED
|
||||
} DEVICE_STATE;
|
||||
|
||||
#define BOT_DIR_IN 0
|
||||
#define BOT_DIR_OUT 1
|
||||
#define BOT_DIR_BOTH 2
|
||||
|
||||
/*****************************************************************************/
|
||||
/*********************** Bulk-Only Transfer State machine ********************/
|
||||
/*****************************************************************************/
|
||||
#define BOT_STATE_IDLE 0 /* Idle state */
|
||||
#define BOT_STATE_DATA_OUT 1 /* Data Out state */
|
||||
#define BOT_STATE_DATA_IN 2 /* Data In state */
|
||||
#define BOT_STATE_DATA_IN_LAST 3 /* Last Data In Last */
|
||||
#define BOT_STATE_CSW_Send 4 /* Command Status Wrapper */
|
||||
#define BOT_STATE_ERROR 5 /* error state */
|
||||
|
||||
#define BOT_CBW_SIGNATURE 0x43425355
|
||||
#define BOT_CSW_SIGNATURE 0x53425355
|
||||
#define BOT_CBW_PACKET_LENGTH 31
|
||||
|
||||
#define BOT_CSW_DATA_LENGTH 0x000D
|
||||
|
||||
/* CSW Status Definitions */
|
||||
#define BOT_CSW_CMD_PASSED 0x00
|
||||
#define BOT_CSW_CMD_FAILED 0x01
|
||||
#define BOT_CSW_PHASE_ERROR 0x02
|
||||
|
||||
#define BOT_SEND_CSW_DISABLE 0
|
||||
#define BOT_SEND_CSW_ENABLE 1
|
||||
|
||||
#define USB_EP1_IN 0x81
|
||||
|
||||
/* Bulk-only Command Block Wrapper */
|
||||
typedef struct _BulkOnlyCBW {
|
||||
uint32_t dSignature;
|
||||
uint32_t dTag;
|
||||
uint32_t dDataLength;
|
||||
uint8_t bmFlags;
|
||||
uint8_t bLUN;
|
||||
uint8_t bCBLength;
|
||||
uint8_t CB[16];
|
||||
} BulkOnlyCBW;
|
||||
|
||||
/* Bulk-only Command Status Wrapper */
|
||||
typedef struct _BulkOnlyCSW {
|
||||
uint32_t dSignature;
|
||||
uint32_t dTag;
|
||||
uint32_t dDataResidue;
|
||||
uint8_t bStatus;
|
||||
} BulkOnlyCSW;
|
||||
|
||||
typedef struct _usb_descriptor_config {
|
||||
usb_descriptor_config_header Config_Header;
|
||||
usb_descriptor_interface MASS_Interface;
|
||||
usb_descriptor_endpoint DataInEndpoint;
|
||||
usb_descriptor_endpoint DataOutEndpoint;
|
||||
} __packed usb_descriptor_config;
|
||||
|
||||
void usb_mass_enable(gpio_dev *disc_dev, uint8 disc_bit);
|
||||
void usb_mass_disable(gpio_dev *disc_dev, uint8 disc_bit);
|
||||
void usb_mass_loop();
|
||||
|
||||
void usb_mass_bot_set_csw(uint8_t cswStatus, uint8_t sendPermission);
|
||||
void usb_mass_transfer_data_request(uint8_t* dataPointer, uint16_t dataLen);
|
||||
void usb_mass_bot_abort(uint8_t direction);
|
||||
|
||||
extern USBCompositePart usbMassPart;
|
||||
|
||||
extern uint8_t usb_mass_botState;
|
||||
extern BulkOnlyCBW usb_mass_CBW;
|
||||
extern BulkOnlyCSW usb_mass_CSW;
|
||||
extern uint8_t usb_mass_bulkDataBuff[MAX_BULK_PACKET_SIZE];
|
||||
extern uint16_t usb_mass_dataLength;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -0,0 +1,12 @@
|
|||
#ifndef _USB_MASS_INTERNAL_H_
|
||||
#define _USB_MASS_INTERNAL_H_
|
||||
|
||||
extern USBEndpointInfo usbMassEndpoints[];
|
||||
|
||||
#define MASS_ENDPOINT_TX 0
|
||||
#define MASS_ENDPOINT_RX 1
|
||||
#define USB_MASS_RX_ENDP (usbMassEndpoints[MASS_ENDPOINT_RX].address)
|
||||
#define USB_MASS_TX_ENDP (usbMassEndpoints[MASS_ENDPOINT_TX].address)
|
||||
#define USB_MASS_RX_ADDR (usbMassEndpoints[MASS_ENDPOINT_RX].pmaAddress)
|
||||
#define USB_MASS_TX_ADDR (usbMassEndpoints[MASS_ENDPOINT_TX].pmaAddress)
|
||||
#endif
|
|
@ -0,0 +1,42 @@
|
|||
#include <stdlib.h>
|
||||
#include "usb_mass_mal.h"
|
||||
|
||||
|
||||
#define USB_MASS_MAL_FAIL 1
|
||||
#define USB_MASS_MAL_SUCCESS 0
|
||||
|
||||
MassStorageDriveInfo usb_mass_drives[USB_MASS_MAX_DRIVES] = { { 0 } };
|
||||
|
||||
uint16_t usb_mass_mal_init(uint8_t lun) {
|
||||
if (lun >= USB_MASS_MAX_DRIVES || (usb_mass_drives[lun].init != NULL && ! usb_mass_drives[lun].init()))
|
||||
return USB_MASS_MAL_FAIL;
|
||||
else
|
||||
return USB_MASS_MAL_SUCCESS;
|
||||
}
|
||||
|
||||
void usb_mass_mal_format(uint8_t lun) {
|
||||
if (lun < USB_MASS_MAX_DRIVES && usb_mass_drives[lun].format != NULL)
|
||||
usb_mass_drives[lun].format();
|
||||
}
|
||||
|
||||
uint16_t usb_mass_mal_get_status(uint8_t lun) {
|
||||
if (lun >= USB_MASS_MAX_DRIVES || (usb_mass_drives[lun].status != NULL && ! usb_mass_drives[lun].status()))
|
||||
return USB_MASS_MAL_FAIL;
|
||||
else
|
||||
return USB_MASS_MAL_SUCCESS;
|
||||
}
|
||||
|
||||
uint16_t usb_mass_mal_read_memory(uint8_t lun, uint32_t memoryOffset, uint8_t *readbuff, uint16_t transferLength) {
|
||||
if (lun >= USB_MASS_MAX_DRIVES || ! usb_mass_drives[lun].read(memoryOffset, readbuff, transferLength))
|
||||
return USB_MASS_MAL_FAIL;
|
||||
else
|
||||
return USB_MASS_MAL_SUCCESS;
|
||||
}
|
||||
|
||||
uint16_t usb_mass_mal_write_memory(uint8_t lun, uint32_t memoryOffset, uint8_t *writebuff, uint16_t transferLength) {
|
||||
if (lun >= USB_MASS_MAX_DRIVES || usb_mass_drives[lun].write == NULL
|
||||
|| ! usb_mass_drives[lun].write(memoryOffset, writebuff, transferLength))
|
||||
return USB_MASS_MAL_FAIL;
|
||||
else
|
||||
return USB_MASS_MAL_SUCCESS;
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
#ifndef __USB_MASS_MAL_H
|
||||
#define __USB_MASS_MAL_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define USB_MASS_MAX_DRIVES 2
|
||||
|
||||
typedef bool (*MassStorageWriter)(uint32_t memoryOffset, const uint8_t *writebuff, uint16_t transferLength);
|
||||
typedef bool (*MassStorageReader)(uint32_t memoryOffset, uint8_t *readbuff, uint16_t transferLength);
|
||||
typedef bool (*MassStorageStatuser)(void);
|
||||
typedef bool (*MassStorageInitializer)(void);
|
||||
typedef bool (*MassStorageFormatter)(void);
|
||||
|
||||
typedef struct {
|
||||
uint32_t blockCount;
|
||||
MassStorageReader read;
|
||||
MassStorageWriter write;
|
||||
MassStorageStatuser status;
|
||||
MassStorageInitializer init;
|
||||
MassStorageFormatter format;
|
||||
} MassStorageDriveInfo;
|
||||
|
||||
extern MassStorageDriveInfo usb_mass_drives[USB_MASS_MAX_DRIVES];
|
||||
uint16_t usb_mass_mal_init(uint8_t lun);
|
||||
uint16_t usb_mass_mal_get_status(uint8_t lun);
|
||||
uint16_t usb_mass_mal_read_memory(uint8_t lun, uint32_t memoryOffset, uint8_t *readbuff, uint16_t transferLength);
|
||||
uint16_t usb_mass_mal_write_memory(uint8_t lun, uint32_t memoryOffset, uint8_t *writebuff, uint16_t transferLength);
|
||||
void usb_mass_mal_format(uint8_t lun);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -0,0 +1,532 @@
|
|||
/******************************************************************************
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2011 LeafLabs LLC.
|
||||
* Copyright (c) 2013 Magnus Lundin.
|
||||
*
|
||||
* 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/nvic.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);
|
||||
static RESULT usbMIDIDataSetup(uint8 request);
|
||||
static RESULT usbMIDINoDataSetup(uint8 request);
|
||||
|
||||
#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_ADDR (midiEndpoints[MIDI_ENDPOINT_RX].pmaAddress)
|
||||
#define USB_MIDI_TX_ADDR (midiEndpoints[MIDI_ENDPOINT_TX].pmaAddress)
|
||||
|
||||
/*
|
||||
* 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 |
|
||||
MIDI_ENDPOINT_RX), // PATCH
|
||||
.bmAttributes = USB_EP_TYPE_BULK,
|
||||
.wMaxPacketSize = USB_MIDI_RX_EPSIZE,
|
||||
.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 | MIDI_ENDPOINT_TX), // PATCH
|
||||
.bmAttributes = USB_EP_TYPE_BULK,
|
||||
.wMaxPacketSize = USB_MIDI_TX_EPSIZE,
|
||||
.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[USB_MIDI_RX_EPSIZE/4];
|
||||
/* Read index into midiBufferRx */
|
||||
static volatile uint32 rx_offset = 0;
|
||||
/* Transmit data */
|
||||
static volatile uint32 midiBufferTx[USB_MIDI_TX_EPSIZE/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;
|
||||
|
||||
|
||||
// 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]
|
||||
|
||||
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, DataOutEndpoint.bEndpointAddress) += usbMIDIPart.startEndpoint;
|
||||
OUT_BYTE(usbMIDIDescriptor_Config, DataInEndpoint.bEndpointAddress) += usbMIDIPart.startEndpoint;
|
||||
}
|
||||
|
||||
static USBEndpointInfo midiEndpoints[2] = {
|
||||
{
|
||||
.callback = midiDataRxCb,
|
||||
.bufferSize = USB_MIDI_RX_EPSIZE,
|
||||
.type = USB_EP_EP_TYPE_BULK,
|
||||
.tx = 0
|
||||
},
|
||||
{
|
||||
.callback = midiDataTxCb,
|
||||
.bufferSize = USB_MIDI_TX_EPSIZE,
|
||||
.type = USB_EP_EP_TYPE_BULK,
|
||||
.tx = 1,
|
||||
}
|
||||
};
|
||||
|
||||
USBCompositePart usbMIDIPart = {
|
||||
.numInterfaces = 2,
|
||||
.numEndpoints = sizeof(midiEndpoints)/sizeof(*midiEndpoints),
|
||||
.descriptorSize = sizeof(usbMIDIDescriptor_Config),
|
||||
.getPartDescriptor = getMIDIPartDescriptor,
|
||||
.usbInit = NULL,
|
||||
.usbReset = usbMIDIReset,
|
||||
.usbDataSetup = usbMIDIDataSetup,
|
||||
.usbNoDataSetup = usbMIDINoDataSetup,
|
||||
.endpoints = midiEndpoints
|
||||
};
|
||||
|
||||
/*
|
||||
* 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_TX_EPSIZE bytes in the buffer. */
|
||||
if (bytes > USB_MIDI_TX_EPSIZE) {
|
||||
bytes = USB_MIDI_TX_EPSIZE;
|
||||
packets=bytes/4;
|
||||
}
|
||||
|
||||
/* Queue bytes for sending. */
|
||||
if (packets) {
|
||||
usb_copy_to_pma((uint8 *)buf, bytes, USB_MIDI_TX_ADDR);
|
||||
}
|
||||
// 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.)
|
||||
usb_set_ep_tx_count(USB_MIDI_TX_ENDP, bytes);
|
||||
n_unsent_packets = packets;
|
||||
transmitting = 1;
|
||||
usb_set_ep_tx_stat(USB_MIDI_TX_ENDP, USB_EP_STAT_TX_VALID);
|
||||
|
||||
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_set_ep_rx_count(USB_MIDI_RX_ENDP, USB_MIDI_RX_EPSIZE);
|
||||
usb_set_ep_rx_stat(USB_MIDI_RX_ENDP, USB_EP_STAT_RX_VALID);
|
||||
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_set_ep_rx_stat(USB_MIDI_RX_ENDP, USB_EP_STAT_RX_NAK);
|
||||
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((uint8*)midiBufferRx, n_unread_packets * 4,
|
||||
USB_MIDI_RX_ADDR);
|
||||
|
||||
// discard volatile
|
||||
LglSysexHandler((uint32*)midiBufferRx,(uint32*)&rx_offset,(uint32*)&n_unread_packets);
|
||||
|
||||
if (n_unread_packets == 0) {
|
||||
usb_set_ep_rx_count(USB_MIDI_RX_ENDP, USB_MIDI_RX_EPSIZE);
|
||||
usb_set_ep_rx_stat(USB_MIDI_RX_ENDP, USB_EP_STAT_RX_VALID);
|
||||
rx_offset = 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void usbMIDIReset(void) {
|
||||
/* Reset the RX/TX state */
|
||||
n_unread_packets = 0;
|
||||
n_unsent_packets = 0;
|
||||
rx_offset = 0;
|
||||
}
|
||||
|
||||
static RESULT usbMIDIDataSetup(uint8 request) {
|
||||
(void)request;//unused
|
||||
#if 0
|
||||
uint8* (*CopyRoutine)(uint16) = 0;
|
||||
|
||||
if (Type_Recipient == (CLASS_REQUEST | INTERFACE_RECIPIENT)) {
|
||||
}
|
||||
|
||||
if (CopyRoutine == NULL) {
|
||||
return USB_UNSUPPORT;
|
||||
}
|
||||
|
||||
pInformation->Ctrl_Info.CopyData = CopyRoutine;
|
||||
pInformation->Ctrl_Info.Usb_wOffset = 0;
|
||||
(*CopyRoutine)(0);
|
||||
return USB_SUCCESS;
|
||||
#endif
|
||||
return USB_UNSUPPORT;
|
||||
}
|
||||
|
||||
static RESULT usbMIDINoDataSetup(uint8 request) {
|
||||
(void)request;//unused
|
||||
#if 0
|
||||
RESULT ret = USB_UNSUPPORT;
|
||||
|
||||
if (Type_Recipient == (CLASS_REQUEST | INTERFACE_RECIPIENT)) {
|
||||
}
|
||||
return ret;
|
||||
#endif
|
||||
return USB_UNSUPPORT;
|
||||
}
|
||||
|
||||
// .............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;
|
||||
}
|
|
@ -0,0 +1,189 @@
|
|||
/******************************************************************************
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2011 LeafLabs LLC.
|
||||
* Copyright (c) 2013 Magnus Lundin.
|
||||
*
|
||||
* 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.
|
||||
*****************************************************************************/
|
||||
|
||||
/**
|
||||
* IMPORTANT: this API is unstable, and may change without notice.
|
||||
*/
|
||||
|
||||
#ifndef _LIBMAPLE_USB_MIDI_H_
|
||||
#define _LIBMAPLE_USB_MIDI_H_
|
||||
|
||||
#include <libmaple/libmaple_types.h>
|
||||
#include <libmaple/gpio.h>
|
||||
#include <libmaple/usb.h>
|
||||
#include <MinSysex.h>
|
||||
#include "usb_generic.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
extern USBCompositePart usbMIDIPart;
|
||||
|
||||
typedef union {
|
||||
uint8 byte[4];
|
||||
uint32 data;
|
||||
} USB_MIDI_Event_Packet;
|
||||
|
||||
/*
|
||||
* USB MIDI Requests
|
||||
*/
|
||||
|
||||
/*
|
||||
* Descriptors, etc.
|
||||
*/
|
||||
#define USB_DESCRIPTOR_TYPE_CS_INTERFACE 0x24
|
||||
#define USB_DESCRIPTOR_TYPE_CS_ENDPOINT 0x25
|
||||
|
||||
|
||||
#define USB_DEVICE_CLASS_UNDEFINED 0x00
|
||||
#define USB_DEVICE_CLASS_CDC 0x02
|
||||
#define USB_DEVICE_SUBCLASS_UNDEFINED 0x00
|
||||
|
||||
#define USB_INTERFACE_CLASS_AUDIO 0x01
|
||||
#define USB_INTERFACE_SUBCLASS_UNDEFINED 0x00
|
||||
#define USB_INTERFACE_AUDIOCONTROL 0x01
|
||||
#define USB_INTERFACE_AUDIOSTREAMING 0x02
|
||||
#define USB_INTERFACE_MIDISTREAMING 0x03
|
||||
|
||||
/* MIDI Streaming class specific interfaces */
|
||||
#define MIDI_IN_JACK 0x02
|
||||
#define MIDI_OUT_JACK 0x03
|
||||
|
||||
#define MIDI_JACK_EMBEDDED 0x01
|
||||
#define MIDI_JACK_EXTERNAL 0x02
|
||||
|
||||
|
||||
#define AC_CS_INTERFACE_DESCRIPTOR_SIZE(DataSize) (8 + DataSize)
|
||||
#define AC_CS_INTERFACE_DESCRIPTOR(DataSize) \
|
||||
struct { \
|
||||
uint8 bLength; \
|
||||
uint8 bDescriptorType; \
|
||||
uint8 SubType; \
|
||||
uint16 bcdADC; \
|
||||
uint16 wTotalLength; \
|
||||
uint8 bInCollection; \
|
||||
uint8 baInterfaceNr[DataSize]; \
|
||||
} __packed
|
||||
|
||||
typedef struct {
|
||||
uint8 bLength;
|
||||
uint8 bDescriptorType;
|
||||
uint8 SubType;
|
||||
uint16 bcdADC;
|
||||
uint16 wTotalLength;
|
||||
} __packed MS_CS_INTERFACE_DESCRIPTOR;
|
||||
|
||||
typedef struct {
|
||||
uint8 bLength;
|
||||
uint8 bDescriptorType;
|
||||
uint8 SubType;
|
||||
uint8 bJackType;
|
||||
uint8 bJackId;
|
||||
uint8 iJack;
|
||||
} __packed MIDI_IN_JACK_DESCRIPTOR;
|
||||
|
||||
#define MIDI_OUT_JACK_DESCRIPTOR_SIZE(DataSize) (7 + 2*DataSize)
|
||||
#define MIDI_OUT_JACK_DESCRIPTOR(DataSize) \
|
||||
struct { \
|
||||
uint8 bLength; \
|
||||
uint8 bDescriptorType; \
|
||||
uint8 SubType; \
|
||||
uint8 bJackType; \
|
||||
uint8 bJackId; \
|
||||
uint8 bNrInputPins; \
|
||||
uint8 baSourceId[DataSize]; \
|
||||
uint8 baSourcePin[DataSize]; \
|
||||
uint8 iJack; \
|
||||
} __packed
|
||||
|
||||
|
||||
#define MS_CS_BULK_ENDPOINT_DESCRIPTOR_SIZE(DataSize) (4 + DataSize)
|
||||
#define MS_CS_BULK_ENDPOINT_DESCRIPTOR(DataSize) \
|
||||
struct { \
|
||||
uint8 bLength; \
|
||||
uint8 bDescriptorType; \
|
||||
uint8 SubType; \
|
||||
uint8 bNumEmbMIDIJack; \
|
||||
uint8 baAssocJackID[DataSize]; \
|
||||
} __packed
|
||||
|
||||
/*
|
||||
* Endpoint configuration
|
||||
*/
|
||||
|
||||
#define USB_MIDI_TX_EPSIZE 0x40
|
||||
|
||||
#define USB_MIDI_RX_EPSIZE 0x40
|
||||
|
||||
#ifndef __cplusplus
|
||||
#define USB_MIDI_DECLARE_DEV_DESC(vid, pid) \
|
||||
{ \
|
||||
.bLength = sizeof(usb_descriptor_device), \
|
||||
.bDescriptorType = USB_DESCRIPTOR_TYPE_DEVICE, \
|
||||
.bcdUSB = 0x0110, \
|
||||
.bDeviceClass = USB_DEVICE_CLASS_UNDEFINED, \
|
||||
.bDeviceSubClass = USB_DEVICE_SUBCLASS_UNDEFINED, \
|
||||
.bDeviceProtocol = 0x00, \
|
||||
.bMaxPacketSize0 = 0x40, \
|
||||
.idVendor = vid, \
|
||||
.idProduct = pid, \
|
||||
.bcdDevice = 0x0200, \
|
||||
.iManufacturer = 0x01, \
|
||||
.iProduct = 0x02, \
|
||||
.iSerialNumber = 0x00, \
|
||||
.bNumConfigurations = 0x01, \
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Sysex Stuff.
|
||||
*/
|
||||
|
||||
#define SYSEX_BUFFER_LENGTH 256
|
||||
|
||||
|
||||
/*
|
||||
* MIDI interface
|
||||
*/
|
||||
|
||||
void usb_midi_putc(char ch);
|
||||
uint32 usb_midi_tx(const uint32* buf, uint32 len);
|
||||
uint32 usb_midi_rx(uint32* buf, uint32 len);
|
||||
uint32 usb_midi_peek(uint32* buf, uint32 len);
|
||||
|
||||
uint32 usb_midi_data_available(void); /* in RX buffer */
|
||||
uint16 usb_midi_get_pending(void);
|
||||
uint8 usb_midi_is_transmitting(void);
|
||||
|
||||
void sendThroughSysex(char *printbuffer, int bufferlength);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -0,0 +1,333 @@
|
|||
#include "usb_mass.h"
|
||||
#include "usb_mass_mal.h"
|
||||
#include "usb_mass_internal.h"
|
||||
#include "usb_scsi.h"
|
||||
|
||||
#include <libmaple/usb.h>
|
||||
#include <libmaple/nvic.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) {
|
||||
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) {
|
||||
usb_mass_bot_set_csw(BOT_CSW_CMD_PASSED, BOT_SEND_CSW_ENABLE);
|
||||
}
|
||||
|
||||
void scsi_mode_sense6_cmd(uint8_t lun) {
|
||||
usb_mass_transfer_data_request(SCSI_modeSense6Data, SCSI_MODE_SENSE6_DATA_LEN);
|
||||
}
|
||||
|
||||
void scsi_mode_sense10_cmd(uint8_t 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;
|
||||
SetEPRxStatus(USB_MASS_RX_ENDP, USB_EP_ST_RX_VAL);
|
||||
} 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) {
|
||||
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) {
|
||||
SCSI_senseData[2] = sensKey;
|
||||
SCSI_senseData[12] = asc;
|
||||
}
|
||||
|
||||
void scsi_invalid_cmd(uint8_t 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 memoryOffset, uint32_t transferLength) {
|
||||
static uint32_t offset, length;
|
||||
|
||||
if (SCSI_transferState == SCSI_TXFR_IDLE) {
|
||||
offset = memoryOffset * SCSI_BLOCK_SIZE;
|
||||
length = transferLength * SCSI_BLOCK_SIZE;
|
||||
SCSI_transferState = SCSI_TXFR_ONGOING;
|
||||
}
|
||||
|
||||
if (SCSI_transferState == SCSI_TXFR_ONGOING) {
|
||||
if (SCSI_blockReadCount == 0) {
|
||||
usb_mass_mal_read_memory(lun, offset, SCSI_dataBuffer, SCSI_BLOCK_SIZE);
|
||||
|
||||
usb_mass_sil_write(SCSI_dataBuffer, MAX_BULK_PACKET_SIZE);
|
||||
|
||||
SCSI_blockReadCount = SCSI_BLOCK_SIZE - MAX_BULK_PACKET_SIZE;
|
||||
SCSI_blockOffset = MAX_BULK_PACKET_SIZE;
|
||||
} else {
|
||||
usb_mass_sil_write(SCSI_dataBuffer + SCSI_blockOffset, MAX_BULK_PACKET_SIZE);
|
||||
|
||||
SCSI_blockReadCount -= MAX_BULK_PACKET_SIZE;
|
||||
SCSI_blockOffset += MAX_BULK_PACKET_SIZE;
|
||||
}
|
||||
|
||||
SetEPTxStatus(USB_MASS_TX_ENDP, USB_EP_ST_TX_VAL);
|
||||
|
||||
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 memoryOffset, uint32_t transferLength) {
|
||||
static uint32_t offset, length;
|
||||
uint32_t idx;
|
||||
uint32_t temp = SCSI_counter + 64;
|
||||
|
||||
if (SCSI_transferState == SCSI_TXFR_IDLE) {
|
||||
offset = memoryOffset * SCSI_BLOCK_SIZE;
|
||||
length = transferLength * 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, offset - SCSI_BLOCK_SIZE, SCSI_dataBuffer, SCSI_BLOCK_SIZE);
|
||||
}
|
||||
|
||||
usb_mass_CSW.dDataResidue -= usb_mass_dataLength;
|
||||
SetEPRxStatus(USB_MASS_RX_ENDP, USB_EP_ST_RX_VAL); /* enable the next transaction*/
|
||||
|
||||
// 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();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,96 @@
|
|||
#ifndef __USB_SCSI_H
|
||||
#define __USB_SCSI_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define SCSI_BLOCK_SIZE 512
|
||||
|
||||
/* SCSI Commands */
|
||||
#define SCSI_FORMAT_UNIT 0x04
|
||||
#define SCSI_INQUIRY 0x12
|
||||
#define SCSI_MODE_SELECT6 0x15
|
||||
#define SCSI_MODE_SELECT10 0x55
|
||||
#define SCSI_MODE_SENSE6 0x1A
|
||||
#define SCSI_MODE_SENSE10 0x5A
|
||||
#define SCSI_ALLOW_MEDIUM_REMOVAL 0x1E
|
||||
#define SCSI_READ6 0x08
|
||||
#define SCSI_READ10 0x28
|
||||
#define SCSI_READ12 0xA8
|
||||
#define SCSI_READ16 0x88
|
||||
|
||||
#define SCSI_READ_CAPACITY10 0x25
|
||||
#define SCSI_READ_CAPACITY16 0x9E
|
||||
|
||||
#define SCSI_REQUEST_SENSE 0x03
|
||||
#define SCSI_START_STOP_UNIT 0x1B
|
||||
#define SCSI_TEST_UNIT_READY 0x00
|
||||
#define SCSI_WRITE6 0x0A
|
||||
#define SCSI_WRITE10 0x2A
|
||||
#define SCSI_WRITE12 0xAA
|
||||
#define SCSI_WRITE16 0x8A
|
||||
|
||||
#define SCSI_VERIFY10 0x2F
|
||||
#define SCSI_VERIFY12 0xAF
|
||||
#define SCSI_VERIFY16 0x8F
|
||||
|
||||
#define SCSI_SEND_DIAGNOSTIC 0x1D
|
||||
#define SCSI_READ_FORMAT_CAPACITIES 0x23
|
||||
|
||||
#define SCSI_NO_SENSE 0
|
||||
#define SCSI_RECOVERED_ERROR 1
|
||||
#define SCSI_NOT_READY 2
|
||||
#define SCSI_MEDIUM_ERROR 3
|
||||
#define SCSI_HARDWARE_ERROR 4
|
||||
#define SCSI_ILLEGAL_REQUEST 5
|
||||
#define SCSI_UNIT_ATTENTION 6
|
||||
#define SCSI_DATA_PROTECT 7
|
||||
#define SCSI_BLANK_CHECK 8
|
||||
#define SCSI_VENDOR_SPECIFIC 9
|
||||
#define SCSI_COPY_ABORTED 10
|
||||
#define SCSI_ABORTED_COMMAND 11
|
||||
#define SCSI_VOLUME_OVERFLOW 13
|
||||
#define SCSI_MISCOMPARE 14
|
||||
|
||||
#define SCSI_INVALID_COMMAND 0x20
|
||||
#define SCSI_INVALID_FIELED_IN_COMMAND 0x24
|
||||
#define SCSI_PARAMETER_LIST_LENGTH_ERROR 0x1A
|
||||
#define SCSI_INVALID_FIELD_IN_PARAMETER_LIST 0x26
|
||||
#define SCSI_ADDRESS_OUT_OF_RANGE 0x21
|
||||
#define SCSI_MEDIUM_NOT_PRESENT 0x3A
|
||||
#define SCSI_MEDIUM_HAVE_CHANGED 0x28
|
||||
|
||||
#define SCSI_READ_FORMAT_CAPACITY_DATA_LEN 0x0C
|
||||
#define SCSI_READ_CAPACITY10_DATA_LEN 0x08
|
||||
#define SCSI_MODE_SENSE10_DATA_LEN 0x08
|
||||
#define SCSI_MODE_SENSE6_DATA_LEN 0x04
|
||||
#define SCSI_REQUEST_SENSE_DATA_LEN 0x12
|
||||
#define SCSI_STANDARD_INQUIRY_DATA_LEN 0x24
|
||||
#define SCSI_BLKVFY 0x04
|
||||
|
||||
extern uint32_t SCSI_lba;
|
||||
extern uint32_t SCSI_blkLen;
|
||||
|
||||
void scsi_inquiry_cmd(uint8_t lun);
|
||||
void scsi_request_sense_cmd(uint8_t lun);
|
||||
void scsi_start_stop_unit_cmd(uint8_t lun);
|
||||
void scsi_mode_sense6_cmd(uint8_t lun);
|
||||
void scsi_mode_sense10_cmd(uint8_t lun);
|
||||
void scsi_read_format_capacity_cmd(uint8_t lun);
|
||||
void scsi_read_capacity10_cmd(uint8_t lun);
|
||||
void scsi_read10_cmd(uint8_t lun, uint32_t lba, uint32_t blockNbr);
|
||||
void scsi_write10_cmd(uint8_t lun, uint32_t lba, uint32_t blockNbr);
|
||||
void scsi_test_unit_ready_cmd(uint8_t lun);
|
||||
void scsi_verify10_cmd(uint8_t lun);
|
||||
void scsi_format_cmd(uint8_t lun);
|
||||
void scsi_set_sense_data(uint8_t lun, uint8_t sensKey, uint8_t asc);
|
||||
void scsi_invalid_cmd(uint8_t lun);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -0,0 +1,120 @@
|
|||
|
||||
#include "usb_scsi.h"
|
||||
|
||||
uint8_t SCSI_page00InquiryData[] = {
|
||||
0x00, /* PERIPHERAL QUALIFIER & PERIPHERAL DEVICE TYPE*/
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00 /* Supported Pages 00*/
|
||||
};
|
||||
|
||||
uint8_t SCSI_standardInquiryData[] = {
|
||||
0x00, /* Direct Access Device */
|
||||
0x80, /* RMB = 1: Removable Medium */
|
||||
0x02, /* Version: No conformance claim to standard */
|
||||
0x02,
|
||||
|
||||
36 - 4, /* Additional Length */
|
||||
0x00, /* SCCS = 1: Storage Controller Component */
|
||||
0x00,
|
||||
0x00,
|
||||
/* Vendor Identification */
|
||||
'S', 'T', 'M', ' ', ' ', ' ', ' ', ' ',
|
||||
/* Product Identification */
|
||||
'S', 'D', ' ', 'F', 'l', 'a', 's', 'h', ' ',
|
||||
'D', 'i', 's', 'k', ' ', ' ', ' ',
|
||||
/* Product Revision Level */
|
||||
'1', '.', '0', ' '
|
||||
};
|
||||
|
||||
uint8_t SCSI_standardInquiryData2[] = {
|
||||
0x00, /* Direct Access Device */
|
||||
0x80, /* RMB = 1: Removable Medium */
|
||||
0x02, /* Version: No conformance claim to standard */
|
||||
0x02,
|
||||
|
||||
36 - 4, /* Additional Length */
|
||||
0x00, /* SCCS = 1: Storage Controller Component */
|
||||
0x00,
|
||||
0x00,
|
||||
/* Vendor Identification */
|
||||
'S', 'T', 'M', ' ', ' ', ' ', ' ', ' ',
|
||||
/* Product Identification */
|
||||
'N', 'A', 'N', 'D', ' ', 'F', 'l', 'a', 's', 'h', ' ',
|
||||
'D', 'i', 's', 'k', ' ',
|
||||
/* Product Revision Level */
|
||||
'1', '.', '0', ' '
|
||||
};
|
||||
|
||||
uint8_t SCSI_senseData[] = {
|
||||
0x70, /*RespCode*/
|
||||
0x00, /*SegmentNumber*/
|
||||
SCSI_NO_SENSE, /* Sens_Key*/
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00, /*Information*/
|
||||
0x0A, /*AdditionalSenseLength*/
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00, /*CmdInformation*/
|
||||
SCSI_NO_SENSE, /*Asc*/
|
||||
0x00, /*ASCQ*/
|
||||
0x00, /*FRUC*/
|
||||
0x00, /*TBD*/
|
||||
0x00,
|
||||
0x00 /*SenseKeySpecific*/
|
||||
};
|
||||
|
||||
uint8_t SCSI_modeSense6Data[] = {
|
||||
0x03,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
};
|
||||
|
||||
uint8_t SCSI_modeSense10Data[] = {
|
||||
0x00,
|
||||
0x06,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00
|
||||
};
|
||||
|
||||
uint8_t SCSI_readFormatCapacityData[] = {
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x08, /* Capacity List Length */
|
||||
|
||||
/* Block Count */
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
|
||||
/* Block Length */
|
||||
0x02, /* Descriptor Code: Formatted Media */
|
||||
0,
|
||||
0,
|
||||
0
|
||||
};
|
||||
|
||||
uint8_t SCSI_readFormatCapacity10Data[] = {
|
||||
/* Last Logical Block */
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
|
||||
/* Block Length */
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0
|
||||
};
|
|
@ -0,0 +1,580 @@
|
|||
/******************************************************************************
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "usb_serial.h"
|
||||
#include "usb_generic.h"
|
||||
#include <string.h>
|
||||
#include <libmaple/usb.h>
|
||||
#include <libmaple/nvic.h>
|
||||
#include <libmaple/delay.h>
|
||||
|
||||
/* Private headers */
|
||||
#include "usb_lib_globals.h"
|
||||
#include "usb_reg_map.h"
|
||||
|
||||
#define CDCACM_ENDPOINT_TX 0
|
||||
#define CDCACM_ENDPOINT_MANAGEMENT 1
|
||||
#define CDCACM_ENDPOINT_RX 2
|
||||
|
||||
uint16 GetEPTxAddr(uint8 /*bEpNum*/);
|
||||
|
||||
/* usb_lib headers */
|
||||
#include "usb_type.h"
|
||||
#include "usb_core.h"
|
||||
#include "usb_def.h"
|
||||
|
||||
static void serialUSBReset(void);
|
||||
static RESULT serialUSBDataSetup(uint8 request);
|
||||
static RESULT serialUSBNoDataSetup(uint8 request);
|
||||
static void vcomDataTxCb(void);
|
||||
static void vcomDataRxCb(void);
|
||||
|
||||
#define NUM_SERIAL_ENDPOINTS 3
|
||||
#define CCI_INTERFACE_OFFSET 0x00
|
||||
#define DCI_INTERFACE_OFFSET 0x01
|
||||
#define SERIAL_MANAGEMENT_INTERFACE_NUMBER (CCI_INTERFACE_OFFSET+usbSerialPart.startInterface)
|
||||
|
||||
/*
|
||||
* Descriptors
|
||||
*/
|
||||
|
||||
|
||||
typedef struct {
|
||||
//CDCACM
|
||||
IADescriptor IAD;
|
||||
usb_descriptor_interface CCI_Interface;
|
||||
CDC_FUNCTIONAL_DESCRIPTOR(2) CDC_Functional_IntHeader;
|
||||
CDC_FUNCTIONAL_DESCRIPTOR(2) CDC_Functional_CallManagement;
|
||||
CDC_FUNCTIONAL_DESCRIPTOR(1) CDC_Functional_ACM;
|
||||
CDC_FUNCTIONAL_DESCRIPTOR(2) CDC_Functional_Union;
|
||||
usb_descriptor_endpoint ManagementEndpoint;
|
||||
usb_descriptor_interface DCI_Interface;
|
||||
usb_descriptor_endpoint DataOutEndpoint;
|
||||
usb_descriptor_endpoint DataInEndpoint;
|
||||
} __packed serial_part_config;
|
||||
|
||||
|
||||
static const serial_part_config serialPartConfigData = {
|
||||
.IAD = {
|
||||
.bLength = 0x08,
|
||||
.bDescriptorType = 0x0B,
|
||||
.bFirstInterface = CCI_INTERFACE_OFFSET, // PATCH
|
||||
.bInterfaceCount = 0x02,
|
||||
.bFunctionClass = 0x02,
|
||||
.bFunctionSubClass = 0x02,
|
||||
.bFunctionProtocol = 0x01,
|
||||
.iFunction = 0x02,
|
||||
},
|
||||
.CCI_Interface = {
|
||||
.bLength = sizeof(usb_descriptor_interface),
|
||||
.bDescriptorType = USB_DESCRIPTOR_TYPE_INTERFACE,
|
||||
.bInterfaceNumber = CCI_INTERFACE_OFFSET, // PATCH
|
||||
.bAlternateSetting = 0x00,
|
||||
.bNumEndpoints = 0x01,
|
||||
.bInterfaceClass = USB_INTERFACE_CLASS_CDC,
|
||||
.bInterfaceSubClass = USB_INTERFACE_SUBCLASS_CDC_ACM,
|
||||
.bInterfaceProtocol = 0x01, /* Common AT Commands */
|
||||
.iInterface = 0x00,
|
||||
},
|
||||
|
||||
.CDC_Functional_IntHeader = {
|
||||
.bLength = CDC_FUNCTIONAL_DESCRIPTOR_SIZE(2),
|
||||
.bDescriptorType = 0x24,
|
||||
.SubType = 0x00,
|
||||
.Data = {0x01, 0x10},
|
||||
},
|
||||
|
||||
.CDC_Functional_CallManagement = {
|
||||
.bLength = CDC_FUNCTIONAL_DESCRIPTOR_SIZE(2),
|
||||
.bDescriptorType = 0x24,
|
||||
.SubType = 0x01,
|
||||
.Data = {0x03, DCI_INTERFACE_OFFSET}, // PATCH
|
||||
},
|
||||
|
||||
.CDC_Functional_ACM = {
|
||||
.bLength = CDC_FUNCTIONAL_DESCRIPTOR_SIZE(1),
|
||||
.bDescriptorType = 0x24,
|
||||
.SubType = 0x02,
|
||||
.Data = {0x06},
|
||||
},
|
||||
|
||||
.CDC_Functional_Union = {
|
||||
.bLength = CDC_FUNCTIONAL_DESCRIPTOR_SIZE(2),
|
||||
.bDescriptorType = 0x24,
|
||||
.SubType = 0x06,
|
||||
.Data = {CCI_INTERFACE_OFFSET, DCI_INTERFACE_OFFSET}, // PATCH, PATCH
|
||||
},
|
||||
|
||||
.ManagementEndpoint = {
|
||||
.bLength = sizeof(usb_descriptor_endpoint),
|
||||
.bDescriptorType = USB_DESCRIPTOR_TYPE_ENDPOINT,
|
||||
.bEndpointAddress = (USB_DESCRIPTOR_ENDPOINT_IN |
|
||||
CDCACM_ENDPOINT_MANAGEMENT), // PATCH
|
||||
.bmAttributes = USB_EP_TYPE_INTERRUPT,
|
||||
.wMaxPacketSize = USBHID_CDCACM_MANAGEMENT_EPSIZE,
|
||||
.bInterval = 0xFF,
|
||||
},
|
||||
|
||||
.DCI_Interface = {
|
||||
.bLength = sizeof(usb_descriptor_interface),
|
||||
.bDescriptorType = USB_DESCRIPTOR_TYPE_INTERFACE,
|
||||
.bInterfaceNumber = DCI_INTERFACE_OFFSET, // PATCH
|
||||
.bAlternateSetting = 0x00,
|
||||
.bNumEndpoints = 0x02,
|
||||
.bInterfaceClass = USB_INTERFACE_CLASS_DIC,
|
||||
.bInterfaceSubClass = 0x00, /* None */
|
||||
.bInterfaceProtocol = 0x00, /* None */
|
||||
.iInterface = 0x00,
|
||||
},
|
||||
|
||||
.DataOutEndpoint = {
|
||||
.bLength = sizeof(usb_descriptor_endpoint),
|
||||
.bDescriptorType = USB_DESCRIPTOR_TYPE_ENDPOINT,
|
||||
.bEndpointAddress = (USB_DESCRIPTOR_ENDPOINT_OUT |
|
||||
CDCACM_ENDPOINT_RX), // patch
|
||||
.bmAttributes = USB_EP_TYPE_BULK,
|
||||
.wMaxPacketSize = USBHID_CDCACM_RX_EPSIZE,
|
||||
.bInterval = 0x00,
|
||||
},
|
||||
|
||||
.DataInEndpoint = {
|
||||
.bLength = sizeof(usb_descriptor_endpoint),
|
||||
.bDescriptorType = USB_DESCRIPTOR_TYPE_ENDPOINT,
|
||||
.bEndpointAddress = (USB_DESCRIPTOR_ENDPOINT_IN | CDCACM_ENDPOINT_TX), // PATCH
|
||||
.bmAttributes = USB_EP_TYPE_BULK,
|
||||
.wMaxPacketSize = USBHID_CDCACM_TX_EPSIZE,
|
||||
.bInterval = 0x00,
|
||||
}
|
||||
};
|
||||
|
||||
#define OUT_BYTE(s,v) out[(uint8*)&(s.v)-(uint8*)&s]
|
||||
|
||||
static USBEndpointInfo serialEndpoints[3] = {
|
||||
{
|
||||
.callback = vcomDataTxCb,
|
||||
.bufferSize = USBHID_CDCACM_TX_EPSIZE,
|
||||
.type = USB_EP_EP_TYPE_BULK,
|
||||
.tx = 1,
|
||||
},
|
||||
{
|
||||
.callback = NULL,
|
||||
.bufferSize = USBHID_CDCACM_MANAGEMENT_EPSIZE,
|
||||
.type = USB_EP_EP_TYPE_INTERRUPT,
|
||||
.tx = 1,
|
||||
},
|
||||
{
|
||||
.callback = vcomDataRxCb,
|
||||
.bufferSize = USBHID_CDCACM_RX_EPSIZE,
|
||||
.type = USB_EP_EP_TYPE_BULK,
|
||||
.tx = 0,
|
||||
},
|
||||
};
|
||||
|
||||
static void getSerialPartDescriptor(uint8* out) {
|
||||
memcpy(out, &serialPartConfigData, sizeof(serial_part_config));
|
||||
|
||||
// patch to reflect where the part goes in the descriptor
|
||||
OUT_BYTE(serialPartConfigData, ManagementEndpoint.bEndpointAddress) += usbSerialPart.startEndpoint;
|
||||
OUT_BYTE(serialPartConfigData, DataOutEndpoint.bEndpointAddress) += usbSerialPart.startEndpoint;
|
||||
OUT_BYTE(serialPartConfigData, DataInEndpoint.bEndpointAddress) += usbSerialPart.startEndpoint;
|
||||
|
||||
OUT_BYTE(serialPartConfigData, IAD.bFirstInterface) += usbSerialPart.startInterface;
|
||||
OUT_BYTE(serialPartConfigData, CCI_Interface.bInterfaceNumber) += usbSerialPart.startInterface;
|
||||
OUT_BYTE(serialPartConfigData, DCI_Interface.bInterfaceNumber) += usbSerialPart.startInterface;
|
||||
OUT_BYTE(serialPartConfigData, CDC_Functional_CallManagement.Data[1]) += usbSerialPart.startInterface;
|
||||
OUT_BYTE(serialPartConfigData, CDC_Functional_Union.Data[0]) += usbSerialPart.startInterface;
|
||||
OUT_BYTE(serialPartConfigData, CDC_Functional_Union.Data[1]) += usbSerialPart.startInterface;
|
||||
}
|
||||
|
||||
USBCompositePart usbSerialPart = {
|
||||
.numInterfaces = 2,
|
||||
.numEndpoints = sizeof(serialEndpoints)/sizeof(*serialEndpoints),
|
||||
.descriptorSize = sizeof(serial_part_config),
|
||||
.getPartDescriptor = getSerialPartDescriptor,
|
||||
.usbInit = NULL,
|
||||
.usbReset = serialUSBReset,
|
||||
.usbDataSetup = serialUSBDataSetup,
|
||||
.usbNoDataSetup = serialUSBNoDataSetup,
|
||||
.endpoints = serialEndpoints
|
||||
};
|
||||
|
||||
#define CDC_SERIAL_RX_BUFFER_SIZE 256 // must be power of 2
|
||||
#define CDC_SERIAL_RX_BUFFER_SIZE_MASK (CDC_SERIAL_RX_BUFFER_SIZE-1)
|
||||
|
||||
/* Received data */
|
||||
static volatile uint8 vcomBufferRx[CDC_SERIAL_RX_BUFFER_SIZE];
|
||||
/* Write index to vcomBufferRx */
|
||||
static volatile uint32 vcom_rx_head;
|
||||
/* Read index from vcomBufferRx */
|
||||
static volatile uint32 vcom_rx_tail;
|
||||
|
||||
#define CDC_SERIAL_TX_BUFFER_SIZE 256 // must be power of 2
|
||||
#define CDC_SERIAL_TX_BUFFER_SIZE_MASK (CDC_SERIAL_TX_BUFFER_SIZE-1)
|
||||
// Tx data
|
||||
static volatile uint8 vcomBufferTx[CDC_SERIAL_TX_BUFFER_SIZE];
|
||||
// Write index to vcomBufferTx
|
||||
static volatile uint32 vcom_tx_head;
|
||||
// Read index from vcomBufferTx
|
||||
static volatile uint32 vcom_tx_tail;
|
||||
|
||||
|
||||
|
||||
/* Other state (line coding, DTR/RTS) */
|
||||
|
||||
static volatile composite_cdcacm_line_coding line_coding = {
|
||||
/* This default is 115200 baud, 8N1. */
|
||||
.dwDTERate = 115200,
|
||||
.bCharFormat = USBHID_CDCACM_STOP_BITS_1,
|
||||
.bParityType = USBHID_CDCACM_PARITY_NONE,
|
||||
.bDataBits = 8,
|
||||
};
|
||||
|
||||
/* DTR in bit 0, RTS in bit 1. */
|
||||
static volatile uint8 line_dtr_rts = 0;
|
||||
|
||||
|
||||
static void (*rx_hook)(unsigned, void*) = 0;
|
||||
static void (*iface_setup_hook)(unsigned, void*) = 0;
|
||||
|
||||
void composite_cdcacm_set_hooks(unsigned hook_flags, void (*hook)(unsigned, void*)) {
|
||||
if (hook_flags & USBHID_CDCACM_HOOK_RX) {
|
||||
rx_hook = hook;
|
||||
}
|
||||
if (hook_flags & USBHID_CDCACM_HOOK_IFACE_SETUP) {
|
||||
iface_setup_hook = hook;
|
||||
}
|
||||
}
|
||||
|
||||
void composite_cdcacm_putc(char ch) {
|
||||
while (!composite_cdcacm_tx((uint8*)&ch, 1))
|
||||
;
|
||||
}
|
||||
|
||||
/* This function is non-blocking.
|
||||
*
|
||||
* It copies data from a user buffer into the USB peripheral TX
|
||||
* buffer, and returns the number of bytes copied. */
|
||||
uint32 composite_cdcacm_tx(const uint8* buf, uint32 len)
|
||||
{
|
||||
if (len==0) return 0; // no data to send
|
||||
|
||||
uint32 head = vcom_tx_head; // load volatile variable
|
||||
uint32 tx_unsent = (head - vcom_tx_tail) & CDC_SERIAL_TX_BUFFER_SIZE_MASK;
|
||||
|
||||
// We can only put bytes in the buffer if there is place
|
||||
if (len > (CDC_SERIAL_TX_BUFFER_SIZE-tx_unsent-1) ) {
|
||||
len = (CDC_SERIAL_TX_BUFFER_SIZE-tx_unsent-1);
|
||||
}
|
||||
if (len==0) return 0; // buffer full
|
||||
|
||||
uint16 i;
|
||||
// copy data from user buffer to USB Tx buffer
|
||||
for (i=0; i<len; i++) {
|
||||
vcomBufferTx[head] = buf[i];
|
||||
head = (head+1) & CDC_SERIAL_TX_BUFFER_SIZE_MASK;
|
||||
}
|
||||
vcom_tx_head = head; // store volatile variable
|
||||
|
||||
while(usbGenericTransmitting >= 0);
|
||||
|
||||
if (usbGenericTransmitting<0) {
|
||||
vcomDataTxCb(); // initiate data transmission
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
|
||||
uint32 composite_cdcacm_data_available(void) {
|
||||
return (vcom_rx_head - vcom_rx_tail) & CDC_SERIAL_RX_BUFFER_SIZE_MASK;
|
||||
}
|
||||
|
||||
uint16 composite_cdcacm_get_pending(void) {
|
||||
return (vcom_tx_head - vcom_tx_tail) & CDC_SERIAL_TX_BUFFER_SIZE_MASK;
|
||||
}
|
||||
|
||||
/* Non-blocking byte receive.
|
||||
*
|
||||
* Copies up to len bytes from our private data buffer (*NOT* the PMA)
|
||||
* into buf and deq's the FIFO. */
|
||||
uint32 composite_cdcacm_rx(uint8* buf, uint32 len)
|
||||
{
|
||||
/* Copy bytes to buffer. */
|
||||
uint32 n_copied = composite_cdcacm_peek(buf, len);
|
||||
|
||||
/* Mark bytes as read. */
|
||||
uint16 tail = vcom_rx_tail; // load volatile variable
|
||||
tail = (tail + n_copied) & CDC_SERIAL_RX_BUFFER_SIZE_MASK;
|
||||
vcom_rx_tail = tail; // store volatile variable
|
||||
|
||||
uint32 rx_unread = (vcom_rx_head - tail) & CDC_SERIAL_RX_BUFFER_SIZE_MASK;
|
||||
// If buffer was emptied to a pre-set value, re-enable the RX endpoint
|
||||
if ( rx_unread <= 64 ) { // experimental value, gives the best performance
|
||||
usb_set_ep_rx_stat(usbSerialPart.endpoints[CDCACM_ENDPOINT_RX].address, USB_EP_STAT_RX_VALID);
|
||||
}
|
||||
return n_copied;
|
||||
}
|
||||
|
||||
/* Non-blocking byte lookahead.
|
||||
*
|
||||
* Looks at unread bytes without marking them as read. */
|
||||
uint32 composite_cdcacm_peek(uint8* buf, uint32 len)
|
||||
{
|
||||
unsigned i;
|
||||
uint32 tail = vcom_rx_tail;
|
||||
uint32 rx_unread = (vcom_rx_head-tail) & CDC_SERIAL_RX_BUFFER_SIZE_MASK;
|
||||
|
||||
if (len > rx_unread) {
|
||||
len = rx_unread;
|
||||
}
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
buf[i] = vcomBufferRx[tail];
|
||||
tail = (tail + 1) & CDC_SERIAL_RX_BUFFER_SIZE_MASK;
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
uint32 composite_cdcacm_peek_ex(uint8* buf, uint32 offset, uint32 len)
|
||||
{
|
||||
unsigned i;
|
||||
uint32 tail = (vcom_rx_tail + offset) & CDC_SERIAL_RX_BUFFER_SIZE_MASK ;
|
||||
uint32 rx_unread = (vcom_rx_head-tail) & CDC_SERIAL_RX_BUFFER_SIZE_MASK;
|
||||
|
||||
if (len + offset > rx_unread) {
|
||||
len = rx_unread - offset;
|
||||
}
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
buf[i] = vcomBufferRx[tail];
|
||||
tail = (tail + 1) & CDC_SERIAL_RX_BUFFER_SIZE_MASK;
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
/* Roger Clark. Added. for Arduino 1.0 API support of Serial.peek() */
|
||||
int composite_cdcacm_peek_char()
|
||||
{
|
||||
if (composite_cdcacm_data_available() == 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
return vcomBufferRx[vcom_rx_tail];
|
||||
}
|
||||
|
||||
uint8 composite_cdcacm_get_dtr() {
|
||||
return ((line_dtr_rts & USBHID_CDCACM_CONTROL_LINE_DTR) != 0);
|
||||
}
|
||||
|
||||
uint8 composite_cdcacm_get_rts() {
|
||||
return ((line_dtr_rts & USBHID_CDCACM_CONTROL_LINE_RTS) != 0);
|
||||
}
|
||||
|
||||
void composite_cdcacm_get_line_coding(composite_cdcacm_line_coding *ret) {
|
||||
ret->dwDTERate = line_coding.dwDTERate;
|
||||
ret->bCharFormat = line_coding.bCharFormat;
|
||||
ret->bParityType = line_coding.bParityType;
|
||||
ret->bDataBits = line_coding.bDataBits;
|
||||
}
|
||||
|
||||
int composite_cdcacm_get_baud(void) {
|
||||
return line_coding.dwDTERate;
|
||||
}
|
||||
|
||||
int composite_cdcacm_get_stop_bits(void) {
|
||||
return line_coding.bCharFormat;
|
||||
}
|
||||
|
||||
int composite_cdcacm_get_parity(void) {
|
||||
return line_coding.bParityType;
|
||||
}
|
||||
|
||||
int composite_cdcacm_get_n_data_bits(void) {
|
||||
return line_coding.bDataBits;
|
||||
}
|
||||
|
||||
/*
|
||||
* Callbacks
|
||||
*/
|
||||
static void vcomDataTxCb(void)
|
||||
{
|
||||
uint32 tail = vcom_tx_tail; // load volatile variable
|
||||
uint32 tx_unsent = (vcom_tx_head - tail) & CDC_SERIAL_TX_BUFFER_SIZE_MASK;
|
||||
if (tx_unsent==0) {
|
||||
if ( (--usbGenericTransmitting)==0) goto flush_vcom; // no more data to send
|
||||
return; // it was already flushed, keep Tx endpoint disabled
|
||||
}
|
||||
usbGenericTransmitting = 1;
|
||||
// We can only send up to USBHID_CDCACM_TX_EPSIZE bytes in the endpoint.
|
||||
if (tx_unsent > USBHID_CDCACM_TX_EPSIZE) {
|
||||
tx_unsent = USBHID_CDCACM_TX_EPSIZE;
|
||||
}
|
||||
// copy the bytes from USB Tx buffer to PMA buffer
|
||||
uint32 *dst = usb_pma_ptr(usbSerialPart.endpoints[CDCACM_ENDPOINT_TX].pmaAddress);
|
||||
uint16 tmp = 0;
|
||||
uint16 val;
|
||||
unsigned i;
|
||||
for (i = 0; i < tx_unsent; i++) {
|
||||
val = vcomBufferTx[tail];
|
||||
tail = (tail + 1) & CDC_SERIAL_TX_BUFFER_SIZE_MASK;
|
||||
if (i&1) {
|
||||
*dst++ = tmp | (val<<8);
|
||||
} else {
|
||||
tmp = val;
|
||||
}
|
||||
}
|
||||
if ( tx_unsent&1 ) {
|
||||
*dst = tmp;
|
||||
}
|
||||
vcom_tx_tail = tail; // store volatile variable
|
||||
flush_vcom:
|
||||
// enable Tx endpoint
|
||||
usb_set_ep_tx_count(usbSerialPart.endpoints[CDCACM_ENDPOINT_TX].address, tx_unsent);
|
||||
usb_set_ep_tx_stat(usbSerialPart.endpoints[CDCACM_ENDPOINT_TX].address, USB_EP_STAT_TX_VALID);
|
||||
}
|
||||
|
||||
|
||||
static void vcomDataRxCb(void)
|
||||
{
|
||||
uint32 head = vcom_rx_head; // load volatile variable
|
||||
|
||||
uint32 ep_rx_size = usb_get_ep_rx_count(usbSerialPart.endpoints[CDCACM_ENDPOINT_RX].address);
|
||||
// This copy won't overwrite unread bytes as long as there is
|
||||
// enough room in the USB Rx buffer for next packet
|
||||
uint32 *src = usb_pma_ptr(usbSerialPart.endpoints[CDCACM_ENDPOINT_RX].pmaAddress);
|
||||
uint16 tmp = 0;
|
||||
uint8 val;
|
||||
uint32 i;
|
||||
for (i = 0; i < ep_rx_size; i++) {
|
||||
if (i&1) {
|
||||
val = tmp>>8;
|
||||
} else {
|
||||
tmp = *src++;
|
||||
val = tmp&0xFF;
|
||||
}
|
||||
vcomBufferRx[head] = val;
|
||||
head = (head + 1) & CDC_SERIAL_RX_BUFFER_SIZE_MASK;
|
||||
}
|
||||
vcom_rx_head = head; // store volatile variable
|
||||
|
||||
uint32 rx_unread = (head - vcom_rx_tail) & CDC_SERIAL_RX_BUFFER_SIZE_MASK;
|
||||
// only enable further Rx if there is enough room to receive one more packet
|
||||
if ( rx_unread < (CDC_SERIAL_RX_BUFFER_SIZE-USBHID_CDCACM_RX_EPSIZE) ) {
|
||||
usb_set_ep_rx_stat(usbSerialPart.endpoints[CDCACM_ENDPOINT_RX].address, USB_EP_STAT_RX_VALID);
|
||||
}
|
||||
|
||||
if (rx_hook) {
|
||||
rx_hook(USBHID_CDCACM_HOOK_RX, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static uint8* vcomGetSetLineCoding(uint16 length) {
|
||||
if (length == 0) {
|
||||
pInformation->Ctrl_Info.Usb_wLength = sizeof(struct composite_cdcacm_line_coding);
|
||||
}
|
||||
return (uint8*)&line_coding;
|
||||
}
|
||||
|
||||
static void serialUSBReset(void) {
|
||||
//VCOM
|
||||
vcom_rx_head = 0;
|
||||
vcom_rx_tail = 0;
|
||||
vcom_tx_head = 0;
|
||||
vcom_tx_tail = 0;
|
||||
}
|
||||
|
||||
static RESULT serialUSBDataSetup(uint8 request) {
|
||||
uint8* (*CopyRoutine)(uint16) = 0;
|
||||
|
||||
if (Type_Recipient == (CLASS_REQUEST | INTERFACE_RECIPIENT) &&
|
||||
pInformation->USBwIndex0 == SERIAL_MANAGEMENT_INTERFACE_NUMBER) {
|
||||
switch (request) {
|
||||
case USBHID_CDCACM_GET_LINE_CODING:
|
||||
CopyRoutine = vcomGetSetLineCoding;
|
||||
break;
|
||||
case USBHID_CDCACM_SET_LINE_CODING:
|
||||
CopyRoutine = vcomGetSetLineCoding;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
/* Call the user hook. */
|
||||
if (iface_setup_hook) {
|
||||
uint8 req_copy = request;
|
||||
iface_setup_hook(USBHID_CDCACM_HOOK_IFACE_SETUP, &req_copy);
|
||||
}
|
||||
}
|
||||
|
||||
if (CopyRoutine == NULL){
|
||||
return USB_UNSUPPORT;
|
||||
}
|
||||
|
||||
pInformation->Ctrl_Info.CopyData = CopyRoutine;
|
||||
pInformation->Ctrl_Info.Usb_wOffset = 0;
|
||||
(*CopyRoutine)(0);
|
||||
return USB_SUCCESS;
|
||||
}
|
||||
|
||||
static RESULT serialUSBNoDataSetup(uint8 request) {
|
||||
RESULT ret = USB_UNSUPPORT;
|
||||
|
||||
if (Type_Recipient == (CLASS_REQUEST | INTERFACE_RECIPIENT) &&
|
||||
pInformation->USBwIndex0 == SERIAL_MANAGEMENT_INTERFACE_NUMBER) {
|
||||
switch(request) {
|
||||
case USBHID_CDCACM_SET_COMM_FEATURE:
|
||||
/* We support set comm. feature, but don't handle it. */
|
||||
ret = USB_SUCCESS;
|
||||
break;
|
||||
case USBHID_CDCACM_SET_CONTROL_LINE_STATE:
|
||||
/* Track changes to DTR and RTS. */
|
||||
line_dtr_rts = (pInformation->USBwValues.bw.bb0 &
|
||||
(USBHID_CDCACM_CONTROL_LINE_DTR |
|
||||
USBHID_CDCACM_CONTROL_LINE_RTS));
|
||||
ret = USB_SUCCESS;
|
||||
break;
|
||||
}
|
||||
/* Call the user hook. */
|
||||
if (iface_setup_hook) {
|
||||
uint8 req_copy = request;
|
||||
iface_setup_hook(USBHID_CDCACM_HOOK_IFACE_SETUP, &req_copy);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -0,0 +1,157 @@
|
|||
/******************************************************************************
|
||||
* 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_SERIAL_H_
|
||||
#define _USB_SERIAL_H_
|
||||
|
||||
#include <libmaple/libmaple_types.h>
|
||||
#include <libmaple/usb.h>
|
||||
#include "usb_generic.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
extern USBCompositePart usbSerialPart;
|
||||
|
||||
/*
|
||||
* CDC ACM Requests
|
||||
*/
|
||||
|
||||
#define USBHID_CDCACM_SET_LINE_CODING 0x20
|
||||
#define USBHID_CDCACM_GET_LINE_CODING 0x21
|
||||
#define USBHID_CDCACM_SET_COMM_FEATURE 0x02
|
||||
#define USBHID_CDCACM_SET_CONTROL_LINE_STATE 0x22
|
||||
#define USBHID_CDCACM_CONTROL_LINE_DTR (0x01)
|
||||
#define USBHID_CDCACM_CONTROL_LINE_RTS (0x02)
|
||||
|
||||
#define USBHID_CDCACM_MANAGEMENT_EPSIZE 0x10
|
||||
#define USBHID_CDCACM_RX_EPSIZE 0x40
|
||||
#define USBHID_CDCACM_TX_EPSIZE 0x40
|
||||
/*
|
||||
* Descriptors, etc.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#define USBHID_CDCACM_CTRL_ENDP 0
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t bLength;
|
||||
uint8_t bDescriptorType;
|
||||
uint8_t bFirstInterface;
|
||||
uint8_t bInterfaceCount;
|
||||
uint8_t bFunctionClass;
|
||||
uint8_t bFunctionSubClass;
|
||||
uint8_t bFunctionProtocol;
|
||||
uint8_t iFunction;
|
||||
} IADescriptor;
|
||||
|
||||
#define CDC_FUNCTIONAL_DESCRIPTOR_SIZE(DataSize) (3 + DataSize)
|
||||
#define CDC_FUNCTIONAL_DESCRIPTOR(DataSize) \
|
||||
struct { \
|
||||
uint8 bLength; \
|
||||
uint8 bDescriptorType; \
|
||||
uint8 SubType; \
|
||||
uint8 Data[DataSize]; \
|
||||
} __packed
|
||||
|
||||
#define USB_DEVICE_CLASS_CDC 0x02
|
||||
#define USB_DEVICE_SUBCLASS_CDC 0x00
|
||||
#define USB_INTERFACE_CLASS_CDC 0x02
|
||||
#define USB_INTERFACE_SUBCLASS_CDC_ACM 0x02
|
||||
#define USB_INTERFACE_CLASS_DIC 0x0A
|
||||
|
||||
/*
|
||||
* CDC ACM interface
|
||||
*/
|
||||
|
||||
void composite_cdcacm_putc(char ch);
|
||||
uint32 composite_cdcacm_tx(const uint8* buf, uint32 len);
|
||||
uint32 composite_cdcacm_rx(uint8* buf, uint32 len);
|
||||
uint32 composite_cdcacm_peek(uint8* buf, uint32 len);
|
||||
uint32 composite_cdcacm_peek_ex(uint8* buf, uint32 offset, uint32 len);
|
||||
|
||||
uint32 composite_cdcacm_data_available(void); /* in RX buffer */
|
||||
uint16 composite_cdcacm_get_pending(void);
|
||||
uint8 usb_is_transmitting(void);
|
||||
|
||||
uint8 composite_cdcacm_get_dtr(void);
|
||||
uint8 composite_cdcacm_get_rts(void);
|
||||
|
||||
typedef struct composite_cdcacm_line_coding {
|
||||
uint32 dwDTERate; /* Baud rate */
|
||||
|
||||
#define USBHID_CDCACM_STOP_BITS_1 0
|
||||
#define USBHID_CDCACM_STOP_BITS_1_5 1
|
||||
#define USBHID_CDCACM_STOP_BITS_2 2
|
||||
uint8 bCharFormat; /* Stop bits */
|
||||
|
||||
#define USBHID_CDCACM_PARITY_NONE 0
|
||||
#define USBHID_CDCACM_PARITY_ODD 1
|
||||
#define USBHID_CDCACM_PARITY_EVEN 2
|
||||
#define USBHID_CDCACM_PARITY_MARK 3
|
||||
#define USBHID_CDCACM_PARITY_SPACE 4
|
||||
uint8 bParityType; /* Parity type */
|
||||
|
||||
uint8 bDataBits; /* Data bits: 5, 6, 7, 8, or 16 */
|
||||
} __packed composite_cdcacm_line_coding;
|
||||
|
||||
/* Retrieve a copy of the current line coding structure. */
|
||||
void composite_cdcacm_get_line_coding(composite_cdcacm_line_coding*);
|
||||
|
||||
/* Line coding conveniences. */
|
||||
int composite_cdcacm_get_baud(void); /* dwDTERate */
|
||||
int composite_cdcacm_get_stop_bits(void); /* bCharFormat */
|
||||
int composite_cdcacm_get_parity(void); /* bParityType */
|
||||
int composite_cdcacm_get_n_data_bits(void); /* bDataBits */
|
||||
|
||||
/*
|
||||
* Hack: hooks for bootloader reset signalling
|
||||
*/
|
||||
|
||||
#define USBHID_CDCACM_HOOK_RX 0x1
|
||||
#define USBHID_CDCACM_HOOK_IFACE_SETUP 0x2
|
||||
|
||||
void composite_cdcacm_set_hooks(unsigned hook_flags, void (*hook)(unsigned, void*));
|
||||
|
||||
static inline __always_inline void composite_cdcacm_remove_hooks(unsigned hook_flags) {
|
||||
composite_cdcacm_set_hooks(hook_flags, 0);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -0,0 +1,61 @@
|
|||
/******************************************************************************
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2012 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 wirish/stm32f1/boards_setup.cpp
|
||||
* @author Marti Bolivar <mbolivar@leaflabs.com>
|
||||
* @brief STM32F1 chip setup.
|
||||
*
|
||||
* This file controls how init() behaves on the STM32F1. Be very
|
||||
* careful when changing anything here. Many of these values depend
|
||||
* upon each other.
|
||||
*/
|
||||
|
||||
#include "boards_private.h"
|
||||
#include "USBHID.h"
|
||||
#include <libmaple/gpio.h>
|
||||
#include <libmaple/timer.h>
|
||||
|
||||
#include <boards.h>
|
||||
#include <usb_serial.h>
|
||||
|
||||
namespace wirish {
|
||||
namespace priv {
|
||||
|
||||
void board_setup_usb(void) {
|
||||
//Serial = CompositeSerial;
|
||||
#ifdef GENERIC_BOOTLOADER
|
||||
//Reset the USB interface on generic boards - developed by Victor PV
|
||||
gpio_set_mode(PIN_MAP[PA12].gpio_device, PIN_MAP[PA12].gpio_bit, GPIO_OUTPUT_PP);
|
||||
gpio_write_bit(PIN_MAP[PA12].gpio_device, PIN_MAP[PA12].gpio_bit,0);
|
||||
|
||||
for(volatile unsigned int i=0;i<512;i++);// Only small delay seems to be needed, and USB pins will get configured in Serial.begin
|
||||
gpio_set_mode(PIN_MAP[PA12].gpio_device, PIN_MAP[PA12].gpio_bit, GPIO_INPUT_FLOATING);
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,486 @@
|
|||
/******************************************************************************
|
||||
* 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.
|
||||
*****************************************************************************/
|
||||
|
||||
/**
|
||||
* 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 "usb_generic.h"
|
||||
#include "usb_x360.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <libmaple/usb.h>
|
||||
#include <libmaple/nvic.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"
|
||||
|
||||
typedef enum _HID_REQUESTS
|
||||
{
|
||||
|
||||
GET_REPORT = 1,
|
||||
GET_IDLE,
|
||||
GET_PROTOCOL,
|
||||
|
||||
SET_REPORT = 9,
|
||||
SET_IDLE,
|
||||
SET_PROTOCOL
|
||||
|
||||
} HID_REQUESTS;
|
||||
|
||||
#define USB_ENDPOINT_IN(addr) ((addr) | 0x80)
|
||||
#define HID_ENDPOINT_INT 1
|
||||
#define USB_ENDPOINT_TYPE_INTERRUPT 0x03
|
||||
|
||||
#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 X360_INTERFACE_OFFSET 0
|
||||
#define X360_INTERFACE_NUMBER (X360_INTERFACE_OFFSET+usbX360Part.startInterface)
|
||||
#define X360_ENDPOINT_TX 0
|
||||
#define X360_ENDPOINT_RX 1
|
||||
#define USB_X360_RX_ADDR x360Endpoints[X360_ENDPOINT_RX].pmaAddress
|
||||
#define USB_X360_TX_ADDR x360Endpoints[X360_ENDPOINT_TX].pmaAddress
|
||||
#define USB_X360_RX_ENDP x360Endpoints[X360_ENDPOINT_RX].address
|
||||
#define USB_X360_TX_ENDP x360Endpoints[X360_ENDPOINT_TX].address
|
||||
|
||||
u16 GetEPTxAddr(u8 bEpNum);
|
||||
|
||||
static uint32 ProtocolValue;
|
||||
|
||||
static void x360DataTxCb(void);
|
||||
static void x360DataRxCb(void);
|
||||
static void (*x360_rumble_callback)(uint8 left, uint8 right);
|
||||
static void (*x360_led_callback)(uint8 pattern);
|
||||
|
||||
static void x360Reset(void);
|
||||
static RESULT x360DataSetup(uint8 request);
|
||||
static RESULT x360NoDataSetup(uint8 request);
|
||||
static uint8 *HID_GetProtocolValue(uint16 Length);
|
||||
|
||||
/*
|
||||
* Descriptors
|
||||
*/
|
||||
|
||||
#if 0
|
||||
const uint8_t hid_report_descriptor[] = {
|
||||
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
|
||||
0x09, 0x05, // USAGE (Game Pad)
|
||||
0xa1, 0x01, // COLLECTION (Application)
|
||||
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
|
||||
0x09, 0x3a, // USAGE (Counted Buffer)
|
||||
0xa1, 0x02, // COLLECTION (Logical)
|
||||
0x75, 0x08, // REPORT_SIZE (8)
|
||||
0x95, 0x02, // REPORT_COUNT (2)
|
||||
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
|
||||
0x09, 0x3f, // USAGE (Reserved)
|
||||
0x09, 0x3b, // USAGE (Byte Count)
|
||||
0x81, 0x01, // INPUT (Cnst,Ary,Abs)
|
||||
0x75, 0x01, // REPORT_SIZE (1)
|
||||
0x15, 0x00, // LOGICAL_MINIMUM (0)
|
||||
0x25, 0x01, // LOGICAL_MAXIMUM (1)
|
||||
0x35, 0x00, // PHYSICAL_MINIMUM (0)
|
||||
0x45, 0x01, // PHYSICAL_MAXIMUM (1)
|
||||
0x95, 0x04, // REPORT_COUNT (4)
|
||||
0x05, 0x09, // USAGE_PAGE (Button)
|
||||
0x19, 0x0c, // USAGE_MINIMUM (Button 12)
|
||||
0x29, 0x0f, // USAGE_MAXIMUM (Button 15)
|
||||
0x81, 0x02, // INPUT (Data,Var,Abs)
|
||||
0x75, 0x01, // REPORT_SIZE (1)
|
||||
0x15, 0x00, // LOGICAL_MINIMUM (0)
|
||||
0x25, 0x01, // LOGICAL_MAXIMUM (1)
|
||||
0x35, 0x00, // PHYSICAL_MINIMUM (0)
|
||||
0x45, 0x01, // PHYSICAL_MAXIMUM (1)
|
||||
0x95, 0x04, // REPORT_COUNT (4)
|
||||
0x05, 0x09, // USAGE_PAGE (Button)
|
||||
0x09, 0x09, // USAGE (Button 9)
|
||||
0x09, 0x0a, // USAGE (Button 10)
|
||||
0x09, 0x07, // USAGE (Button 7)
|
||||
0x09, 0x08, // USAGE (Button 8)
|
||||
0x81, 0x02, // INPUT (Data,Var,Abs)
|
||||
0x75, 0x01, // REPORT_SIZE (1)
|
||||
0x15, 0x00, // LOGICAL_MINIMUM (0)
|
||||
0x25, 0x01, // LOGICAL_MAXIMUM (1)
|
||||
0x35, 0x00, // PHYSICAL_MINIMUM (0)
|
||||
0x45, 0x01, // PHYSICAL_MAXIMUM (1)
|
||||
0x95, 0x03, // REPORT_COUNT (3)
|
||||
0x05, 0x09, // USAGE_PAGE (Button)
|
||||
0x09, 0x05, // USAGE (Button 5)
|
||||
0x09, 0x06, // USAGE (Button 6)
|
||||
0x09, 0x0b, // USAGE (Button 11)
|
||||
0x81, 0x02, // INPUT (Data,Var,Abs)
|
||||
0x75, 0x01, // REPORT_SIZE (1)
|
||||
0x95, 0x01, // REPORT_COUNT (1)
|
||||
0x81, 0x01, // INPUT (Cnst,Ary,Abs)
|
||||
0x75, 0x01, // REPORT_SIZE (1)
|
||||
0x15, 0x00, // LOGICAL_MINIMUM (0)
|
||||
0x25, 0x01, // LOGICAL_MAXIMUM (1)
|
||||
0x35, 0x00, // PHYSICAL_MINIMUM (0)
|
||||
0x45, 0x01, // PHYSICAL_MAXIMUM (1)
|
||||
0x95, 0x04, // REPORT_COUNT (4)
|
||||
0x05, 0x09, // USAGE_PAGE (Button)
|
||||
0x19, 0x01, // USAGE_MINIMUM (Button 1)
|
||||
0x29, 0x04, // USAGE_MAXIMUM (Button 4)
|
||||
0x81, 0x02, // INPUT (Data,Var,Abs)
|
||||
0x75, 0x08, // REPORT_SIZE (8)
|
||||
0x15, 0x00, // LOGICAL_MINIMUM (0)
|
||||
0x26, 0xff, 0x00, // LOGICAL_MAXIMUM (255)
|
||||
0x35, 0x00, // PHYSICAL_MINIMUM (0)
|
||||
0x46, 0xff, 0x00, // PHYSICAL_MAXIMUM (255)
|
||||
0x95, 0x02, // REPORT_COUNT (2)
|
||||
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
|
||||
0x09, 0x32, // USAGE (Z)
|
||||
0x09, 0x35, // USAGE (Rz)
|
||||
0x81, 0x02, // INPUT (Data,Var,Abs)
|
||||
0x75, 0x10, // REPORT_SIZE (16)
|
||||
0x16, 0x00, 0x80, // LOGICAL_MINIMUM (-32768)
|
||||
0x26, 0xff, 0x7f, // LOGICAL_MAXIMUM (32767)
|
||||
0x36, 0x00, 0x80, // PHYSICAL_MINIMUM (-32768)
|
||||
0x46, 0xff, 0x7f, // PHYSICAL_MAXIMUM (32767)
|
||||
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
|
||||
0x09, 0x01, // USAGE (Pointer)
|
||||
0xa1, 0x00, // COLLECTION (Physical)
|
||||
0x95, 0x02, // REPORT_COUNT (2)
|
||||
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
|
||||
0x09, 0x30, // USAGE (X)
|
||||
0x09, 0x31, // USAGE (Y)
|
||||
0x81, 0x02, // INPUT (Data,Var,Abs)
|
||||
0xc0, // END_COLLECTION
|
||||
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
|
||||
0x09, 0x01, // USAGE (Pointer)
|
||||
0xa1, 0x00, // COLLECTION (Physical)
|
||||
0x95, 0x02, // REPORT_COUNT (2)
|
||||
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
|
||||
0x09, 0x33, // USAGE (Rx)
|
||||
0x09, 0x34, // USAGE (Ry)
|
||||
0x81, 0x02, // INPUT (Data,Var,Abs)
|
||||
0xc0, // END_COLLECTION
|
||||
0xc0, // END_COLLECTION
|
||||
0xc0 // END_COLLECTION
|
||||
};
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
// usb_descriptor_config_header Config_Header;
|
||||
usb_descriptor_interface HID_Interface;
|
||||
uint8 unknown_descriptor1[17];
|
||||
usb_descriptor_endpoint DataInEndpoint;
|
||||
usb_descriptor_endpoint DataOutEndpoint;
|
||||
} __packed usb_descriptor_config;
|
||||
|
||||
|
||||
#define MAX_POWER (100 >> 1)
|
||||
static const usb_descriptor_config X360Descriptor_Config =
|
||||
{
|
||||
#if 0
|
||||
.Config_Header = {
|
||||
.bLength = sizeof(usb_descriptor_config_header),
|
||||
.bDescriptorType = USB_DESCRIPTOR_TYPE_CONFIGURATION,
|
||||
.wTotalLength = sizeof(usb_descriptor_config),//0,
|
||||
.bNumInterfaces = 0x01,
|
||||
.bConfigurationValue = 0x01,
|
||||
.iConfiguration = 0x00,
|
||||
.bmAttributes = (USB_CONFIG_ATTR_BUSPOWERED |
|
||||
USB_CONFIG_ATTR_SELF_POWERED),
|
||||
.bMaxPower = MAX_POWER,
|
||||
},
|
||||
#endif
|
||||
|
||||
.HID_Interface = {
|
||||
.bLength = sizeof(usb_descriptor_interface),
|
||||
.bDescriptorType = USB_DESCRIPTOR_TYPE_INTERFACE,
|
||||
.bInterfaceNumber = X360_INTERFACE_OFFSET, // PATCH
|
||||
.bAlternateSetting = 0x00,
|
||||
.bNumEndpoints = 0x02,
|
||||
.bInterfaceClass = 0xFF,
|
||||
.bInterfaceSubClass = 0x5D,
|
||||
.bInterfaceProtocol = 0x01,
|
||||
.iInterface = 0x00,
|
||||
},
|
||||
|
||||
.unknown_descriptor1 = {
|
||||
17,33,0,1,1,37,129,20,0,0,0,0,19,2,8,0,0,
|
||||
},
|
||||
|
||||
.DataInEndpoint = {
|
||||
.bLength = sizeof(usb_descriptor_endpoint),
|
||||
.bDescriptorType = USB_DESCRIPTOR_TYPE_ENDPOINT,
|
||||
.bEndpointAddress = (USB_DESCRIPTOR_ENDPOINT_IN | X360_ENDPOINT_TX),//PATCH
|
||||
.bmAttributes = USB_EP_TYPE_INTERRUPT,
|
||||
.wMaxPacketSize = 0x20,
|
||||
.bInterval = 4,
|
||||
},
|
||||
|
||||
.DataOutEndpoint = {
|
||||
.bLength = sizeof(usb_descriptor_endpoint),
|
||||
.bDescriptorType = USB_DESCRIPTOR_TYPE_ENDPOINT,
|
||||
.bEndpointAddress = (USB_DESCRIPTOR_ENDPOINT_OUT | X360_ENDPOINT_RX),//PATCH
|
||||
.bmAttributes = USB_EP_TYPE_INTERRUPT,
|
||||
.wMaxPacketSize = 0x20,
|
||||
.bInterval = 8,
|
||||
},
|
||||
};
|
||||
|
||||
static USBEndpointInfo x360Endpoints[2] = {
|
||||
{
|
||||
.callback = x360DataTxCb,
|
||||
.bufferSize = 0x20,
|
||||
.type = USB_EP_EP_TYPE_INTERRUPT,
|
||||
.tx = 1
|
||||
},
|
||||
{
|
||||
.callback = x360DataRxCb,
|
||||
.bufferSize = 0x20,
|
||||
.type = USB_EP_EP_TYPE_INTERRUPT,
|
||||
.tx = 0,
|
||||
}
|
||||
};
|
||||
|
||||
#define OUT_BYTE(s,v) out[(uint8*)&(s.v)-(uint8*)&s]
|
||||
|
||||
static void getX360PartDescriptor(uint8* out) {
|
||||
memcpy(out, &X360Descriptor_Config, sizeof(X360Descriptor_Config));
|
||||
// patch to reflect where the part goes in the descriptor
|
||||
OUT_BYTE(X360Descriptor_Config, HID_Interface.bInterfaceNumber) += usbX360Part.startInterface;
|
||||
OUT_BYTE(X360Descriptor_Config, DataOutEndpoint.bEndpointAddress) += usbX360Part.startEndpoint;
|
||||
OUT_BYTE(X360Descriptor_Config, DataInEndpoint.bEndpointAddress) += usbX360Part.startEndpoint;
|
||||
}
|
||||
|
||||
USBCompositePart usbX360Part = {
|
||||
.numInterfaces = 1,
|
||||
.numEndpoints = sizeof(x360Endpoints)/sizeof(*x360Endpoints),
|
||||
.descriptorSize = sizeof(X360Descriptor_Config),
|
||||
.getPartDescriptor = getX360PartDescriptor,
|
||||
.usbInit = NULL,
|
||||
.usbReset = x360Reset,
|
||||
.usbDataSetup = x360DataSetup,
|
||||
.usbNoDataSetup = x360NoDataSetup,
|
||||
.endpoints = x360Endpoints
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Etc.
|
||||
*/
|
||||
|
||||
/* I/O state */
|
||||
|
||||
/* Received data */
|
||||
static volatile uint8 hidBufferRx[USB_X360_RX_EPSIZE];
|
||||
|
||||
|
||||
/* Number of bytes left to transmit */
|
||||
static volatile uint32 n_unsent_bytes = 0;
|
||||
/* Are we currently sending an IN packet? */
|
||||
static volatile uint8 transmitting = 0;
|
||||
|
||||
|
||||
/*
|
||||
* HID interface
|
||||
*/
|
||||
|
||||
void x360_set_rumble_callback(void (*callback)(uint8 left, uint8 right)) {
|
||||
x360_rumble_callback = callback;
|
||||
}
|
||||
|
||||
void x360_set_led_callback(void (*callback)(uint8 pattern)) {
|
||||
x360_led_callback = callback;
|
||||
}
|
||||
|
||||
/*void x360_disable(void) {
|
||||
x360_set_rumble_callback(NULL);
|
||||
x360_set_led_callback(NULL);
|
||||
usb_generic_disable();
|
||||
}*/
|
||||
|
||||
void x360_putc(char ch) {
|
||||
while (!x360_tx((uint8*)&ch, 1))
|
||||
;
|
||||
}
|
||||
|
||||
|
||||
/* 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 x360_tx(const uint8* buf, uint32 len) {
|
||||
/* Last transmission hasn't finished, so abort. */
|
||||
if (x360_is_transmitting()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* We can only put USB_X360_TX_EPSIZE bytes in the buffer. */
|
||||
if (len > USB_X360_TX_EPSIZE) {
|
||||
len = USB_X360_TX_EPSIZE;
|
||||
}
|
||||
|
||||
/* Queue bytes for sending. */
|
||||
if (len) {
|
||||
usb_copy_to_pma(buf, len, GetEPTxAddr(USB_X360_TX_ENDP));//USB_X360_TX_ADDR);
|
||||
}
|
||||
// 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.)
|
||||
usb_set_ep_tx_count(USB_X360_TX_ENDP, len);
|
||||
n_unsent_bytes = len;
|
||||
transmitting = 1;
|
||||
usb_set_ep_tx_stat(USB_X360_TX_ENDP, USB_EP_STAT_TX_VALID);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
uint8 x360_is_transmitting(void) {
|
||||
return transmitting;
|
||||
}
|
||||
|
||||
uint16 x360_get_pending(void) {
|
||||
return n_unsent_bytes;
|
||||
}
|
||||
|
||||
static void x360DataRxCb(void)
|
||||
{
|
||||
uint32 ep_rx_size = usb_get_ep_rx_count(USB_X360_RX_ENDP);
|
||||
// This copy won't overwrite unread bytes as long as there is
|
||||
// enough room in the USB Rx buffer for next packet
|
||||
uint32 *src = usb_pma_ptr(USB_X360_RX_ADDR);
|
||||
uint16 tmp = 0;
|
||||
uint8 val;
|
||||
uint32 i;
|
||||
for (i = 0; i < ep_rx_size; i++) {
|
||||
if (i&1) {
|
||||
val = tmp>>8;
|
||||
} else {
|
||||
tmp = *src++;
|
||||
val = tmp&0xFF;
|
||||
}
|
||||
hidBufferRx[i] = val;
|
||||
}
|
||||
|
||||
if (ep_rx_size == 3) {
|
||||
if (x360_led_callback != NULL && hidBufferRx[0] == 1 && hidBufferRx[1] == 3)
|
||||
x360_led_callback(hidBufferRx[2]);
|
||||
}
|
||||
else if (ep_rx_size == 8) {
|
||||
if (x360_rumble_callback != NULL && hidBufferRx[0] == 0 && hidBufferRx[1] == 8)
|
||||
x360_rumble_callback(hidBufferRx[3],hidBufferRx[4]);
|
||||
}
|
||||
usb_set_ep_rx_stat(USB_X360_RX_ENDP, USB_EP_STAT_RX_VALID);
|
||||
}
|
||||
|
||||
/*
|
||||
* Callbacks
|
||||
*/
|
||||
|
||||
static void x360DataTxCb(void) {
|
||||
n_unsent_bytes = 0;
|
||||
transmitting = 0;
|
||||
}
|
||||
|
||||
static RESULT x360DataSetup(uint8 request) {
|
||||
uint8* (*CopyRoutine)(uint16) = 0;
|
||||
|
||||
#if 0
|
||||
if (request == GET_DESCRIPTOR
|
||||
&& pInformation->USBwIndex0 == X360_INTERFACE_NUMBER &&
|
||||
&& (Type_Recipient == (STANDARD_REQUEST | INTERFACE_RECIPIENT))
|
||||
&& (pInformation->USBwIndex0 == 0)){
|
||||
if (pInformation->USBwValue1 == REPORT_DESCRIPTOR){
|
||||
CopyRoutine = HID_GetReportDescriptor;
|
||||
} else
|
||||
if (pInformation->USBwValue1 == HID_DESCRIPTOR_TYPE){
|
||||
CopyRoutine = HID_GetHIDDescriptor;
|
||||
}
|
||||
|
||||
} /* End of GET_DESCRIPTOR */
|
||||
/*** GET_PROTOCOL ***/
|
||||
else
|
||||
#endif
|
||||
if(pInformation->USBwIndex0 == X360_INTERFACE_NUMBER &&
|
||||
(Type_Recipient == (CLASS_REQUEST | INTERFACE_RECIPIENT))
|
||||
&& request == GET_PROTOCOL){
|
||||
CopyRoutine = HID_GetProtocolValue;
|
||||
}
|
||||
|
||||
if (CopyRoutine == NULL){
|
||||
return USB_UNSUPPORT;
|
||||
}
|
||||
|
||||
pInformation->Ctrl_Info.CopyData = CopyRoutine;
|
||||
pInformation->Ctrl_Info.Usb_wOffset = 0;
|
||||
(*CopyRoutine)(0);
|
||||
return USB_SUCCESS;
|
||||
}
|
||||
|
||||
static RESULT x360NoDataSetup(uint8 request) {
|
||||
if ((Type_Recipient == (CLASS_REQUEST | INTERFACE_RECIPIENT))
|
||||
&& (request == SET_PROTOCOL)){
|
||||
uint8 wValue0 = pInformation->USBwValue0;
|
||||
ProtocolValue = wValue0;
|
||||
return USB_SUCCESS;
|
||||
}else{
|
||||
return USB_UNSUPPORT;
|
||||
}
|
||||
}
|
||||
|
||||
static uint8* HID_GetProtocolValue(uint16 Length){
|
||||
if (Length == 0){
|
||||
pInformation->Ctrl_Info.Usb_wLength = 1;
|
||||
return NULL;
|
||||
} else {
|
||||
return (uint8 *)(&ProtocolValue);
|
||||
}
|
||||
}
|
||||
|
||||
static void x360Reset(void) {
|
||||
/* Reset the RX/TX state */
|
||||
n_unsent_bytes = 0;
|
||||
transmitting = 0;
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
/******************************************************************************
|
||||
* 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.
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef _USB_X360_H
|
||||
#define _USB_X360_H
|
||||
|
||||
#include <libmaple/libmaple_types.h>
|
||||
#include <libmaple/gpio.h>
|
||||
#include <libmaple/usb.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* Descriptors, etc.
|
||||
*/
|
||||
|
||||
//extern const uint8_t hid_report_descriptor[];
|
||||
|
||||
/*
|
||||
* Endpoint configuration
|
||||
*/
|
||||
|
||||
#define USB_X360_TX_EPSIZE 0x20
|
||||
#define USB_X360_RX_EPSIZE 0x20
|
||||
|
||||
/*
|
||||
* HID interface
|
||||
*/
|
||||
|
||||
extern USBCompositePart usbX360Part;
|
||||
void x360_enable();
|
||||
void x360_disable();
|
||||
|
||||
void x360_putc(char ch);
|
||||
uint32 x360_tx(const uint8* buf, uint32 len);
|
||||
uint32 x360_rx(uint8* buf, uint32 len);
|
||||
uint32 x360_hid_peek(uint8* buf, uint32 len);
|
||||
uint32 x360_data_available(void); /* in RX buffer */
|
||||
uint16 x360_get_pending(void);
|
||||
uint8 x360_is_transmitting(void);
|
||||
void x360_set_rx_callback(void (*callback)(const uint8* buffer, uint32 size));
|
||||
void x360_set_rumble_callback(void (*callback)(uint8 left, uint8 right));
|
||||
void x360_set_led_callback(void (*callback)(uint8 pattern));
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue