Initial version of CCTL, 1KB serial bootloader for CC1110/CC1111
This commit is contained in:
commit
11cfdfdcdc
13 files changed
+2950
No files matched your search
@@ -0,0 +1,9 @@
|
||||
CFLAGS=-Wall
|
||||
TARGET=cctl-prog
|
||||
|
||||
all:
|
||||
gcc -o $(TARGET) $(CFLAGS) $(TARGET).c hex.c
|
||||
|
||||
clean:
|
||||
rm -f $(TARGET) $(TARGET).exe
|
||||
|
||||
@@ -0,0 +1,292 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <termios.h>
|
||||
#include <sys/select.h>
|
||||
#include <sys/types.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "hex.h"
|
||||
|
||||
int serial_open(char *port)
|
||||
{
|
||||
int fd;
|
||||
struct termios t_opt;
|
||||
|
||||
fd = open(port, O_RDWR | O_NOCTTY);
|
||||
if (fd == -1) {
|
||||
fprintf(stderr, "Could not open serial port.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
fcntl(fd, F_SETFL, 0);
|
||||
tcgetattr(fd, &t_opt);
|
||||
cfsetispeed(&t_opt, B115200);
|
||||
cfsetospeed(&t_opt, B115200);
|
||||
t_opt.c_cflag |= (CLOCAL | CREAD);
|
||||
t_opt.c_cflag &= ~PARENB;
|
||||
t_opt.c_cflag &= ~CSTOPB;
|
||||
t_opt.c_cflag &= ~CSIZE;
|
||||
t_opt.c_cflag |= CS8;
|
||||
t_opt.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
|
||||
t_opt.c_iflag &= ~(IXON | IXOFF | IXANY);
|
||||
t_opt.c_oflag &= ~OPOST;
|
||||
t_opt.c_cc[VMIN] = 0;
|
||||
t_opt.c_cc[VTIME] = 10;
|
||||
tcflush(fd, TCIFLUSH);
|
||||
tcsetattr(fd, TCSANOW, &t_opt);
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
int program_page(int fd, int page)
|
||||
{
|
||||
char cmd = 'p';
|
||||
char rsp;
|
||||
|
||||
if (write(fd, &cmd, 1) <= 0)
|
||||
return 1;
|
||||
|
||||
if (write(fd, &page, 1) <= 0)
|
||||
return 1;
|
||||
|
||||
if (read(fd, &rsp, 1) <= 0 || rsp != 0)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int read_page(int fd, uint8_t page, uint8_t *data)
|
||||
{
|
||||
int remaining;
|
||||
int rc;
|
||||
char cmd = 'r';
|
||||
char rsp;
|
||||
|
||||
if (write(fd, &cmd, 1) <= 0)
|
||||
return 1;
|
||||
|
||||
if (write(fd, &page, 1) <= 0)
|
||||
return 1;
|
||||
|
||||
remaining = 1024;
|
||||
while(remaining > 0)
|
||||
{
|
||||
rc = read(fd, data + (1024 - remaining), remaining);
|
||||
if (rc <= 0)
|
||||
return 1;
|
||||
remaining -= rc;
|
||||
}
|
||||
|
||||
if (read(fd, &rsp, 1) <= 0 || rsp != 0)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int erase_page(int fd, uint8_t page)
|
||||
{
|
||||
char cmd = 'e';
|
||||
char rsp;
|
||||
|
||||
if (write(fd, &cmd, 1) <= 0)
|
||||
return 1;
|
||||
|
||||
if (write(fd, &page, 1) <= 0)
|
||||
return 1;
|
||||
|
||||
if (read(fd, &rsp, 1) <= 0 || rsp != 0)
|
||||
{
|
||||
fprintf(stderr, "erase_page rsp=%02X\n", rsp);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int load_data(int fd, uint8_t *data)
|
||||
{
|
||||
int remaining;
|
||||
int rc;
|
||||
char cmd = 'l';
|
||||
char rsp;
|
||||
|
||||
if (write(fd, &cmd, 1) <= 0)
|
||||
return 1;
|
||||
|
||||
remaining = 1024;
|
||||
while(remaining > 0)
|
||||
{
|
||||
rc = write(fd, data + (1024 - remaining), remaining);
|
||||
if (rc <= 0)
|
||||
return 1;
|
||||
remaining -= rc;
|
||||
}
|
||||
|
||||
if (read(fd, &rsp, 1) <= 0 || rsp != 0)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void dump(uint8_t *p, size_t len)
|
||||
{
|
||||
while(len--)
|
||||
printf("%02X", *p++);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
int erase_program_verify_page(int fd, uint8_t *data, uint8_t page)
|
||||
{
|
||||
uint8_t verbuf[1024];
|
||||
|
||||
if (0 != load_data(fd, data))
|
||||
{
|
||||
fprintf(stderr, "load_data failed\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (0 != erase_page(fd, page))
|
||||
{
|
||||
fprintf(stderr, "erase_page failed\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (0 != program_page(fd, page))
|
||||
{
|
||||
fprintf(stderr, "program_page failed\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (0 != read_page(fd, page, verbuf))
|
||||
{
|
||||
fprintf(stderr, "read_page failed\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (0!=memcmp(verbuf, data, 1024))
|
||||
{
|
||||
fprintf(stderr, "verify failed\n");
|
||||
|
||||
printf("verbuf = ");
|
||||
dump(verbuf, 1024);
|
||||
printf("expected = ");
|
||||
dump(data, 1024);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int wait_for_bootloader(int fd)
|
||||
{
|
||||
uint8_t c;
|
||||
uint8_t prev_c = 0;
|
||||
int attempts = 100;
|
||||
|
||||
while(attempts-- > 0)
|
||||
{
|
||||
printf("Waiting...\n");
|
||||
if (read(fd, &c, 1) < 0)
|
||||
return 1;
|
||||
else
|
||||
{
|
||||
if (c == 'B' && prev_c == 'B')
|
||||
break;
|
||||
prev_c = c;
|
||||
}
|
||||
}
|
||||
|
||||
c = 0x00;
|
||||
if (write(fd, &c, 1) <= 0)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int send_jump(int fd)
|
||||
{
|
||||
uint8_t cmd = 'j';
|
||||
|
||||
if (write(fd, &cmd, 1) <= 0)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int fd;
|
||||
uint8_t buf[32*1024];
|
||||
int i, j;
|
||||
|
||||
if (argc < 3)
|
||||
{
|
||||
fprintf(stderr, "Usage %s <serial device> <hex file>\n", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
fd = serial_open(argv[1]);
|
||||
if (fd < 0)
|
||||
{
|
||||
fprintf(stderr, "Failed to open %s\n", argv[1]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
memset(buf, 0xFF, sizeof(buf));
|
||||
if (0 != read_hexfile(buf, sizeof(buf), argv[2]))
|
||||
{
|
||||
fprintf(stderr, "Failed to read %s\n", argv[2]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (0 != wait_for_bootloader(fd))
|
||||
{
|
||||
fprintf(stderr, "No bootloader detected\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
for (i=0x400;i<32*1024;i+=1024)
|
||||
{
|
||||
bool all_empty = true;
|
||||
for (j=i;j<i+1024;j++)
|
||||
{
|
||||
if (buf[j] != 0xFF)
|
||||
{
|
||||
all_empty = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!all_empty)
|
||||
{
|
||||
printf("erase/write/verify page %d\n", i/1024);
|
||||
if (0 != erase_program_verify_page(fd, buf + i, i/1024))
|
||||
{
|
||||
fprintf(stderr, "erase_program_verify_page failed\n");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("erasing page %d\n", i/1024);
|
||||
if (0 != erase_page(fd, i/1024))
|
||||
{
|
||||
fprintf(stderr, "erase failed\n");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (0 != send_jump(fd))
|
||||
{
|
||||
fprintf(stderr, "send jump failed\n");
|
||||
}
|
||||
|
||||
close(fd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
+198
@@ -0,0 +1,198 @@
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <ctype.h>
|
||||
#include <getopt.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
static uint8_t tolowercase(uint8_t ch)
|
||||
{
|
||||
if ((ch >= 'A') && (ch <= 'Z'))
|
||||
return ch + 0x20; // Convert uppercase to lowercase
|
||||
return ch; // Simply return original character if it doesn't require any adjustments
|
||||
}
|
||||
|
||||
static int8_t parseHexDigit(uint8_t digit)
|
||||
{
|
||||
digit = tolowercase(digit);
|
||||
if (isdigit(digit))
|
||||
return (int8_t)digit - '0';
|
||||
if ((digit >= 'a') && (digit <= 'f'))
|
||||
return (int8_t)digit + 0xA - 'a';
|
||||
return -1; // Error case - input wasn't a valid hex digit
|
||||
}
|
||||
|
||||
|
||||
static int hexstring_parse(const char *hexstr, uint8_t *buf, size_t *buflen)
|
||||
{
|
||||
size_t hexstrlen = *buflen * 2;
|
||||
size_t i;
|
||||
|
||||
if (hexstrlen & 0x1)
|
||||
{
|
||||
fprintf(stderr, "hexstring_parse: not even\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (*buflen < hexstrlen/2)
|
||||
{
|
||||
fprintf(stderr, "hexstring_parse: buffer too small %d < %d\n", *buflen, hexstrlen/2);
|
||||
return 1;
|
||||
}
|
||||
|
||||
for (i=0;i<hexstrlen;i+=2)
|
||||
{
|
||||
int8_t a, b;
|
||||
if (-1 == (a = parseHexDigit(hexstr[i])))
|
||||
{
|
||||
fprintf(stderr, "hexstring_parse: bad digit 0x%02X\n", hexstr[i]);
|
||||
return 1;
|
||||
}
|
||||
if (-1 == (b = parseHexDigit(hexstr[i+1])))
|
||||
{
|
||||
fprintf(stderr, "hexstring_parse: bad digit 0x%02X\n", hexstr[i+1]);
|
||||
return 1;
|
||||
}
|
||||
*buf++ = (a << 4) | b;
|
||||
}
|
||||
|
||||
*buflen = hexstrlen/2;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int read_record(uint8_t *buf, size_t buflen, const char *line, bool *eof)
|
||||
{
|
||||
size_t len;
|
||||
uint8_t sum = 0;
|
||||
uint8_t record_sum;
|
||||
uint8_t record_len;
|
||||
uint8_t type;
|
||||
uint16_t addr;
|
||||
uint8_t data[256];
|
||||
int i;
|
||||
|
||||
*eof = false;
|
||||
|
||||
if (line[0] != ':')
|
||||
{
|
||||
fprintf(stderr, "bad hexfile: no start\n");
|
||||
return 1;
|
||||
}
|
||||
line+=1;
|
||||
|
||||
len = 1;
|
||||
if (0 != hexstring_parse(line, &record_len, &len))
|
||||
{
|
||||
fprintf(stderr, "bad hexfile: no len '%s'\n", line);
|
||||
return 1;
|
||||
}
|
||||
line+=len * 2;
|
||||
|
||||
len = 2;
|
||||
if (0 != hexstring_parse(line, (uint8_t *)&addr, &len))
|
||||
{
|
||||
fprintf(stderr, "bad hexfile: no addr\n");
|
||||
return 1;
|
||||
}
|
||||
addr = ntohs(addr);
|
||||
line+=len * 2;
|
||||
|
||||
len = 1;
|
||||
if (0 != hexstring_parse(line, &type, &len))
|
||||
{
|
||||
fprintf(stderr, "bad hexfile: no type\n");
|
||||
return 1;
|
||||
}
|
||||
line+=len * 2;
|
||||
|
||||
len = record_len;
|
||||
if (0 != hexstring_parse(line, data, &len))
|
||||
{
|
||||
fprintf(stderr, "bad hexfile: no data\n");
|
||||
return 1;
|
||||
}
|
||||
line+=len * 2;
|
||||
|
||||
len = 1;
|
||||
if (0 != hexstring_parse(line, &record_sum, &len))
|
||||
{
|
||||
fprintf(stderr, "bad hexfile: no sum\n");
|
||||
return 1;
|
||||
}
|
||||
line+=len * 2;
|
||||
|
||||
if (type == 0)
|
||||
{
|
||||
sum += record_len;
|
||||
sum += addr >> 8;
|
||||
sum += addr & 0xFF;
|
||||
|
||||
for (i=0;i<record_len;i++)
|
||||
sum += data[i];
|
||||
sum = (sum ^ 0xFF) + 1;
|
||||
|
||||
if (sum != record_sum)
|
||||
{
|
||||
fprintf(stderr, "bad hexfile, checksum mismatch\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (addr + record_len > buflen)
|
||||
{
|
||||
fprintf(stderr, "bad hexfile, too big\n");
|
||||
return 1;
|
||||
}
|
||||
memcpy(buf + addr, data, record_len);
|
||||
}
|
||||
else
|
||||
if (type == 1)
|
||||
{
|
||||
*eof = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "bad hexfile: unknown record type %02X\n", type);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int read_hexfile(uint8_t *buf, size_t buflen, const char *filename)
|
||||
{
|
||||
FILE *fp;
|
||||
char line[1024];
|
||||
bool eof;
|
||||
|
||||
if (NULL == (fp = fopen(filename, "ro")))
|
||||
return 1;
|
||||
|
||||
while (NULL != fgets(line, sizeof(line), fp))
|
||||
{
|
||||
char *p = (line + strlen(line)) - 1;
|
||||
|
||||
while(p > line)
|
||||
{
|
||||
if (isspace((int)(*p)))
|
||||
*p = 0;
|
||||
p--;
|
||||
}
|
||||
|
||||
if (0 != read_record(buf, buflen, line, &eof))
|
||||
goto fail;
|
||||
|
||||
if (eof)
|
||||
break;
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
return 0;
|
||||
fail:
|
||||
fclose(fp);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
#ifndef HEX_H
|
||||
#define HEX_H 1
|
||||
|
||||
int read_hexfile(uint8_t *buf, size_t buflen, const char *filename);
|
||||
|
||||
#endif
|
||||
|
||||
Reference in new issue
Block a user