Source file: /~heha/basteln/PC/usbfloppy/usbfloppy.zip/usb.cpp

#include "ufi.h"
#include <avr/io.h>
#include <avr/pgmspace.h>
#include <avr/interrupt.h>

#define EP0S 64

/****************
 * Deskriptoren *
 ****************/

#define W(x) (x)&0xFF,(x)>>8
#define D(x) W(x&0xFFFF),W(x>>16)

// Device descriptor
static const byte PROGMEM DeviceDesc[] ={
 18,		// bLength
 1,		// descriptor type	Device
 W(0x0200),	// USB version supported
 0,		// USB_CFG_DEVICE_CLASS,
 0,		// USB_CFG_DEVICE_SUBCLASS,
 0,		// protocol
 EP0S,
 W(0x03EB),	// Atmel
 W(0x2045),	// CDC Demo
 W(0x0100),
 1,
 2,
 3,
 1
};

#define CONFIG_DESC_SIZE (9+9+7+7)

// Configuration descriptor
static const byte PROGMEM ConfigDesc[CONFIG_DESC_SIZE] ={
 9,		// bLength
 2,		// bDescriptorType	Config
 W(CONFIG_DESC_SIZE),	// wTotalLength
 1,		// bNumInterfaces	HID
 1,		// bConfigurationValue	erste und einzige
 0,		// iConfiguration	ohne Text
 0x80,		// bmAttributes		Busversorgt, kein Aufwecken
 50/2,		// MaxPower (in 2 Milliampere)	50 mA

 9,		// bLength
 4,		// bDescriptorType	Interface
 0,		// bInterfaceNumber
 0,		// bAlternateSetting
 2,		// bNumEndpoints	IN+OUT
 8,		// bInterfaceClass	Massenspeicher
 6,		// bInterfaceSubClass	6 = SCSI	(4 = UFI)
 0x50,		// bInterfaceProtocol	80 = Bulk-Only
 0,		// iInterface		ohne Text

 7,		// bLength
 5,		// bDescriptorType	Endpoint
 0x81,		// bEndpointAddress	1 IN
 0x02,		// bmAttributes		Bulk
 W(64),		// wMaxPacketSize
 0,		// bInterval

 7,		// bLength
 5,		// bDescriptorType	Endpoint
 0x02,		// bEndpointAddress	2 OUT
 0x02,		// bmAttributes		Bulk
 W(64),		// wMaxPacketSize
 0,		// bInterval
};

// Alle String-Deskriptoren (deutsch) hintereinander
static const wchar_t PROGMEM strLang[] =
// High-Teil = DTYPE_String, Low-Teil = (String-Länge+1)*2	
 L"\x0304"	L"\x0407"			// (0) Länge 1: deutsch
 L"\x032C"	L"TU Chemnitz, Enas+ZfM"	// (1) Länge 21
 L"\x032C"	L"5¼\"-Diskettenlaufwerk"	// (2) Länge 21
 L"\x0330"	L"heha@hrz.tu-chemnitz.de"	// (3) Länge 23
;

/********
 * Kode *
 ********/


// Aufruf wenn OTG-Pad Spannung führt
static void usbConnect() {
 UHWCON = 0x01;		// Spannungsregler aktivieren (Bit 0 — nicht bei AT90USB162)
 USBCON = 0xB1;		// USB aktivieren, noch ohne Takt (so erforderlich)
 PLLCSR = 0x12;		// PLL: Takt/2, aktivieren (Bits anders als bei AT90USB162)
 while (!(PLLCSR&1));	// warte bis PLL bereit
 USBCON = 0x91;		// USB-Takt anlegen (Bit 5 — OTG-Pad nicht bei AT90USB162)
 UDCON  = 0;		// Pullup-Widerstand aktivieren (Bit 0)
 UDINT  = 0;		// alle Interrupts löschen
 usbCfg = 0;
}

static void usbDisconnect() {
 USBCON = 0x11;		// Kein USB, nur noch OTG-Pad
 PLLCSR = 0x10;		// PLL abschalten
 UHWCON = 0;		// kein USB-Spannungsregler
 UDINT  = 0;
 usbCfg = 0;
}

// Misc functions to wait for ready and send/receive packets
static void usb_send_in(void) {
 UEINTX = ~(1<<TXINI);		// Bit 0 löschen = IN-Paket senden
}
static byte usb_wait_in_ready(void) {
 byte i;
 do i=UEINTX;	// wait for host ready for IN packet
 while (!(i & (1<<TXINI | 1<<RXOUTI)));
 return i & 1<<RXOUTI;
}
/*
static void usb_wait_receive_out(void) {
 while (!(UEINTX & (1<<RXOUTI))) ;
}
*/

// immer 64 Byte = volles Paket; immer aus RAM
bool usbEp1Send(const void*p) {
 UENUM=1;
 if (UEINTX&0x01) return false;		// FIFO ist voll? Kann nicht absenden!
 UEINTX=0xA0;			// Alle Interrupts quittieren
 const byte*a=(const byte*)p;
 while (UEINTX&0x20) UEDATX=*a++;	// Daten in FIFO stecken solange RWAL gesetzt ist
 UEINTX=0;		// Paket voll: FIFO umschalten
 return true;
}

// immer 64 Byte = volles Paket
bool usbEp2Recv(void*p) {
 UENUM=2;
 if (!(UEINTX&0x04)) return false;	// Kein RXOUTI? Keine Daten vorhanden!
 UEINTX=0xA0;			// Alle Interrupts quittieren
 byte*a=(byte*)p;
 while (UEINTX&0x20) *a++=UEDATX;	// Daten aus FIFO herauslesen solange RWAL gesetzt ist
 UEINTX=0;		// Paket wurde abgeholt: FIFO umschalten
 return true;
}

static unsigned wLength;	// wLength des letzten SETUPDAT-Pakets

void usbEp0Send(const void*addr, int len) {
 const byte*a=(const byte*)addr;
 bool zlp=false;	// Null-Byte-Paket als Abschluss bei glatter Transferlänge?
 if ((unsigned)len>wLength) {len=wLength; zlp=true;}	// Nur bei Limitierung
 UENUM=0;
 byte n;
 do{
  if (usb_wait_in_ready()) return;	// abort at OUT packet
  n=len>EP0S?EP0S:(byte)len;	// send IN packet
  if (n) asm volatile(
"	mov	r1,%2	\n"	// r1 = Zählregister
"1:	sbrc	r2,6	\n"
"	 lpm	r0,Z+	\n"	// vom Flash wenn Bit 6 gesetzt
"	sbrs	r2,6	\n"
"	 ld	r0,Z+	\n"	// vom RAM wenn Bit 6 gelöscht
"	sts	%1,r0	\n"
"	dec	r1	\n"
"	brne	1b	\n"	// r1 ist nachher ordnungsgemäß Null
:"+z"(a):"m"(UEDATX),"r"(n));	// a wird erhöht und ist Ein- und Ausgang ("+")
  usb_send_in();
 }while (len-=n || zlp && n==EP0S);
}

int usbEp0Recv(void*addr, int len) {
// TODO
 return len;
}

