Source file: /~heha/basteln/PC/Programmiergeräte/Wild GIF12/mtp3.zip/usb.cpp

#include "usb.h"
#include <avr/pgmspace.h>
// Media Transfer Protocol: "still image capture device class"

static byte usbCfg;
static const char E0_SSH=6;	// 64 Byte	CONTROL	I/O
static const char E1_SSH=6;	// 64 Byte	BULK	OUT (Maximum für Bulk)
static const char E2_SSH=6;	// 64 Byte	BULK	IN
static const char E3_SSH=3;	// 8 Byte	Interrupt IN (Placebo)

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

static const PROGMEM byte DeviceDesc[]={
 18,		// bLength
 1,		// bDescType		Device
 W(0x0110),	// USB version supported
 0,		// bDeviceClass		See interface
 0,		// bDeviceSubclass
 0,		// bDeviceProtocol
 1<<E0_SSH,	// bMaxPacketSize0	64
 W(0x16C0),	// 			Voti
 W(0x27DF),	// 			MIDI+1 = PTP
 W(0x0100),	//
 0,		// iVendor
 0,		// iProduct
 0,		// iSerialNo
 1		// bNumConfig
};

static const PROGMEM byte ConfigDesc[39]={
 9,		// bLength
 2,		// bDescType		Config
 W(sizeof ConfigDesc),	// wTotalLength
 1,		// bNumInterfaces
 1,		// bConfigValue
 0,		// iConfig
 0x80,		// bmAttributes
 50,		// bMaxPower (100 mA)
// still image interface descriptor
 9,		// bLength
 4,		// bDescType		Iface
 0,		// bInterfaceNumber
 0,		// bAlternateSetting	(only one)
 3,		// bNumEndpoints	MUSS 3 sein!!
 6,		// bIfaceClass		image interface
 1,		// bIfaceSubclass	still image capture
 1,		// bIfaceProtocol	bulk-only protocol
 0,		// iIface
// ENDPOINT_DESCRIPTOR_1_OUT
 7,		// bLength
 5,		// bDescType		ENDPOINT
 0x01,		// bEndpointAddress	1 OUT
 0x02,		// bmAttributes		bulk
 W(1<<E1_SSH),	// wMaxPacketSize	64 (Maximum)
 0,		// bInterval
// ENDPOINT_DESCRIPTOR_2_IN
 7,		// bLength
 5,		// bDescType		ENDPOINT
 0x82,		// bEndpointAddress	2 IN
 0x02,		// bmAttributes		bulk
 W(1<<E2_SSH),	// wMaxPacketSize	64 (Maximum)
 0,		// bInterval
// ENDPOINT_DESCRIPTOR_3_IN
 7,		// bLength
 5,		// bDescType		ENDPOINT
 0x83,		// bEndpointAddress	3 IN
 0x03,		// bmAttributes		interrupt
 W(1<<E3_SSH),	// wMaxPacketSize	8
 100,		// bInterval
};
#undef W

