*
* This implements logging over an LLC console, where supported.
* Messages are written to the log when sent as bottom_printf (fmt, ...)
- * and will always be prefixed with __FILE__ and __LINE__ information.
- * The implementation is through a bottom_console_printf() call.
- * If the console is not compiled in, there will be no code generated
- * for such calls.
+ * This is passed to the console routines through inline functions, as
+ * not all embedded compilers support varargs in #defines. The functions
+ * should reduce to no code without CONFIG_FUNCTION_DEVEL_NETCONSOLE.
*
- * For portability to all platforms, there must always be at least one
- * argument, even if it is not used. There are ways to work around this
- * constraint, as described on the link below, but relying on it would
- * be asking for trouble when targetting all kinds of embedded compilers.
- * http://blog.mellenthin.de/archives/2010/10/26/portable-__va_args__-macros-for-linux-hp-ux-solaris-and-aix/
+ * The formats are very close to those of printf():
+ * - %s %c %d %x print string or (NULL), char, decimal and hexadecimal
+ * - %p prints a pointer in hexadecimal notation
+ * - %d and %x print unsigned int; %ld and %lx print unsigned long int
+ * - field lengths like %12s attach spaces; %5d and %4x prefix zeroes
*
* From: Rick van Rein <rick@openfortress.nl>
*/
help
Direct console traffic for the main application to the network.
- The protocol used for this network console is Logical Link Control.
- This is an ethernet-level protocol, so it will only work locally.
-
- This console can also be selected as a main function for the
- image being built, to test network connectivity.
-
- TODO: ports
-
- This facility is normally only of use during development.
+ The protocol used for this network console is Logical Link Control
+ or IEEE 802.2, more to the point LLC2 which is a bit like TCP but
+ directly on top of Ethernet. It is only accessible to directly
+ connected computers on the same LAN, so this is very suitable to
+ release console information to a LAN.
+
+ This console can be an add-on module for other targets, or it can
+ be selected as a development target to become the main function for
+ the image being built. In that capacity it is a good test for the
+ network drivers.
+
+ This facility is normally only of use during development, but may
+ nonetheless be compiled into end-user modules.
config FUNCTION_FIRMWARE_UPGRADES
bool "Support firmware upgrades"
depends on DEVEL
select FUNCTION_DEVEL_NETCONSOLE
help
- This test runs a networked console. In addition to the usual console
- functions, respond to any text sent by mapping it to uppercase.
+ This test runs a networked console. The test will print information
+ about incoming network packets and ignore traffic sent to the device.
+
+ Note that the performance is not based on timers, but instead
+ triggered when something is logged. This may lead to delays in the
+ delivery, of bursty sends that cause rejects at the LLC2 level.
+ This is really just a test, rather than a perfect application.
The details of the networked console are described under the
add-on function for this console, one menu level up.
static uint8_t llc_rr [6 + 6 + 2 + 4];
static uint8_t llc_sent;
static uint8_t llc_received;
+static uint8_t llc_input;
/* Dummy LLC2 send routine, ignoring "cnx" as there is just one LLC2 connection.
- * This is out-only, so N(R) always sends as 0x00 and N(S) increments.
* Before sending, the routine will first establish whether the last send
- * was successful; if not, it will repeat that. The return value is true if
- * at least the send was done, relying on future calls to resend if need be.
+ * was successful; if not, it will repeat that. The return value is true if at
+ * least the new send was done, relying on future calls to resend if need be.
*/
uint8_t llc_pkt [100];
uint16_t llc_pktlen;
llc_pkt [14] = peer_sap; // DSAP
llc_pkt [15] = 20; // SSAP
llc_pkt [16] = llc_sent << 1; // N(S) = 0x00, information frame
- llc_pkt [17] = 0x00; // N(R) = sent-up-to-here, low bit reset
+ llc_pkt [17] = llc_input << 1; // N(R) = sent-up-to-here, low bit reset
memcpy (llc_pkt + 18, data, datalen);
llc_pktlen = 6 + 6 + 2 + 4 + datalen;
llc_sent++;
}
#endif
if (typelen > 1500) {
- // bottom_printf ("Traffic is not LLC but protocol 0x%x\n", (unsigned int) typelen);
+ bottom_printf ("Traffic is not LLC but protocol 0x%4x\n", (unsigned int) typelen);
return;
}
if ((typelen > 64) && (typelen != pktlen)) {
if (cmd == 0x007f) { // SABME (llc.connect)
memcpy (peer_mac, pkt + 6, 6);
peer_sap = pkt [15] & 0xfe;
- llc_sent = llc_received = 0x00;
+ llc_sent = llc_received = llc_input = 0x00;
llc_connected = true;
netcons_connect (&llc2_dummy_handle);
ack = true;
} else if ((cmd & 0x0007) == 0x0001) { // Receiver ready / Receiver Reject
llc_received = (cmd >> 9);
} else {
- bottom_printf ("Selfishly ignoring LLC traffic with cmd bytes 0x%x 0x%x\n", (uint32_t) pkt [16], (uint32_t) pkt [17]);
+ bottom_printf ("Selfishly ignoring LLC traffic with cmd bytes 0x%2x%2x\n", (unsigned int) pkt [16], (unsigned int) pkt [17]);
}
if (ack) {
memcpy (llc_ua + 0, peer_mac, 6);
netinputlen = sizeof (netinput);
if (bottom_network_recv (netinput, &netinputlen)) {
if (memcmp (netinput, "\xff\xff\xff\xff\xff\xff", 6) == 0) {
- bottom_printf ("Broadcast!\n");
-#if 0
- bottom_printf ("Broadcast from %x:%x:%x:%x:%x:%x\n",
+ bottom_printf ("Broadcast from %2x:%2x:%2x:%2x:%2x:%2x\n",
(unsigned int) netinput [ 6],
(unsigned int) netinput [ 7],
(unsigned int) netinput [ 8],
(unsigned int) netinput [ 9],
(unsigned int) netinput [10],
(unsigned int) netinput [11]);
-#endif
} else {
bottom_led_set (LED_IDX_BACKLIGHT, LED_STABLE_OFF);
selfish_llc2_handler (netinput, netinputlen);
static char consbuf [CONSBUFLEN];
-static uint16_t rpos = CONSBUFLEN, wpos = 0;
+static uint16_t rpos = 0, wpos = 0;
/******** NETWORK INTERFACE ROUTINES ********/
static const char digits [] = "0123456789abcdef";
static void cons_putchar (char c) {
- // TODO: Handle buffer full conditions
if (wpos >= CONSBUFLEN) {
wpos = 0;
}
+ if (wpos + 1 == rpos) {
+ rpos++;
+ if (rpos >= CONSBUFLEN) {
+ rpos -= CONSBUFLEN;
+ }
+ }
consbuf [wpos++] = c;
}
-static void cons_putint (uint32_t val, uint8_t base, uint8_t minpos) {
- uint32_t divisor = 1;
- while (minpos-- > 0) {
+static void cons_putint (unsigned long int val, uint8_t base, uint8_t minpos) {
+ unsigned long int divisor = 1;
+ while (minpos-- > 1) {
divisor *= base;
}
while (val / base > divisor) {
divisor *= base;
}
- while (divisor > 0) {
+ do {
cons_putchar (digits [(val / divisor)]);
val %= divisor;
divisor /= base;
- }
+ } while (divisor > 0);
}
void bottom_console_vprintf (char *fmt, va_list argh) {
char *fp = fmt;
char *str;
char ch;
- uint32_t intval;
+ unsigned long int intval;
while (*fp) {
+ uint8_t minpos = 0;
+ bool longval = false;
if (*fp != '%') {
cons_putchar (*fp++);
} else {
fp++;
+ moremeuk:
switch (*fp++) {
case '\0':
fp--;
break;
+ case 'l':
+ longval = true;
+ goto moremeuk;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ minpos *= 10;
+ minpos += fp [-1] - '0';
+ goto moremeuk;
case 'c':
ch = va_arg (argh, char);
cons_putchar (ch);
break;
case 's':
str = va_arg (argh, char *);
+ if (str == NULL) {
+ str = "(NULL)";
+ }
while (*str) {
cons_putchar (*str++);
+ if (minpos > 0) {
+ minpos--;
+ }
+ }
+ while (minpos-- > 0) {
+ cons_putchar (' ');
}
break;
case 'd':
- intval = (uint32_t) va_arg (argh, unsigned int);
- cons_putint (intval, 10, 0);
- break;
- case 'l':
- intval = va_arg (argh, uint32_t);
- cons_putint (intval, 10, 0);
+ if (longval) {
+ intval = va_arg (argh, unsigned long int);
+ } else {
+ intval = (unsigned long int) va_arg (argh, unsigned int);
+ }
+ cons_putint (intval, 10, minpos);
break;
case 'p':
- intval = (uint32_t) va_arg (argh, void *);
- cons_putint (intval, 16, 8);
+ intval = (unsigned long int) va_arg (argh, void *);
+ cons_putint (intval, 16, (minpos > 8)? minpos: 8);
break;
case 'x':
- intval = (uint32_t) va_arg (argh, unsigned int);
- cons_putint (intval, 16, 8);
+ if (longval) {
+ intval = va_arg (argh, unsigned long int);
+ } else {
+ intval = (unsigned long int) va_arg (argh, unsigned int);
+ }
+ cons_putint (intval, 16, minpos);
break;
case '%':
default: