Source file: /~heha/ewa/Reluktanzmotor/firmware.zip/usb.cpp

#include <avr/io.h>
#include <avr/pgmspace.h>
#include <avr/interrupt.h>
#include "usb.h"
/* Realisiert serielle Schnittstelle über USB, so einfach wie möglich
 */
char query[64];		// Stringpuffer für Anfrage (":MAIN:FREQ:0?\r\n" u.ä.)
byte querylen;		// Länge, ab dem 64. Zeichen wird abgeschnitten
char answer[64];
byte answerlen;

static byte usbCfg;
static const char E0_SSH=3;	// 8 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	INTERR	IN

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

static const PROGMEM byte DeviceDesc[]={
 18,		// bLength
 1,		// bDescType		Device
 W(0x0200),	// USB version supported
 2,		// bDeviceClass		Communications
 0,		// bDeviceSubclass
 0,		// bDeviceProtocol
 1<<E0_SSH,	// bMaxPacketSize0	8
 W(0x03EB),	// 			Atmel
 W(0x2045),	// 			CDC Demo
 W(0x0100),	//
 0,		// iVendor
 0,		// iProduct
 0,		// iSerialNo
 1		// bNumConfig
};

static const PROGMEM byte ConfigDesc[67]={
 9,		// bLength
 2,		// bDescType		Config
 W(sizeof ConfigDesc),	// wTotalLength
 2,		// bNumInterfaces
 1,		// bConfigValue
 0,		// iConfig
 0xC0,		// bmAttributes
 0,		// bMaxPower
// interface descriptor
 9,		// bLength
 4,		// bDescType		Iface
 0,		// bInterfaceNumber
 0,		// bAlternateSetting	(only one)
 1,		// bNumEndpoints
 2,		// bIfaceClass		communication
 2,		// bIfaceSubclass	abstract control model
 1,		// bIfaceProtocol	V.25ter, common AT commands
 0,		// iIface
// HEADER_FUNCTIONAL_DESCRIPTOR
 5,		// bLength
 0x24,		// bDescType		CS_INTERFACE
 0,		// bDescSubtype		header
 W(0x0110),	// bcdCDC		specification version 1.1
// ABSTRACT_CONTROL_MANAGEMENT_FUNCTIONAL_DESCRIPTOR
 4,		// bLength
 0x24,		// bDescType		CS_INTERFACE
 2,		// bDescSubtype		abstract control management
 0x02,		// bmCapabilities
// UNION_FUNCTIONAL_DESCRIPTOR
 5,		// bLength
 0x24,		// bDescType		CS_INTERFACE
 6,		// bDescSubtype 	union
 0,		// bMasterIface
 1,		// bSlaveIface0
// CALL_MANAGEMENT_FUNCTIONAL_DESCRIPTOR
 5,		// bLength
 0x24,		// bDescType		CS_INTERFACE
 1,		// bDescSubtype		call management
 3,		// bmCapabilities
 1,		// dDataIface
// ENDPOINT_DESCRIPTOR_3_IN
 7,		// bLength
 5,		// bDescType		ENDPOINT
 0x83,		// bEndpointAddress	3 IN
 0x03,		// bmAttributes		interrupt
 W(1<<E3_SSH),	// wMaxPacketSize	8
 2,		// bInterval		2 ms
// INTERFACE_DESCRIPTOR_1
 9,		// bLength
 4,		// bDescType		IFACE
 1,		// bIfaceNumber
 0,		// bAlternateSetting
 2,		// bNumEndpoints
 0x0A,		// bIfaceClass		data
 0,		// bIfaceSubclass
 0,		// bIfaceProtocol
 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
};

static const PROGMEM byte LineCoding[] = {
 W(9600),W(0),	// Baudrate
 0,
 0,
 8		// Datenbits
};
// Interrut-Daten
static const PROGMEM byte IntData[]= {	// 8 Byte
 0xA1,0x20,0,0,0,0,2,0
};

#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) {	// OUT angekommen?
  UEINTX=~4;		// Daten ignorieren
 }
 if (UEINTX&8) {	// SETUP angekommen?
  byte len;
  const byte*addr;
  byte bmRequestType=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=bmRequestType?0:1;	// Self Powered (bei Device Request)
    UEDATX=0;
   }goto okay;
   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;
   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;
    asm volatile(
"1:	lpm	r0,Z+	\n"
"	sts	%1,r0	\n"
"	dec	%2	\n"
"	brne	1b	\n"
:"+z"(addr):"m"(UEDATX),"r"(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  16 Byte
     UENUM=0;
    }else{
     UERST=0x0E; 
     UERST=0;	// alle 3 Endpoints rücksetzen
    }
    querylen=0;
    goto okay;
   }break;
   case 10: {			// Get Interface (= Alternate Setting)
    UEDATX=0;
   }/*nobreak*/;
   case 11: goto okay;		// Set Interface (ignorieren)
