Files
RTLPlayground/cmd_parser.c
T
d00f 828963b586 cmd: let "isolate off" put the top port back in the member list
The member mask is filled with a loop that stops one short, so the
highest front panel port never gets its bit and stays isolated from the
port that was just opened up. Every other loop over the port range in
the tree treats machine.max_port as the last port rather than the one
past it.
2026-09-01 04:11:30 +02:00

1886 lines
43 KiB
C

/*
* A Command parser for RTL Switch configuration
*/
// #define DEBUG
// #define REGDBG 1
#include "rtl837x_common.h"
#include "rtl837x_port.h"
#include "rtl837x_flash.h"
#include "rtl837x_phy.h"
#include "rtl837x_regs.h"
#include "rtl837x_sfr.h"
#include "rtl837x_stp.h"
#include "rtl837x_igmp.h"
#include "rtl837x_bandwidth.h"
#include "dhcp.h"
#include "syslog.h"
#include "uip/uip.h"
#include "version.h"
#include "machine.h"
#include "phy.h"
#pragma codeseg BANK2
#pragma constseg BANK2
extern __code struct machine machine;
extern __xdata uint8_t stpEnabled;
extern volatile __xdata uint32_t ticks;
extern volatile __xdata uint8_t sfr_data[4];
extern __code uint8_t * __code greeting;
extern __code uint8_t * __code hex;
extern __xdata uint8_t flash_buf[FLASH_BUF_SIZE];
extern __xdata struct flash_region_t flash_region;
extern __xdata char passwd[21];
extern __xdata struct dhcp_state dhcp_state;
__xdata uint8_t vlan_names[VLAN_NAMES_SIZE];
__xdata uint16_t vlan_ptr;
__xdata char port_names[9][PORT_NAME_SIZE];
extern __xdata uint16_t management_vlan;
extern __xdata uint8_t sfp_speed[2];
extern __xdata uint8_t sfp_pins_last;
extern __xdata uint8_t sfp_options[2];
__xdata uint8_t gpio_last_value[8] = { 0 };
// Temporatly for str to hex convertion value.
// Support up to 32_bits.
__xdata uint8_t hexvalue[4] = { 0 };
// Buffer for writing to flash 0x1fd000, copy to 0x1fe000
__xdata uint8_t cmd_buffer[CMD_BUF_SIZE];
__xdata uint8_t cmd_available;
__xdata char save_cmd;
__xdata uint8_t ip[4];
// These variables combined create a Fixed-capacity vector/bounded buffer.
// `N_WORDS`: The total number of command arguments that can be tracked.
// `cmd_words_len` stores the number of arguments found inside `cmd_buffer`
// `cmd_words_b` stores the index into `cmd_buffer`, check `cmd_words_len` is index is valid.
#define N_WORDS 15
__xdata uint8_t cmd_words_len;
__xdata uint8_t cmd_words_b[N_WORDS];
__xdata uint8_t cmd_history[CMD_HISTORY_SIZE];
__xdata uint16_t cmd_history_ptr;
// Error set by commands
__xdata uint8_t err_status;
__xdata uint16_t atoi_results_short;
__xdata uint8_t atoi_results_u8;
inline uint8_t isletter(uint8_t l)
{
// return (l >= 'a' && l <= 'z') || (l >= 'A' && l <= 'Z');
// Make it lowercase
l |= 0x20;
l -= 'a';
return (l <= ('z'-'a'));
}
inline uint8_t isnumber(uint8_t l)
{
// return (l >= '0' && l <= '9');
l -= '0';
return (l <= ('9'-'0'));
}
uint8_t cmd_compare(uint8_t start, __code uint8_t * cmd)
{
if (cmd_words_len == 0 || start > (cmd_words_len - 1)) {
return 0;
}
uint8_t i = cmd_words_b[start];
uint8_t j = 0;
do {
uint8_t c = cmd[j];
uint8_t b = cmd_buffer[i];
// cmd is guaranteed to be NUL-terminated.
if (c == NUL) {
if ((b == ' ') || (b == NUL)) {
// Match
return 1;
}
break;
}
if (b != c) {
break;
}
j += 1;
i += 1;
} while (i < CMD_BUF_SIZE);
// No match
return 0;
}
/* Converts ascii-hex array into value.
returns number of hexvalue[] entries has been written.
return value = 0 means error.
*/
uint8_t atoi_hex(uint8_t idx)
{
uint8_t h_idx = 0;
uint8_t val = 0;
uint8_t c;
while(1) {
c = cmd_buffer[idx];
if (c == NUL || c == ' ') {
break;
}
// swap hex nibbles
val = (val >> 4) | (val << 4);
if (c - '0' < 10) {
val |= c - '0';
} else {
c |= 0x20;
c -= 'a';
if (c > 5) {
h_idx = 0;
break;
}
val |= c + 10;
}
idx++;
hexvalue[h_idx >> 1] = val;
if (h_idx & 1 == 1) {
val = 0;
}
h_idx++;
}
if (h_idx & 1) {
hexvalue[h_idx >> 1] <<= 4;
hexvalue[3] = hexvalue[3] >> 4 | (hexvalue[2] << 4);
hexvalue[2] = hexvalue[2] >> 4 | (hexvalue[1] << 4);
hexvalue[1] = hexvalue[1] >> 4 | (hexvalue[0] << 4);
hexvalue[0] >>= 4;
}
return ((h_idx + 1) >> 1);
}
// Returns 0 or the number of digits taken into account for conversion.
// Stops at any non-digit '0'-'9' char or more than 3 bytes.
uint8_t atoi_byte(uint8_t idx)
{
uint8_t cnt = 0;
uint8_t num = 0;
uint8_t * ptr = &cmd_buffer[idx];
while (1) {
uint8_t val = *ptr++ - '0';
if (val > 9)
break;
if (num > 25 || (num == 25 && val > 5) || cnt >= 3)
return 0;
num = (num * 10) + val;
cnt++;
}
atoi_results_u8 = num;
return cnt;
}
// Returns 0 or the number of digits taken into account for conversion.
// Stops at any non-digit '0'-'9' char or bytes is more then 5.
uint8_t atoi_short(uint8_t idx)
{
uint8_t cnt = 0;
atoi_results_short = 0;
uint8_t *ptr = &cmd_buffer[idx];
while (1) {
uint8_t val = *ptr++ - '0';
if (val > 9)
break;
if (atoi_results_short > 6553 || (atoi_results_short == 6553 && val > 5) || cnt >= 5)
return 0;
atoi_results_short = (atoi_results_short * 10) + val;
cnt++;
}
return cnt;
}
/* Parse, validate and translate phys_to_log_port physical port argument.
* The CPU-port, i.e. port 0, is not a valid argument.
* returns 0 when on parser error or invalid value.
* returns non-zero number of characters consumed.
* Store the value in atoi_results_u8.
*/
uint8_t cmd_parse_port(uint8_t idx) {
uint8_t port = cmd_buffer[idx] - '0' - 1;
if (port > 8)
return 0;
port = machine.phys_to_log_port[port];
if (port < machine.min_port || port > machine.max_port)
return 0;
atoi_results_u8 = port;
return 1;
}
// Same as cmd_parse_port() but additionally check for trailing SPACE or NUL.
// returns 0 when on parser error or invalid value or no SPACE or no NUL.
// returns non-zero number of characters consumed including the SPACE.
uint8_t cmd_parse_port_separator(uint8_t idx) {
uint8_t ret = cmd_parse_port(idx);
if (ret != 0) {
idx += ret;
uint8_t c = cmd_buffer[idx];
if (c == ' ') {
ret++;
} else if (c != NUL)
ret = 0;
}
return ret;
}
// Same as cmd_parse_port_separator() but additionally allow CPU-port.
// returns 0 when on parser error or invalid value or no SPACE or no NUL.
// returns number of characters consumed including the SPACE.
uint8_t cmd_parse_port_cpu_separator(uint8_t idx) {
uint8_t ret = cmd_parse_port(idx);
if (ret == 0 && cmd_buffer[idx] == '0') {
ret = 1;
atoi_results_u8 = CPU_PORT;
}
if (ret != 0) {
idx += ret;
uint8_t c = cmd_buffer[idx];
if (c == ' ') {
ret++;
} else if (c != NUL)
ret = 0;
}
return ret;
}
// check if the cmd_buffer[idx] is a SPACE.
__bit cmd_is_space(uint8_t idx) {
return cmd_buffer[idx] == ' ';
}
// check if the cmd_buffer[idx] is a SPACE or NUL.
__bit cmd_is_space_or_nul(uint8_t idx) {
uint8_t c = cmd_buffer[idx];
return c == ' ' || c == NUL;
}
// Parse an IPv4 address
// returns 0 when on parse error or invalid value or it don't ends with SPACE or NUL.
// returns non-zero number of characters consumed including the SPACE.
uint8_t parse_ip(uint8_t idx)
{
uint8_t b = 0;
uint8_t ret;
uint8_t idx_start = idx;
while(1) {
ret = atoi_byte(idx);
if (ret == 0)
goto err;
idx += ret;
ip[b++] = atoi_results_u8;
ret = cmd_buffer[idx];
if (b == 4) {
if (ret == ' ') {
idx++;
break;
}
if (ret == NUL)
break;
goto err;
}
idx++;
if (ret != '.')
goto err;
}
return idx - idx_start;
err:
print_string("Error in IP format\n");
return 0;
}
// Prints an IPv4 address.
void print_ip(__xdata uint8_t * ptr)
{
uint8_t idx = 0;
uint8_t num;
while(1) {
num = *ptr++;
itoa(num);
if (++idx == 4)
break;
write_char('.');
}
}
void parse_lag(void)
{
__xdata uint8_t group;
__xdata uint16_t members = 0;
if (cmd_compare(1, "show")) {
print_string("LAG status:\n");
for (uint8_t i = 0; i < 4; i++) {
write_char(' '); write_char('1' + i);
members = port_lag_members_get(i);
if (!members) {
print_string(" disabled\n");
continue;
}
print_string(" member ports: ");
for (uint8_t j = 0; j < 10; j++) {
if (members & 1) {
print_phys_port(j);
write_char(' ');
}
members >>= 1;
}
print_string(" (hash: 0x");
reg_read_m(RTL837X_TRK_HASH_CTRL_BASE + (i << 2));
print_byte(sfr_data[3]);
print_string(")\n");
}
return;
}
if (cmd_words_len < 2)
goto err;
// Parse group, expect only one number 0-9;
if (atoi_byte(cmd_words_b[1]) != 1)
goto err;
group = atoi_results_u8 - 1;
if (group > 3)
goto err;
uint8_t w = 2;
while (w < cmd_words_len) {
// write_char('|'); print_byte(w); write_char(':'); write_char(cmd_buffer[cmd_words_b[w]]); write_char('-');
// Parse port.
if (cmd_parse_port_separator(cmd_words_b[w++]) == 0)
goto err;
members |= ((uint16_t)1) << atoi_results_u8;
}
port_lag_members_set(group, members);
return;
err:
print_string("Error: lag <1-4> [port]...\n");
}
void parse_lag_hash(void)
{
// Parse group, expect only one number 0-9.
if (cmd_words_len < 3 || atoi_byte(cmd_words_b[1]) != 1)
goto err;
uint8_t group = atoi_results_u8 - 1;
if (group > 3)
goto err;
uint8_t hash = 0;
uint8_t w = 2;
while (w < cmd_words_len) {
if (cmd_compare(w, "spa"))
hash |= LAG_HASH_SOURCE_PORT_NUMBER;
else if (cmd_compare(w, "smac"))
hash |= LAG_HASH_L2_SMAC;
else if (cmd_compare(w, "dmac"))
hash |= LAG_HASH_L2_DMAC;
else if (cmd_compare(w, "sip"))
hash |= LAG_HASH_L3_SIP;
else if (cmd_compare(w, "dip"))
hash |= LAG_HASH_L3_DIP;
else if (cmd_compare(w, "sport"))
hash |= LAG_HASH_L4_SPORT;
else if (cmd_compare(w, "dport"))
hash |= LAG_HASH_L4_DPORT;
else {
print_string("Error: invalid hash type:");
print_string_x(&cmd_buffer[cmd_words_b[w]]);
write_char('\n');
goto err;
}
w++;
}
port_lag_hash_set(group, hash);
return;
err:
print_string("Error: laghash <1-4> [smac|dmac|sip|dip|sport|dport]\n");
}
void parse_vlan(void)
{
vlan_settings.vlan = 0;
vlan_settings.members = 0;
vlan_settings.tagged = 0;
if (cmd_words_len < 2)
goto err;
// Parse the VLAN number
if (atoi_short(cmd_words_b[1]) != 0) {
if (atoi_results_short > 4094)
goto err;
vlan_settings.vlan = atoi_results_short;
if (cmd_compare(2, "mgmt")) {
management_vlan = vlan_settings.vlan;
if (!vlan_settings.vlan)
print_string("Management VLAN disabled\n");
else
print_string("Management VLAN set to "); print_short(management_vlan); write_char('\n');
return;
}
// Other commands vlan 0 is invalid
if (!vlan_settings.vlan)
goto err;
if (cmd_words_len == 3 && cmd_buffer[cmd_words_b[2]] == 'd') {
vlan_delete(vlan_settings.vlan);
return;
}
uint8_t w = 2;
if (cmd_words_len > w && isletter(cmd_buffer[cmd_words_b[w]])) {
uint8_t i = 0;
vlan_name_remove(vlan_settings.vlan);
vlan_names[vlan_ptr++] = hex[(vlan_settings.vlan >> 8) & 0xf];
vlan_names[vlan_ptr++] = hex[(vlan_settings.vlan >> 4) & 0xf] ;
vlan_names[vlan_ptr++] = hex[vlan_settings.vlan & 0xf];
while(!cmd_is_space_or_nul(cmd_words_b[w] + i)) {
write_char(cmd_buffer[cmd_words_b[w] + i]);
vlan_names[vlan_ptr++] = cmd_buffer[cmd_words_b[w] + i++];
}
vlan_names[vlan_ptr++] = ' '; vlan_names[vlan_ptr] = NUL;
w++;
print_string("<\n");
}
uint8_t ret;
uint8_t idx;
while (cmd_words_len > w) {
idx = cmd_words_b[w++];
ret = cmd_parse_port(idx);
if (ret == 0)
goto err;
idx += ret;
uint16_t pmask = ((uint16_t)1) << atoi_results_u8;
vlan_settings.members |= pmask;
if (cmd_buffer[idx] == 't') {
vlan_settings.tagged |= pmask;
idx++;
}
if (!cmd_is_space_or_nul(idx))
goto err;
}
vlan_create();
} else if (cmd_compare(1, "show")) {
vlan_dump();
} else {
goto err;
}
if (cmd_words_len >= 3 && isletter(cmd_buffer[cmd_words_b[2]])) {
print_string("vlan_ptr "); print_short(vlan_ptr); write_char(':');
write_char('>'); print_string_x(&vlan_names[0]); write_char('<'); write_char('\n');
}
return;
err:
print_string("Error: vlan (<vlan-id>|show) [port][t/u]...\n");
}
void parse_isolate(void)
{
uint16_t members = 0;
if (cmd_words_len < 3)
goto err;
print_string("\nISOLATE ");
if (cmd_parse_port_separator(cmd_words_b[1]) == 0)
goto err;
uint8_t port_configured = atoi_results_u8;
print_byte(port_configured); write_char('\n');
if (cmd_compare(2, "show")) {
members = port_isolation_get(port_configured);
for (uint8_t i = 0; i < 10; i++) {
if (members & 1) {
print_phys_port(i);
write_char(' ');
}
members >>= 1;
}
return;
}
if (cmd_compare(2, "off")) {
for (uint8_t i = machine.min_port; i <= machine.max_port; i++)
members |= ((uint16_t)1) << i;
members |= 0x200; // CPU-port
port_isolate(port_configured, members);
return;
}
uint8_t w = 2;
while (w < cmd_words_len) {
if (cmd_parse_port_cpu_separator(cmd_words_b[w++]) == 0)
goto err;
uint8_t port = atoi_results_u8;
members |= ((uint16_t)1) << port;
}
port_isolate(port_configured, members);
return;
err:
print_string("Error: isolate <port> [show|off] [port]...\n");
}
bool vlan_ingress_mode_parse(char c, __xdata vlan_ingress_mode_t *mode)
{
switch (c) {
case 'u':
*mode = VLAN_UNTAGGED;
return true;
case 't':
*mode = VLAN_TAGGED;
return true;
case 'a':
*mode = VLAN_ALL;
return true;
default:
*mode = VLAN_INVALID;
return false;
}
}
void parse_ingress(void)
{
if (cmd_words_len < 2) {
goto err;
}
uint8_t log_port = 0;
__xdata vlan_ingress_mode_t mode = VLAN_INVALID;
uint8_t idx = cmd_words_b[1];
if (vlan_ingress_mode_parse(cmd_buffer[idx++], &mode)) {
if (!cmd_is_space_or_nul(idx))
goto err;
// Setting mode for all ports at once
for (log_port = machine.min_port; log_port <= machine.max_port; log_port++) {
if (!port_ingress_filter(log_port, mode)) {
print_string("Error setting ingress filter for port "); print_phys_port(log_port); write_char('\n');
return;
}
print_string("All ports ingress filter set to: ");
print_port_ingress_filter_mode(mode); write_char('\n');
}
} else {
for(uint8_t w = 1; w < cmd_words_len; w++) {
idx = cmd_words_b[w];
uint8_t ret = cmd_parse_port(idx);
if (ret != 1) {
print_string("Invalid physical port number\n");
continue;
}
log_port = atoi_results_u8;
idx += ret;
if (!vlan_ingress_mode_parse(cmd_buffer[idx++], &mode) || !cmd_is_space_or_nul(idx)) {
print_string("Invalid ingress mode for port "); print_phys_port(log_port); print_string(" in ingress command\n");
goto err;
}
if (!port_ingress_filter(log_port, mode)) {
print_string("Error setting ingress filter for port "); print_phys_port(log_port); write_char('\n');
return;
}
print_string("Port "); print_phys_port(log_port);
print_string(" ingress filter set to: ");
print_port_ingress_filter_mode(mode); write_char('\n');
}
}
return;
err:
print_string("Error: ingress [p]<u/t/a>...\n");
}
void parse_mirror(void)
{
__xdata uint8_t mirroring_port = 0;
__xdata uint16_t rx_pmask = 0;
__xdata uint16_t tx_pmask = 0;
if (cmd_compare(1, "status")) {
reg_read_m(RTL837x_MIRROR_CTRL);
uint8_t mPort = sfr_data[3];
if (mPort & 1) {
print_string("Enabled: ");
} else {
print_string("NOT Enabled: ");
}
print_string("Mirroring port: ");
print_phys_port(mirroring_port);
reg_read_m(RTL837x_MIRROR_CONF);
uint16_t m = sfr_data[0];
m = (m << 8) | sfr_data[1];
print_string(", Port mask RX: ");
print_short(m);
m = sfr_data[2];
m = (m << 8) | sfr_data[3];
print_string(", Port mask TX: ");
print_short(m);
write_char('\n');
return;
} else if (cmd_compare(1, "off")) {
port_mirror_del();
return;
}
if (cmd_words_len < 2)
goto err;
uint8_t w = 2;
uint8_t port;
uint8_t ret;
while (w < cmd_words_len) {
uint8_t idx = cmd_words_b[w++];
ret = cmd_parse_port(idx);
if (ret == 0)
goto err;
idx += ret;
port = atoi_results_u8;
// Use the first port argument as mirroring_port
if (w == 2)
mirroring_port = port;
ret = cmd_buffer[idx];
uint16_t pmask = ((uint16_t)1) << port;
if (ret != 't')
rx_pmask |= pmask;
if (ret != 'r')
tx_pmask |= pmask;
}
port_mirror_set(mirroring_port, rx_pmask, tx_pmask);
return;
err:
print_string("Port/command missing: mirror [status/off/<mirroring port> [port][t/r]]...\n");
return;
}
void parse_port(void)
{
if (cmd_words_len < 3) {
print_string("\nUsage:" \
"\nport <port> [show|on|off]" \
"\nport <port> [10m|100m|1g|2g5|duplex] [half|full]" \
"\nport <port> name [custom port name]\n");
return;
}
if (cmd_parse_port_separator(cmd_words_b[1]) == 0) {
print_string("Invalid port number\n");
return;
}
phy_settings.port = atoi_results_u8;
print_string("Logical Port: "); print_byte(phy_settings.port); write_char('\n');
phy_settings.duplex = PHY_DUPLEX_BOTH;
if (cmd_compare(2, "show")) {
print_string("Name: ");
print_string_x(port_names[phy_settings.port]);
if (!machine.is_sfp[phy_settings.port]) {
phy_show(phy_settings.port);
}
} else if (cmd_compare(2, "name")) {
uint8_t i = 0;
while ( (i < PORT_NAME_SIZE-1) && (cmd_buffer[cmd_words_b[3] + i] != NUL) ) {
port_names[phy_settings.port][i] = cmd_buffer[cmd_words_b[3] + i];
i++;
}
port_names[phy_settings.port][i] = NUL;
print_string("\nName set to: \"");
print_string_x(port_names[phy_settings.port]);
print_string("\"\n");
} else if (machine.is_sfp[phy_settings.port]) {
print_string(" is SFP no PHY information available.\n");
} else if (cmd_compare(2, "10m")) {
print_string(" 10M\n");
phy_settings.speed = PHY_SPEED_10M;
if (cmd_compare(3, "half"))
phy_settings.duplex = PHY_DUPLEX_HALF;
else if (cmd_compare(3, "full"))
phy_settings.duplex = PHY_DUPLEX_FULL;
phy_set_speed();
} else if (cmd_compare(2, "100m")) {
print_string(" 100M\n");
phy_settings.speed = PHY_SPEED_100M;
if (cmd_compare(3, "half"))
phy_settings.duplex = PHY_DUPLEX_HALF;
else if (cmd_compare(3, "full"))
phy_settings.duplex = PHY_DUPLEX_FULL;
phy_set_speed();
} else if (cmd_compare(2, "10g")) {
print_string(" 10G\n");
phy_settings.speed = PHY_SPEED_10G;
phy_set_speed();
} else if (cmd_compare(2, "5g")) {
print_string(" 5G\n");
phy_settings.speed = PHY_SPEED_5G;
phy_set_speed();
} else if (cmd_compare(2, "2g5")) {
print_string(" 2.5G\n");
phy_settings.speed = PHY_SPEED_2G5;
phy_set_speed();
} else if (cmd_compare(2, "1g")) {
print_string(" 1G\n");
phy_settings.speed = PHY_SPEED_1G;
phy_set_speed();
} else if (cmd_compare(2, "auto")) {
print_string(" AUTO\n");
phy_settings.speed = PHY_SPEED_AUTO;
phy_set_speed();
} else if (cmd_compare(2, "off")) {
print_string(" OFF\n");
phy_settings.speed = PHY_OFF;
phy_set_speed();
} else if (cmd_compare(2, "on")) {
print_string(" ON\n");
phy_settings.speed = PHY_SPEED_AUTO;
phy_set_speed();
} else if (cmd_compare(2, "duplex")) {
print_string(" DUPLEX\n");
if (cmd_compare(3, "full"))
phy_settings.duplex = PHY_DUPLEX_FULL;
else
phy_settings.duplex = PHY_DUPLEX_HALF;
phy_set_duplex();
} else {
print_string("Unknown port command\n");
}
}
void parse_mtu(void)
{
uint8_t p;
if (cmd_compare(1, "show")) {
for (p = machine.min_port; p <= machine.max_port; p++) {
reg_read_m(RTL8373_REG_MAC_L2_PORT_MAX_LEN + ((uint16_t) p << 8));
uint16_t mtu = SFR_DATA_U16 & 0x3fff;
print_string("Port "); print_phys_port(p);
write_char(' '); print_short(mtu); write_char('\n');
}
return;
}
if (cmd_words_len != 3)
goto err;
if (cmd_parse_port_separator(cmd_words_b[1]) == 0)
goto err;
p = atoi_results_u8;
print_byte(p);
if (atoi_short(cmd_words_b[2]) == 0 || atoi_results_short < 64 || atoi_results_short > 0x3fff) {
print_string("MTU must be 64..16383\n");
return;
}
REG_WRITE(RTL8373_REG_MAC_L2_PORT_MAX_LEN + ((uint16_t) p << 8), (atoi_results_short >> 10) & 0xf, (atoi_results_short >> 2) & 0xff,
((atoi_results_short & 0x3) << 6) | ((atoi_results_short >> 8) & 0x3f), atoi_results_short & 0xff);
write_char('\n');
return;
err:
print_string("mtu [port] [size]\n");
return;
}
bool sfp_print_measurements(uint8_t sfp)
{
if (!sfp_read_block(sfp, 92, 1))
return false;
print_string("Options: "); print_byte(sfp_buf[0]); write_char('\n');
if (!(sfp_options[sfp] & 0x40))
return true;
if (!sfp_read_block(sfp, 224, 16))
return false;
print_string("Temp: "); print_byte(sfp_buf[0]); print_byte(sfp_buf[1]); write_char('\n');
print_string("Vcc: "); print_byte(sfp_buf[2]); print_byte(sfp_buf[3]); write_char('\n');
print_string("TX Bias: "); print_byte(sfp_buf[4]); print_byte(sfp_buf[5]); write_char('\n');
print_string("TX Power: "); print_byte(sfp_buf[6]); print_byte(sfp_buf[7]); write_char('\n');
print_string("RX Power: "); print_byte(sfp_buf[8]); print_byte(sfp_buf[9]); write_char('\n');
print_string("Laser: "); print_byte(sfp_buf[10]); print_byte(sfp_buf[11]); write_char('\n');
print_string("State: "); print_byte(sfp_buf[14]); write_char('\n');
return true;
}
void parse_sfp(void)
{
uint8_t slot;
if (cmd_words_len != 1 && cmd_words_len != 3)
goto err;
if (cmd_words_len == 1) {
for (slot = 0; slot < machine.n_sfp; slot++) {
print_string("\nSlot "); write_char('1' + slot);
if (gpio_pin_test(machine.sfp_port[slot].pin_detect)) {
print_string(" - empty\n");
continue;
}
if (!sfp_read_block(slot, 11, 2)) {
print_string(" - I2C read failed on this slot\n");
continue;
}
print_string(" - Rate: "); print_byte(sfp_buf[1]);
print_string(" Encoding: "); print_byte(sfp_buf[0]);
write_char('\n');
if (!sfp_print_info(slot) || !sfp_print_measurements(slot))
print_string("I2C read failed on this slot\n");
}
return;
}
uint8_t idx = cmd_words_b[1];
uint8_t ret = atoi_byte(idx);
idx += ret;
slot = atoi_results_u8 - 1;
if (ret == 0 || !cmd_is_space(idx) || slot > 1) {
print_string("Illegal SFP slot number\n");
return;
}
if (slot >= machine.n_sfp) {
print_string("SFP slot not present\n");
return;
}
if (cmd_compare(2, "10g")) {
print_string(" 10G\n");
sfp_speed[slot] = SFP_SPEED_10G;
} else if (cmd_compare(2, "2g5")) {
print_string(" 2.5G\n");
sfp_speed[slot] = SFP_SPEED_2G5;
} else if (cmd_compare(2, "1g")) {
print_string(" 1G\n");
sfp_speed[slot] = SFP_SPEED_1G;
} else if (cmd_compare(2, "100m")) {
print_string(" 100M\n");
sfp_speed[slot] = SFP_SPEED_100M;
} else if (cmd_compare(2, "auto")) {
print_string(" AUTO\n");
sfp_speed[slot] = SFP_SPEED_AUTO;
} else {
goto err;
}
sfp_pins_last |= 0x1 << (slot << 2);
handle_sfp();
return;
err:
print_string("\nUsage:\n\tsfp\n\tsfp [1|2] [1g|2g5|10g]\n");
}
void parse_regget(void)
{
if (cmd_words_len != 2) {
goto err;
}
uint8_t hex_size = atoi_hex(cmd_words_b[1]);
if (hex_size == 0 || hex_size > 2) {
goto err;
}
uint16_t reg = hexvalue[0];
if (hex_size == 2) {
reg <<= 8;
reg |= hexvalue[1];
}
print_string("REGGET: ");
print_short(reg);
print_string(": VAL: ");
reg_read_m(reg);
print_sfr_data();
write_char('\n');
return;
err:
print_string("usage: regget <hexvalue>\n\tlike: regget 0BB0 or regget 0c");
return;
}
void parse_regset(void)
{
if (cmd_words_len != 3) {
goto err;
}
uint8_t hex_size = atoi_hex(cmd_words_b[1]);
if (hex_size == 0 || hex_size > 2) {
goto err;
}
uint16_t reg = hexvalue[0];
if (hex_size == 2) {
reg <<= 8;
reg |= hexvalue[1];
}
hex_size = atoi_hex(cmd_words_b[2]);
if (hex_size == 0 || hex_size > 4) {
goto err;
}
// zero sfr memory data
sfr_set_zero();
// copy data over sfr memory
uint8_t offset = 4 - hex_size;
while(hex_size) {
hex_size -= 1;
sfr_data[offset + hex_size] = hexvalue[hex_size];
}
print_string("REGSET: ");
print_short(reg);
reg_write_m(reg);
print_string(": VAL: ");
print_sfr_data();
write_char('\n');
return;
err:
print_string("usage: regset <hexvalue> <hexvalue>\n\tlike regset 0b abcd1234.");
}
void parse_sdsget(void)
{
__xdata uint8_t sds_id, page, reg, hex_size;
if (cmd_words_len != 4) {
goto err;
}
if (!atoi_byte(cmd_words_b[1])) {
goto err;
}
sds_id = atoi_results_u8;
hex_size = atoi_hex(cmd_words_b[2]);
if (hex_size != 1) {
goto err;
}
page = hexvalue[0];
hex_size = atoi_hex(cmd_words_b[3]);
if (hex_size != 1) {
goto err;
}
reg = hexvalue[0];
print_string("SDSGET: ");
itoa(sds_id);
print_string(":0x");
print_byte(page);
print_string(":0x");
print_byte(reg);
print_string(": VAL: ");
sds_read(sds_id, page, reg);
print_phy_data();
write_char('\n');
return;
err:
print_string("usage: sdsget <sds-id> <hex:page> <hex:reg>\n");
return;
}
void parse_sdsset(void)
{
__xdata uint8_t sds_id, page, reg, hex_size;
__xdata uint16_t val;
if (cmd_words_len != 5) {
goto err;
}
if (!atoi_byte(cmd_words_b[1])) {
goto err;
}
sds_id = atoi_results_u8;
hex_size = atoi_hex(cmd_words_b[2]);
if (hex_size != 1) {
goto err;
}
page = hexvalue[0];
hex_size = atoi_hex(cmd_words_b[3]);
if (hex_size != 1) {
goto err;
}
reg = hexvalue[0];
hex_size = atoi_hex(cmd_words_b[4]);
if (hex_size == 0 || hex_size > 2) {
goto err;
}
val = hexvalue[0];
if (hex_size == 2) {
val <<= 8;
val |= hexvalue[1];
}
print_string("SDSSET: ");
itoa(sds_id);
print_string(":0x");
print_byte(page);
print_string(":0x");
print_byte(reg);
print_string(": VAL: ");
sds_write_v(sds_id, page, reg, val);
print_short(val);
write_char('\n');
return;
err:
print_string("usage: sdsset <sds-id> <hex:page> <hex:reg> <hex:val>\n");
return;
}
void parse_phyget(void)
{
__xdata uint8_t phy_id, dev_id, hex_size;
__xdata uint16_t reg;
if (cmd_words_len != 4) {
goto err;
}
if (!atoi_byte(cmd_words_b[1])) {
goto err;
}
phy_id = atoi_results_u8;
if (!atoi_byte(cmd_words_b[2])) {
goto err;
}
dev_id = atoi_results_u8;
hex_size = atoi_hex(cmd_words_b[3]);
if (hex_size == 0 || hex_size > 2) {
goto err;
}
reg = hexvalue[0];
if (hex_size == 2) {
reg <<= 8;
reg |= hexvalue[1];
}
print_string("PHYGET: ");
itoa(phy_id);
print_string(":");
itoa(dev_id);
print_string(".");
print_short(reg);
print_string(": VAL: ");
phy_read(phy_id, dev_id, reg);
print_phy_data();
write_char('\n');
return;
err:
print_string("usage: phyget <phy-id> <dev-id> <hex:reg>\n");
return;
}
void parse_physet(void)
{
__xdata uint8_t phy_id, dev_id, hex_size;
__xdata uint16_t reg, val;
if (cmd_words_len != 5) {
goto err;
}
if (!atoi_byte(cmd_words_b[1])) {
goto err;
}
phy_id = atoi_results_u8;
if (!atoi_byte(cmd_words_b[2])) {
goto err;
}
dev_id = atoi_results_u8;
hex_size = atoi_hex(cmd_words_b[3]);
if (hex_size == 0 || hex_size > 2) {
goto err;
}
reg = hexvalue[0];
if (hex_size == 2) {
reg <<= 8;
reg |= hexvalue[1];
}
hex_size = atoi_hex(cmd_words_b[4]);
if (hex_size == 0 || hex_size > 2) {
goto err;
}
val = hexvalue[0];
if (hex_size == 2) {
val <<= 8;
val |= hexvalue[1];
}
print_string("PHYSET: ");
itoa(phy_id);
print_string(":");
itoa(dev_id);
print_string(".");
print_short(reg);
print_string(": VAL: ");
phy_write(phy_id, dev_id, reg, val);
print_short(val);
write_char('\n');
return;
err:
print_string("usage: physet <phy-id> <dev-id> <hex:reg> <hex:val>\n");
return;
}
void parse_rnd(void)
{
// In order to get a new random numner, this bit has to be set each time!
reg_bit_set(RTL837X_RLDP_RLPP, RLDP_RND_EN);
reg_read_m(RTL837X_RAND_NUM1);
print_byte(sfr_data[2]);
print_byte(sfr_data[3]);
reg_read_m(RTL837X_RAND_NUM0);
print_byte(sfr_data[0]);
print_byte(sfr_data[1]);
print_byte(sfr_data[2]);
print_byte(sfr_data[3]);
write_char('\n');
}
void parse_passwd(void)
{
// cmd_words_len can be more then 2 if a SPACE in the password.
if (cmd_words_len >= 2) {
uint8_t i = cmd_words_b[1];
uint8_t c = 0;
uint8_t j = 0;
do {
c = cmd_buffer[i++];
passwd[j++] = c;
} while (c != NUL && j < 20);
passwd[j] = NUL;
return;
}
print_string("Missing password\n");
}
void parse_eee(void)
{
__xdata int8_t port = -1;
__xdata uint8_t speed = EEE_2G5;
__xdata uint8_t speed_word = 0;
if (machine.n_10g)
speed = EEE_10G;
// Check if word 2 is a speed (contains 'g' or 'm') or a port number
if (cmd_words_len >= 3) {
uint8_t idx = cmd_words_b[2];
// Skip digits to check if there's a letter after
while (isnumber(cmd_buffer[idx]))
idx++;
if (cmd_buffer[idx] == 'g' || cmd_buffer[idx] == 'm') {
// Word 2 is a speed (e.g., "2g5", "100m", "1g")
speed_word = 2;
} else if (cmd_is_space_or_nul(idx)) {
// Word 2 is a port number
if (cmd_parse_port_separator(idx) == 0) {
print_string("Speed word invalid, use: [100m|1g|2g5]\n");
return;
}
port = atoi_results_u8;
// Check if word 3 is a speed
if (cmd_words_len >= 4)
speed_word = 3;
}
}
// Parse speed if found
if (speed_word > 0) {
if (cmd_compare(speed_word, "100m"))
speed = EEE_100;
else if (cmd_compare(speed_word, "1g"))
speed = EEE_1000;
else if (cmd_compare(speed_word, "2g5"))
speed = EEE_2G5;
else
{
print_string("Speed word invalid, use: [100m|1g|2g5]\n");
return;
}
}
if (cmd_compare(1, "on")) {
if (port >= 0)
port_eee_enable(port, speed);
else
port_eee_enable_all(speed);
} else if (cmd_compare(1, "off")) {
if (port >= 0)
port_eee_disable(port);
else
port_eee_disable_all();
} else if (cmd_compare(1, "status")) {
if (port >= 0)
port_eee_status(port);
else
port_eee_status_all();
} else {
print_string("eee [on|off|status] [port] [100m|1g|2g5]\n");
}
}
void parse_bw(void)
{
if (cmd_words_len < 3) // Check for at least 3 arguments
goto err;
if (cmd_parse_port_separator(cmd_words_b[2]) == 0)
goto err;
uint8_t port = atoi_results_u8;
if (cmd_compare(1, "status")) {
bandwidth_status(port);
return;
}
if (cmd_words_len < 4) // Check for at least 4 arguments
goto err;
// Ensure first argument is only `in` or `out`.
__bit is_in = cmd_compare(1, "in") != 0;
if (!(is_in || cmd_compare(1, "out")))
goto err;
if (cmd_compare(3, "drop")) {
if (is_in) {
bandwidth_ingress_drop(port);
return;
}
goto err;
}
if (cmd_compare(3, "fc")) {
if (is_in) {
bandwidth_ingress_fc(port);
return;
}
goto err;
}
if (cmd_compare(3, "off")) {
if (is_in) {
bandwidth_ingress_disable(port);
} else {
bandwidth_egress_disable(port);
}
return;
}
__xdata uint32_t bw = 0;
uint8_t hex_size = atoi_hex(cmd_words_b[3]);
if (hex_size == 0 || hex_size > 4)
goto err;
uint8_t i = 0;
do {
hex_size--;
*(((uint8_t *) &bw) + hex_size) = hexvalue[i++];
} while (hex_size);
if (is_in) {
bandwidth_ingress_set(port, bw);
} else {
bandwidth_egress_set(port, bw);
}
return;
err:
print_string("usage: bw [in|out|status] <port> [<hexvalue>|off|drop|fc]\n");
}
void parse_syslog(void)
{
if (cmd_words_len < 2) // no argument -> print status
{
print_string("Current syslog status: ");
if (syslog_state.enabled) {
print_string("enabled, sending to ");
print_ip(syslog_state.server_ip);
write_char('\n');
} else {
print_string("disabled\n");
}
return;
}
if (cmd_compare(1, "on")) {
syslog_start();
} else if (cmd_compare(1, "off")){
syslog_stop();
} else if (cmd_compare(1, "ip")) {
if (cmd_words_len < 3) { // no additional arguemnt -> print current ip
print_string("Current syslog IP: ");
print_ip(syslog_state.server_ip);
return;
} else if (parse_ip(cmd_words_b[2]) != 0) {
uint8_t was_enabled = syslog_state.enabled;
if (was_enabled)
syslog_stop();
print_string("Setting new syslog IP.\n");
syslog_state.server_ip[0] = ip[0]; syslog_state.server_ip[1] = ip[1];
syslog_state.server_ip[2] = ip[2]; syslog_state.server_ip[3] = ip[3];
if (was_enabled)
syslog_start();
} else {
print_string("Invalid IP address\n");
}
}
else
{
print_string("Error: syslog [on|off|ip [ip-address]]\n");
print_string(" on/off enables or disables syslog, ip sets the syslog server IP address\n");
}
}
// Parse command into words
// cmd_words_len contains the number of words found.
// cmd_words_b[] contains only start of a word offset.
// Returns the parsing status via `err_status`-variable.
void cmd_tokenize(void) __banked
{
#ifdef DEBUG
print_string("Tokenizing command\n");
print_string_x(&cmd_buffer[0]);
write_char('<'); write_char('\n');
#endif
err_status = ERR_OK;
uint8_t line_ptr = 0;
uint8_t is_white = 1;
uint8_t word = 0;
uint8_t c = 0;
while(1) {
c = cmd_buffer[line_ptr];
if (c == NUL) {
// Store the word count
cmd_words_len = word;
break;
}
if (line_ptr == CMD_BUF_SIZE - 1) {
err_status = ERR_CMD_TOO_LONG;
return;
}
if (is_white && c != ' ') {
is_white = 0;
cmd_words_b[word++] = line_ptr;
if (word >= N_WORDS) {
cmd_words_len = 0;
print_string("\nSyntax error: too many arguments.");
err_status = ERR_TOO_MANY_ARGUMENTS;
return;
}
} else if (c == ' ') {
is_white = 1;
}
line_ptr++;
}
}
// Print GPIO status
void print_gpio_status(void) {
for (uint8_t idx = 0; idx < 2; idx++) {
reg_read(RTL837X_REG_GPIO_00_31_INPUT + (idx * 4));
print_string("GPIO ");
write_char(idx + '0');
write_char(':');
write_char(' ');
print_byte(SFR_DATA_24);
print_byte(SFR_DATA_16);
print_byte(SFR_DATA_8);
print_byte(SFR_DATA_0);
write_char(' ');
print_byte( gpio_last_value[(idx *4)] ^ SFR_DATA_24);
gpio_last_value[(idx *4)] = SFR_DATA_24;
print_byte( gpio_last_value[(idx *4) + 1] ^ SFR_DATA_16);
gpio_last_value[(idx *4) + 1] = SFR_DATA_16;
print_byte( gpio_last_value[(idx *4) + 2] ^ SFR_DATA_8);
gpio_last_value[(idx *4) + 2] = SFR_DATA_8;
print_byte( gpio_last_value[(idx *4) + 3] ^ SFR_DATA_0);
gpio_last_value[(idx *4) + 3] = SFR_DATA_0;
write_char('\n');
}
}
// Show software version
void print_sw_version(void) __banked {
print_string("Software version: " VERSION_SW);
print_string("\nBuild date: " BUILD_DATE);
print_string("\nHardware: ");
print_string(machine.machine_name);
write_char('\n');
}
// Identify command
void cmd_parser(void) __banked
{
#ifdef DEBUG
print_long(ticks);
print_string("Parsing command\n");
print_string_x(&cmd_buffer[0]);
write_char('<'); write_char('\n');
print_string("CMD-words: ");
print_byte(cmd_words_len); write_char(' ');
print_byte(cmd_words_b[0]); write_char(' ');
print_byte(cmd_words_b[1]); write_char(' ');
print_byte(cmd_words_b[2]); write_char(' ');
print_byte(cmd_words_b[3]); write_char(' ');
print_byte(cmd_words_b[4]); write_char(' ');
print_byte(cmd_words_b[5]); write_char(' ');
print_byte(cmd_words_b[6]); write_char('\n');
#endif
if (cmd_words_len >= 1) {
if (cmd_compare(0, "reset")) {
print_string("\nRESET\n\n");
reset_chip();
} else if (cmd_compare(0, "sfp")) {
parse_sfp();
} else if (cmd_compare(0, "stat")) {
port_stats_print();
} else if (cmd_compare(0, "flash") && cmd_words_len == 2) {
uint8_t c = cmd_buffer[cmd_words_b[1]];
if (c == 's') {
print_string("\nSECURITY REGISTERS\n");
// The following will only show something else than 0xff if it was programmed for a managed switch
print_string("Region 1: ");
flash_region.addr = 0x0001000;
flash_region.len = 40;
flash_read_security();
print_string("\nRegion 2: ");
flash_region.addr = 0x0002000;
flash_region.len = 40;
flash_read_security();
print_string("\nRegion 3: ");
flash_region.addr = 0x0003000;
flash_region.len = 40;
flash_read_security();
} else if (c == 'j') {
print_string("\nJEDEC ID\n");
flash_read_jedecid();
} else if (c == 'u') {
print_string("\nUNIQUE ID (note: only 4 bytes are likely correct here!)\n");
flash_read_uid();
}
} else if (cmd_compare(0, "port")) {
parse_port();
} else if (cmd_compare(0, "mtu")) {
parse_mtu();
} else if (cmd_compare(0, "syslog")) {
parse_syslog();
} else if (cmd_compare(0, "ip")) {
if (cmd_compare(1, "dhcp")) {
dhcp_start();
} else if (cmd_words_len == 1) {
print_string("Current IP: ");
print_ip(uip_hostaddr);
if (dhcp_state.state == DHCP_LEASING) {
print_string(" (dhcp, renewal in sec: ");
print_short(dhcp_state.dhcp_timer);
write_char(')');
} else {
print_string(" (static)");
}
write_char('\n');
} else {
if (dhcp_state.state)
dhcp_stop();
if (parse_ip(cmd_words_b[1]) != 0) {
uip_ipaddr(&uip_hostaddr, ip[0], ip[1], ip[2], ip[3]);
print_string("Setting ip: ");
print_ip(ip); write_char('\n');
} else {
print_string("Invalid IP address\n" \
"Error: ip [<ip-address>|dhcp]\n" \
" The dhcp option enables the dhcp client, calling ip without options prints the current IP\n" \
" Calling with a valid IP address will stop any ongoing dhcp client and set the IP address\n");
}
}
} else if (cmd_compare(0, "gw")) {
if (cmd_words_len == 1) {
print_string("Current gw: ");
print_ip(uip_draddr); write_char('\n');
} else {
if (parse_ip(cmd_words_b[1]) != 0) {
uip_ipaddr(&uip_draddr, ip[0], ip[1], ip[2], ip[3]);
print_string("Setting gw: ");
print_ip(ip); write_char('\n');
} else {
print_string("Invalid IP address\n" \
"Error: gw <ip-address>\n");
}
}
} else if (cmd_compare(0, "netmask")) {
if (cmd_words_len == 1) {
print_string("Current netmask: ");
print_ip(uip_netmask); write_char('\n');
} else {
if (parse_ip(cmd_words_b[1]) != 0) {
uip_ipaddr(&uip_netmask, ip[0], ip[1], ip[2], ip[3]);
print_string("Setting netmask: ");
print_ip(ip); write_char('\n');
} else {
print_string("Invalid IP address\n");
}
}
write_char('\n');
} else if (cmd_compare(0, "l2")) {
if (cmd_compare(1, "forget"))
port_l2_forget();
else
port_l2_learned();
} else if (cmd_compare(0, "igmp")) {
if (cmd_compare(1, "on"))
igmp_enable();
else if (cmd_compare(1, "off"))
igmp_setup();
else if (cmd_compare(1, "show"))
igmp_show();
else
print_string("Error: igmp on|off|show\n");
} else if (cmd_compare(0, "hostname")) {
/* "hostname" alone reports the current name; "hostname <text>"
* sets it, sanitized to JSON-safe printable ASCII. A name with
* spaces would tokenize into several words - reject it instead
* of silently keeping the first one. */
if (cmd_words_len == 1) {
print_string_x(hostname);
write_char('\n');
} else if (cmd_words_len == 2) {
__xdata uint8_t *hp = &cmd_buffer[cmd_words_b[1]];
__xdata char *dst = hostname;
for (uint8_t hn = 0; hn < sizeof(hostname) - 1; hn++) {
uint8_t c = *hp++;
if (c == NUL || c == '\r' || c == '\n')
break;
if (c < 0x20 || c > 0x7e || c == '"' || c == '\\')
c = '.';
*dst++ = c;
}
*dst = NUL;
} else {
print_string("Error: hostname [name] - the name must not contain spaces\n");
}
} else if (cmd_compare(0, "stp")) {
if (cmd_compare(1, "on")) {
print_string("STP enabled\n");
stpEnabled = 1;
stp_setup();
} else {
print_string("STP disabled\n");
stp_off();
stpEnabled = 0;
}
} else if (cmd_compare(0, "pvid")) {
if (cmd_words_len == 3 && cmd_parse_port_separator(cmd_words_b[1]) != 0
&& atoi_short(cmd_words_b[2]) && atoi_results_short && atoi_results_short <= 4094)
port_pvid_set(atoi_results_u8, atoi_results_short);
else
print_string("Error: pvid <port> <1-4094>\n");
} else if (cmd_compare(0, "vlan")) {
parse_vlan();
} else if (cmd_compare(0, "isolate")) {
parse_isolate();
} else if (cmd_compare(0, "mirror")) {
parse_mirror();
} else if (cmd_compare(0, "lag")) {
parse_lag();
} else if (cmd_compare(0, "laghash")) {
parse_lag_hash();
} else if (cmd_compare(0, "sds")) {
print_reg(RTL837X_REG_SDS_MODES);
write_char('\n');
} else if (cmd_compare(0, "gpio")) {
print_gpio_status();
} else if (cmd_compare(0, "regget")) {
parse_regget();
} else if (cmd_compare(0, "regset")) {
parse_regset();
} else if (cmd_compare(0, "sdsget")) {
parse_sdsget();
} else if (cmd_compare(0, "sdsset")) {
parse_sdsset();
} else if (cmd_compare(0, "phyget")) {
parse_phyget();
} else if (cmd_compare(0, "physet")) {
parse_physet();
} else if (cmd_compare(0, "rnd")) {
parse_rnd();
} else if (cmd_compare(0, "passwd")) {
parse_passwd();
} else if (cmd_compare(0, "eee")) {
parse_eee();
} else if (cmd_compare(0, "bw")) {
parse_bw();
} else if (cmd_compare(0, "version")) {
print_sw_version();
} else if (cmd_compare(0, "time")) {
print_string(" Tick counter: "); print_long(ticks); print_string(" Sec Counter: ");
reg_read_m(RTL837X_REG_SEC_COUNTER);
print_sfr_data();
write_char('\n');
} else if (cmd_compare(0, "history")) {
__xdata uint16_t p = (cmd_history_ptr + 1) & CMD_HISTORY_MASK;
__xdata uint8_t found_begin = 0;
// print_string("History ptr: ");
// print_short(cmd_history_ptr); write_char('\n');
while (p != cmd_history_ptr) {
// print_short(p); write_char(' ');
if (!cmd_history[p] || cmd_history[p] == '\n')
found_begin = 1;
if (found_begin && cmd_history[p])
write_char(cmd_history[p]);
p = (p + 1) & CMD_HISTORY_MASK;
}
} else if (cmd_compare(0, "ingress")) {
parse_ingress();
}
else {
print_string("Unknown command\n");
}
if (save_cmd && cmd_words_len) {
// Find end of the cmd-buffer, looking for the NUL-byte.
uint8_t i = cmd_words_b[cmd_words_len - 1];
do {
i++;
} while(cmd_buffer[i] != NUL);
// Copy last cmd-buffer to history.
cmd_history_ptr = (cmd_history_ptr + i) & CMD_HISTORY_MASK;
__xdata uint16_t p = cmd_history_ptr;
cmd_history[cmd_history_ptr++] = '\n';
do {
i--;
cmd_history[--p & CMD_HISTORY_MASK] = cmd_buffer[i];
} while (i);
}
}
}
void clear_command_history(void) __banked
{
for (cmd_history_ptr = 0; cmd_history_ptr < CMD_HISTORY_SIZE; cmd_history_ptr++)
cmd_history[cmd_history_ptr] = 0;
cmd_history_ptr = 0;
return;
}
#define FLASH_READ_BURST_SIZE 0x100
#define PASSWORD "1234"
#if CONFIG_LEN % FLASH_READ_BURST_SIZE
#error "CONFIG_LEN not a multiple of FLASH_READ_BURST_SIZE"
#endif
void execute_config(void) __banked
{
__xdata uint32_t pos = CONFIG_START;
__xdata uint8_t pages_left = CONFIG_LEN / FLASH_READ_BURST_SIZE;
// Set default password, it can be overwritten in the configuration file
strtox(passwd, PASSWORD);
save_cmd = 0;
uint8_t cmd_idx = 0;
do {
flash_region.addr = pos;
flash_region.len = FLASH_READ_BURST_SIZE;
flash_read_bulk(flash_buf);
__xdata uint8_t cfg_idx = 0;
uint8_t c = 0;
do {
if (cmd_idx >= (CMD_BUF_SIZE - 1)) {
cmd_buffer[cmd_idx] = NUL;
print_string("ERROR: Command too long: ");
print_string_x(cmd_buffer);
write_char('\n');
err_status = ERR_CMD_TOO_LONG;
goto config_done;
}
c = flash_buf[cfg_idx++];
if (c == 0 || c == '\n') {
cmd_buffer[cmd_idx] = NUL;
if (cmd_idx) {
cmd_tokenize();
if (err_status != ERR_OK)
goto config_done;
cmd_parser();
}
if (c == 0)
goto config_done;
cmd_idx = 0;
continue;
}
cmd_buffer[cmd_idx] = c;
cmd_idx++;
} while (cfg_idx);
pages_left--;
pos += FLASH_READ_BURST_SIZE;
} while(pages_left);
config_done:
// Start saving commands to cmd_history
clear_command_history();
save_cmd = 1;
}
// Execute multiple commands
// If a command is too long or can't be tokenized, remaining commands are not executed
// Returns the status via `err_status`-variable.
void execute_commands(__xdata uint8_t *p) __banked {
err_status = ERR_OK;
uint8_t cmd_idx = 0;
while (1) {
if (*p == 0 || *p == '\n' || *p == '\r') {
if (cmd_idx) {
cmd_buffer[cmd_idx] = NUL;
cmd_tokenize();
if (err_status != ERR_OK)
return;
cmd_parser();
}
if (*p == 0)
return;
cmd_idx = 0;
} else {
if (cmd_idx < (CMD_BUF_SIZE - 1)) {
cmd_buffer[cmd_idx++] = *p;
} else {
cmd_buffer[CMD_BUF_SIZE - 1] = NUL;
print_string("ERROR: Command too long: ");
print_string_x(cmd_buffer);
write_char('\n');
err_status = ERR_CMD_TOO_LONG;
return;
}
}
p++;
};
}