Audio-RaveMP

 view release on metacpan or  search on metacpan

ravemp.c  view on Meta::CPAN

/*

  Upload/download/management utility for the RaveMP portable MP3 player.

  TO DO:

	Improve recovery if block write/read fails - at present a single failure
    seems to cause the retries to fail as well. As there is an internal
    player timeout, a temporary solution is to sleep between retries.

	Test for Device presence on startup (e.g. read/check first page of FAT)

    Add automatic power up (allows upload/download even if device appears to be "off")

	Add "summary" command to display usage, capacity etc

	Make all XP's XPV() inside read/functions

    Tidy up command structure (common routine to send commands)

    Add support for ECP (requires EPP at present)

    Add configuration for port address

    Add support for different memory configurations

    Twiddle for clean compile at max. warnings

 History
 -------

  25-Dec-99 Version 0.0.1:	Initial version by hazeii (of the Snowblind
	Alliance),for basic upload and download testings and exploration of
	player device memory structure.


  30-Dec-99: Version 0.0.2:	Improved timing, made upload and download rather
	more friendly, implemented file deletion. 

  Jan-00: modified for Perl interface by dougm

*/

#include "ravemp.h"
#include "font_8x16.c"

static int read_page(unsigned block,unsigned page,struct page *pp);
static int read_block(unsigned block,unsigned char *buff);
static int read_numbers(unsigned char *p,unsigned count,...);
static int read_page_attempt(unsigned block,unsigned page,struct page *pp);
static int write_block_attempt(unsigned block,unsigned char *blkbuff);
static int read_block_attempt(unsigned block,unsigned char *blkbuff);
static void hexdump_line(unsigned char *p);
static int show_info(unsigned firstblock,unsigned char *baseblk,unsigned char *datablk);
static void iodelay(void);
static int status_wait(unsigned pattern,unsigned *rc);
static int dump_tags(unsigned value);
static int write_tests(void);
static void XPV(int level,char *fmt,...);

static int widescr=0;	/* Option flag - indicates wide image display wanted */

static ravemp_show_status = 0;

void ravemp_set_show_status(int status)
{
    ravemp_show_status = status;
}

#define ravemp_status_dot() \
if (ravemp_show_status) printf(".")

#if 0
/********************** interact **************************/
interact()
{
    int i,j;
    unsigned page,block;
    char c,*p,*s,line[512];

    XP("\nInteractive Mode (? for help)\n");

    while (1) {

	XP(">");

	if (fgets(line,sizeof(line),stdin)==NULL)
	    return;

	p = &line[0];

	while (*p==' ' || *p=='\t')	/* Skip leading white space */
	    p++;

	if (*p=='\n' || strlen(p)==0)
	    continue;

ravemp.c  view on Meta::CPAN

		XP("Specify Block number in decimal (ddd) or hex (0xnnn), e.g. B 0x2f\n");
	    else
		dump_block(block);
	    break;

	  case 'd':
	    if (read_numbers(p,1,&j) < 1)
		XP("Specify starting slot number, e.g. D 6\n");
	    else
		download(j);
	    break;

	  case 'i':
	    if (read_numbers(p,1,&j) < 1)
		XP("Specify starting slot number, e.g. I 7\n");
	    else
		info(j);
	    break;

	  case 'l':
	    list_contents(*p=='*');
	    break;

	  case 'p':
	    if ((i=read_numbers(p,2,&block,&page)) < 2)
		XP("Specify Block and Page number in decimal (ddd) or hex (0xnnn), e.g. P 6 0\n");
	    else
		dump_page(block,page);
	    break;

	  case 'r':
	    if (read_numbers(p,1,&j) < 1)
		XP("Specify starting slot number, e.g. R 12\n");
	    else
		remove_file(j);
	    break;


	  case 's':
	    if ((i=read_numbers(p,1,&block)) < 1)
		XP("Specify Block number in decimal (ddd) or hex (0xnnn), e.g. S 0x2f\n");
	    else
		dump_image(block,widescr);
	    break;


	  case 't':
	    if (read_numbers(p,1,&j) > 0)
		lpt_test(j);	/* Test specified port */
	    else
		lpt_test(port); /* Test default port */
	    break;

	  case 'u':
	    s = p;
	    while (*s>=' ')
		s++;
	    *s='\0';	/* Trim filename at first control character */

	    if (strlen(p)==0)
		XP("No file specified for upload\n");
	    else
		upload_file(p);
	    break;

	  case 'q':
	  case 'x':
	    return;

	  case '?':
	  case 'h':
	    XP("B <b>      Block Dump\n");
	    XP("D <slot>   Download file from player\n");
	    XP("H          Help (this message)\n");
	    XP("I <slot>   Information on file\n");
	    XP("L [*]      List MP3 files (* = All Files)\n");
	    XP("P <b> <p>  Page dump\n");
	    XP("Q          Quit (same as Exit)\n");
	    XP("R <slot>   Remove (delete) file\n");
	    XP("S <slot>   Show display image\n");
	    XP("T [addr]   Test port hardware\n");
	    XP("U <file>   Upload file to player\n");
	    XP("X          Exit (same as Quit)\n");
	    break;

	  default:
	    XP("Unknown command (? for list)\n");
	    break;
	}
    }
}
#endif