// Klassenspezifische Requests
   case 0x20: {			// Set Line Coding (wLength==7)
    //outlen=wLengthL;
    while (!(UEINTX&4));
    UEINTX=~4;
/*
    byte n;
    while (!(n=UEINTX)&0x4C);	// warte bis OUT-Daten gekommen sind
    if (n&0x40) goto stall;	// NAKIN: Host will Status zu früh
    if (n&0x08) return;		// Ende bei unerwartetem SETUP (kommt nie)
    beep(60,10);
    byte*addr=modeBuffer;
    n=sizeof modeBuffer;
    do *addr++=UEDATX; while (--n);	// Daten einlesen (schlimmstenfalls ungültige)
    UEINTX&=~4;
*/
   }goto okay;			// 0-Byte-IN-Paket absenden
   case 0x21: {			// Get Line Coding (wLength==7)
#if 1
    addr=LineCoding;
    len=sizeof LineCoding;
   }goto gd2;
#else
    byte n=7;
    if (!wLengthH&&n>wLengthL) n=wLengthL;
    addr=modeBuffer;
    if (n) do UEDATX=*addr++; while (--n);
   }goto okay;			// Daten absenden, OUT-Quittung ignorieren
#endif
   case 0x22: usbCfg=0x21;	// Set Control Line State (wLength==0)
   case 0x23:			// Send Break (wLength==0?)
okay:UEINTX=~1; return;
  }
stall:UECONX=0x21;		// Alles andere: STALL aktivieren
 }
}

// Abfrage von E1
static void usbPollEp1() {
 UENUM=1;
 if (UEINTX&0x80) {	// Daten angekommen?
// Anfrage darf zerhackt ankommen und muss mit '\n' enden
  while (UEINTX&0x20) {
   char c=UEDATX;	// Zeichen einsammeln
   if (querylen<sizeof query) query[querylen++]=c;	// in Stringpuffer ablegen
   if (c=='\r' && !(UEINTX&0x20)	// Manuelle Eingabe von ENTER (= '\r') in PuTTY (kein Zeichen folgt)
   ||  c=='\n') {	// maschinelle Eingabe von "\r\n" (en bloc) oder '\n' (solo)
    handleQuery();
    UENUM=1;
    querylen=0;
   }
  }
  UEINTX=0;		// Datenblock zurück zur SIE
 }
}

static void usbPollEp3() {
 UENUM=3;
 if (usbCfg&0x30 && UEINTX&1) {	// Puffer frei?
  UEINTX=~1;		// Interrupt quittieren (extra erforderlich?)
  if (usbCfg&0x20) {	// Erster Interrupt: 8 Bytes
   asm volatile(
"1:	lpm	r0,Z+	\n"
"	sts	%1,r0	\n"
"	dec	%2	\n"
"	brne	1b	\n"
::"z"(IntData),"m"(UEDATX),"r"(sizeof IntData));
  }else{		// Zweiter Interrupt: 2 Bytes
   UEDATX=3;
   UEDATX=0;
  }
  UEINTX=0;		// Interruptdaten abschicken
  usbCfg-=0x10;		// erst 2x → 1x, dann 1x → 0x0x
 }
}

void usbPoll() {
 if (USBINT&1) {	// Ab- oder Anstecken des Hosts?
  USBINT=0;
  if (USBSTA&1) {	// Angesteckt
   UDCON=0;		// Pullup aktivieren
  }else{		// Abgesteckt (Dazu muss am Arduino der Transistor T1 ausgelötet werden!)
   UDCON=1;		// Pullup deaktivieren
   USBCON=0xB0;		// Takt deaktivieren
   usbCfg=0;		// Meldung für Geigel-Menü
  }
 }
 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|E0_SSH-3<<4;// 8 Bytes für Endpoint 0 im DPRAM reservieren
 }
 usbPollEp0();
 if (!(usbCfg&0x20)) usbPollEp1();
 usbPollEp3();
}

void usbSend() {
 do{
  usbPoll();		// verändert UENUM
  UENUM=2;
 }while (!(UEINTX&0x80));	// Warten bis Puffer frei
 const char*p=answer;
 for (byte i=0; i<answerlen; i++) UEDATX=*p++;	// Puffer füllen
 UEINTX=0;			// Puffer abschicken
}

void usbPurge() {
 UENUM=1;			// OUT-Puffer leeren
 while (UEINTX&0x80) UEINTX=0;
 UENUM=2;
 while (UESTA0X&0x03) {
  UEINTX=0x05;			// Kill Bank IN
  while (UEINTX&0x04);		// warte bis fertig
 }
 UERST=0x04;
 UERST=0;			// Keine Daten, die auf Abholung warten
}
Detected encoding: UTF-80