#include "hea.h"
#include <string> // std::char_traits
#include <array> // std::array
namespace usb{
enum{
EP0S=64, // 64 Byte Control I/O
EP1TS=64, // 64 Byte Interrupt In (für HID)
EP1RS=0,
EP2TS=64, // 64 Byte Bulk In (für WebUSB)
EP2RS=0
};
// Zum Verwalten der Buffer Table
struct PMA_USAGE {
byte header[0x20], // Platz für BTABLE (4 Einträge, 3 genutzt)
ep0t[EP0S], // EP0-Schreibpuffer
ep0r[EP0S], // EP0-Lesepuffer, alles einfach gepuffert
ep1t[EP1TS], // EP1-Schreibpuffer
ep1r[EP1RS], // EP1-Lesepuffer
ep2t[EP2TS], // EP2-Schreibpuffer
ep2r[EP2RS]; // EP2-Lesepuffer
PMA_USAGE()=delete;
};
static_assert(sizeof(PMA_USAGE)<=512,"PMA voll!"); // USB/CAN SRAM
/* USB Descriptors */
#define W(x) byte(x),byte((x)>>8) // erst Low dann High
#define D(x) W(x),W((x)>>16)
static const byte HidRepDesc[] = {
0x06, W(0xFF20), // G Usage Page (Sensor)
0x09, 0x33, // L Usage (Bewegung)
0xA1, 1, // M Collection (Physical)
// feature report 2: Bremskraftvorgabe, Triggerschwellen für Kraftstoß
0x85, 2, // G Report-ID
0x95, 1, // G Report Count
0x75, 8, // G Report Size
0x15, 0, // G Logical Minimum
0x25, 255, // G Logical Maximum
0x09, 0x62, // L Usage: Boolean Switch Array
0xB1, 2, // M Feature (Data_Var_Abs) (1)
0x75, 16, // G Report Size
0x26, W(4095), // G Logical Maximum
0x09, 0x65, // L Usage: Pressure (Bremskraftvorgabe)
0xB1, 2, // M Input (Data_Var_Abs) (3:2)
0x95, 2, // G Report Count
0x09, 100, // L Usage: Force (Triggerschwelle für Kraftstoß-Report)
0xB1, 2, // M Feature (Data_Var_Abs) (5:4, 7:6)
0x95, 4, // G Report Count
0x19, 0x6D, // L Usage Minimum: Prätrigger-Zeit in Samples
0x29, 0x70, // L Usage Maximum: Kanalzuordnung
0xB1, 2, // M Feature (Data_Var_Abs) (9:8, 11:10, 13:12, 15:14)
//input report 1: Verdrehsensor, Potenziometer, Kraft, Messschieber
0x85, 1, // G Report-ID
0x95, 1, // G Report Count
0x75, 8, // G Report Size
0x25, 255, // G Logical Maximum
0x09, 98, // L Usage: Boolean Switch Array
0x81, 2, // M Input (Data_Var_Abs) (1)
0x75, 16, // G Report Size
0x95, 4, // G Report Count
0x26, W(4095), // G Logical Maximum
// Hand-Potenziometer
0x09, 101, // L Usage: Hand-Potenziometer
0x79, 101, // L String Index
0xA4, // G Push
0x35, 0, // G Physical Minimum
0x45, 100, // G Physical Maximum (ohne Einheit: %)
0x81, 2, // M Input (Data_Var_Abs) (9:2)
0xB4, // G Pop
// Kraftmessdose
0x09, 102, // L Usage: Kraftmesser
0x79, 102, // L String Index
0xA4, // G Push
0x15, 0, // G Logical Minimum
0x26, W(778), // G Logical Maximum (hier: für kleine Kräfte)
0x35, 0, // G Physical Minimum
0x45, 10, // G Physical Maximum
0x55, 8, // G Unit Exponent (10^8) d.h. „kN“ darstellen
0x66, W(0xE111), // G Unit (g×cm÷s²)
0x81, 2, // M Input (Data_Var_Abs) (17:10)
0xB4, // G Pop
// Weg-Poti
0x09, 103, // L Usage: Weg-Potenziometer
0x79, 103, // L String Index
0xA4, // G Push
0x15, 39, // G Logical Minimum
0x26, W(3878), // G Logical Maximum
0x35, 0, // G Physical Minimum
0x45, 50, // G Physical Maximum
0x55, byte(-1), // G Unit Exponent (10^-1) d.h. „mm“ darstellen
0x66, W(0x0011), // G Unit (cm)
0x81, 2, // M Input (Data_Var_Abs) (25:18)
0xB4, // G Pop
// ADC6
0x09, 104, // L Usage: Verdrehsensor (ADC6)
0x79, 104, // L String Index
0x81, 2, // M Input (Data_Var_Abs) (33:26)
// ADC1
0x09, 105, // L Usage: (ADC1)
0x79, 105, // L String Index
0x81, 2, // M Input (Data_Var_Abs) (41:34)
0x95, 1, // G Report Count
// Für den Messschieber die Umrechnungsanweisung mitliefern
0x09, 106, // L Usage: Messschieber
0x79, 106, // L String Index
0xA4, // G Push
0x16, W(-15118), // G Logical Minimum
0x26, W(+15118), // G Logical Maximum
0x36, W(-150), // G Physical Minimum
0x46, W(+150), // G Physical Maximum
0x55, byte(-1), // G Unit Exponent (10^-1) d.h. „mm“ darstellen
0x66, W(0x0011), // G Unit (cm)
0x81, 2, // M Input (Data_Var_Abs) (43:42)
0xB4, // G Pop
// Kraftstoß
0x09, 107, // L Usage: Kraftstoß (Integral der positiven Werte über 1 ms)
0x79, 107, // L String Index
0xA4, // G Push
0x15, 0, // G Logical Minimum
0x26, W(778), // G Logical Maximum (hier: für kleine Kräfte)
0x35, 0, // G Physical Minimum
0x45, 10, // G Physical Maximum
0x55, 5, // G Unit Exponent (10^5) d.h. „Ns“ darstellen
0x66, W(0xF111), // G Unit (g×cm÷s)
0x81, 2, // M Input (Data_Var_Abs) (45:44)
0xB4, // G Pop
// Kraft-Maximum (gleiche Umrechnung wie bei Usage 102)
0x09, 108, // L Usage: Kraft-Maximum im Kraftstoß
0x79, 108, // L String Index
0x81, 2, // M Input (Data_Var_Abs) (47:46)
// Wegstrecke (gleiche Umrechnung wie bei Usage 103)
0x09, 109, // L Usage: Zurückgelegter Weg
0x79, 109, // L String Index
0x81, 2, // M Input (Data_Var_Abs) (47:46)
//feature report 4: Wert vom/zum Linak-Servo, die serielle Schnittstelle ersetzend
// (nicht implementiert)
0x85, 4, // G Report-ID
0x75, 8, // G Report Size
0x25, 255, // G Logical Maximum
0x0A, W(0x5C0), // L Usage: Custom (Objektadresse im Linak-Servo, Bit 7 = lesen)
0xB1, 2, // M Feature (Data_Var_Abs) (1)
0x75, 16, // G Report Size
0x26, W(65535), // G Logical Maximum
0x0A, W(0x5C1), // L Usage: Custom Value 1 (Objektwert im Linak-Servo)
0xB1, 2, // M Feature (Data_Var_Abs) (3:2)
//feature report 3: Oszilloskopdaten (High-Speed-Abtastung bis zu 4 Kanäle)
0x85, 3, // G Report-ID
0x75, 8, // G Report Size
0x25, 255, // G Logical Maximum
0x09, 116, // L Usage: Kanalzahl
0xB1, 2, // M Feature (Data_Var_Abs) (1)
0x75, 16, // G Report Size
0x26, W(65535), // G Logical Maximum
0x09, 117, // L Usage: Samplezahl
0xB1, 2, // M Feature (Data_Var_Abs) (3:2)
0x09, 118, // L Usage: Interleaved Samples
0x96, W(elemof(repT.data)), // G Report Count (viel!)
0xB1, 2, // M Feature (Data_Var_Abs) (viel:4)
// Dieser Report hat mit >2000 Bytes eine erhebliche Länge!
// Als Input-Report ungeeignet.
//feature report 99: Reboot
0x85, 99, // G Report-ID
0x75, 8, // G Report Size
0x95, 1, // G Report Count
0x25, 1, // G Logical Maximum
0x0A, W(0x5C2), // L Usage: Custom Value 2
0xB1, 2, // M Feature (Data_Arr_Abs)
//output report 42: Manual Trigger
0x85, 42, // G Report-ID
0x0A, W(0x5C3), // L Usage: Custom Value 3
0x91, 2, // M Output (Data_Var_Abs) // wird ignoriert
0xC0, // M End Collection
};
static const byte DeviceDesc[] = {
18, // bLength
1, // bDescType Device
W(0x0210), // bcdUSB 2.10
0, // bDeviceClass See Iface Descs
0, // bDeviceSubClass
0, // bDeviceProtocol
EP0S, // bMaxPacketSize0
W(0x16C0), // idVendor
W(0x27D9), // idProduct
W(0x2111), // bcdDevice Jahr+Monat
1, // iManufacturer
2, // iProduct
3, // iSerialNumber
1 // bNumConfigurations
};
static const byte ConfigDesc[9+9+9+7+9+7] = {
9, // bLength
2, // bDescType Config
W(sizeof ConfigDesc),// wTotalLength
2, // bNumInterfaces
1, // bConfigValue
0, // iConfig
0x80, // bmAttributes Busversorgt
50>>1, // bMaxPower 50 mA
9, // bLength
4, // bDescType Iface
0, // bIfaceNumber 0
0, // bAlternateSetting
1, // bNumEndpoints
3, // bIfaceClass HID
0, // bIfaceSubClass
0, // bIfaceProtocol
4, // iIface
9, // bLength
0x21, // bDescType HID
W(0x0111), // bcdHID 1.11
0, // bCountryCode
1, // bNumDescriptors
0x22, // bDescType[0] Report
W(sizeof HidRepDesc),// wDescriptorLength[0]
7, // bLength
5, // bDescType Endpoint
0x81, // bEndpointAddress 1 IN
3, // bmAttributes interrupt, nur für Messdaten
W(EP1TS), // wMaxPacketSize
32, // bInterval
// Das betrifft auch Mehrpakete-Transfers!!
9, // bLength
4, // bDescType Iface
1, // bInterfaceNumber 1
0, // bAlternateSetting (only this one)
1, // bNumEndpoints
0xFF, // bIfaceClass vendor-specific
0, // bIfaceSubclass
0, // bIfaceProtocol
5, // iIface
7, // bLength
5, // bDescType ENDPOINT
0x82, // bEndpointAddress 2 IN
2, // bmAttributes bulk, für Mess- und Oszidaten
W(EP2TS), // wMaxPacketSize
0, // bInterval wird immer aggresiv abgefragt
};
// erforderlich für WebUSB für Windows
static const byte MsOs20Desc[] = {
// Microsoft OS 2.0 descriptor set header (table 10)
W(10), // Descriptor size (10 bytes)
W(0), // MS OS 2.0 descriptor set header
D(0x06030000), // Windows version (8.1) (0x06030000)
W(178), // Size, MS OS 2.0 descriptor set
// Microsoft OS 2.0 configuration subset header
W(8), // Descriptor size (8 bytes)
W(1), // MS OS 2.0 configuration subset header
W(0), // bConfigurationValue
W(168), // Size, MS OS 2.0 configuration subset
// Microsoft OS 2.0 function subset header
W(8), // Descriptor size (8 bytes)
W(2), // MS OS 2.0 function subset header
W(1), // interface number
W(160), // Size, MS OS 2.0 function subset
// Microsoft OS 2.0 compatible ID descriptor (table 13)
W(20), // wLength
W(3), // MS_OS_20_FEATURE_COMPATIBLE_ID
'W','I','N','U','S','B',0,0,
0,0,0,0,0,0,0,0,
W(4+21+1+40<<1),//wLength:
W(4), // wDescriptorType: MS_OS_20_FEATURE_REG_PROPERTY: 0x04 (Table 9)
W(7), //wPropertyDataType: REG_MULTI_SZ (Table 15)
W(21<<1), //wPropertyNameLength:
W('D'),W('e'),W('v'),W('i'),W('c'),W('e'),W('I'),W('n'),
W('t'),W('e'),W('r'),W('f'),W('a'),W('c'),W('e'),W('G'),
W('U'),W('I'),W('D'),W('s'),W( 0 ), //bPropertyName
W(40<<1), // wPropertyDataLength
W('{'),W('9'),W('7'),W('5'),W('F'),W('4'),W('4'),W('D'),
W('9'),W('-'),W('0'),W('D'),W('0'),W('8'),W('-'),W('4'),
W('3'),W('F'),W('D'),W('-'),W('8'),W('B'),W('3'),W('E'),
W('-'),W('1'),W('2'),W('7'),W('C'),W('A'),W('8'),W('A'),
W('F'),W('F'),W('F'),W('9'),W('D'),W('}'),W( 0 ),W( 0 ) //bPropertyData
};
static_assert(sizeof MsOs20Desc==23+66<<1,"Wrong struct!");
static_assert(sizeof MsOs20Desc==178,"Falsch!");
// erforderlich für WebUSB:
static const byte BosDesc[5+24+28] = {
5, // Length
0x0F, // Binary Object Store descriptor
W(sizeof BosDesc), // Total length
2, // Number of device capabilities
// WebUSB Platform Capability descriptor (bVendorCode == 0x01).
24, // Length
0x10, // Device Capability descriptor
0x05, // Platform Capability descriptor
0x00, // Reserved
D(0x3408B638),W(0x09A9),W(0x47A0),W(0xFD8B),0xA0,0x76,0x88,0x15,0xB6,0x65, // WebUSB GUID
W(0x0100), // Version 1.0
0x01, // Vendor request code (-> bRequest)
0x01, // Landing page (-> wValueL)
// Microsoft OS 2.0 Platform Capability Descriptor (MS_VendorCode == 0x02)
28, // Length
0x10, // Device Capability descriptor
0x05, // Platform Capability descriptor
0x00, // Reserved
D(0xD8DD60DF),W(0x4589),W(0x4CC7),W(0xD29C),0x65,0x9D,0x9E,0x64,0x8A,0x9F, // MS OS 2.0 GUID
D(0x06030000), // Windows version
W(sizeof MsOs20Desc), // Descriptor set length
0x02, // Vendor request code (-> bRequest)
0x00 // Alternate enumeration code
};
static_assert(sizeof BosDesc==57,"Wrong struct!");
#undef D
#undef W
template<size_t N>constexpr auto urldesc(byte proto, const char*s) {
constexpr auto L=3+N;
static_assert(L<256,"String zu lang!");
std::array<byte,L> A={L,3,proto};
for (size_t i=0; i<N; i++) A[3+i] = s[i];
return A;
}
#define URLDESC(p,s) (urldesc<std::char_traits<char8_t>::length(u8##s)>(p,s))
// erforderlich für WebUSB
static const auto UrlDesc =
URLDESC(1,"www.tu-chemnitz.de/~heha/mb-iwp/Antriebe/Linak-Servo/app.htm");
#undef URLDESC
static constexpr volatile uint32_t* bufferAddress(unsigned btable_addr) {
return reinterpret_cast<volatile uint32_t*>(PMAAddr + btable_addr*2);
}
// The <src> argument is the first here to help place the return value in the same register
// <src> must be aligned! (divisable by 4)
static void fromPMA(unsigned btable_addr,byte*dst,unsigned len) {
const volatile uint32_t*src=bufferAddress(btable_addr);
union{
byte*pb;
uint16_t*ph;
}d={dst};
auto l=len>>1;
if (l) do *d.ph++=*src++; while (--l); // copy 2 bytes
if (len&1) *d.pb=*src; // copy low byte - the return value cannot be used for continuing!
}
static void toPMA(unsigned btable_addr,const byte*src,unsigned len) {
volatile uint32_t*dst=bufferAddress(btable_addr);
union{
const byte*pcb;
const uint16_t*pch;
}s={src}; // Quellzeiger, darf unausgerichtet sein, auf Bytes und Halfwords
auto l=len>>1;
if (l) do *dst++=*s.pch++; while (--l); // copy 2 bytes
if (len&1) *dst=*s.pcb; // copy low byte - continuation impossible
}
SetupPacket setup; // last received setup packet
byte DevConfig; // current configuration value (0 after USB reset)
bool send(byte ep, const byte*data, unsigned length) {
if (!DevConfig) return false; // don't allow sending over EP1+ when not configured
byte flags=ep; ep&=0x0F;
constexpr unsigned eplen=64; // Hier: Überall gleich
for(;;) {
unsigned l=length;
if (!l && flags&noZlp) return true;
if (USB.ISTR&0x7C00) return false; // PMA-OVR, ERR, WKUP, SUSP, RESET: Alles Fehler
byte timeout = flags&onBusyWait ? 105 : 5; // Millisekunden = USB-Frames
//230331: 2 Frames Wartezeit sind zu wenig, da kommt manchmal nur ein Trace-Bruchstück (Edge, nicht Chrome)
//230331: 5 helfen aber auch nicht! Außerdem kommt rätselhafterweise das Last-Packet in der richtigen Länge durch.
// Dürfte nach dieser Logik gar nicht vorkommen.
byte tic = USB.FNR;
while ((USB.EPR[ep]&EPTX_STAT)==EP_TX_VALID) {
idle(); // warten bis vorheriges Paket abgesendet
if (USB.ISTR&0x7C00) return false; // PMA-OVR, ERR, WKUP, SUSP, RESET: Alles Fehler
if (byte(USB.FNR-tic)>=timeout) break;
}
// Der Endpoint muss sich in einem Zustand befinden, bei der dieser auf
// IN-Tokens mit NAK antwortet, also leer sein. Keine Doppelpufferung.
switch (USB.EPR[ep]&EPTX_STAT) {
case EP_TX_NAK: break; // in Ordnung
case EP_TX_VALID: if (flags&onBusySend) break; // Daten überschreiben
[[fallthrough]]; // Geht hier SET_TX_STATUS(ep,EP_TX_NAK)?
default: return false;
}
debug::workstart();
flags&=~(onBusySend|onBusyWait); // Folgepakete mit Standardverhalten
if (l>eplen) l=eplen;
toPMA(BTABLE[ep].ADDR_TX,data,l);
BTABLE[ep].COUNT_TX=l; // kann auch ein 0-Byte-Paket sein
SET_TX_STATUS(ep,EP_TX_VALID);
debug::workend();
if (l<eplen) return true; // Short-Packet ist das letzte
length-=l;
data+=l;
}
}
// Warten ohne Timeout
static bool waitSent0() {
while (!(USB.EPR[0]&EP_CTR_TX)) {
if (USB.ISTR&(ISTR_RESET|ISTR_SUSP)) return false;
idle();
}
USB.EPR[0]=0x8200; // Tx-Interrupt löschen
return true;
}
bool send0(const byte*data, unsigned len) {
auto cmp=len<=>setup.wLength;
if (cmp>0) len=setup.wLength; // limit data length according to setup-requested length
for(;;){
unsigned l=len; if (l>EP0S) l=EP0S;
toPMA(offsetof(PMA_USAGE,ep0t),data,l);
BTABLE[0].COUNT_TX=l;
SET_TX_STATUS(0,EP_TX_VALID); // absenden
data+=l;
len-=l;
if (!(len || cmp<0 && l==EP0S)) break; // raus wenn fertig, aber Null-Byte-Paket nicht vergessen
if (!waitSent0()) return false;// warten bis abgeholt, raus bei USB-Reset u.ä.
}
USB.EPR[0]=0x8380; // EP_KIND setzen: Nur Null-Byte-OUT akzeptieren
return true;
}
static bool ack0() {
BTABLE[0].COUNT_TX=0;
SET_TX_STATUS(0,EP_TX_VALID);
return true;
}
static bool waitRecv(byte ep) {
auto&epr=USB.EPR[ep];
while (!(epr&EP_CTR_RX)) {
if (USB.ISTR&(ISTR_RESET|ISTR_SUSP)) return false;
idle();
}
epr=0x0280;
return true;
}
/*
static bool onHidOut(byte repid) {
if (repid) return false;
unsigned len=BTABLE[0].COUNT_RX&0x1FF;
if (len>sizeof repO0) return false;
fromPMA(offsetof(PMA_USAGE,ep0r),repO0,len);
USB.EPR[0]=0x9280; // change NAK to VALID
// if (!((flashAddress+=len)&(PAGE_SIZE-1))) { // entire page received?
// }else SET_RX_STATUS(0,EP_RX_VALID); // expect next packet
return true;
}
*/
bool recv(byte ep, byte*data,unsigned length,unsigned*pLen) {
for (unsigned len=0; len<length;) {
if (!waitRecv(ep)) return false; // USB-Reset oder sonstwas doofes
uint16_t l=BTABLE[ep].COUNT_RX&0x1FF;
if (l>length-len) return false; // Babble
if (!l) return pLen ? *pLen=len, true : false;
// Null-Byte-Paket: Je nach pLen erlaubt oder nicht
// TODO: Short-Packet-Erkennung (erfordert ggf. weiteren Parameter)
fromPMA(offsetof(PMA_USAGE,ep0r),data,l); break;
USB.EPR[ep]=0x9280; // change NAK to VALID
len+=l;
data+=l;
}
return true;
}
//s muss ein gültiger, nullterminierter UTF8-String sein,
//der in nicht mehr als 126 UTF16-Zeichen konvertiert
static byte buildStringDesc(byte*d,const char*s) {
auto*p=reinterpret_cast<char16_t*>(d+2);
d[1]=3;
for (uint32_t c; c=*s++; *p++=c) {
if (!(c&0x80)) continue;// 0000 0000 0000 0000 0000 0000 0aaa aaaa
c=c<<6|*s++&0x3F; // 0000 0000 0000 0000 0011 ?aaa aabb bbbb
if (c&0x800) { // 3-Byte-UTF8: 0000 0000 0000 0000 0011 !?aa aabb bbbb
c=c<<6|*s++&0x3F; // 0000 0000 0000 111? aaaa bbbb bbcc cccc
if (c&0x10000) { // 4-Byte-UTF8: 1111 1111 1111 111! 0aaa bbbb bbcc cccc
c=c<<6|*s++&0x3F; // 1111 1111 110a aabb bbbb cccc ccdd dddd
c-=0x10000; // 1111 1111 1100 AABB bbbb cccc ccdd dddd
*p++=0xD800|c>>10&0x3FF; // High Surrogate 1101 10AA BBbb bbcc
c=0xDC00|c&0x3FF; // Low Surrogate 1101 11cc ccdd dddd
}
}else{ // 2-Byte-UTF8: 0000 0000 0000 0000 0011 0aaa aabb bbbb
c&=0x7FF; // 0000 0000 0000 0000 0000 0aaa aabb bbbb
}
}
return d[0]=static_cast<byte>(reinterpret_cast<byte*>(p)-d);
}
static bool onGetDescriptor() {
const byte*desc;
unsigned length = 0;
switch (setup.wValueH) {
case 1: desc = DeviceDesc; break;
case 2: desc = ConfigDesc; length = sizeof ConfigDesc; break;
case 0x0F: desc = BosDesc; length = sizeof BosDesc; break;
case 0x21: desc = ConfigDesc+9+9; break;
case 0x22: desc = HidRepDesc; length = sizeof HidRepDesc; break;
case 3: {
byte buf[256];
switch (setup.wValueL) {
case 0: buildStringDesc(buf,"\xD0\x87"); break; // 0x0407 = deutsch
case 1: buildStringDesc(buf,"Henrik Haftmann"); break;
case 2: buildStringDesc(buf,"Hüftgelenkmontage-Einschlag-Apparat „hea“ 🔨"); break;
case 3: buildStringDesc(buf,"heha@hrz.tu-chemnitz.de"); break;
case 4: buildStringDesc(buf,"Einschlag-HID"); break; // Windoofs: Darf nur ASCII enthalten!!
case 5: buildStringDesc(buf,"⭐WebUSB"); break;
case 6: buildStringDesc(buf,"Minimum"); break;
case 7: buildStringDesc(buf,"Maximum"); break;
case 8: buildStringDesc(buf,"Mittelwert"); break;
case 9: buildStringDesc(buf,"Varianz"); break;
case 101: buildStringDesc(buf,"Brems-Poti/%"); break;
case 102: buildStringDesc(buf,"Kraft/kN"); break;
case 103: buildStringDesc(buf,"Weg-Poti/mm"); break;
case 104: buildStringDesc(buf,"ADC6"); break;
case 105: buildStringDesc(buf,"ADC1"); break;
case 106: buildStringDesc(buf,"Messschieber/mm"); break;
case 107: buildStringDesc(buf,"Kraftstoß/Ns"); break;
case 108: buildStringDesc(buf,"Kraft-Maximum/kN"); break;
case 109: buildStringDesc(buf,"Verschiebeweg/mm"); break;
default: return false;
}
desc=buf;
}break;
default: return false;
}
if (!length) length = desc[0];
return send0(desc, length);
}
static void initEP(uint32_t ep) {
MOD_REG(USB.EPR[ep&0x0F],EP_CTR_RX|EP_T_FIELD|EP_KIND|EP_CTR_TX|EPADDR_FIELD,ep);
}
static void onReset() {
/* Initialize Endpoint 0 */
initEP(EP_CONTROL|EP_RX_VALID);
USB.DADDR = DADDR_EF; // device address = 0 and enable USB function
}
// USB dual-port RAM memory map:
// ARM adr USB adr size (all hex)
// 40006000 0 20 buffer descriptor table (4 entries)
// 40006030 20 40 EP0RX buffer (SETUP and hid-OUT data)
// 400060B0 60 40 EP0TX buffer (IN data, mostly descriptors)
// A0 40 EP1RX
// 40006130 E0 40 EP1TX buffer (interrupt IN)
// 120 40 EP2RX
// 160 40 EP2TX
// 40006140 1A0 60 free for more buffers (not much!!)
// 40006400 200 - end of dual-port RAM
byte DevStatus;
static bool onSetup() {
byte stat[2]={0,0};
switch (setup.wRequest) {
case 0x0080: stat[0]=usb::DevStatus; return send0(stat,2); // Get Status (In, Standard, Device)
case 0x0081: return send0(stat,2); // Get Status (In, Standard, Iface)
case 0x0082: { // Get Status (In, Standard, Endpoint)
auto epr=USB.EPR[setup.wIndexL&0x0F];
if (setup.wIndexL&0x80) epr<<=8; // IN-Endpoint? TX (Low-Byte) nach RX (High-Byte) schieben
if (epr&EPRX_STAT==EP_RX_STALL) stat[0]|=1; // Bit 0 = 1 wenn STALL
return send0(stat,2);
}
case 0x0100: if (setup.wValue!=1) return false;// Clear Feature (Out, Standard, Device)
usb::DevStatus&=~2; return ack0(); // Remote Wakeup wegnehmen
case 0x0102: if (setup.wValue) return false; // Clear Feature (Out, Standard, Endpoint)
{
auto&epr=USB.EPR[setup.wIndexL&0x0F];
if (setup.wIndexL&0x80) SET_TX_STATUS(epr,EP_TX_NAK);
else SET_RX_STATUS(epr,EP_RX_VALID);
}return ack0();
case 0x01C0: return send0(&UrlDesc[0],UrlDesc[0]);
case 0x02C0: return send0(MsOs20Desc,sizeof MsOs20Desc);
case 0x0300: if (setup.wValue!=1) return false; // Set Feature (Out, Standard, Device)
usb::DevStatus|=2; return ack0(); // Remote Wakeup unterstützen
case 0x0302: if (setup.wValue) return false; // Set Feature (Out, Standard, Endpoint)
{
auto&epr=USB.EPR[setup.wIndexL&0x0F];
if (setup.wIndexL&0x80) SET_TX_STATUS(epr,EP_TX_STALL);
else SET_RX_STATUS(epr,EP_RX_STALL);
}return ack0();
case 0x0500: { // Set Address (Out, Standard, Device)
ack0();
if (waitSent0()) USB.DADDR = DADDR_EF|setup.wValueL;
}return true;
case 0x0680: // Get Descriptor (In, Standard, Device)
case 0x0681: return onGetDescriptor(); // Get Descriptor (In, Standard, Iface)
case 0x0880: return send0(&DevConfig,1); // Get Config (In, Standard, Device)
case 0x0900: DevConfig=setup.wValueL; // Set Config (Out, Standard, Device)
initEP(EP_RX_NAK|EP_INTERRUPT|EP_TX_NAK|1); // Reset toggles
initEP(EP_RX_NAK|EP_BULK|EP_TX_NAK|2);
return ack0();
case 0x0A81: return send0(stat,1); // Get Iface (In, Standard, Iface)
case 0x0B01: return !setup.wValueL && ack0();// Set Iface (Out, Standard, Iface)
case 0x01A1: return onGetReport(setup); // Get Report (In, Class, Iface)
case 0x0921: return onSetReport(setup) && ack0();// Set Report (Out, Class, Iface): don't stall, data follows
}
return false;
}
static void onEp0() {
uint16_t epr = USB.EPR[0];
if (epr&EP_CTR_RX) { // OUT and SETUP packets (data reception)
if (epr&USB_EP0R_SETUP) {
fromPMA(offsetof(PMA_USAGE,ep0r),setup,8); // pull setup data
USB.EPR[0]=0x1280; // clear EP_CTR_RX bit and change NAK to VALID
if (!onSetup()) USB.EPR[0]=0x0230; // Tx: Change NAK to STALL
}else{
USB.EPR[0]=0x1280; // clear EP_CTR_RX bit and change NAK to VALID
}
SET_RX_STATUS(0,EP_RX_VALID);
}
// Check for transferred IN packets, i.e. “emptied-out” PMA buffers
// Normally, more data has to been sent here.
// But as all descriptors are less than 64 Bytes in size, no more data has to be processed here.
if (epr&EP_CTR_TX) { // Something sent on EP0
if (setup.wRequest==0x0500) USB.DADDR = DADDR_EF|setup.wValueL; // setup data is still available
USB.EPR[0]=0x8200; // clear EP_CTR_TX bit
}
}
static void onEp1() {
USB.EPR[1]=1|EP_INTERRUPT; // Endpoint-Interrupt löschen (Hauptschleife weniger belasten)
}
static void onEp2() {
USB.EPR[2]=2|EP_BULK; // Endpoint-Interrupt löschen (Hauptschleife weniger belasten)
}
static void onEndpoint(uint16_t status) {
switch (status&USB_ISTR_EP_ID) {
case 0: onEp0(); break;
case 1: onEp1(); break;
case 2: onEp2(); break;
}
}
void shutdown() {
USB.CNTR = CNTR_FRES|CNTR_PDWN; // USB-Schaltungsteil abschalten
}
void init() {
DevConfig = 0;
USB.CNTR = CNTR_FRES; // PowerDown-Bit löschen
USB.CNTR = 0; // Normalzustand
// USB-Puffertabelle initialisieren (ändert sich später nie)
BTABLE[0].ADDR_TX = offsetof(PMA_USAGE,ep0t); // 0x400060C0
BTABLE[0].ADDR_RX = offsetof(PMA_USAGE,ep0r); // 0x40006040
BTABLE[0].setRxBufSize(EP0S);
BTABLE[1].ADDR_TX = offsetof(PMA_USAGE,ep1t); // 0x40006200
BTABLE[1].ADDR_RX = offsetof(PMA_USAGE,ep1r);
if (EP1RS) BTABLE[1].setRxBufSize(EP1RS);
BTABLE[2].ADDR_TX = offsetof(PMA_USAGE,ep2t);
BTABLE[2].ADDR_RX = offsetof(PMA_USAGE,ep2r);
if (EP2RS) BTABLE[2].setRxBufSize(EP2RS);
}
void poll() {
uint16_t istr = USB.ISTR; // save interrupts
USB.ISTR = 0; // clear interrupts
if (istr&ISTR_CTR) onEndpoint(istr); // istr contains endpoint number
if (istr&ISTR_RESET) onReset();
if (istr&ISTR_SUSP) {
USB.CNTR = CNTR_FSUSP|CNTR_LPMODE;
::revert_sysclock();
USB.DADDR = 0; // disable function and reset USB address
LED = 1; // LED aus
}
if (istr&ISTR_WKUP) {
::set_sysclock();
USB.CNTR = 0;
}
if (istr&(ISTR_SOF|ISTR_ESOF)) {
LED = BB_BIT(USB.FNR,9);
}
}
}//namespace
Detected encoding: UTF-8 | 0
|