Source file: /~heha/Mikrocontroller/RpiI2cClockStretcher/i2c.zip/usb.cpp

#include "usb.h"
/* Realisiert WinUSB/WebUSB, so einfach wie möglich
 */
static byte usbCfg;
enum {
 EP0L=6,	// Endpoint 0 Size Log2	Control I/O
};

#define W(x) byte(x),byte((x)>>8)
#define D(x) W(x),W((x)>>16)

static const PROGMEM byte DeviceDesc[]={
 18,		// bLength
 1,		// bDescType		Device
 W(0x0200),	// USB version supported
 0,		// bDeviceClass
 0,		// bDeviceSubclass
 0,		// bDeviceProtocol
 1<<EP0L,	// bMaxPacketSize0	64
 W(0x16C0),	// 			Voti
 W(0x27D8),	// 			libusb = WebUSB
 W(0x0200),	//
 1,		// iVendor
 2,		// iProduct
 3,		// iSerialNo
 1		// bNumConfig
};

static const PROGMEM byte ConfigDesc[9+9]={
 9,		// bLength
 2,		// bDescType		Config
 W(sizeof ConfigDesc),	// wTotalLength
 1,		// bNumInterfaces
 1,		// bConfigValue
 0,		// iConfig
 0x80,		// bmAttributes		BusPower
 10,		// bMaxPower		20 mA
// interface descriptor
 9,		// bLength
 4,		// bDescType		Iface
 0,		// bInterfaceNumber
 0,		// bAlternateSetting	(only one)
 0,		// bNumEndpoints
 0xFF,		// bIfaceClass		vendor-specific
 0,		// bIfaceSubclass
 0,		// bIfaceProtocol
 0,		// iIface
};

