#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <dev/usb/usb.h>
#include <sys/ioctl.h>

#define DEFAULT_UGEN_DEVICE "/dev/ugen0"

#define USB_VENDOR_OERSTED 0x0e19
#define USB_PRODUCT_OERSTED_PIECECTL 0x1000

#define USB_PIECE_CMD_GETVER 0
#define USB_PIECE_CMD_EXEC 1
#define USB_PIECE_CMD_READ 2
#define USB_PIECE_CMD_WRITE 3
#define USB_PIECE_CMD_SETAPP 4
#define USB_PIECE_CMD_GETAPP 5
#define USB_PIECE_CMD_GETWAV 6
#define USB_PIECE_CMD_OUTWAV 7
#define USB_PIECE_CMD_FERASE 8
#define USB_PIECE_CMD_FWRITE 9
#define USB_PIECE_CMD_UREAD 10
#define USB_PIECE_CMD_UWRITE 11
#define USB_PIECE_CMD_UGET 12
#define USB_PIECE_CMD_UOPEN 13
#define USB_PIECE_CMD_UCLOSE 14
#define USB_PIECE_CMD_SETRTC 15

#define PFFS_BLKSIZ 4096
#define PFFS_MAXDIR 96
#define PFFS_MAXFAT 256
#define PFFS_SIGSIZE 32
#define PFFS_DIRENT_SIZE 32
#define PFFS_FAT_FREE 0xffff
#define PFFS_FAT_END 0xeeee
#define PFFS_FAT_INVALID 0xdddd
#define PFFS_FAT_SYSTEM 0xcccc
#define PFFS_SIGOFF 8
#define PFFS_SIGNATURE "PFFS Master Block"
#define PFFS_FILENAME_SIZE 24
#define PFFS_FILEATTR_SIZE 1
#define PFFS_FILECHAIN_OFF 26
#define PFFS_FILESIZE_OFF 28


struct piece_sysinfo {
  u_short size;
  u_short hard_ver;
  u_short bios_ver;
  u_short bios_date;
  u_long sys_clock;
  u_short vdde_voltage;
  u_short resv1;
  u_long sram_top;
  u_long sram_end;
  u_long pffs_top;
  u_long pffs_end;
};

int usbfd_blk;
struct piece_sysinfo psinf;

unsigned char pffs_superblock[PFFS_BLKSIZ];

#define GETLONG(buf) (((((((buf)[3]<<8)|(buf)[2])<<8)|(buf)[1])<<8)|(buf)[0])
#define GETSHORT(buf) ((((buf)[1])<<8)|(buf)[0])
#define SETLONG(buf,val) do{\
                          (buf)[0]=(val)&0xff;\
                          (buf)[1]=((val)>>8)&0xff;\
                          (buf)[2]=((val)>>16)&0xff;\
                          (buf)[3]=((val)>>24)&0xff;\
                         }while(0);
#define SETSHORT(buf,val) do{\
                          (buf)[0]=(val)&0xff;\
                          (buf)[1]=((val)>>8)&0xff;\
                         }while(0);

int get_sysinfo()
{
  static unsigned char buf[512];
  int sysinfo_len;
  buf[0] = USB_PIECE_CMD_GETVER;
  if(write(usbfd_blk, buf, 1)== -1){
    perror("WRITE");
    exit(-1);
  }
  if(read(usbfd_blk, buf, 8) == -1){
    perror("READ");
    exit(-1);    
  }

  sysinfo_len = buf[0]+(buf[1]<<8);
  buf[0] = 0;
  buf[1] = sysinfo_len & 0xff;
  
  if(write(usbfd_blk, buf, 2)== -1){
    perror("WRITE");
    exit(-1);
  }
  if(read(usbfd_blk, buf, sysinfo_len) == -1){
    perror("READ");
    exit(-1);    
  }
  psinf.size = GETSHORT(&buf[0]);
  psinf.hard_ver = GETSHORT(&buf[2]);
  psinf.bios_ver = GETSHORT(&buf[4]);
  psinf.bios_date = GETSHORT(&buf[6]);
  psinf.sys_clock = GETLONG(&buf[8]);
  psinf.vdde_voltage = GETSHORT(&buf[12]);
  psinf.sram_top = GETLONG(&buf[16]);
  psinf.sram_end = GETLONG(&buf[20]);
  psinf.pffs_top = GETLONG(&buf[24]);
  psinf.pffs_end = GETLONG(&buf[28]);
  printf("P/ECE ver. %x BIOS ver %x(%d/%d/%d) %dHz vdde %d \n",
	 psinf.hard_ver, psinf.bios_ver, 
	 (psinf.bios_date>>9)+2000, (psinf.bios_date >>5)&0xf,
	 psinf.bios_date&0x1f,
	 psinf.sys_clock, psinf.vdde_voltage);
  printf("SRAM:Top %x END %x, PFFS: Top %x END %x\n", 
	 psinf.sram_top, psinf.sram_end, psinf.pffs_top, psinf.pffs_end);
  
}

