#define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #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); } }