#include "usbd.h"
#include <usb.h>
extern "C" {
#include <debug.h> // ASSERT
}
namespace usb{
#pragma DATA_SECTION("UsbARegs")
volatile Regs RegsA;
bool tDevice::Init() {
tDevice::onReset(); // Direktaufruf unter Umgehung von "virtual"
USBDevMode(BASE); // Force device mode
USBDevConnect(BASE);
return true;
}
void tDevice::Term() {
USBDevDisconnect(BASE);
}
bool tDevice::SendDataEP0(const uint8_t *data, unsigned size) {
do{
unsigned sz=size>64?64:size;
while (USBEndpointDataPut(BASE, 0, const_cast<uint8_t*>(data), sz)) {
idle();
if (USBIntStatusControl(BASE)&USB_INTCTRL_RESET) return false;
}
USBEndpointDataSend(BASE, 0, sz==64 ? USB_TRANS_IN : USB_TRANS_IN_LAST);
data+=sz;
size-=sz;
}while(size);
return true;
}
bool tDevice::SendDataEP0(const uint8_t *data, unsigned size, const tRequest&rq) {
rq.limitLength(size);
return SendDataEP0(data, size);
}
void tDevice::pollEP0() {
tRequest rq;
uint32_t ui32Size = sizeof rq;
// Get the data from the USB controller end point 0.
USBEndpointDataGet(BASE, 0, rq,&ui32Size);
// If there was a null setup packet then just return.
if (!ui32Size) return;
USBDevEndpointDataAck(BASE, 0, !(rq.bmRequestType&0x80));
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;
//TODO: Stall-Zustand von EP1IN abfragen
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
USBDevEndpointStallClear(BASE,1<<4,USB_EP_DEV_IN);
USBEndpointDataToggleClear(BASE,1<<4,USB_EP_DEV_IN);
}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
USBDevEndpointStall(BASE,1<<4,USB_EP_DEV_IN);
}return;
case 0x0500: { // SetAddress-Device
while (!(RegsA.TXIS&1)) {
idle();
if (HWREGB(&RegsA.IS)&4) return;
};
// Byte-Zugriffe müssen über HWREGB bzw. __byte(,0) gehen!
HWREGB(&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)
// Need to ACK the data on end point 0 without setting last data as there will be a data phase.
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
USBDevEndpointConfigSet(BASE,1<<4,64,USB_EP_MODE_INT/*|USB_EP_AUTO_SET*/);
// EP0 verwendet die ersten 64 Bytes, EP1 kommt danach:
USBFIFOConfigSet(BASE,1<<4,64,64,USB_EP_DEV_IN);
}
}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:
USBDevEndpointStall(BASE, 0, USB_EP_DEV_OUT);
}
void tDevice::poll() {
uint32_t IntStatusEP,sta = USBIntStatus(BASE,&IntStatusEP);
if (sta & USB_INTCTRL_RESET) onReset();
if (sta & USB_INTCTRL_DISCONNECT) onDisconnect();
if (sta & USB_INTCTRL_SOF) onSOF();
if (IntStatusEP & USB_INTEP_0) {
uint32_t sta = USBEndpointStatus(BASE, 0);
if (sta & USB_DEV_EP0_OUT_PKTRDY) pollEP0();
// USBDevEndpointDataAck(BASE, USB_EP_0, true);
if (sta & USB_DEV_EP0_SENT_STALL)
USBDevEndpointStatusClear(BASE, USB_EP_0, USB_DEV_EP0_SENT_STALL);
}
}
// Report-Deskriptor für eine (bootfähige) Maus
static const uint8_t ReportDesc[] = {
0x05,1, // UsagePage(GENERIC_DESKTOP)
0x09,2, // Usage(MOUSE),
0xA1,1, // Collection(APPLICATION)
0x09,1, // Usage(POINTER)
0xA1,0, // Collection(Physical)
// 3 Knöpfe
0x05,9, // UsagePage(BUTTONS)
0x19,1, // UsageMinimum(1)
0x29,3, // UsageMaximum(3)
0x15,0, // LogicalMinimum(0)
0x25,1, // LogicalMaximum(1)
0x75,1, // ReportSize(1)
0x95,3, // ReportCount(3)
0x81,2, // Input(VAR,ABS)
0x75,5, // ReportSize(5)
0x95,1, // ReportCount(1)
0x81,3, // Input(CONST|ARY|ABS)
// X- und Y-Achse
0x05,1, // UsagePage(GENERIC_DESKTOP)
0x09,0x30, // Usage(X),
0x09,0x31, // Usage(Y),
0x15,(uint8_t)-127, // LogicalMinimum(-127)
0x25,127, // LogicalMaximum(127)
0x75,8, // ReportSize(8)
0x95,2, // ReportCount(2)
0x81,6, // Input(DATA|VAR|REL)
0xC0, // EndCollection
0xC0, // EndCollection
};
// Device-Deskriptor
static const uint8_t DeviceDesc[] = {
18, // bLength
1, // bDescType Device
USB2(0x210), // bcdUsbVersion 2.1 (erforderlich für WebUSB)
0, // bDeviceClass "siehe Interfaces" = Multifunktion
0, // bDeviceSubclass
0, // bDeviceProtocol
64, // bMaxPacketSize0 64 Byte
USB2(0x1CBE), // Vendor ID Texas Instruments
USB2(1234), // Product ID irgendwas
USB2(0x110), // bcdDeviceVersion
1, // iVendor "TI"
2, // iProduct "Maus"
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(sizeof ReportDesc), // 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 uint8_t MS_OS_20_DESCRIPTOR[] = {
// Microsoft OS 2.0 descriptor set header (table 10)
USB2(0x000A), // Descriptor size (10 bytes)
USB2(0x0000), // MS OS 2.0 descriptor set header
USB4(0x06030000), // Windows version (8.1) (0x06030000)
USB2(0x00B2), // Size, MS OS 2.0 descriptor set
// Microsoft OS 2.0 configuration subset header
USB2(0x0008), // Descriptor size (8 bytes)
USB2(0x0001), // MS OS 2.0 configuration subset header
USB2(0x0000), // bConfigurationValue
USB2(0x00A8), // Size, MS OS 2.0 configuration subset
// Microsoft OS 2.0 function subset header
USB2(0x0008), // Descriptor size (8 bytes)
USB2(0x0002), // MS OS 2.0 function subset header
USB2(0x0001), // interface number
USB2(0x00A0), // Size, MS OS 2.0 function subset
// Microsoft OS 2.0 compatible ID descriptor (table 13)
USB2(20), // wLength
USB2(3), // MS_OS_20_FEATURE_COMPATIBLE_ID
'W','I','N','U','S','B',0,0,
0,0,0,0,0,0,0,0,
USB2(4+21+1+40<<1),//wLength:
USB2(4), // wDescriptorType: MS_OS_20_FEATURE_REG_PROPERTY: 0x04 (Table 9)
USB2(7), //wPropertyDataType: REG_MULTI_SZ (Table 15)
USB2(21<<1), //wPropertyNameLength:
'D',0,'e',0,'v',0,'i',0,'c',0,'e',0,'I',0,'n',0,'t',0,'e',0,
'r',0,'f',0,'a',0,'c',0,'e',0,'G',0,'U',0,'I',0,'D',0,'s',0,0,0, //bPropertyName
USB2(40<<1), // wPropertyDataLength
'{',0,'9',0,'7',0,'5',0,'F',0,'4',0,'4',0,'D',0,'9',0,'-',0,
'0',0,'D',0,'0',0,'8',0,'-',0,'4',0,'3',0,'F',0,'D',0,'-',0,
'8',0,'B',0,'3',0,'E',0,'-',0,'1',0,'2',0,'7',0,'C',0,'A',0,
'8',0,'A',0,'F',0,'F',0,'F',0,'9',0,'D',0,'}',0, 0, 0, 0, 0, //bPropertyData
};
// erforderlich für WebUSB
static const char LandingPageUrlDesc[] =
"\x2D" // strlen(landingPageUrl)+3
"\x03" // descriptorType(String)
"\x01" // landingPageScheme(https://)
"www.tu-chemnitz.de/~heha/ewa/Motor/app.htm";
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
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,sizeof MS_OS_20_DESCRIPTOR,rq);
case 0x01C0: return SendDataEP0(reinterpret_cast<const uint8_t*>(LandingPageUrlDesc),LandingPageUrlDesc[0],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, sizeof DeviceDesc, rq); // 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, sizeof ReportDesc, rq);
}
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) {
int32_t i32Retcode = USBEndpointDataPut(BASE,USB_EP_1,data,len);
if (!i32Retcode) {
i32Retcode = USBEndpointDataSend(BASE,USB_EP_1,USB_TRANS_IN);
}
return i32Retcode==0;
}
}
Detected encoding: UTF-8 | 0
|