mirror of
https://github.com/gwenhael-le-moine/x49gp.git
synced 2024-12-25 21:58:49 +01:00
837 lines
19 KiB
C
837 lines
19 KiB
C
/* $Id: flash.c,v 1.18 2008/12/11 12:18:17 ecd Exp $
|
|
*/
|
|
|
|
#include <unistd.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <fcntl.h>
|
|
#include <sys/mman.h>
|
|
#include <errno.h>
|
|
|
|
#include <gtk/gtk.h>
|
|
#include <glib.h>
|
|
#include <cairo.h>
|
|
|
|
#include <x49gp.h>
|
|
#include <x49gp_ui.h>
|
|
#include <memory.h>
|
|
#include <byteorder.h>
|
|
|
|
#define FLASH_STATE_NORMAL 0
|
|
|
|
#define FLASH_STATE_UNLOCK1 1
|
|
#define FLASH_STATE_UNLOCK2 2
|
|
|
|
#define FLASH_STATE_ERASE1 3
|
|
#define FLASH_STATE_ERASE2 4
|
|
#define FLASH_STATE_ERASE3 5
|
|
|
|
#define FLASH_STATE_SOFTWARE_EXIT1 6
|
|
#define FLASH_STATE_SOFTWARE_EXIT2 7
|
|
#define FLASH_STATE_CFI_QUERY_EXIT1 8
|
|
#define FLASH_STATE_CFI_QUERY_EXIT2 9
|
|
|
|
#define FLASH_STATE_SOFTWARE_ID 10
|
|
#define FLASH_STATE_CFI_QUERY 11
|
|
#define FLASH_STATE_WORD_PROG 12
|
|
|
|
typedef struct {
|
|
void *data;
|
|
int state;
|
|
unsigned short vendor_ID;
|
|
unsigned short device_ID;
|
|
const unsigned short *cfi_data;
|
|
uint32_t cfi_size;
|
|
uint32_t sector_size;
|
|
uint32_t block_size;
|
|
char *filename;
|
|
int fd;
|
|
size_t size;
|
|
|
|
uint32_t iotype;
|
|
uint32_t offset;
|
|
} x49gp_flash_t;
|
|
|
|
#define SST29VF160_VENDOR_ID 0x00bf
|
|
#define SST29VF160_DEVICE_ID 0x2782
|
|
|
|
#define SST29VF160_SECTOR_SIZE 0x00001000
|
|
#define SST29VF160_BLOCK_SIZE 0x00010000
|
|
#define SST29VF160_SIZE 0x00200000
|
|
|
|
#define BOOT_SIZE 0x00004000
|
|
|
|
static const unsigned short sst29vf160_cfi_data[] =
|
|
{
|
|
[0x10] = 0x0051,
|
|
[0x11] = 0x0052,
|
|
[0x12] = 0x0059,
|
|
[0x13] = 0x0001,
|
|
[0x14] = 0x0007,
|
|
[0x15] = 0x0000,
|
|
[0x16] = 0x0000,
|
|
[0x17] = 0x0000,
|
|
[0x18] = 0x0000,
|
|
[0x19] = 0x0000,
|
|
[0x1a] = 0x0000,
|
|
|
|
[0x1b] = 0x0027,
|
|
[0x1c] = 0x0036,
|
|
[0x1d] = 0x0000,
|
|
[0x1e] = 0x0000,
|
|
[0x1f] = 0x0004,
|
|
[0x20] = 0x0000,
|
|
[0x21] = 0x0004,
|
|
[0x22] = 0x0006,
|
|
[0x23] = 0x0001,
|
|
[0x24] = 0x0000,
|
|
[0x25] = 0x0001,
|
|
[0x26] = 0x0001,
|
|
|
|
[0x27] = 0x0015,
|
|
[0x28] = 0x0001,
|
|
[0x29] = 0x0000,
|
|
[0x2a] = 0x0000,
|
|
[0x2b] = 0x0000,
|
|
[0x2c] = 0x0002,
|
|
[0x2d] = 0x00ff,
|
|
[0x2e] = 0x0001,
|
|
[0x2f] = 0x0010,
|
|
[0x30] = 0x0000,
|
|
[0x31] = 0x003f,
|
|
[0x32] = 0x0000,
|
|
[0x33] = 0x0000,
|
|
[0x34] = 0x0001
|
|
};
|
|
#define SST29VF160_CFI_SIZE (sizeof(sst29vf160_cfi_data) / sizeof(sst29vf160_cfi_data[0]))
|
|
|
|
static void
|
|
flash_state_reset(x49gp_flash_t *flash)
|
|
{
|
|
if (flash->state != FLASH_STATE_NORMAL) {
|
|
cpu_register_physical_memory(0x00000000, SST29VF160_SIZE,
|
|
flash->offset | flash->iotype | IO_MEM_ROMD);
|
|
flash->state = FLASH_STATE_NORMAL;
|
|
}
|
|
}
|
|
|
|
static uint32_t
|
|
flash_get_halfword(x49gp_flash_t *flash, uint32_t offset)
|
|
{
|
|
uint8_t *datap = flash->data;
|
|
uint16_t data;
|
|
|
|
switch (flash->state) {
|
|
default:
|
|
flash_state_reset(flash);
|
|
/* fall through */
|
|
|
|
case FLASH_STATE_NORMAL:
|
|
data = lduw_p(datap + offset);
|
|
break;
|
|
|
|
case FLASH_STATE_SOFTWARE_ID:
|
|
if (offset & 2) {
|
|
data = flash->device_ID;
|
|
} else {
|
|
data = flash->vendor_ID;
|
|
}
|
|
break;
|
|
|
|
case FLASH_STATE_CFI_QUERY:
|
|
if ( (offset>>1) < flash->cfi_size) {
|
|
data = flash->cfi_data[offset >> 1];
|
|
} else {
|
|
data = 0x0000;
|
|
}
|
|
break;
|
|
}
|
|
|
|
return data;
|
|
}
|
|
|
|
static void
|
|
flash_put_halfword(x49gp_flash_t *flash, uint32_t offset, uint32_t data)
|
|
{
|
|
uint8_t *datap = flash->data;
|
|
uint16_t temp;
|
|
|
|
data &= 0xffff;
|
|
|
|
switch (flash->state) {
|
|
default:
|
|
flash_state_reset(flash);
|
|
/* fall through */
|
|
|
|
case FLASH_STATE_NORMAL:
|
|
if (((offset >> 1) == 0x5555) && ((data & 0xff) == 0xaa)) {
|
|
flash->state = FLASH_STATE_UNLOCK1;
|
|
cpu_register_physical_memory(0x00000000, SST29VF160_SIZE, flash->iotype);
|
|
}
|
|
break;
|
|
|
|
case FLASH_STATE_UNLOCK1:
|
|
if (((offset >> 1) == 0x2aaa) && ((data & 0xff) == 0x55)) {
|
|
flash->state = FLASH_STATE_UNLOCK2;
|
|
} else {
|
|
flash_state_reset(flash);
|
|
}
|
|
break;
|
|
|
|
case FLASH_STATE_UNLOCK2:
|
|
if ((offset >> 1) == 0x5555) {
|
|
switch (data & 0xff) {
|
|
case 0xa0:
|
|
flash->state = FLASH_STATE_WORD_PROG;
|
|
break;
|
|
case 0x80:
|
|
flash->state = FLASH_STATE_ERASE1;
|
|
break;
|
|
case 0x90:
|
|
flash->state = FLASH_STATE_SOFTWARE_ID;
|
|
break;
|
|
case 0x98:
|
|
flash->state = FLASH_STATE_CFI_QUERY;
|
|
break;
|
|
default:
|
|
flash_state_reset(flash);
|
|
break;
|
|
}
|
|
} else {
|
|
flash_state_reset(flash);
|
|
}
|
|
break;
|
|
|
|
case FLASH_STATE_ERASE1:
|
|
if (((offset >> 1) == 0x5555) && ((data & 0xff) == 0xaa)) {
|
|
flash->state = FLASH_STATE_ERASE2;
|
|
} else {
|
|
flash_state_reset(flash);
|
|
}
|
|
break;
|
|
|
|
case FLASH_STATE_ERASE2:
|
|
if (((offset >> 1) == 0x2aaa) && ((data & 0xff) == 0x55)) {
|
|
flash->state = FLASH_STATE_ERASE3;
|
|
} else {
|
|
flash_state_reset(flash);
|
|
}
|
|
break;
|
|
|
|
case FLASH_STATE_SOFTWARE_EXIT1:
|
|
if (((offset >> 1) == 0x2aaa) && ((data & 0xff) == 0x55)) {
|
|
flash->state = FLASH_STATE_SOFTWARE_EXIT2;
|
|
} else {
|
|
flash->state = FLASH_STATE_SOFTWARE_ID;
|
|
}
|
|
break;
|
|
|
|
case FLASH_STATE_SOFTWARE_EXIT2:
|
|
if (((offset >> 1) == 0x5555) && ((data & 0xff) == 0xf0)) {
|
|
flash_state_reset(flash);
|
|
} else {
|
|
flash->state = FLASH_STATE_SOFTWARE_ID;
|
|
}
|
|
break;
|
|
|
|
case FLASH_STATE_CFI_QUERY_EXIT1:
|
|
if (((offset >> 1) == 0x2aaa) && ((data & 0xff) == 0x55)) {
|
|
flash->state = FLASH_STATE_CFI_QUERY_EXIT2;
|
|
} else {
|
|
flash->state = FLASH_STATE_CFI_QUERY;
|
|
}
|
|
break;
|
|
|
|
case FLASH_STATE_CFI_QUERY_EXIT2:
|
|
if (((offset >> 1) == 0x5555) && ((data & 0xff) == 0xf0)) {
|
|
flash_state_reset(flash);
|
|
} else {
|
|
flash->state = FLASH_STATE_CFI_QUERY;
|
|
}
|
|
break;
|
|
|
|
case FLASH_STATE_SOFTWARE_ID:
|
|
if (((offset >> 1) == 0x5555) && ((data & 0xff) == 0xaa)) {
|
|
flash->state = FLASH_STATE_SOFTWARE_EXIT1;
|
|
} else if ((data & 0xff) == 0xf0) {
|
|
flash_state_reset(flash);
|
|
}
|
|
break;
|
|
|
|
case FLASH_STATE_CFI_QUERY:
|
|
if (((offset >> 1) == 0x5555) && ((data & 0xff) == 0xaa)) {
|
|
flash->state = FLASH_STATE_CFI_QUERY_EXIT1;
|
|
} else if ((data & 0xff) == 0xf0) {
|
|
flash_state_reset(flash);
|
|
}
|
|
break;
|
|
|
|
case FLASH_STATE_WORD_PROG:
|
|
temp = lduw_p(datap + offset);
|
|
stw_p(datap + offset, data & temp);
|
|
|
|
#ifdef DEBUG_X49GP_FLASH_WRITE
|
|
printf("write FLASH 2 (state %u) at offset %08x: %04x, result: %04x\n",
|
|
flash->state, offset, data, lduw_p(datap + offset));
|
|
#endif
|
|
|
|
flash_state_reset(flash);
|
|
break;
|
|
|
|
case FLASH_STATE_ERASE3:
|
|
switch (data & 0xff) {
|
|
case 0x10: /* Chip Erase */
|
|
#ifdef DEBUG_X49GP_FLASH_WRITE
|
|
printf("erase FLASH %08x %08x\n", 0, SST29VF160_SIZE);
|
|
#endif
|
|
memset(datap, 0xff, SST29VF160_SIZE);
|
|
break;
|
|
|
|
case 0x30: /* Sector Erase */
|
|
#ifdef DEBUG_X49GP_FLASH_WRITE
|
|
printf("erase FLASH %08x %08x\n",
|
|
(offset & ~(flash->sector_size - 1)),
|
|
flash->sector_size);
|
|
#endif
|
|
memset(datap + (offset & ~(flash->sector_size - 1)), 0xff, flash->sector_size);
|
|
|
|
#ifdef DEBUG_X49GP_FLASH_WRITE
|
|
printf("erase FLASH %08x: %04x, %08x: %04x, %08x: %04x\n",
|
|
offset, lduw_p(datap + offset),
|
|
offset + 0x800, lduw_p(datap + offset + 0x800),
|
|
offset + 0xffc, lduw_p(datap + offset + 0xffc));
|
|
#endif
|
|
break;
|
|
|
|
case 0x50: /* Block Erase */
|
|
#ifdef DEBUG_X49GP_FLASH_WRITE
|
|
printf("erase FLASH %08x %08x\n",
|
|
(offset & ~(flash->block_size - 1)),
|
|
flash->block_size);
|
|
#endif
|
|
memset(datap + (offset & ~(flash->block_size - 1)), 0xff, flash->block_size);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
flash_state_reset(flash);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static uint32_t
|
|
flash_readb(void *opaque, target_phys_addr_t offset)
|
|
{
|
|
x49gp_flash_t *flash = opaque;
|
|
uint8_t *datap = flash->data;
|
|
unsigned short temp;
|
|
uint32_t shift;
|
|
unsigned char data;
|
|
|
|
if (flash->state == FLASH_STATE_NORMAL) {
|
|
data = *(datap + offset);
|
|
} else {
|
|
temp = flash_get_halfword(flash, offset & ~(1));
|
|
shift = (offset & 1) << 3;
|
|
data = (temp >> shift) & 0xff;
|
|
}
|
|
|
|
#ifdef DEBUG_X49GP_FLASH_READ
|
|
printf("read FLASH 1 (state %u) at offset %08lx: %02x\n",
|
|
flash->state, (unsigned long) offset, data);
|
|
#endif
|
|
|
|
return data;
|
|
}
|
|
|
|
static uint32_t
|
|
flash_readw(void *opaque, target_phys_addr_t offset)
|
|
{
|
|
x49gp_flash_t *flash = opaque;
|
|
uint8_t *datap = flash->data;
|
|
uint32_t data;
|
|
|
|
if (flash->state == FLASH_STATE_NORMAL) {
|
|
data = lduw_p(datap + offset);
|
|
} else {
|
|
data = flash_get_halfword(flash, offset);
|
|
}
|
|
|
|
#ifdef DEBUG_X49GP_FLASH_READ
|
|
printf("read FLASH 2 (state %u) at offset %08lx: %04x\n",
|
|
flash->state, (unsigned long) offset, data);
|
|
#endif
|
|
|
|
return data;
|
|
}
|
|
|
|
static uint32_t
|
|
flash_readl(void *opaque, target_phys_addr_t offset)
|
|
{
|
|
x49gp_flash_t *flash = opaque;
|
|
uint8_t *datap = flash->data;
|
|
uint32_t data;
|
|
|
|
if (flash->state == FLASH_STATE_NORMAL) {
|
|
data = ldl_p(datap + offset);
|
|
} else {
|
|
data = (flash_get_halfword(flash, offset + 2) << 16) |
|
|
(flash_get_halfword(flash, offset + 0) << 0);
|
|
}
|
|
|
|
#ifdef DEBUG_X49GP_FLASH_READ
|
|
printf("read FLASH 4 (state %u) at offset %08lx: %08x\n",
|
|
flash->state, (unsigned long) offset, data);
|
|
#endif
|
|
|
|
return data;
|
|
}
|
|
|
|
static void
|
|
flash_writeb(void *opaque, target_phys_addr_t offset, uint32_t data)
|
|
{
|
|
x49gp_flash_t *flash = opaque;
|
|
uint32_t shift;
|
|
|
|
data &= 0xff;
|
|
|
|
#ifdef DEBUG_X49GP_FLASH_WRITE
|
|
printf("write FLASH 1 (state %u) at offset %08lx: %02x\n",
|
|
flash->state, offset, data);
|
|
#endif
|
|
|
|
/*
|
|
* This does not issue read-modify-write, i.e. you will get
|
|
* broken data in FLASH memory. This would be the case with
|
|
* real hardware, too.
|
|
*/
|
|
shift = (offset & 1) << 3;
|
|
flash_put_halfword(flash, offset & ~(1), data << shift);
|
|
}
|
|
|
|
static void
|
|
flash_writew(void *opaque, target_phys_addr_t offset, uint32_t data)
|
|
{
|
|
x49gp_flash_t *flash = opaque;
|
|
|
|
data &= 0xffff;
|
|
|
|
#ifdef DEBUG_X49GP_FLASH_WRITE
|
|
printf("write FLASH 2 (state %u) at offset %08lx: %04x\n",
|
|
flash->state, offset, data);
|
|
#endif
|
|
|
|
flash_put_halfword(flash, offset, data);
|
|
}
|
|
|
|
static void
|
|
flash_writel(void *opaque, target_phys_addr_t offset, uint32_t data)
|
|
{
|
|
x49gp_flash_t *flash = opaque;
|
|
|
|
#ifdef DEBUG_X49GP_FLASH_WRITE
|
|
printf("write FLASH 4 (state %u) at offset %08lx: %08x\n",
|
|
flash->state, offset, data);
|
|
#endif
|
|
|
|
flash_put_halfword(flash, offset + 2, (data >> 16) & 0xffff);
|
|
flash_put_halfword(flash, offset + 0, (data >> 0) & 0xffff);
|
|
}
|
|
|
|
static int
|
|
flash_load(x49gp_module_t *module, GKeyFile *key)
|
|
{
|
|
x49gp_flash_t *flash = module->user_data;
|
|
x49gp_t *x49gp = module->x49gp;
|
|
x49gp_ui_t *ui = x49gp->ui;
|
|
int calc = ui->calculator;
|
|
char *filename;
|
|
struct stat st;
|
|
char *bootfile;
|
|
int bootfd, fwfd;
|
|
int error;
|
|
int i;
|
|
char bank_marker[5] = {0xf0, 0x02, 0x00, 0x00, 0x00};
|
|
int bytes_read;
|
|
|
|
#ifdef DEBUG_X49GP_MODULES
|
|
printf("%s: %s:%u\n", module->name, __FUNCTION__, __LINE__);
|
|
#endif
|
|
|
|
error = x49gp_module_get_filename(module, key, "filename", "flash",
|
|
&(flash->filename), &filename);
|
|
|
|
flash->fd = open(filename, O_RDWR | O_CREAT, 0644);
|
|
if (flash->fd < 0) {
|
|
error = -errno;
|
|
fprintf(stderr, "%s: %s:%u: open %s: %s\n",
|
|
module->name, __FUNCTION__, __LINE__,
|
|
filename, strerror(errno));
|
|
g_free(filename);
|
|
return error;
|
|
}
|
|
|
|
flash->size = SST29VF160_SIZE;
|
|
if (fstat(flash->fd, &st) < 0) {
|
|
error = -errno;
|
|
fprintf(stderr, "%s: %s:%u: fstat %s: %s\n",
|
|
module->name, __FUNCTION__, __LINE__,
|
|
filename, strerror(errno));
|
|
g_free(filename);
|
|
close(flash->fd);
|
|
flash->fd = -1;
|
|
return error;
|
|
}
|
|
|
|
if (ftruncate(flash->fd, flash->size) < 0) {
|
|
error = -errno;
|
|
fprintf(stderr, "%s: %s:%u: ftruncate %s: %s\n",
|
|
module->name, __FUNCTION__, __LINE__,
|
|
filename, strerror(errno));
|
|
g_free(filename);
|
|
close(flash->fd);
|
|
flash->fd = -1;
|
|
return error;
|
|
}
|
|
|
|
flash->data = mmap(phys_ram_base + flash->offset, flash->size,
|
|
PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED,
|
|
flash->fd, 0);
|
|
if (flash->data == (void *) -1) {
|
|
error = -errno;
|
|
fprintf(stderr, "%s: %s:%u: mmap %s: %s\n",
|
|
module->name, __FUNCTION__, __LINE__,
|
|
filename, strerror(errno));
|
|
g_free(filename);
|
|
close(flash->fd);
|
|
flash->fd = -1;
|
|
return error;
|
|
}
|
|
|
|
|
|
if (flash->size > st.st_size) {
|
|
fprintf(stderr, "Flash too small, rebuilding\n");
|
|
x49gp->startup_reinit = X49GP_REINIT_FLASH_FULL;
|
|
}
|
|
if (x49gp->startup_reinit >= X49GP_REINIT_FLASH) {
|
|
|
|
if (x49gp->startup_reinit == X49GP_REINIT_FLASH_FULL)
|
|
memset(phys_ram_base + flash->offset, 0xff,
|
|
flash->size - st.st_size);
|
|
|
|
bootfd = x49gp_module_open_rodata(module,
|
|
calc == UI_CALCULATOR_HP49GP ||
|
|
calc == UI_CALCULATOR_HP49GP_NEWRPL ?
|
|
"boot-49g+.bin" :
|
|
"boot-50g.bin",
|
|
&bootfile);
|
|
|
|
if (bootfd < 0) {
|
|
g_free(filename);
|
|
close(flash->fd);
|
|
flash->fd = -1;
|
|
return bootfd;
|
|
}
|
|
|
|
if (read(bootfd, phys_ram_base + flash->offset,
|
|
BOOT_SIZE) < 0) {
|
|
error = -errno;
|
|
fprintf(stderr, "%s: %s:%u: read %s: %s\n",
|
|
module->name, __FUNCTION__, __LINE__,
|
|
filename, strerror(errno));
|
|
g_free(filename);
|
|
g_free(bootfile);
|
|
close(bootfd);
|
|
close(flash->fd);
|
|
flash->fd = -1;
|
|
return error;
|
|
}
|
|
|
|
g_free(filename);
|
|
close(bootfd);
|
|
g_free(bootfile);
|
|
|
|
if (x49gp->startup_reinit == X49GP_REINIT_FLASH_FULL) {
|
|
/* The stock firmware expects special markers in certain
|
|
spots across the flash. Without these, the user banks
|
|
act up and are not usable, and PINIT apparently won't
|
|
fix it. Let's help it out; custom firmware will have
|
|
to deal with remnants of the user banks on real
|
|
calculators anyway, so if they break here, they will
|
|
too on actual hardware because that always comes with
|
|
the stock firmware and its user banks marked
|
|
properly. */
|
|
for (i=2;i<14;i++) {
|
|
bank_marker[1] = i;
|
|
memcpy(phys_ram_base + flash->offset + 0x40100 +
|
|
0x20000 * i, bank_marker, 5);
|
|
}
|
|
}
|
|
|
|
retry:
|
|
filename = NULL;
|
|
if (x49gp->firmware != NULL) {
|
|
filename = g_strdup(x49gp->firmware);
|
|
} else {
|
|
x49gp_ui_open_firmware(x49gp, &filename);
|
|
}
|
|
if (filename != NULL) {
|
|
fwfd = open(filename, O_RDONLY);
|
|
if (fwfd < 0) {
|
|
fprintf(stderr, "%s: %s:%u: open %s: %s\n",
|
|
module->name, __FUNCTION__, __LINE__,
|
|
filename, strerror(errno));
|
|
/* Mark firmware as invalid if there is one */
|
|
memset(phys_ram_base + flash->offset +
|
|
BOOT_SIZE, 0, 16);
|
|
if (x49gp->firmware != NULL) {
|
|
fprintf(stderr, "Warning: Could not "
|
|
"open selected firmware, "
|
|
"falling back to bootloader "
|
|
"recovery tools\n");
|
|
} else {
|
|
x49gp_ui_show_error(x49gp,
|
|
"Could not open "
|
|
"selected "
|
|
"firmware!");
|
|
goto retry;
|
|
}
|
|
} else {
|
|
bytes_read = read(fwfd, phys_ram_base +
|
|
flash->offset + BOOT_SIZE,
|
|
16);
|
|
if (bytes_read < 0) {
|
|
fprintf(stderr, "%s: %s:%u: read %s: %s\n",
|
|
module->name, __FUNCTION__,
|
|
__LINE__, filename,
|
|
strerror(errno));
|
|
/* Mark firmware as invalid
|
|
if there is one */
|
|
memset(phys_ram_base + flash->offset +
|
|
BOOT_SIZE, 0, 16);
|
|
if (x49gp->firmware != NULL) {
|
|
fprintf(stderr, "Warning: "
|
|
"Could not read "
|
|
"selected firmware, "
|
|
"falling back to "
|
|
"bootloader recovery "
|
|
"tools\n");
|
|
} else {
|
|
x49gp_ui_show_error(x49gp,
|
|
"Could not "
|
|
"read "
|
|
"selected "
|
|
"firmware!");
|
|
goto retry;
|
|
}
|
|
} else if (bytes_read < 16 ||
|
|
memcmp(phys_ram_base +
|
|
flash->offset + BOOT_SIZE,
|
|
"KINPOUPDATEIMAGE", 16)
|
|
!= 0) {
|
|
/* Mark firmware as invalid */
|
|
memset(phys_ram_base + flash->offset +
|
|
BOOT_SIZE, 0, 16);
|
|
if (x49gp->firmware != NULL) {
|
|
fprintf(stderr, "Warning: "
|
|
"Firmware is invalid, "
|
|
"falling back to "
|
|
"bootloader recovery "
|
|
"tools\n");
|
|
} else {
|
|
x49gp_ui_show_error(x49gp,
|
|
"Selected "
|
|
"firmware "
|
|
"is "
|
|
"invalid!");
|
|
goto retry;
|
|
}
|
|
/* The firmware may be shorter than
|
|
SST29VF160_SIZE - BOOT_SIZE, but if so,
|
|
read will just give us what it sees.
|
|
The space after that will remain empty. */
|
|
} else if (read(fwfd, phys_ram_base +
|
|
flash->offset + BOOT_SIZE + 16,
|
|
SST29VF160_SIZE -
|
|
(BOOT_SIZE + 16))
|
|
< 0) {
|
|
fprintf(stderr, "%s: %s:%u: read %s: %s\n",
|
|
module->name, __FUNCTION__,
|
|
__LINE__, filename,
|
|
strerror(errno));
|
|
/* Mark firmware as invalid
|
|
if there is one */
|
|
memset(phys_ram_base + flash->offset +
|
|
BOOT_SIZE, 0, 16);
|
|
if (x49gp->firmware != NULL) {
|
|
fprintf(stderr, "Warning: "
|
|
"Could not read "
|
|
"selected firmware, "
|
|
"falling back to "
|
|
"bootloader recovery "
|
|
"tools\n");
|
|
} else {
|
|
x49gp_ui_show_error(x49gp,
|
|
"Could not "
|
|
"read "
|
|
"selected "
|
|
"firmware!");
|
|
goto retry;
|
|
}
|
|
} else {
|
|
/* Mark firmware as valid in the same
|
|
way the bootloader does */
|
|
memcpy(phys_ram_base + flash->offset +
|
|
BOOT_SIZE, "Kinposhcopyright",
|
|
16);
|
|
}
|
|
close(fwfd);
|
|
}
|
|
g_free(filename);
|
|
}
|
|
} else {
|
|
g_free(filename);
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
static int
|
|
flash_save(x49gp_module_t *module, GKeyFile *key)
|
|
{
|
|
x49gp_flash_t *flash = module->user_data;
|
|
int error;
|
|
|
|
#ifdef DEBUG_X49GP_MODULES
|
|
printf("%s: %s:%u\n", module->name, __FUNCTION__, __LINE__);
|
|
#endif
|
|
|
|
x49gp_module_set_filename(module, key, "filename", flash->filename);
|
|
|
|
error = msync(flash->data, flash->size, MS_ASYNC);
|
|
if (error) {
|
|
fprintf(stderr, "%s:%u: msync: %s\n",
|
|
__FUNCTION__, __LINE__, strerror(errno));
|
|
return error;
|
|
}
|
|
|
|
error = fsync(flash->fd);
|
|
if (error) {
|
|
fprintf(stderr, "%s:%u: fsync: %s\n",
|
|
__FUNCTION__, __LINE__, strerror(errno));
|
|
return error;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
flash_reset(x49gp_module_t *module, x49gp_reset_t reset)
|
|
{
|
|
x49gp_flash_t *flash = module->user_data;
|
|
|
|
#ifdef DEBUG_X49GP_MODULES
|
|
printf("%s: %s:%u\n", module->name, __FUNCTION__, __LINE__);
|
|
#endif
|
|
|
|
flash_state_reset(flash);
|
|
return 0;
|
|
}
|
|
|
|
static CPUReadMemoryFunc *flash_readfn[] =
|
|
{
|
|
flash_readb,
|
|
flash_readw,
|
|
flash_readl
|
|
};
|
|
|
|
static CPUWriteMemoryFunc *flash_writefn[] =
|
|
{
|
|
flash_writeb,
|
|
flash_writew,
|
|
flash_writel
|
|
};
|
|
|
|
static int
|
|
flash_init(x49gp_module_t *module)
|
|
{
|
|
x49gp_flash_t *flash;
|
|
|
|
#ifdef DEBUG_X49GP_MODULES
|
|
printf("%s: %s:%u\n", module->name, __FUNCTION__, __LINE__);
|
|
#endif
|
|
|
|
flash = malloc(sizeof(x49gp_flash_t));
|
|
if (NULL == flash) {
|
|
fprintf(stderr, "%s: %s:%u: Out of memory\n",
|
|
module->name, __FUNCTION__, __LINE__);
|
|
return -1;
|
|
}
|
|
memset(flash, 0, sizeof(x49gp_flash_t));
|
|
|
|
flash->vendor_ID = SST29VF160_VENDOR_ID;
|
|
flash->device_ID = SST29VF160_DEVICE_ID;
|
|
flash->cfi_data = sst29vf160_cfi_data;
|
|
flash->cfi_size = SST29VF160_CFI_SIZE;
|
|
flash->sector_size = SST29VF160_SECTOR_SIZE;
|
|
flash->block_size = SST29VF160_BLOCK_SIZE;
|
|
flash->fd = -1;
|
|
|
|
module->user_data = flash;
|
|
|
|
flash->iotype = cpu_register_io_memory(flash_readfn,
|
|
flash_writefn, flash);
|
|
|
|
flash->data = (void *) -1;
|
|
flash->offset = phys_ram_size;
|
|
phys_ram_size += SST29VF160_SIZE;
|
|
|
|
cpu_register_physical_memory(0x00000000, SST29VF160_SIZE,
|
|
flash->offset | flash->iotype | IO_MEM_ROMD);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
flash_exit(x49gp_module_t *module)
|
|
{
|
|
x49gp_flash_t *flash;
|
|
|
|
#ifdef DEBUG_X49GP_MODULES
|
|
printf("%s: %s:%u\n", module->name, __FUNCTION__, __LINE__);
|
|
#endif
|
|
|
|
if (module->user_data) {
|
|
flash = module->user_data;
|
|
|
|
if (flash->data != (void *) -1) {
|
|
munmap(flash->data, flash->size);
|
|
}
|
|
if (flash->fd) {
|
|
close(flash->fd);
|
|
}
|
|
|
|
free(flash);
|
|
}
|
|
|
|
x49gp_module_unregister(module);
|
|
free(module);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
x49gp_flash_init(x49gp_t *x49gp)
|
|
{
|
|
x49gp_module_t *module;
|
|
|
|
if (x49gp_module_init(x49gp, "flash", flash_init, flash_exit,
|
|
flash_reset, flash_load, flash_save, NULL,
|
|
&module)) {
|
|
return -1;
|
|
}
|
|
|
|
return x49gp_module_register(module);
|
|
}
|