#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);
}
Vorgefundene Kodierung: UTF-8 | 0
|