// MTP.h - MTP Responder
#include "MTP.h"
#include "myFlash.h"
#include "usb.h"
#include <string.h>
struct MTPHeader {
union{
byte lenL; // 0
word lenW;
uint32_t lenD;
byte lenB[4];
};
byte typeL,typeH; // 4
byte opL,opH; // 6
uint32_t transid; // 8
};
struct{
MTPHeader h;
uint32_t params[3]; // 12
}bulkcs;
struct{
MTPHeader h;
uint8_t payload[200];
}bulkdata;
struct event_t{
byte codeL;
byte codeH;
uint32_t sessid;
uint32_t transid;
uint32_t params[3];
void send(byte,byte);
}event;
void event_t::send(byte code,byte param) {
codeL=code;
codeH=0x40;
*(byte*)params = param;
usbSend(this,sizeof*this,true,3);
}
union varptr_t{ // Chamäleon-Zeiger, zeigt auf bulkdata.payload
void*v;
uint8_t*v8;
uint16_t*v16;
uint32_t*v32;
uint64_t*v64;
char*vc;
void write8 (uint8_t x) {*v8++=x;}
void write16(uint16_t x) {*v16++=x;}
void write32(uint16_t x) {*v32++=x;}
void write64(uint16_t x) {*v64++=x;}
void fill(uint8_t c, uint8_t len) {do write8(c); while(--len);}
void writestring(const char* str) {
byte len=str?(int)str<0?strlen_P(str):strlen(str):0;
if (len) {
++len;
write8(len);
do write16((int)str<0?pgm_read_byte(str++):(byte)*str++); // TODO: UTF-8-Konvertierung!!
while (--len);
}else write8(0);
}
varptr_t() {v8=bulkdata.payload;}
varptr_t(void*p) {v=p;}
};
// MTP Responder.
namespace MTP {
char extendedeventdata[8];
devicestatus_t devicestatus={4,0x2001};
varptr_t p;
static void outDescriptor() {
p.write16(100); // MTP version
p.write32(6); // MTP extension
p.write16(100); // MTP version
p.writestring(F("microsoft.com: 1.0;"));
p.write16(0); // functional mode
// Supported operations (array of uint16)
p.write32(14);
p.write16(0x1001); // GetDeviceInfo
p.write16(0x1002); // OpenSession
p.write16(0x1003); // CloseSession
p.write16(0x1004); // GetStorageIDs
p.write16(0x1005); // GetStorageInfo
p.write16(0x1006); // GetNumObjects
p.write16(0x1007); // GetObjectHandles
p.write16(0x1008); // GetObjectInfo
p.write16(0x1009); // GetObject
p.write16(0x100B); // DeleteObject
p.write16(0x100C); // SendObjectInfo
p.write16(0x100D); // SendObject
p.write16(0x1014); // GetDevicePropDesc
p.write16(0x1015); // GetDevicePropValue
// Events (array of uint16)
p.write32(2);
p.write16(0x4002); // file added
p.write16(0x4003); // file deleted
// Device properties (array of uint16)
p.write32(1);
p.write16(0xd402); // Device friendly name
// Capture formats (array of uint16)
p.write32(0);
// Playback formats (array of uint16)
p.write32(1);
p.write16(0x3000); // Undefined format
// p.write16(0x3001); // Folders (associations)
// Strings (nichts davon ist zu sehen)
p.writestring(F("h#s")); // Manufacturer
p.writestring(F("GIF reader")); // Model
p.writestring(0); // version
p.writestring(0); // serial
}
static void outStorageIDs() {
p.write32(1); // 1 entry
p.write32(1); // 1 storage
}
static void outStorageInfo() {
p.write16(0x0004); // storage type (removable RAM)
p.write16(0x0001); // filesystem type (generic flat)
p.write16(0x0000); // access capability (read-write)
p.write64(myFlash::getCapacity()); // max capacity (bytes)
p.write64(myFlash::getFreeSpace()); // free space (bytes)
p.write32(myFlash::getFreeObjects()); // free space (objects)
p.writestring(F("GRM10 card")); // storage descriptor
p.writestring(0); // volume identifier
}
static void outObjectHandles() {
byte num = myFlash::GetNumObjects();
p.write32(num);
for (byte a=1; a<myFlash::FSTART; a++) {
if (myFlash::eer(a)!=0xFF) p.write32(a);
}
}
static void outObjectInfo(byte handle) {
const myFlash::sec_t*s=myFlash::GetObjectInfo(handle);
p.write32(1); // Logical Storage ID
p.write32(0x3000); // undefiniert, Text
p.write32(pgm_read_word(&s->dirent.sizeL)); // size
p.fill(0,40); // Info für Bilder
p.writestring(s->dirent.name);
p.writestring(s->dirent.ctime); // date captured - automatischer Typecast-Operator!
p.writestring(s->dirent.mtime); // date modified - automatischer Typecast-Operator!
p.writestring(0); // keywords
}
static bool GetObject(byte handle) {
byte b=myFlash::eer(handle); // 1. Sektor (muss gültig sein)
if (b<myFlash::FSTART || b>=myFlash::FEND1) return false;
const char*addr=FP((const char*)(b<<myFlash::SEC_SH));
unsigned len=pgm_read_word(addr); // Datei-Länge (sollte der Sektor-Kette entsprechen!)
bulkdata.h.lenW=12+len;
usbSend(&bulkdata,12,false);
while (len) {
b=myFlash::eer(b); // (Nächster) Datensektor
if (b<myFlash::FSTART || b>=myFlash::FEND1) return false; // Fehler!
addr=FP((const char*)(b<<myFlash::SEC_SH));
unsigned l=1<<myFlash::SEC_SH; if (l>len) l=len;
usbSend(addr,l,false); // direkt aus Flash
len-=l;
}
usbSend(0,0,true); // Short-Packet
return true;
}
#define TRANSMIT(FUN) { \
FUN; \
byte fill=p.v8-(byte*)&bulkdata; \
bulkdata.h.lenL=fill; \
usbSend(&bulkdata,fill,true); \
}
static myFlash::sec_t secbuf __attribute__((section(".noinit")));
static byte usbGetString(char*s,byte space) {
byte n;
usbRecv(&n,1);
if (n) for (byte i=0; i<n; i++) {
word w;
usbRecv(&w,2); // UTF-16-Zeichen + Nullterminierung
if (space) {*s++=--space?(byte)w:0;} // nullterminieren wenn Puffer kleiner
}
else if (space) *s=0; // immer nullterminieren
return 1+n+n; // Anzahl von USB abgeholte Bytes
}
// Datei erzeugen: Hier: Verzeichniseintrag initialisieren
static bool SendObjectInfo() {
// usbRecvWait();
usbRecv(&bulkdata,20); // Header, Dateityp ignorieren
byte l=bulkdata.h.lenL;
secbuf.empty(); // mit 0xFF füllen
usbRecv(&secbuf.dirent.size,4);
usbRecv(nullptr,40); // Krimskrams dazwischen ignorieren
l-=20+4+40;
l-=usbGetString(secbuf.dirent.name,sizeof secbuf.dirent.name);
l-=usbGetString(fattime::string,sizeof fattime::string);
secbuf.dirent.ctime();
l-=usbGetString(fattime::string,sizeof fattime::string);
secbuf.dirent.mtime();
usbRecv(nullptr,l); // Rest („keywords“) auslesen
return myFlash::Create(&secbuf);
}
// Datei erzeugen: Jetzt kommen die Daten, und Dateilänge sollte gleich sein.
static bool SendObject() {
usbRecv(&bulkdata,12);
word size=bulkdata.h.lenW-12;
while (size) {
byte l=size>sizeof secbuf.data?sizeof secbuf.data:size;
secbuf.empty();
usbRecv(secbuf.data,l);
if (!myFlash::writeNextPage(&secbuf)) return false;
size-=l;
}
return true;
}
static void outDevicePropValue(unsigned prop) {
switch (prop) {
case 0xd402: // friendly name
// This is the name we'll actually see in the windows explorer.
// Should probably be configurable.
p.writestring(F("GIF reader"));
break;
}
}
static void outDevicePropDesc(unsigned prop) {
switch (prop) {
case 0xd402: // friendly name
p.write16(prop);
p.write16(0xFFFF); // string type
p.write8(0); // read-only
outDevicePropValue(prop);
outDevicePropValue(prop);
p.write8(0); // no form
}
}
static void handleCommand() {
byte p1 = 0;
byte retcode = 1; // Ok
event.transid=bulkcs.h.transid;
memcpy(&bulkdata,&bulkcs,12); // Datenantwort generell mit ähnlichem Header wie das Kommando
bulkdata.h.typeL=2; // anderer Typ
bulkdata.h.lenL=12; // andere Größe
p.v8=bulkdata.payload; // für Antworten
if (bulkcs.h.opH!=0x10) retcode=5;
else switch (bulkcs.h.opL) {
case 1: // GetDescription
TRANSMIT(outDescriptor());
break;
case 2: event.sessid=bulkcs.params[0]; break; // OpenSession
case 3: event.sessid=0; break; // CloseSession
case 4: // GetStorageIDs
TRANSMIT(outStorageIDs());
break;
case 5: // GetStorageInfo
TRANSMIT(outStorageInfo());
break;
case 6: // GetNumObjects
if (bulkcs.params[1]) retcode = 0x14; // spec by format unsupported
else p1 = myFlash::GetNumObjects();
break;
case 7: // GetObjectHandles
if (bulkcs.params[1]) retcode = 0x14; // spec by format unsupported
else TRANSMIT(outObjectHandles());
break;
case 8: // GetObjectInfo
TRANSMIT(outObjectInfo(bulkcs.params[0]));
break;
case 9: // GetObject
if (!GetObject(bulkcs.params[0])) retcode=21;
break;
case 11: // DeleteObject
if (!myFlash::DeleteObject(bulkcs.params[0])) retcode = 0x12; // partial deletion
else event.send(3/*object removed*/,bulkcs.params[0]);
break;
case 12: // SendObjectInfo
if (!SendObjectInfo()) retcode=12; // store_full
else bulkcs.params[2] = myFlash::createdDirEnt;
bulkcs.h.lenL = 12 + 3*4; // 3 Parameter
goto ex1;
case 13: // SendObject
if (!SendObject()) retcode=21; // no_valid_objectinfo
bulkcs.h.lenL = 12;
event.send(2/*object added*/,myFlash::createdDirEnt);
break;
case 20: // GetDevicePropDesc
TRANSMIT(outDevicePropDesc(bulkcs.params[0]));
break;
case 21: // GetDevicePropvalue
TRANSMIT(outDevicePropValue(bulkcs.params[0]));
break;
default:
retcode = 5; // operation not supported
}
bulkcs.h.lenL = 16; // Standard: Rückgabe 1 Parameter
bulkcs.params[0] = p1;
ex1: // für mehr als 1 Parameter
bulkcs.h.typeL = 3; // Antwort
bulkcs.h.opL = retcode;
bulkcs.h.opH = 0x20;
usbSend(&bulkcs,bulkcs.h.lenL,true);
}
// Einzige exportierte Funktion
void pollUSB() {
if (usbRecvPoll()) {
PORTD&=~0x20; // TX
usbRecv(&bulkcs,8);
if (bulkcs.h.typeL==1 // "command", alles andere: STALL (Protokollfehler)
// && bulkcs.h.typeH==0x10
&& bulkcs.h.lenD<=sizeof bulkcs
&& bulkcs.h.lenL>=12) {
usbRecv(&bulkcs.h.transid,bulkcs.h.lenL-8); // Rest des Kommandos abholen
handleCommand();
}else{
// eelog(&bulkcs,8); // Murks!
UEINTX=0; // aufs nächste ganze USB-Paket synchronisieren
}
PORTD|= 0x20;
}
}
}/*namespace*/
Vorgefundene Kodierung: UTF-8 | 0
|