mirror of
https://github.com/logicog/RTLPlayground.git
synced 2026-10-29 17:54:14 +08:00
The boot banner and the version command print the SDCC release and revision from the compiler's own macros, and the name of the in-tree toolchain the Makefile built, which is the checksum of sdcc/ and so changes with every patch in it. A build with CC given on the command line says "system" instead, since that compiler need not carry the patches. A plain release and the same release with the patches report the same revision, so the toolchain line is what tells them apart. Costs 66 bytes of BANK2.
2394 lines
56 KiB
C
2394 lines
56 KiB
C
/*
|
|
* A Command parser for RTL Switch configuration
|
|
*/
|
|
|
|
// #define DEBUG
|
|
// #define REGDBG 1
|
|
|
|
#include <8051.h>
|
|
#include "cmd_parser.h"
|
|
#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_lacp.h"
|
|
#include "rtl837x_igmp.h"
|
|
#include "rtl837x_bandwidth.h"
|
|
#include "rtl837x_lldp.h"
|
|
#include "rtl837x_storm.h"
|
|
#include "rtl837x_trap.h"
|
|
#include "rtl837x_cabletest.h"
|
|
#include "sfp.h"
|
|
#include "dhcp.h"
|
|
#include "syslog.h"
|
|
#include "sessions.h"
|
|
#include "dns.h"
|
|
#include "ntp.h"
|
|
#include "uip/uip.h"
|
|
#include "version.h"
|
|
|
|
#include "machine.h"
|
|
#include "rtl837x_leds.h"
|
|
#include "phy.h"
|
|
|
|
#pragma codeseg BANK2
|
|
#pragma constseg BANK2
|
|
|
|
extern __code const struct machine machine;
|
|
extern __xdata bool stp_enabled;
|
|
extern __code const uint8_t log_to_phys_port[9];
|
|
extern __xdata bool lldp_enabled;
|
|
|
|
extern volatile __xdata uint32_t ticks;
|
|
extern volatile __xdata uint8_t sfr_data[4];
|
|
|
|
extern __code const uint8_t * __code const greeting;
|
|
extern __code const uint8_t * __code const 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 uint16_t session_timeout;
|
|
|
|
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 struct uip_eth_addr uip_ethaddr;
|
|
__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 char save_cmd;
|
|
|
|
__xdata uint8_t ip[4];
|
|
__xdata uint8_t mac_parse_result[6];
|
|
|
|
/* Scratch for parse_syslog(), in xdata: a local here would take internal RAM
|
|
* the linker has none of. */
|
|
__xdata uint8_t syslog_port_was_on;
|
|
|
|
// 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 const 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;
|
|
}
|
|
|
|
if (h_idx == 8) {
|
|
h_idx = 0;
|
|
break;
|
|
}
|
|
|
|
// swap hex nibbles
|
|
val = (val >> 4) | (val << 4);
|
|
|
|
if ((uint8_t)(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 (!MACHINE_PORT_VALID(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) __banked
|
|
{
|
|
uint8_t idx = 0;
|
|
uint8_t num;
|
|
|
|
while(1) {
|
|
num = *ptr++;
|
|
itoa(num);
|
|
if (++idx == 4)
|
|
break;
|
|
|
|
write_char('.');
|
|
}
|
|
}
|
|
|
|
// Prints a MAC address.
|
|
void print_mac(__xdata uint8_t *ptr) __banked
|
|
{
|
|
uint8_t idx = 0;
|
|
uint8_t num;
|
|
|
|
while (1) {
|
|
num = *ptr++;
|
|
print_byte(num);
|
|
if (++idx == 6)
|
|
break;
|
|
|
|
write_char(':');
|
|
}
|
|
}
|
|
|
|
// Parses a MAC address "aa:bb:cc:dd:ee:ff" (":" or "-" separators) from
|
|
// cmd_buffer starting at idx into the global mac_parse_result[6]. Each octet
|
|
// must be exactly two hex digits with a separator between octets and nothing
|
|
// trailing, so repeated separators, missing digits or a trailing hex digit are
|
|
// rejected. Returns the number of bytes consumed, or 0 on error.
|
|
uint8_t parse_mac(uint8_t idx)
|
|
{
|
|
uint8_t b = 0;
|
|
uint8_t c = ':';
|
|
|
|
do {
|
|
if (c != ':' && c != '-')
|
|
goto err;
|
|
|
|
__bit again = true;
|
|
uint8_t v = 0;
|
|
|
|
while(1) {
|
|
c = cmd_buffer[idx++];
|
|
c -= '0';
|
|
if (c > 9) {
|
|
c |= 0x20;
|
|
c += '0' - 'a';
|
|
if (c > 5)
|
|
goto err;
|
|
c += 10;
|
|
}
|
|
// force swap-instr.
|
|
v = (v << 4) | (v >> 4);
|
|
v |= c;
|
|
|
|
if (again)
|
|
again = false;
|
|
else
|
|
break;
|
|
}
|
|
mac_parse_result[b++] = v;
|
|
|
|
c = cmd_buffer[idx++];
|
|
} while(b < 6);
|
|
|
|
if (c == ' ' || c == NUL)
|
|
return idx;
|
|
|
|
err:
|
|
return 0;
|
|
}
|
|
|
|
static void cmd_error(__code const char *msg)
|
|
{
|
|
err_status = ERR_INVALID_ARGUMENT;
|
|
print_string("Error: ");
|
|
print_string(msg);
|
|
}
|
|
|
|
|
|
static uint8_t name_char(uint8_t c)
|
|
{
|
|
if (c < 0x20 || c > 0x7e || c == '"' || c == '\\')
|
|
return '.';
|
|
return c;
|
|
}
|
|
|
|
|
|
// Sets the management MAC from a "mac <aa:bb:cc:dd:ee:ff>" command (or prints
|
|
// the current one with a bare "mac"). Refuses blank/multicast/locally
|
|
// administered/all-zero-OUI addresses so the running MAC stays reachable, and
|
|
// re-registers the static L2 management entry so the change applies without a
|
|
// reboot. As a regular command a "mac ..." line in the startup config applies
|
|
// on every boot (execute_config runs before the final static L2 entry).
|
|
void parse_mac_cmd(void)
|
|
{
|
|
if (cmd_words_len == 1) {
|
|
print_string("Current MAC: ");
|
|
print_mac(uip_ethaddr.addr);
|
|
write_char('\n');
|
|
return;
|
|
}
|
|
if (cmd_words_len != 2 || !parse_mac(cmd_words_b[1])) {
|
|
cmd_error("mac [<aa:bb:cc:dd:ee:ff>]\n");
|
|
return;
|
|
}
|
|
if ((mac_parse_result[0] & 0x03) || ((mac_parse_result[0] | mac_parse_result[1] | mac_parse_result[2]) == 0)) {
|
|
cmd_error("the MAC must be unicast and globally administered\n");
|
|
return;
|
|
}
|
|
if (memcmp(mac_parse_result, uip_ethaddr.addr, 6) == 0) {
|
|
print_string("MAC unchanged\n");
|
|
return;
|
|
}
|
|
|
|
// Swap the static management entry over to the new address: remove the
|
|
// old one (still in uip_ethaddr) before overwriting it.
|
|
port_l2_static_mgmt(uip_ethaddr.addr, management_vlan, true);
|
|
memcpy(uip_ethaddr.addr, mac_parse_result, 6);
|
|
port_l2_static_mgmt(uip_ethaddr.addr, management_vlan, false);
|
|
|
|
print_string("MAC set to: ");
|
|
print_mac(uip_ethaddr.addr);
|
|
write_char('\n');
|
|
print_string("Save the startup config to keep it across reboots\n");
|
|
}
|
|
|
|
|
|
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 < 3)
|
|
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;
|
|
|
|
if (cmd_compare(2, "d")) {
|
|
lacp_lag_set(group, 0);
|
|
port_lag_members_set(group, 0);
|
|
return;
|
|
}
|
|
|
|
uint8_t w = 2;
|
|
__xdata uint8_t lacp_mode = 0;
|
|
if (cmd_words_len > 2 && cmd_compare(2, "lacp")) {
|
|
lacp_mode = 1;
|
|
w = 3;
|
|
if (cmd_words_len > 3 && cmd_compare(3, "off")) {
|
|
lacp_lag_set(group, 0);
|
|
return;
|
|
}
|
|
}
|
|
while (w < cmd_words_len) {
|
|
// Parse port.
|
|
if (cmd_parse_port_separator(cmd_words_b[w++]) == 0)
|
|
goto err;
|
|
|
|
members |= ((uint16_t)1) << atoi_results_u8;
|
|
}
|
|
if (lacp_mode) {
|
|
lacp_lag_set(group, members);
|
|
} else {
|
|
lacp_lag_set(group, 0);
|
|
port_lag_members_set(group, members);
|
|
}
|
|
return;
|
|
err:
|
|
cmd_error("lag (show | <1-4> (d | [lacp] <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:
|
|
cmd_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")) {
|
|
port_l2_static_mgmt(uip_ethaddr.addr, management_vlan, true);
|
|
management_vlan = vlan_settings.vlan;
|
|
port_l2_static_mgmt(uip_ethaddr.addr, management_vlan, false);
|
|
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;
|
|
uint8_t pos = cmd_words_b[w];
|
|
if (cmd_words_len > w && isletter(cmd_buffer[pos])) {
|
|
uint8_t i = 0;
|
|
while (!cmd_is_space_or_nul(pos + i))
|
|
i++;
|
|
if (vlan_ptr + i > (VLAN_NAMES_SIZE - 5)) {
|
|
print_string("VLAN name table full, name ignored\n");
|
|
} else {
|
|
vlan_name_remove(vlan_settings.vlan);
|
|
__xdata uint8_t *vlan_str = &vlan_names[vlan_ptr];
|
|
*vlan_str++ = hex[(vlan_settings.vlan >> 8) & 0xf];
|
|
*vlan_str++ = hex[(vlan_settings.vlan >> 4) & 0xf] ;
|
|
*vlan_str++ = hex[vlan_settings.vlan & 0xf];
|
|
while(!cmd_is_space_or_nul(pos)) {
|
|
uint8_t c = name_char(cmd_buffer[pos++]);
|
|
write_char(c);
|
|
*vlan_str++ = c;
|
|
}
|
|
*vlan_str++ = ' '; *vlan_str = NUL;
|
|
vlan_ptr = vlan_str - vlan_names;
|
|
print_string("<\n");
|
|
}
|
|
w++;
|
|
}
|
|
|
|
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:
|
|
cmd_error("vlan (<vlan-id>|show) [port][t]...\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 |= PMASK_CPU; // 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:
|
|
cmd_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)) {
|
|
cmd_error("cannot set the 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) {
|
|
cmd_error("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)) {
|
|
cmd_error("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)) {
|
|
cmd_error("cannot set the 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:
|
|
cmd_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(mPort >> 1);
|
|
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;
|
|
|
|
// The first argument is the mirroring (monitor) port
|
|
uint8_t ret = cmd_parse_port(cmd_words_b[1]);
|
|
if (ret == 0)
|
|
goto err;
|
|
mirroring_port = atoi_results_u8;
|
|
|
|
uint8_t w = 2;
|
|
uint8_t port;
|
|
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;
|
|
|
|
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:
|
|
cmd_error("Port/command missing: mirror [status/off/<mirroring port> [port][t/r]]...\n");
|
|
return;
|
|
}
|
|
|
|
|
|
void parse_port(void)
|
|
{
|
|
if (cmd_words_len < 3) {
|
|
cmd_error("\nUsage:" \
|
|
"\nport <port> [show|on|off]" \
|
|
"\nport <port> [10m|100m|1g|2g5|duplex] [half|full]" \
|
|
"\nport <port> lldp [block|permit]" \
|
|
"\nport <port> cabletest [show]" \
|
|
"\nport <port> name [custom port name]\n");
|
|
return;
|
|
}
|
|
|
|
if (cmd_parse_port_separator(cmd_words_b[1]) == 0) {
|
|
cmd_error("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 {
|
|
write_char('\n');
|
|
}
|
|
port_media_show(phy_settings.port);
|
|
} else if (cmd_compare(2, "name")) {
|
|
uint8_t i = 0;
|
|
while ( cmd_words_len > 3 && (i < PORT_NAME_SIZE-1) && (cmd_buffer[cmd_words_b[3] + i] != NUL) ) {
|
|
port_names[phy_settings.port][i] = name_char(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 (cmd_compare(2, "lldp")) {
|
|
if (cmd_compare(3, "block")){
|
|
lldp_logical_port_status &= ~((uint16_t)1 << phy_settings.port);
|
|
} else if (cmd_compare(3, "permit")) {
|
|
lldp_logical_port_status |= (uint16_t)1 << phy_settings.port;
|
|
} else {
|
|
print_string ("Unknown port <port> lldp [block|permit] command\n");
|
|
}
|
|
} else if (machine.is_sfp[phy_settings.port]) {
|
|
print_string(" is SFP no PHY information available.\n");
|
|
} else if (cmd_compare(2, "cabletest")) {
|
|
if (cmd_words_len > 3 && cmd_compare(3, "show"))
|
|
cabletest_show();
|
|
else
|
|
cabletest_run();
|
|
} 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 {
|
|
cmd_error("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) {
|
|
cmd_error("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:
|
|
cmd_error("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) {
|
|
cmd_error("Illegal SFP slot number\n");
|
|
return;
|
|
}
|
|
if (slot >= machine.n_sfp) {
|
|
cmd_error("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:
|
|
cmd_error("\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:
|
|
cmd_error("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:
|
|
cmd_error("regset <hexvalue> <hexvalue>\n\tlike regset 0b abcd1234.");
|
|
}
|
|
|
|
|
|
static void print_temperature(uint16_t reg)
|
|
{
|
|
int16_t v;
|
|
|
|
reg_read(reg);
|
|
v = SFR_DATA_U16;
|
|
if (v < 0) {
|
|
write_char('-');
|
|
v = -v;
|
|
}
|
|
itoa(v >> 7);
|
|
write_char('.');
|
|
itoa(((v & 0x7f) * 10) >> 7);
|
|
print_string(" C\n");
|
|
}
|
|
|
|
|
|
void parse_temp(void)
|
|
{
|
|
print_string("\nChip temperature: ");
|
|
print_temperature(RTL837X_TM_RESULT);
|
|
print_string("At power-on: ");
|
|
print_temperature(RTL837X_TM_RESULT_POWERON);
|
|
}
|
|
|
|
|
|
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:
|
|
cmd_error("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:
|
|
cmd_error("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:
|
|
cmd_error("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:
|
|
cmd_error("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;
|
|
/* Logins made with the old password end here. With one session the next
|
|
* login pushed a leftover one out; with several it would stay valid. */
|
|
session_init();
|
|
return;
|
|
}
|
|
cmd_error("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(cmd_words_b[2]) == 0) {
|
|
cmd_error("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
|
|
{
|
|
cmd_error("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 {
|
|
cmd_error("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:
|
|
cmd_error("bw [in|out|status] <port> [<hexvalue>|off|drop|fc]\n");
|
|
}
|
|
|
|
static __code const char * __code const storm_types[STORM_TYPES] = { "bcast", "mcast", "ucast", "umcast" };
|
|
|
|
void parse_storm(void)
|
|
{
|
|
uint8_t port, type, d;
|
|
__xdata uint32_t rate;
|
|
__xdata uint8_t *p;
|
|
|
|
if (cmd_words_len == 1)
|
|
return storm_show();
|
|
if (cmd_words_len < 4 || !cmd_parse_port_separator(cmd_words_b[1]))
|
|
goto err;
|
|
port = atoi_results_u8;
|
|
|
|
type = 0;
|
|
while (!cmd_compare(2, storm_types[type])) {
|
|
if (++type == STORM_TYPES)
|
|
goto err;
|
|
}
|
|
|
|
if (cmd_words_len == 4 && cmd_compare(3, "off"))
|
|
return storm_off(port, type);
|
|
if (cmd_words_len != 5)
|
|
goto err;
|
|
|
|
rate = 0;
|
|
p = &cmd_buffer[cmd_words_b[3]];
|
|
do {
|
|
d = *p++ - '0';
|
|
if (d > 9 || rate > 99999999)
|
|
goto err;
|
|
rate = (rate << 3) + (rate << 1) + d;
|
|
} while (*p != ' ' && *p != NUL);
|
|
|
|
if (cmd_compare(4, "pps")) {
|
|
if (!rate || rate > 0xfffff)
|
|
goto err;
|
|
storm_set(port, type, rate, 1);
|
|
} else if (cmd_compare(4, "kbps")) {
|
|
if (!rate || rate > 10000000)
|
|
goto err;
|
|
storm_set(port, type, rate, 0);
|
|
} else {
|
|
goto err;
|
|
}
|
|
return;
|
|
|
|
err:
|
|
print_string("usage: storm [<port> bcast|mcast|ucast|umcast <rate> pps|kbps|off]\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(':'); itoa_short(syslog_state.server_port);
|
|
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 {
|
|
cmd_error("Invalid IP address\n");
|
|
}
|
|
} else if (cmd_compare(1, "port")) {
|
|
if (cmd_words_len < 3) { // no additional argument -> print current port
|
|
print_string("Current syslog port: ");
|
|
itoa_short(syslog_state.server_port); write_char('\n');
|
|
return;
|
|
}
|
|
/* atoi_short() returns zero for no digits and for a value past
|
|
* 65535; port zero is not a port either, so both tests are needed. */
|
|
if (!atoi_short(cmd_words_b[2]) || !atoi_results_short) {
|
|
cmd_error("Invalid port\n");
|
|
return;
|
|
}
|
|
/* The port is frozen into the connection by uip_udp_new(), so
|
|
* changing it has to tear the connection down and build a new one -
|
|
* same dance as setting the address above. */
|
|
syslog_port_was_on = syslog_state.enabled;
|
|
if (syslog_port_was_on)
|
|
syslog_stop();
|
|
print_string("Setting new syslog port.\n");
|
|
syslog_state.server_port = atoi_results_short;
|
|
if (syslog_port_was_on)
|
|
syslog_start();
|
|
}
|
|
else
|
|
{
|
|
cmd_error("syslog [on|off|ip [ip-address]|port [number]]\n"
|
|
" on/off enables or disables syslog, ip sets the syslog server IP address,\n"
|
|
" port sets the destination UDP port (default 514)\n");
|
|
}
|
|
}
|
|
|
|
void parse_session(void)
|
|
{
|
|
if (cmd_words_len >= 2) {
|
|
/* atoi_short() returns zero for no digits and for a value past
|
|
* 65535; a zero-second timeout is not useful either, so both
|
|
* tests are needed. */
|
|
if (!atoi_short(cmd_words_b[1]) || !atoi_results_short) {
|
|
print_string("Must be 1-65535 s\n");
|
|
return;
|
|
}
|
|
session_timeout = atoi_results_short;
|
|
}
|
|
/* Also the confirmation after setting: one message for both. */
|
|
print_string("Session timeout: ");
|
|
itoa_short(session_timeout);
|
|
print_string(" s\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("\nCompiler: SDCC " STR(__SDCC_VERSION_MAJOR) "." STR(__SDCC_VERSION_MINOR) "." STR(__SDCC_VERSION_PATCH) " #" STR(__SDCC_REVISION));
|
|
print_string("\nToolchain: " SDCC_TOOLCHAIN);
|
|
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, "dns")) {
|
|
dns_parse();
|
|
} else if (cmd_compare(0, "ntp") || cmd_compare(0, "time")) {
|
|
ntp_parse();
|
|
} 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 {
|
|
cmd_error("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 {
|
|
cmd_error("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 {
|
|
cmd_error("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 {
|
|
cmd_error("igmp on|off|show\n");
|
|
}
|
|
} else if (cmd_compare(0, "mac")) {
|
|
parse_mac_cmd();
|
|
} 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;
|
|
*dst++ = name_char(c);
|
|
}
|
|
*dst = NUL;
|
|
} else {
|
|
cmd_error("hostname [name] - the name must not contain spaces\n");
|
|
}
|
|
} else if (cmd_compare(0, "stp")) {
|
|
stp_parse();
|
|
#ifdef HEALTH
|
|
} else if (cmd_compare(0, "health")) {
|
|
health_show();
|
|
#endif
|
|
} else if (cmd_compare(0, "lacp")) {
|
|
if (cmd_compare(1, "show"))
|
|
lacp_show();
|
|
else if (cmd_compare(1, "off"))
|
|
lacp_off();
|
|
else
|
|
cmd_error("lacp (show | off); a group starts LACP with: lag <1-4> lacp <port>...\n");
|
|
} 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 {
|
|
cmd_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, "leds")) {
|
|
leds_dump();
|
|
} 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, "temp")) {
|
|
parse_temp();
|
|
} else if (cmd_compare(0, "storm")) {
|
|
parse_storm();
|
|
} else if (cmd_compare(0, "version")) {
|
|
print_sw_version();
|
|
} 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 if (cmd_compare(0, "session")) {
|
|
parse_session();
|
|
} else if (cmd_compare(0, "lldp")) {
|
|
if (cmd_compare(1, "on")) {
|
|
print_string("LLDP enabled\n");
|
|
lldp_enabled = 1;
|
|
trap_rma_set(RTL837X_RMA0E_CONF, RTL837X_RMA_ACT_TRAP);
|
|
} else {
|
|
print_string("LLDP disabled\n");
|
|
lldp_enabled = 0;
|
|
trap_rma_set(RTL837X_RMA0E_CONF, RTL837X_RMA_ACT_FORWARD);
|
|
}
|
|
}
|
|
else {
|
|
cmd_error("Unknown command\n");
|
|
}
|
|
|
|
|
|
if (save_cmd && cmd_words_len && err_status == ERR_OK) {
|
|
// 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';
|
|
cmd_history_ptr = (cmd_history_ptr + 1) & CMD_HISTORY_MASK;
|
|
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;
|
|
uint8_t skipping = 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, skipped: ");
|
|
print_string_x(cmd_buffer);
|
|
write_char('\n');
|
|
cmd_idx = 0;
|
|
skipping = 1;
|
|
}
|
|
c = flash_buf[cfg_idx++];
|
|
if (c == 0 || c == '\n' || c == '\r') {
|
|
if (skipping) {
|
|
skipping = 0;
|
|
} else {
|
|
cmd_buffer[cmd_idx] = NUL;
|
|
if (cmd_idx) {
|
|
cmd_tokenize();
|
|
if (err_status == ERR_OK)
|
|
cmd_parser();
|
|
}
|
|
}
|
|
if (c == 0)
|
|
goto config_done;
|
|
cmd_idx = 0;
|
|
continue;
|
|
}
|
|
|
|
if (!skipping) {
|
|
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 {
|
|
__xdata uint8_t failed = ERR_OK;
|
|
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 (failed == ERR_OK)
|
|
failed = err_status;
|
|
}
|
|
if (*p == 0) {
|
|
err_status = failed;
|
|
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++;
|
|
};
|
|
}
|
|
|
|
#ifdef HEALTH
|
|
/*
|
|
* Health instrumentation. The counters live in xdata and are fed from the
|
|
* main loop through the banked entry points below; the "health" command
|
|
* prints everything in one go. All figures are raw hex, diffing two dumps
|
|
* gives the rates. Phase times are in system ticks (SYS_TICK_HZ = 200,
|
|
* 5 ms each); a phase counts as slow when it spans 2 or more ticks.
|
|
*/
|
|
extern __xdata uint16_t len_left; /* httpd: bytes still to send of the current file */
|
|
extern __xdata uint8_t entry; /* httpd: index of the file being sent */
|
|
|
|
static __xdata uint32_t health_loops;
|
|
__xdata uint16_t health_rx_frames;
|
|
|
|
static __xdata uint16_t ph_last_t;
|
|
static __xdata uint8_t ph_max[HEALTH_PHASES];
|
|
static __xdata uint16_t ph_slow[HEALTH_PHASES];
|
|
|
|
/* The ISR increments ticks between the two byte reads of a 16-bit load, so
|
|
* read until two loads agree. */
|
|
static uint16_t ticks_lo(void)
|
|
{
|
|
uint16_t a, b;
|
|
a = (uint16_t)ticks;
|
|
do {
|
|
b = a;
|
|
a = (uint16_t)ticks;
|
|
} while (a != b);
|
|
return a;
|
|
}
|
|
|
|
|
|
void health_loop_start(void) __banked
|
|
{
|
|
health_loops++;
|
|
ph_last_t = ticks_lo();
|
|
}
|
|
|
|
|
|
void health_phase(uint8_t id) __banked
|
|
{
|
|
__xdata uint16_t now = ticks_lo();
|
|
__xdata uint16_t d = now - ph_last_t;
|
|
ph_last_t = now;
|
|
if (d > 0xff)
|
|
d = 0xff;
|
|
if ((uint8_t)d > ph_max[id])
|
|
ph_max[id] = d;
|
|
if (d >= 2)
|
|
ph_slow[id]++;
|
|
}
|
|
|
|
|
|
/* Fill the stack area above the current frame with a pattern; the dump scans
|
|
* for how much of it survived. The 8051 stack grows upward, so everything
|
|
* above SP is free at this point. Interrupts are held off so an ISR does not
|
|
* push into the area mid-paint. */
|
|
void health_stack_paint(void) __banked
|
|
{
|
|
__xdata uint8_t from = SP + 4;
|
|
__xdata uint8_t ea = EA;
|
|
EA = 0;
|
|
for (uint8_t i = 0xff; i >= from; i--)
|
|
*(__idata uint8_t *)i = 0xa5;
|
|
EA = ea;
|
|
}
|
|
|
|
|
|
static void ph_line(__code const char *name, uint8_t id)
|
|
{
|
|
print_string(name);
|
|
print_string(" max ");
|
|
print_byte(ph_max[id]);
|
|
print_string(" slow ");
|
|
print_short(ph_slow[id]);
|
|
write_char('\n');
|
|
}
|
|
|
|
|
|
void health_show(void) __banked
|
|
{
|
|
__xdata uint8_t i;
|
|
|
|
print_string("up ");
|
|
reg_read_m(RTL837X_REG_SEC_COUNTER);
|
|
print_byte(sfr_data[0]); print_byte(sfr_data[1]);
|
|
print_byte(sfr_data[2]); print_byte(sfr_data[3]);
|
|
print_string(" ticks ");
|
|
print_long(ticks);
|
|
print_string(" loops ");
|
|
print_long(health_loops);
|
|
print_string(" rx ");
|
|
print_short(health_rx_frames);
|
|
write_char('\n');
|
|
|
|
/* Phase timing: max duration seen (ticks of 5 ms) and slow-pass count */
|
|
ph_line("link", HEALTH_PH_LINK);
|
|
ph_line("sfp ", HEALTH_PH_SFP);
|
|
ph_line("rx ", HEALTH_PH_RX);
|
|
ph_line("tx ", HEALTH_PH_TX);
|
|
ph_line("stp ", HEALTH_PH_STP);
|
|
ph_line("cmd ", HEALTH_PH_CMD);
|
|
ph_line("lldp", HEALTH_PH_LLDP);
|
|
|
|
/* Stack: current depth plus how much of the painted area is untouched */
|
|
print_string("sp ");
|
|
print_byte(SP);
|
|
print_string(" untouched ");
|
|
i = 0;
|
|
while (*(__idata uint8_t *)(0xff - i) == 0xa5 && i < 0x86)
|
|
i++;
|
|
print_byte(i);
|
|
write_char('\n');
|
|
|
|
/* The TCP connection slot. One on this configuration; a build with more
|
|
* has to say which slot each line is, so make that a visible decision. */
|
|
#if UIP_CONNS != 1
|
|
#error "health: the tcp dump prints a single slot, extend it for UIP_CONNS > 1"
|
|
#endif
|
|
i = 0;
|
|
{
|
|
print_string("tcp ");
|
|
print_string("st ");
|
|
print_byte(uip_conns[i].tcpstateflags);
|
|
print_string(" tmr ");
|
|
print_byte(uip_conns[i].timer);
|
|
print_string(" rtx ");
|
|
print_byte(uip_conns[i].nrtx);
|
|
print_string(" rto ");
|
|
print_byte(uip_conns[i].rto);
|
|
print_string(" len ");
|
|
print_short(uip_conns[i].len);
|
|
print_string(" mss ");
|
|
print_short(uip_conns[i].mss);
|
|
write_char('\n');
|
|
print_string(" lport ");
|
|
print_short(HTONS(uip_conns[i].lport));
|
|
print_string(" rport ");
|
|
print_short(HTONS(uip_conns[i].rport));
|
|
print_string(" rip ");
|
|
print_short(HTONS(uip_conns[i].ripaddr[0]));
|
|
write_char(' ');
|
|
print_short(HTONS(uip_conns[i].ripaddr[1]));
|
|
write_char('\n');
|
|
}
|
|
|
|
/* httpd transfer state and the protocol flags */
|
|
print_string("httpd left ");
|
|
print_short(len_left);
|
|
print_string(" entry ");
|
|
print_byte(entry);
|
|
print_string(" stp ");
|
|
print_byte(stp_enabled);
|
|
print_string(" mvlan ");
|
|
print_short(management_vlan);
|
|
write_char('\n');
|
|
}
|
|
#endif
|