Source file: /~heha/basteln/PC/Programmiergeräte/Wild GIF12/mtp.zip/MTP.cpp

// 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*/
Detected encoding: UTF-80