// erforderlich für WebUSB für Windows
static const PROGMEM struct{
 struct{
  word descsize,headertype;
  dword windowsversion;
  word descsetsize;
 }DescSetHeader;
 word ConfSubsetHeader[4];
 word FuncSubsetHeader[4];
 word CompatIdDesc[2];
 char compatID[8];
 char subcompatID[8];
 struct{
  word wLength,wDescriptorType,wPropertyDataType,wPropertyNameLength;
  char16_t bPropertyName[21];
  word wPropertyDataLength;
  char16_t bPropertyData[40];
 }CUSTOM_PROPERTY;
}MsOs20Desc = {
// Microsoft OS 2.0 descriptor set header (table 10)
 {0x000A,	// Descriptor size (10 bytes)
  0x0000,	// MS OS 2.0 descriptor set header
  0x06030000,	// 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
  0x0000,	// 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
 "WINUSB",
 "",
 {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:
  u"DeviceInterfaceGUIDs",	//bPropertyName
  40<<1,	// wPropertyDataLength
  u"{975F44D9-0D08-43FD-8B3E-127CA8AFFF9D}"	//bPropertyData
 }
};
static_assert(sizeof MsOs20Desc==23+66<<1,"Wrong struct!");
static_assert(sizeof MsOs20Desc<256,"Too large!");

// erforderlich für WebUSB:
static const PROGMEM 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

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++*
 *  Automatisch abgezählter URL- und String-Desktiptor	*
 *++++++++++++++++++++++++++++++++++++++++++++++++++++++*/

template<typename T>constexpr size_t length(const T*s) {
 return *s ? 1+length(s+1) : 0;	// zählt nullterminierte Strings
}
template<typename T,size_t N>struct array{	// ähnlich std::array
 T a[N];
 constexpr operator const T*() const {return a;}
 constexpr operator T*() {return a;}
 template<size_t M> constexpr auto operator+(const array<T,M>&&b) const {
  array<T,N+M> K={};
  size_t k=0;
  for(size_t i=0; i<N; i++,k++) K[k]=a[i];
  for(size_t i=0; i<M; i++,k++) K[k]=b[i];
  return K;
 }
};
template<size_t N>constexpr auto urldesc(byte proto, const char*s) {
 constexpr auto L=3+N;
 static_assert(L<256,"String zu lang!");
 array<byte,L> A={L,3,proto};
 for (size_t i=0; i<N; i++) A[3+i] = s[i];
 return A;
}
template<size_t N>constexpr auto strdesc(const char16_t*s) {
 constexpr auto L=2+N+N;
 static_assert(L<256,"String zu lang!");
 array<byte,L> A={L,3};
 for (size_t i=0; i<N; i++) {A[2+i+i] = s[i]&0xFF; A[3+i+i] = s[i]>>8;}
 return A;
}
#define URLDESC(p,s) (urldesc<length(u8##s)>(p,s))
#define STRDESC(s) (strdesc<length(u##s)>(u##s))
/*++++++++++++++++++++++++++++++*
 *  Ende Template-Hokuspokus	*
 *++++++++++++++++++++++++++++++*/

// erforderlich für WebUSB
static const PROGMEM auto UrlDesc =
 URLDESC(1,"www.tu-chemnitz.de/~heha/mb-iwp/Bergwerk/app.htm");

static const PROGMEM auto StrDesc =
 STRDESC("\x0407")+			// (0) deutsch
 STRDESC("TU Chemnitz, IWP")+		// (1)
 STRDESC("USB-I²C-Adapter")+		// (2)
 STRDESC("heha@hrz.tu-chemnitz.de");	// (3)

#undef STRDESC
#undef URLDESC
 
// Nur von Assembler aufzurufen, macht *Z++ aus Daten- oder Flashspeicher
extern "C" void getbyte() { asm(
"	sbrs	ZH,7	\n"
"	 ld	r0,Z+	\n"
"	sbrc	ZH,7	\n"
"	 lpm	r0,Z+	\n");}

void usb::Init() {
 UHWCON=1;
 USBCON=0xB0;	// Ubus aktivieren, aber ohne Takt
 USBINT=1;	// beim nächsten usbPoll USB aktivieren oder deaktivieren lassen
}	// Dokumentationsfehler: USBCON.7 muss 1 sein damit UDCON.0 0 bleibt!

namespace usb{
static bool Ep0Send(const void*,byte);
}

static bool usb::Ep0Send(const void*addr, byte len) {
 bool zlp=len<setup.wLength;	// Null-Byte-Paket als Abschluss bei glatter Transferlänge?
 if (len>setup.wLength) len=setup.wLength;
 UENUM=0;
 byte n;
 do{
  while (!((n=UEINTX)&0x0D));	// warten bis Interrupt — oder USB abgesteckt
  if (n&0x0C) return false;	// Ende bei (unerwartetem) OUT- oder SETUP-Paket (15h kommt vorbei!)
  n=1<<EP0L; if (n>len) n=len;
  if (n) asm volatile(
"	push	%2	\n"
"1:	rcall	getbyte	\n"
"	sts	%1,r0	\n"
"	dec	%2	\n"
"	brne	1b	\n"
"	pop	%2	\n"
:"+z"(addr):"m"(UEDATX),"r"(n));
//  do UEDATX=pgm_read_byte(addr++); while (--n);
  UEINTX=0b1111'1110;		// Gefüllte FIFO senden
 }while (len-=n || zlp && n==1<<EP0L);
 return true;
}

usb::SetupDesc usb::setup NOINIT;	// aktueller Setup-Transfer-Header

static void usbPollEp0() {
 UENUM=0;		// Endpoint 0 betreffend
 if (UEINTX&4) {	// OUT angekommen?
  UEINTX=~4;		// Daten ignorieren
 }
 if (UEINTX&8) {	// SETUP angekommen?
  byte*ptr=usb::setup;
// Das SETUP-Paket in einzelne Bytes zu stecken ermöglicht Optimierung,
// sofern nicht Unterprogramme (damit) aufgerufen werden müssen.
  byte bmRequestType=*ptr++=UEDATX;
  byte bRequest=*ptr++=UEDATX;
  byte wValueL=*ptr++=UEDATX;
  byte wValueH=*ptr++=UEDATX;
  *ptr++=UEDATX;
  *ptr++=UEDATX;
  *ptr++=UEDATX;
  *ptr++=UEDATX;
  UEINTX=~8;		// Jetzt erst(!) alles löschen außer TXINI
  byte len;
  const byte*addr;	// kann in RAM oder Flash (≥ 0x8000) zeigen
#define RETURN_OKAY {UEINTX=0; return;}
#define RETURN_STALL {UEINTX=0; UECONX=0x21; return;}
  switch (bmRequestType) {
// Standard-Requests
   case 0x00: switch (bRequest) {	// OUT, DEVICE
    case 1:			// Clear Feature
    case 3: RETURN_OKAY;	// Set Feature
    case 5: {			// Set Address (wValueL)
     UEINTX=0;			// mit 0-Byte-IN-Paket beantworten
     while (!(UEINTX&1));	// warten bis abgeschickt (TODO: Deadlock bei USB-Reset!)
     UDADDR=wValueL|0x80;	// jetzt erst Adresse setzen
    }return;
    case 9: {
     usbCfg=wValueL;
     RETURN_OKAY;
    }break;
   }break;
   case 0x01: switch (bRequest) {	// OUT, STD, IFACE[wIndex]
    case 11: RETURN_OKAY;		// Set Interface (ignorieren)
   }break;
   case 0x40: switch (bRequest) {	// OUT, VENDOR, DEVICE
    case 0xA0: if (i2c::send()) RETURN_OKAY; break;
   }break;
   case 0x80: switch (bRequest) {	// IN, STD, DEVICE
    case 0: {			// Get Status
     UEDATX=0;			// Bus Powered
     UEDATX=0;
    }RETURN_OKAY;
    case 6: switch (wValueH) {	// Get Descriptor
     case 1: addr=FP(DeviceDesc); goto gd1;
     case 2: addr=FP(ConfigDesc); len=sizeof ConfigDesc; goto gd2;
     case 3: {
      if (wValueL>3) RETURN_STALL;
      for (addr=FP(StrDesc);wValueL;wValueL--) addr+=pgm_read_byte(addr);
     }goto gd1;
     case 15: addr=FP(BosDesc); len=sizeof BosDesc; goto gd2;
    }RETURN_STALL;
    case 8: {		// Get Configuration
     UEDATX=usbCfg;
    }RETURN_OKAY;
   }break;
   case 0x81: switch (bRequest) {	// IN, STD, IFACE[wIndex]
    case 0: {			// Get Status
     UEDATX=0;
     UEDATX=0;
    }RETURN_OKAY;
    case 10: {			// Get Interface (= Alternate Setting)
     UEDATX=0;
    }RETURN_OKAY;
   }break;
   case 0xC0: switch (bRequest) {	// IN, VENDOR, DEVICE
    case 2: addr=FP(&MsOs20Desc); len=sizeof MsOs20Desc; goto gd2;	// wIndex=7
    case 1: addr=FP(UrlDesc);	// wIndex=2, wValueL=1
gd1:	    len=pgm_read_byte(addr);
gd2:	    usb::Ep0Send(addr,len);
	    return;
    case 0xA0: if (i2c::recv()) return; break;
    case 0xA1: UEDATX = i2c::lastError; RETURN_OKAY;
   }break;
  }
  RETURN_STALL;		// Alles andere: STALL aktivieren
 }
#undef RETURN_OKAY
#undef RETURN_STALL
}

void usb::Poll() {
 if (USBINT&1) {	// Ab- oder Anstecken des Hosts?
  USBINT=0;
  if (USBSTA&1) {	// Angesteckt
   UDCON=0;		// Pullup aktivieren
  }else{		// Abgesteckt
   UDCON=1;		// Pullup deaktivieren
   USBCON=0xB0;		// Takt deaktivieren
   usbCfg=0;
  }
 }
 byte udint=UDINT;
 UDINT=0;
 if (udint&0x10 && USBCON&0x20) {	// Aktivität && Takt eingefroren?
  PLLCSR=0x12;		// PLL aktivieren (für 16-MHz-Quarz)
  while(!(PLLCSR&1));	// warten bis eingerastet
  USBCON=0x90;		// Takt aktivieren
 }
 if (udint&1) {		// USB-Suspend
  USBCON=0xB0;		// Takt deaktivieren
  PLLCSR=0;		// PLL deaktivieren
 }
 if (udint&8) {		// USB-Reset-Ende?
  usbCfg=0;		// Nicht konfiguriert (Adresse wird von Hardware zurückgesetzt)
  UENUM=0;
  UECONX=1;		// Endpoint 0 aktivieren
  UECFG1X=2|EP0L-3<<4;	// Bytes für Endpoint 0 im DPRAM reservieren
 }
 usbPollEp0();
}
Detected encoding: UTF-80