#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-8 | 0
|