void usbPollEP0() {
 UENUM = 0;
 uint8_t ueintx=UEINTX;
 if (ueintx&0x08) {			// Setup angekommen?
  byte len;
  const byte *addr;
//SETUPDAT-Paket in 8 handliche 8-Bit-Register einlesen
  byte bmRequestType = UEDATX;
  byte bRequest= UEDATX;
  byte wValueL = UEDATX;
  byte wValueH = UEDATX;
  byte wIndexL = UEDATX;
  byte wIndexH __attribute__((unused)) = UEDATX;
  byte wLengthL= UEDATX;
  wLength=UEDATX<<8|wLengthL;		// globalen Längenzähler setzen
  UEINTX=ueintx&0xF3;			// Interrupt jetzt erst löschen!

  switch (bmRequestType) {

   case 0x00: switch (bRequest) {	// out, device, device
   
    case 1:				// CLEAR_FEATURE
    case 3: {				// SET_FEATURE
     if (wValueL==1) {			// Feature „Remote Wakeup“
      usb_send_in();			// Null-Byte-IN-Paket absenden
      return;
     }
    }break;
    
    case 5: {				// SET_ADDRESS
     usb_send_in();			// Null-Byte-IN-Paket absenden
     usb_wait_in_ready();		// warte bis abgeholt
     UDADDR = wValueL|0x80;		// Adresse jetzt setzen
    }return;

    case 9: {				// SET_CONFIGURATION
     if (wValueL<2) {
      usb_send_in();			// Null-Byte-IN-Paket absenden
      usbCfg=wValueL;			// setzt Alternate Settings zurück
      if (wValueL) {
       UENUM = 1;
       UECONX= 1;			// EPEN
       UECFG0X=0x81;			// Bulk-IN-Endpoint
       UECFG1X=0x36;			// 64 Byte doppelt gepuffert
       UENUM = 2;
       UECONX= 1;
       UECFG0X=0x80;			// Bulk-OUT-Endpoint
       UECFG1X=0x36;			// 64 Byte doppelt gepuffert
      }
      UERST = 0x7E;			// alle Endpoints außer EP0 rücksetzen
      UERST = 0;
      return;
     }
    }break;

   }break;
    
   case 0x01: switch (bRequest) {	// out, device, interface

    case 11: {				// SET_INTERFACE
     usb_send_in();			// Null-Byte-IN-Paket absenden
    }return;

   }break;

   case 0x02: switch (bRequest) {	// out, device, endpoint

    case 1:				// CLEAR_FEATURE
    case 3: {				// SET_FEATURE
     if (!wValueL) {			// Feature 0
      byte i = wIndexL & 0x7F;		// Endpoint
      if (i<=2) {
       usb_send_in();			// Null-Byte-IN-Paket absenden
       UENUM = i;
       if (bRequest&2) {		// SET_FEATURE
	UECONX = 0x21;			// Stall setzen
       }else{
	UECONX = 0x19;			// Stall sowie Data-Toggle löschen
	UERST = 1<<i;			// Puffer und UEINTX löschen
	UERST = 0;
       }
       return;
      }
     }
    }break;

   }break;
   
   case 0x21: switch (bRequest) {	// out, class, interface
    case 0xFF: {			// USB_MASS_STORAGE_RESET (out, class)
     usb_send_in();			// Null-Byte-IN-Paket absenden
    }return;
   }break;


   case 0x80: switch (bRequest) {	// in, device, device
    
    case 0: {				// GET_STATUS
     usb_wait_in_ready();		// (sollte unnötig sein!!)
     UEDATX=0;				// Bus-Powered
     UEDATX=0;
     usb_send_in();			// Zwei-Byte-Paket abschicken
    }return;				// Null-Byte-Paket ignorieren
		
    case 6: {				// GET_DESCRIPTOR
     usbCfg|=0x40;			// aus Flash
     switch (wValueH) {
      case 1: addr=DeviceDesc; break;
      case 2: {
       addr=ConfigDesc;
       len=sizeof ConfigDesc;
      }goto gd2;
      case 3: {
       if (wValueL>3) goto stall;
       addr=(const byte*)strLang;	// Zusammenhängende String-Deskriptoren
       if (wValueL) do addr+=pgm_read_byte(addr); while(--wValueL);
      }break;
      default: goto stall;
     }
     len=pgm_read_byte(addr);		// Länge vom Deskriptor nehmen
gd2: usbEp0Send(addr,len);
    }return;

    case 8: {				// GET_CONFIGURATION
     usb_wait_in_ready();
     UEDATX = usbCfg&1;
     usb_send_in();
    }return;

   }break;
    
   case 0x81: switch (bRequest) {	// in, device, iface
    case 10: {				// GET_INTERFACE
     if (wIndexL<2) {
      usb_wait_in_ready();
      UEDATX=0;
      usb_send_in();
      return;
     }
    }break;
   }break;

   case 0x82: switch (bRequest) {	// in, device, endpoint
    case 0: {				// GET_STATUS
     usb_wait_in_ready();
     byte i=0;
     UENUM = wIndexL&7;
     if (UECONX&0x20) i=1;		// bei STALL (fehlerhafte Doku!)
     UENUM =0;
     UEDATX=i;
     UEDATX=0;
     usb_send_in();
    }return;
   }break;
   
   case 0xA1: switch (bRequest) {	// in, class, interface
    case 0xFE: {			// USB_GET_MAX_LUN (in, class)
     usb_wait_in_ready();
     UEDATX=LUNS-1;			// Anzahl logischer Laufwerke
     usb_send_in();
    }return;
   }break;

  }
stall:
  UECONX = 0x21;			// stall
 }
}

void usbPoll() {
// Generellen USB-Interrupt behandeln
 if (USBINT&1		// Pegelwechsel am OTG-Pad?
#ifdef ARDUINO
 || USBSTA&1&&UDCON	// Hintertür für Start aus Arduino-Urlader
#endif
	     ) {
  USBINT=0;
  if (USBSTA&1) usbConnect(); else usbDisconnect();
 }
// USB-Device-Interrupts behandeln
 byte udint=UDINT;	// handliches Register
 UDINT=0x01;		// Interrupts löschen, außer SUSPI (Bit 0): Pad-Idle zulassen
 if (udint&0x08) {	// Ende USB-Reset
  UENUM  = 0;		// EP0 einrichten
  UECFG0X= 0;		// Control-Endpoint
  UECFG1X= 0x32;	// 64 Byte einfach gepuffert
  UECONX = 1;
  UEINTX = 4;		// Kill Bank IN??
  usbCfg = 0;		// unkonfiguriert
 }
 if (udint&0x01		// Suspend (Hardware löscht WAKEUPI)
 && !(USBCON&0x20)) {	// USB-Takt läuft noch (FRZCLK gelöscht)?
  USBCON=0xB0;		// USB-Takt anhalten (FRZCLK setzen)
  PLLCSR=0x10;		// PLL anhalten (Bit 1)
  onSuspend();
 }
 if (udint&0x10		// Wakeup (Hardware löscht SUSPI) — kommt ständig
 && USBCON&0x20) {	// USB-Takt angehalten (FRZCLK gesetzt)?
  PLLCSR=0x12;		// PLL starten
  onResume();
  while (!(PLLCSR&1));	// warte bis PLL eingerastet
  USBCON=0x90;		// USB-Takt anlegen
 }
 usbPollEP0();
 usbPollEP2();
}
Detected encoding: UTF-80