#include "usb.h"
#include <avr/interrupt.h>
#include "MTP.h" // MTP::devicestatus, MTP::extendedeventdata
// Media Transfer Protocol: "still image capture device class"
char ep0dat[8];
byte ep0len;
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=5; // 32 Byte Interrupt IN
#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), //
1, // iVendor
2, // 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 16
100, // bInterval
};
#undef W
extern "C" byte getbyte(/*const char*&Z*/) __attribute__((naked));
byte getbyte() { asm(
" sbrs ZH,7 \n"
" ld r24,Z+ \n"
" sbrc ZH,7 \n"
" lpm r24,Z+ \n"
" ret \n"
);}
#define getByte(addr) (__extension__({ \
byte r; \
__asm__( \
" sbrs ZH,7 \n" \
" ld %0,Z \n" \
" sbrc ZH,7 \n" \
" lpm %0,Z \n" \
:"=r"(r):"z"((addr)) \
); \
r; \
}))
extern "C" word getutf8(/*const char*&Z*/) __attribute__((naked));
// PE: Z = Zeiger auf fehlerfreien UTF-8-String (nur BMP0) in RAM oder Flash
// PA: Z vorgerückt um 1..3 Bytes
// R25:R24 = UTF-16-Zeichen
// VR: R18,R19
word getutf8() {asm(
" rcall getbyte \n"
" clr r25 \n"
" sbrs r24,7 \n"
" rjmp 3f \n" // plain ASCII to UTF-16
" ldi r18,0 \n" // prepare OR data
" ldi r19,0x1F\n" // prepare mask
" sbrs r24,5 \n"
" rjmp 2f \n" // jump when 2 byte UTF-8
" mov r18,r0 \n" // 1110 xxxx = 3 byte UTF-8
" andi r18,0x0F\n" // 0000 xxxx
" swap r18 \n" // xxxx 0000
" rcall getbyte \n" // 10xx xxxx
" ldi r19,0x3F\n"
"2: mov r25,r24 \n" // 110x xxxx or 10xx xxxx
" and r25,r19 \n" // 000x xxxx or 00xx xxxx
" rcall getbyte \n" // 10xx xxxx
" lsl r24 \n" // 0xxx xxx0
" lsl r24 \n" // xxxx xx00
" lsr r25 \n" // 000x xxxx -> x
" ror r24 \n" // xxxx xxx0
" lsr r25 \n" // 0000 xxxx -> x
" ror r24 \n" // xxxx xxxx
" or r25,r18 \n" // apply OR data (!0 in case of 3 byte UTF-8)
"3: ret \n"
);}
// Macht aus UTF-8 einen USB-mäßigen String-Deskriptor
// Der String darf im RAM oder Flash liegen
static byte*makeStringDesc(const char*s) {
static byte sbuf[32];
asm(
" movw r0,XL \n" // save address
" ldi r25,3 \n"
"1: st X+,r24 \n" // at first execution, fill with garbage
" st X+,r25 \n" // at first execution, set "3" for type
" rcall getutf8 \n"
" adiw r24,0 \n" // end of string?
" brne 1b \n" // no, append character
" mov r24,XL \n" // save end address
" movw XL,r0 \n" // restore start address
" sub r24,XL \n" // calculate byte length
" st X,r24 \n" // set length byte
" clr r1 \n"
:"+z"(s),"=memory"(sbuf):"x"(sbuf):"r24","r25","r18","r19");
return sbuf;
}
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?
ep0len=0;
while (UEINTX&0x20) ep0dat[ep0len++]=UEDATX; // Bytes einsammeln
UEINTX=~4;
handleEp0Out();
}
if (UEINTX&8) { // SETUP angekommen?
byte len;
const void*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=FP(DeviceDesc); break;
case 2: addr=FP(ConfigDesc); len=sizeof ConfigDesc; goto gd2;
case 3: switch (wValueL) {
case 0: addr=makeStringDesc(F("\xD0\x89")); break; // english-US
case 1: addr=makeStringDesc(F("Haftmann")); break;
case 2: addr=makeStringDesc(F("GIF reader")); break;
default: goto stall;
}break;
default: goto stall;
}
len=getByte(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;
asm( // do UEDATX=getByte(addr++); while (--n);
"1: rcall getbyte \n" // Von-Neumannisierte Adresse lesen
" sts %1,r24 \n"
" dec %2 \n"
" brne 1b \n"
:"+z"(addr):"m"(UEDATX),"r"(n):"r24");
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 32 Byte
UENUM=0;
}else{
UERST=0x0E;
UERST=0; // alle 3 Endpoints rücksetzen
}
// bulkoutlen=0;
goto okay;
}break;
case 10: UEDATX=0; /*nobreak*/; // Get Interface (= Alternate Setting)
case 11: okay:UEINTX=~1; return; // Set Interface (ignorieren)
// class specific requests
case 100: return; // cancel set-up (00100001, wLength=6)
case 101: { // get extended event data (10100001, wLength=bufsize);
addr=MTP::extendedeventdata; len=8;
}goto gd2;
case 102: goto okay; // device reset set-up (00100001, wLength=0)
case 103: { // get device status (10100001, wLength=bufsize)
addr=&MTP::devicestatus; len=MTP::devicestatus.wLength;
}goto gd2;
}
stall:UECONX=0x21; // Alles andere: STALL aktivieren
}
}
bool usbRecvPoll() {
UENUM=1;
return UEINTX&0x80;
}
// Zwangsweise USB-Daten lesen
byte usbRecv(void*buf,byte exlen) {
byte*p=(byte*)buf;
byte len=0;
UENUM=1;
while (len<exlen) {
while (!(UEINTX&0x20)) { // BUGBUG: Paket könnte inzwischen ankommen
UEINTX=0; // Datenblock zurück zur SIE
while (!(UEINTX&0x80)); // Warten auf weitere Daten (unendlich)
}
byte b=UEDATX;
if (p) *p++=b; // RAM schreiben
len++;
}
if (!(UEINTX&0x20)) UEINTX=0; // ausgelesen: zurück zur SIE
return len;
}
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();
}
void usbSend(const void*buf,byte len,bool flush,byte ep) {
const byte*p=(const byte*)buf;
do{
while (UENUM=ep,!(UEINTX&0x80)) usbPoll(); // Warten bis Puffer frei
while (UEINTX&0x20 && len) {
#if 0
UEDATX=(int)p<0?pgm_read_byte(p++):*p++; // aus RAM oder Flash
#else
asm(" rcall getbyte\n sts %1,r24 \n":"+z"(p):"m"(UEDATX):"r24");
#endif
--len;
}
if (flush || !(UEINTX&0x20)) UEINTX=0; // Puffer abschicken
}while(len);
}
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-8 | 0
|