Artifact [c371190679]

Artifact c371190679f32754769ca964cec3f314e347df44:


/*
 * jq6500 - Program the SPI flash of a JQ6500 MP3 player module.
 *
 * Copyright (C) 2017 Reinhard Max <reinhard@m4x.de>
 *
 * "THE BEER-WARE LICENSE" (Revision 42): As long as you retain this
 * notice you can do whatever you want with this stuff. If we meet
 * some day, and you think this stuff is worth it, you can buy me a
 * beer in return.
 */

#include <stdint.h>
#include <scsi/sg.h>
#include <unistd.h>
#include <endian.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <unistd.h>
#include <err.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#define JQ6500_CM

struct jqcmd {
    uint16_t j_cmd;
    uint32_t j_off;
    uint32_t j_foo;
    uint32_t j_len;
    uint16_t j_bar;
} __attribute__((packed));

#define BASE 0x40000
#define MAXSIZE 0x200000 - BASE /* 2MB - 256kB */

off_t
filesize(char *file)
{
    int result;
    struct stat buf;
    result = stat(file, &buf);
    if (result != 0)
	err(1, "cannot stat %s", file);
    return buf.st_size;
}

void
w32le(uint8_t **loc, uint32_t val)
{
    memcpy(*loc, &htole32(val), 4);
    (*loc) += 4;
}

void
loadbar(unsigned int x, unsigned int n)
{
    int i;
    unsigned int w = 50;
    float ratio    =  x/(float)n;
    int   c        =  ratio * w;
    
    fprintf(stderr, "\r%7d (%3d%%) [", x, (int)(ratio*100));
    for (i=0; i<c; i++) fprintf(stderr, "-");
    for (i=c; i<w; i++) fprintf(stderr, " ");
    fprintf(stderr, "]");
    fflush(stderr);
}

void
usage(void)
{
    printf("Usage: jq6500 [OPTION]... [FILE]...\n"
	   "\n"
	   "Write files to an MP3 player based on the JQ6500 chip that is connected\n"

	   "via USB. Files can be written in raw mode or as a simple file system which\n"
	   "can created on the fly.\n"
	   "The file system is needed by the device in order to play MP3 or WAV\n"
	   "files. Raw mode can be used to overwrite the ISO image in the first\n"
	   "256kiB of the device or to flash an existing file system image with audio\n"
	   "files.\n"
	   "\n"
	   "Options:\n"
	   "  -o device   SCSI generic device or file to write to (mandatory)\n"
	   "  -f offset   start writing here (default: 0x40000 for devices, 0 for files)\n"
	   "  -r          write a single file in raw mode\n"
	   "  -s          create pseudo file system from the given files (default)\n"
	   "  -d prefix   dump the content of the pseudo file system on the device\n"
	   "              to a set of files named prefix-dirno-fileno\n");
    exit(1);
}

int
main(int argc, char **argv)
{
    uint8_t buf[MAXSIZE];
    uint8_t *dir, *data;
  
    int count, size, dev;
    int i, len;
  
    if (argc < 3) {
	usage();
    }
    memset(buf, 0xff, MAXSIZE);

    count = argc-2;
    dir = buf;
    size = (3 + 2 * count) * 4;
    data = dir + size;

    w32le(&dir, 1);		/* Number of Subdirs */
    w32le(&dir, BASE + 8);	/* Offset of first subdir */
    w32le(&dir, count);		/* Number of files in subdir 1 */

    for (i = 0; i < count; i++) {
	int fd;
	char *fname = argv[i+2];
	len = filesize(fname);
	size += len;
	if (size >= MAXSIZE) {
	    fprintf(stderr, "length %d exceeds maximum of %d\n", size, MAXSIZE);
	    return 1;
	}
	fd = open(fname, O_RDONLY);
	if (fd < 0) 
	    err(1, "cannot open %s", fname);
	if (read(fd, data, len) != len) 
	    err(1, "cannot read %s", fname);
	close(fd);
	w32le(&dir, BASE + data - buf);	/* File offset */
	w32le(&dir, len);		/* File length */
	data += len;
    }
    
#define ERASE 0xfbd8
#define WRITE 0xfbd9
#define BLKSZ 0x1000
#define TIMEOUT 30000

    dev = open(argv[1], O_RDWR);
    if (dev < 0)
	err(1, "cannot open device %s", argv[1]);
    
    
    fprintf(stderr, "Uploading %d bytes...\n", size);
	    
    for (i = 0; i < size; i += BLKSZ) {
	
	struct jqcmd cmd;
	struct sg_io_hdr hdr;

	loadbar(i, size);

	memset(&cmd, 0, sizeof(cmd));
	cmd.j_cmd = htobe16(ERASE);
	cmd.j_off = htobe32(BASE + i);

	memset(&hdr, 0, sizeof(hdr));
	hdr.interface_id = 'S';
	hdr.timeout = TIMEOUT;
	hdr.cmdp = (unsigned char *) &cmd;
	hdr.cmd_len = sizeof(cmd);
	hdr.dxfer_direction = SG_DXFER_NONE;

	if (ioctl(dev, SG_IO, &hdr) < 0) {
	     fprintf(stderr, "\n");
	     err(1, "erase failed at offset %x", BASE + i);
	}

	memset(&cmd, 0, sizeof(cmd));
	cmd.j_cmd = htobe16(WRITE);
	cmd.j_off = htobe32(BASE + i);
	cmd.j_len = htobe32(BLKSZ);

	memset(&hdr, 0, sizeof(hdr));
	hdr.interface_id = 'S';
	hdr.timeout = TIMEOUT;
	hdr.cmdp = (unsigned char *) &cmd;
	hdr.cmd_len = sizeof(cmd);
	hdr.dxfer_direction = SG_DXFER_TO_DEV;
	hdr.dxferp = buf + i;
	hdr.dxfer_len = BLKSZ;
	
	if (ioctl(dev, SG_IO, &hdr) < 0) {
	    fprintf(stderr, "\n");
	    err(1, "\nwrite failed at offset %x", BASE + i);
	}
    }
    loadbar(size, size);
    fprintf(stderr, "\n");
    close(dev);
    return 0;
}

/* Local Variables:  */
/* mode: c           */
/* c-basic-offset: 4 */
/* End:              */