void usbInit() {
 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!

static void usbPollEp0() {
 UENUM=0;		// Endpoint 0 betreffend
 if (UEINTX&4) UEINTX=~4; // OUT angekommen? (Da kommt tatsächlich etwas!!)
 if (UEINTX&8) {	// SETUP angekommen?
  byte len;
  const byte*addr;
  byte bmRequestType __attribute__((unused)) =UEDATX;
  byte bRequest=UEDATX;
  byte wValueL=UEDATX;
  byte wValueH=UEDATX;
  byte wIndexL __attribute__((unused)) =UEDATX;
  byte wIndexH __attribute__((unused)) =UEDATX;
  byte wLengthL=UEDATX;
  byte wLengthH=UEDATX;
  UEINTX=~8;		// Jetzt erst(!) alles löschen außer TXINI
// Der Einfachheit halber (Platzmangel!!) ohne bmRequestType auszuwerten,
// ohne String-Deskriptoren
//  wLength=wLengthH<<8|wLengthL;
  switch (bRequest) {
// Standard-Requests
   case 0: {			// Get Status
    UEDATX=0;			// Bus Powered (Device)
    UEDATX=0;			// 0 (Configuration)
   }goto okay;			// Kein HALT (Endpoint)
   case 1:			// Clear Feature
   case 3: goto okay;		// Set Feature
   case 5: {			// Set Address (wValueL)
    UEINTX=0;			// mit 0-Byte-IN-Paket beantworten
    UDADDR=wValueL;		// laut Datenblatt, praktisch nicht erforderlich
    while (!(UEINTX&1));	// warten bis abgeschickt
    UDADDR=wValueL|0x80;
   }return;
   case 6: switch (wValueH) {	// Get Descriptor
    case 1: addr=DeviceDesc; break;
    case 2: addr=ConfigDesc; len=sizeof ConfigDesc; goto gd2;
    default: goto stall;
   }
   len=pgm_read_byte(addr);
gd2:
   if (!wLengthH && len>wLengthL) len=wLengthL;	// Nur soviel der Host will
   while (len) {
    byte n;
    while (!((n=UEINTX)&0x0D));// warten bis Interrupt
    if (n&0x0C) return;	// Ende bei (unerwartetem) OUT- oder SETUP-Paket (15h kommt vorbei!)
    n=1<<E0_SSH; if (n>len) n=len;
    len-=n;
    do UEDATX=pgm_read_byte(addr++); while (--n);
    UEINTX&=~1;
   }return;
   case 8: {		// Get Configuration
    UEDATX=usbCfg&1;
   }goto okay;
   case 9: if (wValueL<2) {	// Set Configuration
    if (usbCfg=wValueL) {	// alles einfach gepuffert (reicht hier)
     UENUM=1; UECONX=1; UECFG0X=0x80; UECFG1X=2|E1_SSH-3<<4;	// EP1OUT 64 Byte
     UENUM=2; UECONX=1; UECFG0X=0x81; UECFG1X=2|E2_SSH-3<<4;	// EP2IN  64 Byte
     UENUM=3; UECONX=1; UECFG0X=0xC1; UECFG1X=2|E3_SSH-3<<4;	// EP3IN  8 Byte
     UENUM=0;
    }else{
     UERST=0x0E; 
     UERST=0;	// alle 3 Endpoints rücksetzen
    }
    goto okay;
   }break;
   case 10: UEDATX=0; /*nobreak*/;	// Get Interface (= Alternate Setting)
   case 11: okay:UEINTX=~1; return;	// Set Interface (ignorieren)
// class specific requests: ignore all! Not needed!
  }
stall:UECONX=0x21;		// Alles andere: STALL aktivieren
 }
}

void usbPoll() {
 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?
#if F_CPU==16000000
  PLLCSR=0x12;		// PLL aktivieren (für 16-MHz-Quarz)
#elif F_CPU==8000000
  PLLCSR=0x02;		// PLL aktivieren (für 8-MHz-Quarz)
#else
# error Symbol "F_CPU" muss 8 oder 16 MHz ergeben!
#endif
  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|E0_SSH-3<<4;// 8 Bytes für Endpoint 0 im DPRAM reservieren
 }
 usbPollEp0();
}

// Zwangsweise USB-Daten lesen
void usbRecv(void*buf,byte len) {
 byte*p=(byte*)buf;
 while (len) {
  while (UENUM=1,!(UEINTX&0x80)) usbPoll();	// Warten bis Daten verfügbar
  if (UEINTX&0x20) {		// Zero-Length-Packet ignorieren
   byte b=UEDATX;
   if (p) *p++=b;		// RAM schreiben
   --len;
  }
  if (!(UEINTX&0x20)) UEINTX=0;	// ausgelesen: zurück zur SIE
 }
}

void usbSend(const void*buf,byte len) {
 const byte*p=(const byte*)buf;
 do{
  while (UENUM=2,!(UEINTX&0x80)) usbPoll();	// Warten bis Puffer frei
  while (UEINTX&0x20 && len) {
   UEDATX=p?*p++:0;		// aus RAM oder Nullen
   --len;
  }
  if (!(UEINTX&0x20)) UEINTX=0;	// Puffer abschicken
 }while(len);
}
Detected encoding: UTF-80