mount_pffs()
{
  static unsigned char buf[512];
  int i,ptr,link;
  unsigned char *dirent,*fat;
  int ptop = psinf.pffs_top;
  buf[0] = USB_PIECE_CMD_READ;
  SETLONG(&buf[1],ptop);
  SETLONG(&buf[5],PFFS_BLKSIZ);
  if(write(usbfd_blk, buf, 9)== -1){
    perror("WRITE");
    exit(-1);
  }
  if(read(usbfd_blk, pffs_superblock, PFFS_BLKSIZ) == -1){
    perror("READ");
    exit(-1);
  }
  ptr = GETLONG(pffs_superblock);

  if(strncmp(&pffs_superblock[8],
	     PFFS_SIGNATURE, PFFS_SIGSIZE-PFFS_SIGOFF )!=0){
    fprintf(stderr,"Invalid Signature\n");
    exit(-1);
  }

  for(i=0; i < PFFS_MAXDIR; i++){
    dirent = &pffs_superblock[i*PFFS_DIRENT_SIZE + PFFS_SIGSIZE];
    if(*dirent != 0xff)
      
      printf("%24s attr%x chain %x size %d \n",
	     dirent, dirent[PFFS_FILENAME_SIZE],
	     GETSHORT(&dirent[PFFS_FILECHAIN_OFF]),
	     GETLONG(&dirent[PFFS_FILESIZE_OFF]));
  }
  fat = &pffs_superblock[PFFS_MAXDIR*PFFS_DIRENT_SIZE + PFFS_SIGSIZE];
  for(i=0; i < PFFS_MAXFAT; i++){
    if(i%16 == 0)
      printf("\n");
    link = GETSHORT(&fat[i*2]);
    switch(link){
    case PFFS_FAT_FREE:
      printf("FREE ");
      break;
    case PFFS_FAT_END:
      printf("END  ");
      break;
    case PFFS_FAT_INVALID:
      printf("INVL ");
      break;
    case PFFS_FAT_SYSTEM:
      printf("SYST ");
      break;
    default:
      printf("%04x ",link);
      break;
    }
  }
  return;
}
int piece_probe(char *ugen)
{
  struct usb_device_info devi;
  int usbfd_ctl;
  int res=0;
  usbfd_ctl = open(ugen ,O_WRONLY);
  if(usbfd_ctl == -1)
    return 0; 
  if(ioctl(usbfd_ctl, USB_GET_DEVICEINFO, &devi) == -1)
    goto end;
  if((devi.vendorNo == USB_VENDOR_OERSTED)&&
     (devi.productNo == USB_PRODUCT_OERSTED_PIECECTL))
    res=1;
end:
  close(usbfd_ctl);
  return res;
}
main(int argc, char *argv[])
{

  int i;
  char *gendev,blkdev[512];
  gendev = DEFAULT_UGEN_DEVICE;
  snprintf(blkdev,512,"%s.2",gendev);
  if(!piece_probe(gendev)){
    fprintf(stderr, "This is not P/ECE control mode\n");
    exit(-1);
  }
  usbfd_blk = open(blkdev, O_RDWR);
  if(usbfd_blk == -1){
    perror("OPEN");
    exit(-1);
  }
  get_sysinfo();
#if 1
  mount_pffs();
#endif
}
