Source file: /~heha/ewa/Motor/Maxmaus12.zip/usbd.cpp

#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;
}

}
Detected encoding: UTF-80