module brntool;
import core.sys.posix.unistd;
import core.sys.posix.fcntl;
import core.sys.linux.termios;
import core.sys.posix.sys.select;
import core.stdc.errno;
import core.stdc.string;
import std.algorithm: map;
import std.algorithm.searching;
import std.array;
import std.conv;
import std.format;
import std.stdio : File, stdout;
void main()
{
auto device = "/dev/ttyUSB0";
auto output = "o2box6431.bin";
auto addr = 0xb0040000;
auto size = 0x40000;
auto count = 10000;
auto tty_fd = open(device.ptr, O_RDWR | O_NONBLOCK | O_NOCTTY);
scope(exit) close(tty_fd);
//dump_config(tty_fd);
set_config(tty_fd);
//dump_config(tty_fd);
stdout.writeln("ready");
enter_administrator_mode(tty_fd);
// read 0x40000 bytes from address 0xb0040000
// and dump them into o2box6431.bin file
auto f = File(output, "w");
for (auto a = addr; a < addr + size;)
{
auto n = read_from_memory(tty_fd, f, a, count);
// indicate progress
stdout.write((count == n) ? "." : "!");
stdout.flush();
// advance the address
a += n;
if ((a + count) > (addr + size))
{
// adjust the required bytes count
count = addr + size - a;
}
}
stdout.writeln("");
stdout.writeln("done");
return;
}
void enter_administrator_mode(int fd)
{
execute(fd, "Press Space Bar 3 times to enter command mode ...",
[' ', ' ', ' ', '!']);
return;
}
uint read_from_memory(int fd, File f, uint address, uint count = 10000)
{
// request read mode
execute(fd, "[DANUBE Boot]:", ['r']);
// the start address for the read operation
execute(fd, "Enter the Start Address to Read....0x",
to!(char[])(format("%x", address)) ~ '\r');
// request a single byte format
execute(fd, "Data Length is (1) 4 Bytes (2) 2 Bytes (3) 1 Byte...", ['3']);
// and finally ask the device to dump 'count' bytes
execute(fd, "Enter the Count to Read....(Maximun 10000)",
to!(char[])(count) ~ '\r');
/*
----------------------------------------------------------
Address 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
----------------------------------------------------------
0xB0040000 4F 42 43 36 1F B4 A4 95 2D 34 4C 44 14 F2 9A 74
...
*/
uint result;
char[64] data;
auto n = read_line(fd, data);
while ((n >= 0) && (result < count))
{
if (n > 0)
{
auto s = split(cast(string)data[0..n]);
if (startsWith(s[0], "0x"))
{
auto b = s[1..$].map!(a => a.parse!ubyte(16)).array();
// do write
f.rawWrite(b);
result += b.length;
}
}
n = read_line(fd, data);
}
return result;
}
bool execute(int fd, string condition, char[] command)
{
long i;
char[512] buffer;
for (auto c = read_char(fd); c != char.init; c = read_char(fd))
{
if ((c == '\r') || (c == '\n'))
{
// reset
i = 0;
}
else
{
buffer[i++] = c;
}
if (cast(string)buffer[0..i] == condition)
{
auto n = write(fd, command.ptr, command.length);
return true;
}
}
return false;
}
long read_line(int fd, char[] buffer)
{
long i;
buffer[0..$] = 0;
auto c = read_char(fd);
while ((c != '\r') && (c != '\n'))
{
buffer[i++] = c;
if (i > buffer.length)
{
return -1;
}
c = read_char(fd);
}
return i;
}
char read_char(int fd)
{
char c;
fd_set rset;
FD_ZERO(&rset);
FD_SET(fd, &rset);
if (select(fd + 1, &rset, null, null, null) < 0)
{
throw new Error(format("select failed: %d : %s", errno, strerror(errno)));
}
if (FD_ISSET(fd, &rset))
{
long n;
do
{
n = read(fd, &c, 1);
}
while ((n < 0) && (errno == EINTR));
if (n == 0)
{
throw new Error("terminal closed");
}
else if (n < 0)
{
if ((errno != EAGAIN) && (errno != EWOULDBLOCK))
{
throw new Error(format("read failed: %d : %s", errno, strerror(errno)));
}
}
}
return c;
}
void set_config(int fd)
{
termios tio;
if (tcgetattr(fd, &tio) < 0)
{
throw new Error(format("tcgetattr failed: %d : %s", errno, strerror(errno)));
}
// set speed to 115200
if (cfsetospeed(&tio, B115200) < 0)
{
throw new Error(format("cfsetospeed failed: %d : %s", errno, strerror(errno)));
}
if (cfsetispeed(&tio, B115200) < 0)
{
throw new Error(format("cfsetispeed failed: %d : %s", errno, strerror(errno)));
}
// set 8 databits
tio.c_cflag &= (tio.c_cflag & ~CSIZE) | CS8;
// set parity to none
tio.c_cflag &= ~(PARENB | PARODD);
// disable break processing
tio.c_iflag &= ~IGNBRK;
// no signaling chars, no echo, no canonical processing
tio.c_lflag = 0;
// no remapping, no delays
tio.c_oflag = 0;
// read doesn't block
tio.c_cc[VMIN] = 0;
// 0.5 seconds read timeout
tio.c_cc[VTIME] = 5;
// shut off xon/xoff ctrl
tio.c_iflag &= ~(IXON | IXOFF | IXANY);
// ignore modem controls, enable reading
tio.c_cflag |= (CLOCAL | CREAD);
// 1 stop bit
tio.c_cflag &= ~CSTOPB;
// disable hardware flow control
tio.c_cflag &= ~CRTSCTS;
if (tcsetattr(fd, TCSAFLUSH, &tio) < 0)
{
throw new Error(format("tcsetattr failed: %d : %s", errno, strerror(errno)));
}
return;
}
void dump_config(int fd)
{
termios tio;
if (tcgetattr(fd, &tio) < 0)
{
throw new Error(format("tcgetattr failed: %d : %s", errno, strerror(errno)));
}
stdout.writefln("c_iflag = 0x%08X", tio.c_iflag);
stdout.writefln("c_oflag = 0x%08X", tio.c_oflag);
stdout.writefln("c_cflag = 0x%08X", tio.c_cflag);
stdout.writefln("c_lflag = 0x%08X", tio.c_lflag);
stdout.writefln("c_line = 0x%08X", tio.c_line);
stdout.writefln("c_ispeed = %d", tio.c_ispeed);
stdout.writefln("c_ospeed = %d", tio.c_ospeed);
return;
}