/********************** read_page ******************************/

static int read_page(unsigned block,unsigned page,struct page *pp)
{
    int tries;

    for (tries=0;tries<READ_RETRY_COUNT;tries++) {
	if (read_page_attempt(block,page,pp))
	    return 1;
    }

    return 0;
}

/**************** read_page_attempt *******************/

static int read_page_attempt(unsigned block,unsigned page,struct page *pp)
{
    register int i;
    register unsigned char *p;
    unsigned rc;
    unsigned char cmd[]={0x5,0x45,0x0,0x0,0x0,0x4};

    /* Check/wait for idle state */

    if (!ravemp_check_idle()) {
	RMP_TRACE(fprintf(stderr, "Read Page failed: Device not ready\n"));
	return 0;
    }

ravemp.c  view on Meta::CPAN

}
/************************ store_fat **********************/

static int store_fat(unsigned char *fatbuff)
{
    int i;
    unsigned char *blkbuff;

    /* Allocate block buffer */

    if ((blkbuff=malloc(MAX_BLOCK_SIZE)) == NULL) {
	XP("Memory Allocation failure\n");
	exit();
    }

    /* Read existing FAT data block */

    if (!read_block(0,blkbuff)) {
	XP("Update failed: Couldn't read FAT\n");
	free(blkbuff);
	return 0;
    }

    if (*blkbuff != 'F') {
	XP("Failure: Pre-write FAT validation error (0x%x should be 0xx)\n",*blkbuff,'F');
	free(blkbuff);
	return 0;
    }

    /* Copy new FAT area into block */

    for (i=0;i<TOTAL_BLOCKS/PAGE_SIZE;i++)	/* For each FAT data page... */
	memcpy(blkbuff+i*(PAGE_SIZE+TAG_SIZE),fatbuff+i*PAGE_SIZE,PAGE_SIZE);

    /* Write FAT data */

    if (!write_block(0,blkbuff)) {
	XP("FAT update error: Block write failed\n");
	free(blkbuff);
	return 0;
    }

#if 0 /* Display updated block */
    for (i=0;i<(8*(512+16))/16;i++) {
	XP("%u %04x  ",i%32,i*16);
	hexdump_line(blkbuff+i*16);
	XP("\n");
	if (i%32==31) {
	    XP("--more--\n");
	    if (getchar()=='q')
		break;
	}
    }
#endif

    free(blkbuff);

    return 1;
}

/************************ upload_file *********************/

/* Upload a file to the device */

