289 lines
7.9 KiB
C
289 lines
7.9 KiB
C
// termdemo.c — SecLink terminal emulator demo
|
|
//
|
|
// Uses DVX GUI ANSI terminal widget with SecLink encrypted serial link
|
|
// to provide a BBS terminal over a secured serial connection.
|
|
//
|
|
// Usage: termdemo [com_port] [baud_rate]
|
|
// com_port — 1-4 (default 1)
|
|
// baud_rate — baud rate (default 115200)
|
|
|
|
#include "dvxApp.h"
|
|
#include "dvxWidget.h"
|
|
#include "secLink.h"
|
|
#include "security.h"
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
// ============================================================
|
|
// Constants
|
|
// ============================================================
|
|
|
|
#define CMD_FILE_QUIT 100
|
|
|
|
#define RECV_BUF_SIZE 4096
|
|
|
|
#define CHANNEL_DATA 0
|
|
|
|
// ============================================================
|
|
// Types
|
|
// ============================================================
|
|
|
|
// Bridges secLink receive callback and terminal widget comm interface
|
|
typedef struct {
|
|
SecLinkT *link;
|
|
AppContextT *app;
|
|
WidgetT *term;
|
|
uint8_t recvBuf[RECV_BUF_SIZE];
|
|
int32_t recvHead;
|
|
int32_t recvTail;
|
|
} TermContextT;
|
|
|
|
// ============================================================
|
|
// Prototypes
|
|
// ============================================================
|
|
|
|
static int32_t commRead(void *ctx, uint8_t *buf, int32_t maxLen);
|
|
static int32_t commWrite(void *ctx, const uint8_t *data, int32_t len);
|
|
static void idlePoll(void *ctx);
|
|
static void onCloseCb(WindowT *win);
|
|
static void onMenuCb(WindowT *win, int32_t menuId);
|
|
static void onRecv(void *ctx, const uint8_t *data, int len, uint8_t channel);
|
|
|
|
|
|
// ============================================================
|
|
// commRead — drain receive ring buffer for terminal widget
|
|
// ============================================================
|
|
|
|
static int32_t commRead(void *ctx, uint8_t *buf, int32_t maxLen) {
|
|
TermContextT *tc = (TermContextT *)ctx;
|
|
int32_t count = 0;
|
|
|
|
while (count < maxLen && tc->recvTail != tc->recvHead) {
|
|
buf[count++] = tc->recvBuf[tc->recvTail];
|
|
tc->recvTail = (tc->recvTail + 1) % RECV_BUF_SIZE;
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
|
|
// ============================================================
|
|
// commWrite — send keystrokes via secLink (encrypted, channel 0)
|
|
// ============================================================
|
|
|
|
static int32_t commWrite(void *ctx, const uint8_t *data, int32_t len) {
|
|
TermContextT *tc = (TermContextT *)ctx;
|
|
int rc = secLinkSend(tc->link, data, len, CHANNEL_DATA, true, false);
|
|
|
|
return (rc == SECLINK_SUCCESS) ? len : 0;
|
|
}
|
|
|
|
|
|
// ============================================================
|
|
// idlePoll — poll serial link instead of yielding CPU
|
|
// ============================================================
|
|
|
|
static void idlePoll(void *ctx) {
|
|
TermContextT *tc = (TermContextT *)ctx;
|
|
secLinkPoll(tc->link);
|
|
}
|
|
|
|
|
|
// ============================================================
|
|
// onCloseCb — quit when the terminal window is closed
|
|
// ============================================================
|
|
|
|
static void onCloseCb(WindowT *win) {
|
|
AppContextT *ctx = (AppContextT *)win->userData;
|
|
|
|
if (ctx) {
|
|
dvxQuit(ctx);
|
|
}
|
|
}
|
|
|
|
|
|
// ============================================================
|
|
// onMenuCb — handle menu commands
|
|
// ============================================================
|
|
|
|
static void onMenuCb(WindowT *win, int32_t menuId) {
|
|
AppContextT *ctx = (AppContextT *)win->userData;
|
|
|
|
switch (menuId) {
|
|
case CMD_FILE_QUIT:
|
|
if (ctx) {
|
|
dvxQuit(ctx);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
// ============================================================
|
|
// onRecv — secLink receive callback, fills ring buffer
|
|
// ============================================================
|
|
|
|
static void onRecv(void *ctx, const uint8_t *data, int len, uint8_t channel) {
|
|
(void)channel;
|
|
TermContextT *tc = (TermContextT *)ctx;
|
|
|
|
for (int i = 0; i < len; i++) {
|
|
int32_t next = (tc->recvHead + 1) % RECV_BUF_SIZE;
|
|
|
|
if (next == tc->recvTail) {
|
|
break;
|
|
}
|
|
|
|
tc->recvBuf[tc->recvHead] = data[i];
|
|
tc->recvHead = next;
|
|
}
|
|
}
|
|
|
|
|
|
// ============================================================
|
|
// main
|
|
// ============================================================
|
|
|
|
int main(int argc, char *argv[]) {
|
|
int comPort = 0;
|
|
int32_t baudRate = 115200;
|
|
|
|
// Parse command line
|
|
if (argc >= 2) {
|
|
if (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0) {
|
|
printf("Usage: termdemo [com_port] [baud_rate]\n");
|
|
printf(" com_port -- 1-4 (default 1)\n");
|
|
printf(" baud_rate -- baud rate (default 115200)\n");
|
|
return 0;
|
|
}
|
|
|
|
comPort = atoi(argv[1]) - 1;
|
|
|
|
if (comPort < 0 || comPort > 3) {
|
|
fprintf(stderr, "Invalid COM port (must be 1-4)\n");
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
if (argc >= 3) {
|
|
baudRate = atol(argv[2]);
|
|
|
|
if (baudRate <= 0) {
|
|
fprintf(stderr, "Invalid baud rate\n");
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
printf("SecLink Terminal Demo\n");
|
|
printf("COM%d at %ld 8N1\n\n", comPort + 1, (long)baudRate);
|
|
|
|
// Seed the RNG with hardware entropy
|
|
printf("Gathering entropy...\n");
|
|
uint8_t entropy[16];
|
|
int got = secRngGatherEntropy(entropy, sizeof(entropy));
|
|
secRngSeed(entropy, got);
|
|
|
|
// Initialize terminal context
|
|
TermContextT tc;
|
|
memset(&tc, 0, sizeof(tc));
|
|
|
|
// Open secLink on the specified COM port
|
|
printf("Opening SecLink on COM%d...\n", comPort + 1);
|
|
tc.link = secLinkOpen(comPort, baudRate, 8, 'N', 1, 0, onRecv, &tc);
|
|
|
|
if (!tc.link) {
|
|
fprintf(stderr, "Failed to open SecLink\n");
|
|
return 1;
|
|
}
|
|
|
|
// Perform DH key exchange (blocks until both sides complete)
|
|
printf("Performing key exchange (waiting for remote side)...\n");
|
|
int rc = secLinkHandshake(tc.link);
|
|
|
|
if (rc != SECLINK_SUCCESS) {
|
|
fprintf(stderr, "Handshake failed (%d)\n", rc);
|
|
secLinkClose(tc.link);
|
|
return 1;
|
|
}
|
|
|
|
printf("Secure link established.\n\n");
|
|
|
|
// Initialize DVX GUI
|
|
AppContextT ctx;
|
|
|
|
printf("Initializing video...\n");
|
|
|
|
if (dvxInit(&ctx, 1024, 768, 16) != 0) {
|
|
fprintf(stderr, "Failed to initialize DVX GUI\n");
|
|
secLinkClose(tc.link);
|
|
return 1;
|
|
}
|
|
|
|
tc.app = &ctx;
|
|
|
|
// Create the terminal window
|
|
WindowT *win = dvxCreateWindow(&ctx, "SecLink Terminal", 40, 30, 680, 460, false);
|
|
|
|
if (!win) {
|
|
dvxShutdown(&ctx);
|
|
secLinkClose(tc.link);
|
|
return 1;
|
|
}
|
|
|
|
win->userData = &ctx;
|
|
win->onClose = onCloseCb;
|
|
win->onMenu = onMenuCb;
|
|
|
|
// File menu
|
|
MenuBarT *bar = wmAddMenuBar(win);
|
|
|
|
if (bar) {
|
|
MenuT *fileMenu = wmAddMenu(bar, "File");
|
|
|
|
if (fileMenu) {
|
|
wmAddMenuItem(fileMenu, "Quit", CMD_FILE_QUIT);
|
|
}
|
|
}
|
|
|
|
// Widget tree: terminal + status bar
|
|
WidgetT *root = wgtInitWindow(&ctx, win);
|
|
WidgetT *term = wgtAnsiTerm(root, 80, 25);
|
|
|
|
term->weight = 100;
|
|
wgtAnsiTermSetScrollback(term, 1000);
|
|
tc.term = term;
|
|
|
|
// Connect terminal widget to secLink via comm interface
|
|
wgtAnsiTermSetComm(term, &tc, commRead, commWrite);
|
|
|
|
// Status bar showing connection info
|
|
WidgetT *sb = wgtStatusBar(root);
|
|
char statusText[64];
|
|
snprintf(statusText, sizeof(statusText), "COM%d %ld 8N1 [Encrypted]", comPort + 1, (long)baudRate);
|
|
wgtLabel(sb, statusText);
|
|
|
|
// Fit window to widget tree
|
|
dvxFitWindow(&ctx, win);
|
|
|
|
// Poll serial during idle instead of yielding CPU
|
|
ctx.idleCallback = idlePoll;
|
|
ctx.idleCtx = &tc;
|
|
|
|
// Main loop — poll serial, dvxUpdate handles terminal widget automatically
|
|
while (ctx.running) {
|
|
secLinkPoll(tc.link);
|
|
|
|
if (!dvxUpdate(&ctx)) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Cleanup
|
|
dvxShutdown(&ctx);
|
|
secLinkClose(tc.link);
|
|
|
|
printf("SecLink Terminal Demo ended.\n");
|
|
|
|
return 0;
|
|
}
|