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

#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-80