#include "usbd.h"
//#include <usb.h>
namespace usb{
#pragma DATA_SECTION("UsbARegs")
volatile Regs RegsA;
bool tDevice::Init() {
tDevice::onReset(); // Direktaufruf unter Umgehung von "virtual"
RegsA.GPCS = 0x03; // als Device
RegsA.POWER = 0x40; // Pullup aktivieren = verbinden
return true;
}
void tDevice::Term() {
RegsA.POWER = 0; // Pullup deaktivieren = trennen
}
bool tDevice::SendDataEP0(const uint8_t *data, unsigned size,bool packed) {
do{
while (RegsA.CSR & 0x02) {
idle();
if (RegsA.IS&0x27) return false; // Disconnect, Reset, Resume, Suspend: Raus!
}
unsigned sz=size>64?64:size;
for (unsigned i=sz; i;) {
if (packed && i>1) {
RegsA.FIFO[0].w = *data++; i-=2;
}else{
RegsA.FIFO[0].b = *data++; --i;
}
}
RegsA.CSR = sz<64 ? 0x0A : 0x02;
size-=sz;
}while(size);
return true;
}
bool tDevice::SendDataEP0(const uint8_t *data, unsigned size, const tRequest&rq,bool packed) {
rq.limitLength(size);
return SendDataEP0(data, size, packed);
}
void tDevice::pollEP0() {
tRequest rq;
for (unsigned i=0; i<sizeof rq; i++) {
rq[i] = RegsA.FIFO[0].b;
}
RegsA.CSR = rq.bmRequestType&0x80 ? 0x40 : 0x48; // Lösche RXRDY mit oder ohne DATAEND
uint8_t answer[2]={0,0};
switch (rq.wRequest()) {
case 0x0080: { // GetStatus-Device
answer[0]=devstat;
SendDataEP0(answer,2);
}return;
case 0x0081: { // GetStatus-Interface(wIndexL)
SendDataEP0(answer,2);
}return;
case 0x0082: { // GetStatus-Endpoint(wIndexL)
if (rq.wIndexL != 0x81) goto stall;
//Stall-Zustand von EP1IN abfragen
if (RegsA.ep[1].TXCSR&0x10) answer[0]=0x01;
SendDataEP0(answer,2);
}return;
case 0x0100: { // ClearFeature-Device
if (rq.wValueL != 1) goto stall;
devstat &= ~2; // Clear the remote wake up state.
}return;
case 0x0102: { // ClearFeature-Endpoint(wIndexL)
if (rq.wIndexL != 0x81) goto stall; // Nur EP1IN
if (rq.wValueL != 0) goto stall; // nur HALT
RegsA.ep[1].TXCSR&=~0x30; // Stall + Stalled löschen
RegsA.ep[1].TXCSR|= 0x40; // DataToggle löschen
}return;
case 0x0300: { // SetFeature-Device
if (rq.wValueL != 1) goto stall;
devstat |= 2; // Set the remote wake up state
}return;
case 0x0302: { // SetFeature-Endpoint(wIndexL)
if (rq.wIndexL != 0x81) goto stall; // Nur EP1IN
if (rq.wValueL != 0) goto stall; // nur HALT
RegsA.ep[1].TXCSR |= 0x10; // Stall Endpoint
}return;
case 0x0500: { // SetAddress-Device
while (!(RegsA.TXIS&1)) {
idle();
if (RegsA.IS&4) return;
};
// Dieser Byte-Zugriff mussen über HWREGB bzw. __byte(,0) gehen!
// Das erledigt die usb::Regs::byte-Klasse
// Mit Wortzugriff würde RegsA.POWER beeinflusst werden, hier auf 0 gesetzt werden
RegsA.FADDR=rq.wValueL; // USBDevAddrSet(BASE,rq.wValueL);
}return;
case 0x0680: // GetDescriptor-Device (für HID-Deskriptor aus UsbTreeView)
case 0x0681: { // GetDescriptor-Interface (für HID-Deskriptor aus Windows)
if (cbGetDescriptor(rq)) return;
}goto stall;
case 0x0700: goto stall; // SetDescriptor-Device
case 0x0880: { // GetConfig-Device
answer[0] = config;
SendDataEP0(answer,1);
}return;
case 0x0900: { // SetConfig-Device
if (rq.wValueL>1) goto stall; // nur 0 oder 1 erlaubt
if ((config = rq.wValueL)) {
// Konfiguriere EP1IN
RegsA.ep[1].TXMAXP = 64;
RegsA.ep[1].TXCSR = 0x40; // Clear Stall
RegsA.EPIDX = 1; // Endpoint 1
RegsA.TXFIFOSZ = 3; // 64 Byte, Einfachpufferung
RegsA.TXFIFOADDR = 8; // 64 Byte für EP0 freihalten
}
}return;
case 0x0A81: { // GetInterface-Interface(wIndexL)
if (rq.wIndexL>1) goto stall;
SendDataEP0(answer,1); // Einfaches Beispiel hat nur eine Alternate Setting
}return;
case 0x0B01: { // SetInterface-Interface(wIndexL)
// bmRequestType=0x01, wIndexL=Interface, wValueL=AltSetting, wLength=0
if (rq.wIndexL>1) goto stall;
if (rq.wValueL) goto stall;
}return;
case 0x0C82: goto stall; // SyncFrame-Endpoint(wIndexL)
// bmRequestType=0x82, wIndexL=Endpoint, wLength=2: Antwort = FrameNumber
default: if (cbRequestHandler(rq)) return;
}
stall:
RegsA.CSR|=0x60; // Stall + Clear RxRdy
}
void tDevice::poll() {
unsigned sta = RegsA.IS;
if (sta & 0x04) onReset();
if (sta & 0x20) onDisconnect();
if (sta & 0x08) onSOF();
unsigned IntStatusEP = RegsA.TXIS;
if (IntStatusEP & 0x0001) { // EP0 RX+TX?
unsigned sta = RegsA.CSR;//USBEndpointStatus(BASE, 0);
if (sta & 0x01) pollEP0(); // Ob es ein SETUP-Paket war ist nicht rauszukriegen!
// USBDevEndpointDataAck(BASE, USB_EP_0, true);
if (sta & 0x04) RegsA.CSR&= 0x04;
// USBDevEndpointStatusClear(BASE, USB_EP_0, USB_DEV_EP0_SENT_STALL);
}
}
#define PACK(l,h) (h)<<8|(l)
#define UNPACKED(a,b) sizeof a*2-b
// Report-Deskriptor für eine (bootfähige) Maus
static const uint16_t ReportDesc[] = {
PACK(0x05,1), // UsagePage(GENERIC_DESKTOP)
PACK(0x09,2), // Usage(MOUSE),
PACK(0xA1,1), // Collection(APPLICATION)
PACK(0x09,1), // Usage(POINTER)
PACK(0xA1,0), // Collection(Physical)
// 3 Knöpfe
PACK(0x05,9), // UsagePage(BUTTONS)
PACK(0x19,1), // UsageMinimum(1)
PACK(0x29,3), // UsageMaximum(3)
PACK(0x15,0), // LogicalMinimum(0)
PACK(0x25,1), // LogicalMaximum(1)
PACK(0x75,1), // ReportSize(1)
PACK(0x95,3), // ReportCount(3)
PACK(0x81,2), // Input(VAR,ABS)
PACK(0x75,5), // ReportSize(5)
PACK(0x95,1), // ReportCount(1)
PACK(0x81,3), // Input(CONST|ARY|ABS)
// X- und Y-Achse
PACK(0x05,1), // UsagePage(GENERIC_DESKTOP)
PACK(0x09,0x30), // Usage(X),
PACK(0x09,0x31), // Usage(Y),
PACK(0x15,(uint8_t)-127), // LogicalMinimum(-127)
PACK(0x25,127), // LogicalMaximum(127)
PACK(0x75,8), // ReportSize(8)
PACK(0x95,2), // ReportCount(2)
PACK(0x81,6), // Input(DATA|VAR|REL)
PACK(0xC0, // EndCollection
0xC0), // EndCollection
};
// Device-Deskriptor
static const uint16_t DeviceDesc[] = {
PACK(18, // bLength
1), // bDescType Device
0x210, // bcdUsbVersion 2.1 (erforderlich für WebUSB)
PACK(0, // bDeviceClass "siehe Interfaces" = Multifunktion
0), // bDeviceSubclass
PACK(0, // bDeviceProtocol
64), // bMaxPacketSize0 64 Byte
0x1CBE, // Vendor ID Texas Instruments
1234, // Product ID irgendwas
0x110, // bcdDeviceVersion
PACK(1, // iVendor "TI"
2), // iProduct "Maus"
PACK(0, // iSerialNo nicht erforderlich
1), // bNumConfig Keine Alternate Settings
};
// Config-Deskriptor
static const uint8_t ConfigDesc[9+9+9+7+9] = {
9, // bLength
2, // bDescType Config
USB2(sizeof ConfigDesc), // wTotalLength
2, // bNumInterfaces HID + WebUSB
1, // bConfigValue
0, // iConfig kein Text
0xC0, // bmAttribute SelfPower
50, // bMaxPower 100 mA
// Interface-Deskriptor
9, // bLength
4, // bDescType Interface
0, // bIfaceNumber
0, // bAltSetting
1, // bNumEndpoints
3, // bIfaceClass HID
0, // bIfaceSubclass
0, // bIfaceProtocol
0, // iIface kein Text
// HID-Deskriptor
9, // bLength
0x21, // bDescType HID
USB2(0x110), // bcdHID 1.10
0, // bCountryCode weltweit
1, // bNumDescriptors
0x22, // bDescType REPORT
USB2(UNPACKED(ReportDesc,0)), // wDescLength
// Interrupt IN Endpoint-Deskriptor
7, // bLength
5, // bDescType Endpoint
0x81, // bEndpointAddress EP1IN
0x03, // bmAttributes interrupt
USB2(64), // wMaxPacketSize 64
16, // bInterval 16 ms
// Interface-Deskriptor
9, // bLength
4, // bDescType Iface
1, // bIfaceNumber
0, // bAlternateSetting (only one)
0, // bNumEndpoints
0xFF, // bIfaceClass keine Klasse
0, // bIfaceSubclass
0, // bIfaceProtocol
3, // iIface
};
// erforderlich für WebUSB:
static const uint8_t BOS_DESCRIPTOR[] = {
5, // Length
0x0F, // Binary Object Store descriptor
USB2(57), // Total length
2, // Number of device capabilities
// WebUSB Platform Capability descriptor (bVendorCode == 0x01).
0x18, // Length
0x10, // Device Capability descriptor
0x05, // Platform Capability descriptor
0x00, // Reserved
USB4(0x3408B638),USB2(0x09A9),USB2(0x47A0),USB2(0xFD8B),0xA0,0x76,0x88,0x15,0xB6,0x65, // WebUSB GUID
USB2(0x0100), // Version 1.0
0x01, // Vendor request code (-> bRequest)
0x01, // Landing page (-> wValueL)
// Microsoft OS 2.0 Platform Capability Descriptor (MS_VendorCode == 0x02)
0x1C, // Length
0x10, // Device Capability descriptor
0x05, // Platform Capability descriptor
0x00, // Reserved
USB4(0xD8DD60DF),USB2(0x4589),USB2(0x4CC7),USB2(0xD29C),0x65,0x9D,0x9E,0x64,0x8A,0x9F, // MS OS 2.0 GUID
USB4(0x06030000), // Windows version
USB2(0x00B2), // Descriptor set length
0x02, // Vendor request code
0x00 // Alternate enumeration code
};
// erforderlich für WebUSB für Windows
static const uint16_t MS_OS_20_DESCRIPTOR[] = {
// Microsoft OS 2.0 descriptor set header (table 10)
0x000A, // Descriptor size (10 bytes)
0x0000, // MS OS 2.0 descriptor set header
0x0000,
0x0603, // Windows version (8.1) (0x06030000)
0x00B2, // Size, MS OS 2.0 descriptor set
// Microsoft OS 2.0 configuration subset header
0x0008, // Descriptor size (8 bytes)
0x0001, // MS OS 2.0 configuration subset header
0x0000, // bConfigurationValue
0x00A8, // Size, MS OS 2.0 configuration subset
// Microsoft OS 2.0 function subset header
0x0008, // Descriptor size (8 bytes)
0x0002, // MS OS 2.0 function subset header
0x0001, // interface number
0x00A0, // Size, MS OS 2.0 function subset
// Microsoft OS 2.0 compatible ID descriptor (table 13)
20, // wLength
3, // MS_OS_20_FEATURE_COMPATIBLE_ID
PACK('W','I'),PACK('N','U'),PACK('S','B'),0,
0,0,0,0,
4+21+1+40<<1,//wLength:
4, // wDescriptorType: MS_OS_20_FEATURE_REG_PROPERTY: 0x04 (Table 9)
7, //wPropertyDataType: REG_MULTI_SZ (Table 15)
21<<1, //wPropertyNameLength:
'D','e','v','i','c','e','I','n','t','e',
'r','f','a','c','e','G','U','I','D','s',0, //bPropertyName
USB2(40<<1), // wPropertyDataLength
'{','9','7','5','F','4','4','D','9','-',
'0','D','0','8','-','4','3','F','D','-',
'8','B','3','E','-','1','2','7','C','A',
'8','A','F','F','F','9','D','}', 0, 0, //bPropertyData
};
// erforderlich für WebUSB
static const uint16_t LandingPageUrlDesc[] = {
PACK(0x2D, // strlen(landingPageUrl)+3
0x03), // descriptorType(String)
PACK(0x01, // landingPageScheme(https://)
'w'),PACK('w','w'),PACK('.','t'),PACK('u','-'),
PACK('c','h'),PACK('e','m'),PACK('n','i'),PACK('t','z'),
PACK('.','d'),PACK('e','/'),PACK('~','h'),PACK('e','h'),
PACK('a','/'),PACK('e','w'),PACK('a','/'),PACK('M','o'),
PACK('t','o'),PACK('r','/'),PACK('a','p'),PACK('p','.'),
PACK('h','t'),'m'};
#undef PACK
bool tMyDevice::cbRequestHandler(const tRequest&rq) {
switch (rq.wRequest()) {
case 0x01A1: if ( // GET_REPORT, class-specific, target=interface
rq.wIndexL == 0 // Interface 0
&& rq.wValueH == 0x01 // report_type = INPUT
&& rq.wValueL == 0) { // report_id
RegsA.CSR = 0x48; // Lösche RXRDY mit DATAEND
// USBDevEndpointDataAck(BASE, USB_EP_0, true);
return SendDataEP0(inrep,3,rq);
}break;
case 0x06A1: return cbGetDescriptor(rq);
case 0x02C0: return SendDataEP0(MS_OS_20_DESCRIPTOR,UNPACKED(MS_OS_20_DESCRIPTOR,0),rq,true);
case 0x01C0: return SendDataEP0(LandingPageUrlDesc,UNPACKED(LandingPageUrlDesc,1),rq);
}
return tDevice::cbRequestHandler(rq); // chain down
}
static void buildStringDesc(uint8_t d[256],const char*s) {
uint8_t*p=d+1;
*p++=3;
wchar_t c;
while ((c=*s++)) {
if (c&0x80) {
if (c&0x20) { // 3-Byte-UTF-8
c<<=6;
c|=*s++&0x3F;
}else c&=0x3F; // 2-Byte-UTF-8
c<<=6;
c|=*s++&0x3F;
}
*p++=c&0xFF;
*p++=c>>8&0xFF;
}
// assert(p-d<256);
*d=p-d;
}
bool tMyDevice::cbGetDescriptor(const tRequest&rq) {
switch (rq.wValueH) {
case 1: return SendDataEP0(DeviceDesc, UNPACKED(DeviceDesc,0), rq,true); // device descriptor
case 2: return SendDataEP0(ConfigDesc, sizeof ConfigDesc, rq); // configuration descriptor
case 3: {
uint8_t sd[256];
switch (rq.wValueL) { // string descriptor
// Bug+Workaround: C++2000 unterstützt weder L"\x0409" noch L"\u0409" richtig, nimmt nur Low-Byte
// Bug+Workaround: C++2000 unterstützt kein L"Ä", speichert UTF-8, daher UTF-8-Strings
case 0: buildStringDesc(sd,"\xD0\x87"); break; // 0x0407 deutsch
case 1: buildStringDesc(sd,"Texas Instruments"); break;
case 2: buildStringDesc(sd,"Mausbeispiel: ÄÖÜäöüß"); break;
case 3: buildStringDesc(sd,"WebUSB-Motor"); break;
default: return false;
}
return SendDataEP0(sd,sd[0],rq);
}
case 0x0F: return SendDataEP0(BOS_DESCRIPTOR,sizeof BOS_DESCRIPTOR, rq);
case 0x21: return SendDataEP0(ConfigDesc+9+9, 9, rq); // wird nie verlangt
case 0x22: return SendDataEP0(ReportDesc, UNPACKED(ReportDesc,0), rq, true);
}
return tDevice::cbGetDescriptor(rq);
}
bool tMyDevice::StateChange(uint8_t Buttons, int8_t DeltaX, int8_t DeltaY) {
inrep[0]=Buttons;
inrep[1]=(uint8_t)DeltaX;
inrep[2]=(uint8_t)DeltaY;
return SendReport(inrep,3);
}
bool tMyDevice::SendReport(uint8_t *data, unsigned len) {
// len ≤ 64
if (RegsA.ep[1].TXCSR&0x01) return false;
for (unsigned i=0; i<len; i++) RegsA.FIFO[1].b = *data++;
RegsA.ep[1].TXCSR|=0x01; // Paket sendebereit
return true;
}
}
Vorgefundene Kodierung: UTF-8 | 0
|