int ravemp_upload_file(char *fname, char *dest_name)
{
    int i,j,mp3file,uploadok;
    int block,blk1no,blk2no,prvblk,nxtblk,page;
    unsigned fsize,fblocks,avail,namelen;
    unsigned char *p,*t,*firstblk,*datablk,*filebuff;
    unsigned char fat[TOTAL_BLOCKS];
    FILE *fp;
    struct stat fileinfo;

    uploadok = 0;

    /* Read device FAT */

    if (!load_fat(fat)) {
	RMP_TRACE(fprintf(stderr, "Unable to load allocation table\n"));
	return 0;
    }

    /* Count free blocks and determine header block numbers */

    avail = 0;

    blk1no = blk2no = -1;

    for (i=0;i<TOTAL_BLOCKS;i++) {

	if (fat[i]==0xff) {

	    if (blk1no == -1)
		blk1no = i;
	    else if (blk2no == -1)
		blk2no = i;

	    avail++;

	}
    }

    /* Get file information (note we simplistically determine if it's an MP3
       file or not by checking for a .mp3 extension) */

    if (stat(fname,&fileinfo) != 0) {
	RMP_TRACE(fprintf(stderr, "Unable to access file %s\n",fname));
	return 0;
    }

    fsize = fileinfo.st_size;

    fblocks = (fsize+DATA_SIZE-1)/DATA_SIZE;

    RMP_TRACE(fprintf(stderr, "File size: %u bytes [%u blocks needed, %u available]\n",fsize,fblocks,avail));

    if (fblocks+2 > avail) {	/* +2 for 2 header blocks */
	RMP_TRACE(fprintf(stderr, "Not enough space available for file\n"));
	return 0;
    }

    mp3file = ((p=strrchr(fname,'.')) != NULL && strcasecmp(p,".mp3")==0);

    /* Open file for upload */

    if ((fp=fopen(fname,"r"))==NULL) {
	RMP_TRACE(fprintf(stderr, "Unable to open file %s\n",fname));
	return 0;
    }

    /* Allocate memory for block buffers */

    if ((firstblk=malloc(MAX_BLOCK_SIZE))==NULL || 
	(datablk=malloc(MAX_BLOCK_SIZE)) == NULL ||
	(filebuff=malloc(DATA_SIZE)) == NULL) {
	RMP_TRACE(fprintf(stderr, "Unable to allocate memory for data buffers\n"));
	fclose(fp);
	return 0;
    }

    /* Build block list for file and put tag characters into FAT buffer */

    memset(firstblk,0xff,MAX_BLOCK_SIZE);	/* Set every value to "unused" */

    p = firstblk;

    *p++ = (blk1no>>8) & 0xff;
    *p++ = blk1no & 0xff;
    fat[blk1no] = mp3file?'M':'E';

    *p++ = (blk2no>>8) &0xff;
    *p++ = blk2no &0xff;
    fat[blk2no] = mp3file?'m':'e';

    for (i=TOTAL_BLOCKS-1,j=fblocks;i>=0&&j>0;i--) {

	if (fat[i]==0xff) {
	    fat[i]=mp3file?'m':'e';
	    *p++ = ((unsigned)i>>8) & 0xff;
	    *p++ = i & 0xff;
	    j--;
	}
    }

    /* Debug */
#if 0
    XP("MP3 File: %s\n",mp3file?"YES":"NO");
    p = firstblk;
    for (i=0;i<fblocks;i++) {
	printf("Block %u\n",(*p<<8) + *(p+1));
	p+=2;
    }
#endif

    /* Read file data and upload one block at a time */

    p = firstblk+4;	/* Pointer to first data block number */

    prvblk = blk1no;	/* Initialise "previous block" number for tag area */

    memset(datablk,0xff,DATA_SIZE); /* Initialise buffer (so all tag areas are 0xff) */

    for (i=0;i<fblocks;i++) {

	/* Read as much data as will fit into a single block */

	if ((j=fread(filebuff,1,DATA_SIZE,fp)) <= 0) {
	    RMP_TRACE(fprintf(stderr, "File Read failed - Upload aborted\n"));
	    goto uldone;
	}

	/* Copy file data into the pages within the block */

	for (page=0;page<PAGES_PER_BLOCK;page++)
	    memcpy(datablk+page*(PAGE_SIZE+TAG_SIZE),filebuff+(page*PAGE_SIZE),PAGE_SIZE);

	/* Set up the tag area in the first page of the block */

	block = (*p<<8) | *(p+1);	/* Get this block number */

	p+=2;						/* Update pointer for next time */

	nxtblk = (*p<<8) | *(p+1);	/* Next block we'll be writing to */

	t = datablk+PAGE_SIZE;
	memcpy(t,mp3file?"mp3\002":"ext\002",4);	/* File identifer */
	t += 4;

	*t++ = i>>8;		/* [4-5]: Block index */
	*t++ = i&0xff;

	*t++ = prvblk>>8;	/* [6-7]: Previous block number */
	*t++ = prvblk&0xff;

	*t++ = block>>8;	/* [8-9]: Current block number */
	*t++ = block&0xff;

	*t++ = nxtblk>>8;	/* [10-11]: Next block number (0xffff if none) */
	*t++ = nxtblk&0xff;

	*t++ = j>>8;		/* [12-13]: Data used within block */
	*t++ = j&0xff;

	*t++ = 0;			/* [14-15]: Zero (in data blocks) */
	*t++ = 0;

#if 0
	RMP_TRACE(fprintf(stderr, "Block %4u: ",block));
	hexdump_line(datablk+PAGE_SIZE);
	RMP_TRACE(fprintf(stderr, "\n"));
#endif

	prvblk = block;		/* Save current block as previous block for next time */

	/* Write the block out */

ravemp.c  view on Meta::CPAN

      therefore 128*16/8 bytes per character row and 32 characters per page.
    */

    p = dest_name;	/* Text to be converted to pixmap */

    if ((namelen=strlen(p)) > 4) { /* Don't count ".mp3" at end if present */
	if (strcasecmp(".mp3",p+namelen-4) == 0)
	    namelen -= 4;
    }

    for (i=0;i<namelen;i++) {

	t = datablk + (i/32)*(PAGE_SIZE+TAG_SIZE); /* Pointer to page */

	t += (i&16)*16+(i%16);	/* Point to first byte of character cell */

	for (j=0;j<16;j++) {	/* For each row of the character...*/
	    *t = fontdata_8x16[*p*16+j];	/* Copy row of pixels */
	    t += 16;		/* Advance to next row of pixels of char. cell */
	}

	p++;
    }

    /* We now know how many characters were placed in the display data - update
       the page 0 tags for the second block. */

    t = datablk + PAGE_SIZE + 12;	/* Offset to Page 0 tags, size info */

    j = (namelen+31)/32 * (PAGE_SIZE+TAG_SIZE); /* Total pages used for image data */

    /* WARNING: These values may not be correct as the full details of how the player
       interprets them isn't know. If incorrect, the expected consequence is over or
       under display of track titles on the player display */

    /*XP("Name length %u Data usage %u Screens %u\n",namelen,j,(namelen+15)/16); /**/

    *t++ = j>>8;		/* [2nd block Page 1 12-13]: Data used in block */
    *t++ = j&0xff;

    *t++ = (namelen+15)/16;	/* [2nd block Page 1 14-15]: Display screen count? */
    *t++ = 0;

    /* Write first and second blocks out, and update FAT */

    if (!write_block(blk1no,firstblk)) {
	RMP_TRACE(fprintf(stderr, "Upload Failure: Unable to update block list [Block %u]\n",blk1no));
	goto uldone;
    }

    if (!write_block(blk2no,datablk)) {
	RMP_TRACE(fprintf(stderr, "Upload Failure: Unable to update header block [Block %u]\n",blk2no));
	goto uldone;
    }

    if (!store_fat(fat)) {
	RMP_TRACE(fprintf(stderr, "Upload failure: Unable to update directory\n"));
	goto uldone;
    }

    uploadok = 1;

    RMP_TRACE(fprintf(stderr, "File %s (size %u) uploaded.\n",fname,fsize));

    /* Exit path - clean up and return success/failure indication */

 uldone:
    free(filebuff);
    free(datablk);
    free(firstblk);
    fclose(fp);

    return uploadok;

}
/************************ download *************************/

/*

  File download (from player device to file)

  The first block of file holds the block number list

  The second block of file contains information about the file
  (including its name) in the tags area, while the data area
  contains the pixel image data for the LCD display.

  The third and subsequent blocks contain the MP3 data.

*/

int ravemp_download(unsigned firstblock, char *dest)
{
    unsigned i,j,k,dlok,page;
    unsigned chunksize,buffsize,fsize,wrsize;
    unsigned char *p,tagbuff[PAGES_PER_BLOCK*TAG_SIZE],*baseblk,*datablk;
    struct stat fileinfo;
    struct page xpage;
    FILE *fp;

    /* Determine buffer size and allocate it */

    chunksize = PAGE_SIZE+TAG_SIZE;

    buffsize = PAGES_PER_BLOCK * chunksize;

    if ((baseblk=malloc(buffsize)) == NULL || (datablk=malloc(buffsize)) == NULL) {
	RMP_TRACE(fprintf(stderr, "Memory Allocation failure\n"));
	return 0;
    }

    /* Read first block of file, which contains the list of blocks used */

    if (!read_block(firstblock,baseblk)) {
	RMP_TRACE(fprintf(stderr, "Read of First Block failed\n"));
	free(baseblk);
	free(datablk);
	return 0;
    }

    /* Read second block of file, which holds the file information */

    j = (*(baseblk+2)<<8) | *(baseblk+3);

    if (!read_block(j,datablk)) {
	RMP_TRACE(fprintf(stderr, "Read of Second Block failed\n"));
	free(baseblk);
	free(datablk);
	return 0;
    }

    /* Extract file size */



( run in 0.706 second using v1.01-cache-2.11-cpan-b16cb0d3907 )