You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
269 lines
4.4 KiB
269 lines
4.4 KiB
#define _GNU_SOURCE
|
|
#include <libopencm3/stm32/rcc.h>
|
|
#include <libopencm3/stm32/gpio.h>
|
|
#include <libopencm3/stm32/spi.h>
|
|
#include <libopencm3/stm32/usart.h>
|
|
|
|
#include <ctype.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <stddef.h>
|
|
#include <limits.h>
|
|
#include <sys/types.h>
|
|
|
|
#include "macros.h"
|
|
#include "shell.h"
|
|
#include "spi_flash.h"
|
|
#include "usart.h"
|
|
|
|
static void do_probe(FILE *fp, const char *s);
|
|
static void do_read(FILE *fp, const char *s);
|
|
static void do_write(FILE *fp, const char *s);
|
|
static void do_erase(FILE *fp, const char *s);
|
|
|
|
struct cmd cmds[] = {
|
|
{ "probe", do_probe },
|
|
{ "read", do_read },
|
|
{ "write", do_write },
|
|
{ "erase", do_erase },
|
|
{ NULL, NULL },
|
|
};
|
|
|
|
/* TODO: implement a parser for hex arrays that is more user-friendly.
|
|
* Also implement dry runs for programmers. */
|
|
static void parse_hex(FILE *fp, char *buf, size_t len)
|
|
{
|
|
size_t i;
|
|
char s[3];
|
|
|
|
for (i = 0; i < len; ++i) {
|
|
memset(s, '\0', 3);
|
|
|
|
while ((s[0] = fgetc(fp)) && !isxdigit(s[0]))
|
|
fputc(s[0], fp);
|
|
|
|
fputc(s[0], fp);
|
|
|
|
while ((s[1] = fgetc(fp)) && !isxdigit(s[1]))
|
|
fputc(s[1], fp);
|
|
|
|
fputc(s[1], fp);
|
|
|
|
*buf++ = strtoul(s, NULL, 16);
|
|
}
|
|
}
|
|
|
|
static void print_hex_ascii(FILE *fp, const char *buf, size_t len)
|
|
{
|
|
size_t i, j;
|
|
char c;
|
|
|
|
for (j = 0; j < len; j += 16, buf += 16) {
|
|
for (i = 0; i < 16; ++i) {
|
|
c = (i < len) ? buf[i] : 0;
|
|
fprintf(fp, "%02x", c);
|
|
}
|
|
|
|
fprintf(fp, " ");
|
|
|
|
for (i = 0; i < 16; ++i) {
|
|
c = (i < len) ? buf[i] : 0;
|
|
fprintf(fp, "%c", isalnum(c) ? c : '.');
|
|
}
|
|
|
|
fprintf(fp, "\r\n");
|
|
}
|
|
}
|
|
|
|
static char *prompt(FILE *fp, const char *prefix)
|
|
{
|
|
char *alloc, *line, *s;
|
|
size_t nbytes = 0, nalloc_bytes = 16;
|
|
char c;
|
|
|
|
fprintf(fp, prefix);
|
|
|
|
if (!(line = malloc(nalloc_bytes)))
|
|
return NULL;
|
|
|
|
s = line;
|
|
|
|
while ((c = fgetc(fp)) && c != '\r') {
|
|
fputc(c, fp);
|
|
|
|
if (nbytes + 1 >= nalloc_bytes) {
|
|
nalloc_bytes *= 2;
|
|
|
|
if (!(alloc = realloc(line, nalloc_bytes)))
|
|
goto err_free_line;
|
|
|
|
line = alloc;
|
|
}
|
|
|
|
*s++ = c;
|
|
nbytes++;
|
|
}
|
|
|
|
fprintf(fp, "\r\n");
|
|
*s = '\0';
|
|
|
|
return line;
|
|
|
|
err_free_line:
|
|
free(line);
|
|
return NULL;
|
|
}
|
|
|
|
static void do_probe(FILE *fp, const char *s)
|
|
{
|
|
char cmd[1] = { CMD_READ_ID };
|
|
char buf[6];
|
|
|
|
(void)s;
|
|
|
|
spi_send_cmd(SPI1, cmd, 1, buf, 6);
|
|
fprintf(fp, "JEDEC ID: %02x%02x%02x\r\n", buf[0], buf[1], buf[2]);
|
|
}
|
|
|
|
static void do_read(FILE *fp, const char *s)
|
|
{
|
|
char buf[256], *end = NULL;
|
|
size_t addr, len, nbytes;
|
|
|
|
if (strncmp(s, "0x", 2) == 0)
|
|
s += 2;
|
|
|
|
addr = strtoull(s, &end, 16);
|
|
s = end;
|
|
|
|
if (errno == EINVAL || errno == ERANGE)
|
|
return;
|
|
|
|
s += strspn(s, " \r\n");
|
|
|
|
if (strncmp(s, "0x", 2) == 0)
|
|
s += 2;
|
|
|
|
len = strtoull(s, &end, 16);
|
|
s = end;
|
|
|
|
if (errno == EINVAL || errno == ERANGE)
|
|
return;
|
|
|
|
if (!len)
|
|
return;
|
|
|
|
while (len) {
|
|
nbytes = min(len, 256);
|
|
spi_flash_read(SPI1, addr, buf, nbytes);
|
|
print_hex_ascii(fp, buf, nbytes);
|
|
|
|
addr += nbytes;
|
|
len -= nbytes;
|
|
}
|
|
}
|
|
|
|
static void do_write(FILE *fp, const char *s)
|
|
{
|
|
char buf[256], *end = NULL;
|
|
size_t addr, len, nbytes;
|
|
|
|
if (strncmp(s, "0x", 2) == 0)
|
|
s += 2;
|
|
|
|
addr = strtoull(s, &end, 16);
|
|
s = end;
|
|
|
|
if (errno == EINVAL || errno == ERANGE)
|
|
return;
|
|
|
|
s += strspn(s, " \r\n");
|
|
|
|
if (strncmp(s, "0x", 2) == 0)
|
|
s += 2;
|
|
|
|
len = strtoull(s, &end, 16);
|
|
s = end;
|
|
|
|
if (errno == EINVAL || errno == ERANGE)
|
|
return;
|
|
|
|
if (!len)
|
|
return;
|
|
|
|
while (len) {
|
|
nbytes = (len > 256) ? 256 : len;
|
|
parse_hex(fp, buf, nbytes);
|
|
fprintf(fp, "\r\n");
|
|
print_hex_ascii(fp, buf, nbytes);
|
|
spi_flash_write(SPI1, addr, buf, nbytes);
|
|
|
|
addr += nbytes;
|
|
len -= nbytes;
|
|
}
|
|
}
|
|
|
|
static void do_erase(FILE *fp, const char *s)
|
|
{
|
|
char *end = NULL;
|
|
size_t addr, len;
|
|
|
|
(void)fp;
|
|
|
|
if (strncmp(s, "0x", 2) == 0)
|
|
s += 2;
|
|
|
|
addr = strtoull(s, &end, 16);
|
|
s = end;
|
|
|
|
if (errno == EINVAL || errno == ERANGE)
|
|
return;
|
|
|
|
s += strspn(s, " \r\n");
|
|
|
|
if (strncmp(s, "0x", 2) == 0)
|
|
s += 2;
|
|
|
|
len = strtoull(s, &end, 16);
|
|
s = end;
|
|
|
|
if (errno == EINVAL || errno == ERANGE)
|
|
return;
|
|
|
|
if (!len)
|
|
return;
|
|
|
|
spi_flash_erase(fp, SPI1, addr, len);
|
|
}
|
|
|
|
static void parse_cmd(FILE *fp, const char *s)
|
|
{
|
|
struct cmd *cmd;
|
|
const char *args;
|
|
char *key;
|
|
size_t n;
|
|
|
|
n = strcspn(s, " \r\n");
|
|
key = strndup(s, n);
|
|
|
|
args = s + n;
|
|
args += strspn(args, " \r\n");
|
|
|
|
for (cmd = cmds; cmd->key; cmd++) {
|
|
if (strcmp(cmd->key, key) == 0)
|
|
cmd->exec(fp, args);
|
|
}
|
|
}
|
|
|
|
void cmd_loop(FILE *fp, const char *show)
|
|
{
|
|
char *line;
|
|
|
|
while (1) {
|
|
line = prompt(fp, show);
|
|
parse_cmd(fp, line);
|
|
free(line);
|
|
}
|
|
}
|
|
|