Really loved the idea, and played around a little with it to remove dependency from the sockets API, relying only on the more efficient netconn API.
Not sure if I did everything in the correct/best way, but it seems to work
netstream.h
Code: Select all
#ifndef NETSTREAM_H_
#define NETSTREAM_H_
#include "ch.h"
#define _net_stream_data \
_base_sequential_stream_data \
struct netconn * conn; \
struct netbuf * inbuf; \
size_t in_offset;
struct NetStreamVMT {
_base_sequential_stream_methods
};
/**
* @extends BaseSequentialStream
*/
typedef struct {
const struct NetStreamVMT *vmt;_net_stream_data
} NetStream;
#ifdef __cplusplus
extern "C" {
#endif
void nsObjectInit(NetStream *sp);
void nsStart(NetStream *sp, struct netconn * conn);
#ifdef __cplusplus
}
#endif
#endif /* NETSTREAM_H_ */
netstream.c
Code: Select all
#include "netstream.h"
#include "lwip/api.h"
#include "ch.h"
#include "hal.h"
static size_t write(void *ip, const uint8_t *bp, size_t n) {
NetStream *sp = ip;
return netconn_write_partly(sp->conn, bp, n, NETCONN_COPY, NULL);
}
static size_t read(void *ip, uint8_t *bp, size_t n) {
NetStream *sp = ip;
err_t err;
/* If last input buffer was completely consumed, wait for a new packet. */
while (sp->inbuf == NULL) {
/* Wait for new packet. */
err = netconn_recv(sp->conn, &sp->inbuf);
if (err != ERR_OK) {
/* Connection closed (or any other errors). */
return 0;
}
}
netbuf_copy_partial(sp->inbuf, bp, n, sp->in_offset);
sp->in_offset += n;
/* Check if there is more data to read. */
if (sp->in_offset >= netbuf_len(sp->inbuf)) {
n -= (sp->in_offset - netbuf_len(sp->inbuf));
netbuf_delete(sp->inbuf);
sp->in_offset = 0;
sp->inbuf = NULL;
}
return n;
}
static msg_t put(void *ip, uint8_t b) {
return (write(ip, &b, 1) == 1 ? Q_OK : Q_RESET);
}
static msg_t get(void *ip) {
uint8_t b;
return (read(ip, &b, 1) == 1 ? b : Q_RESET);
}
static const struct NetStreamVMT vmt = { write, read, put, get };
void nsObjectInit(NetStream *sp) {
sp->vmt = &vmt;
sp->inbuf = NULL;
sp->in_offset = 0;
}
void nsStart(NetStream *sp, struct netconn * conn) {
sp->conn = conn;
}
Then I can spawn a shell for each connection like this:
Code: Select all
/*
* TCP server thread.
*/
static msg_t server_thread(void *arg) {
uint16_t port = *((uint16_t *) arg);
struct netconn *conn, *newconn;
err_t err;
chRegSetThreadName("server");
/* Create a new TCP connection handle */
conn = netconn_new(NETCONN_TCP);
LWIP_ERROR("TCP server: invalid conn", (conn != NULL), return RDY_RESET;);
/* Bind to a port. */
netconn_bind(conn, NULL, port);
/* Listen for connections. */
netconn_listen(conn);
while (TRUE) {
err = netconn_accept(conn, &newconn);
if (err != ERR_OK)
continue;
/* Dynamic allocation to allow multiple shell instances. */
NetStream * nsp = chHeapAlloc(NULL, sizeof(NetStream));
ShellConfig * shell_cfgp = chHeapAlloc(NULL, sizeof(ShellConfig));
if(nsp && shell_cfgp) {
nsObjectInit(nsp);
nsStart(nsp, newconn);
shell_cfgp->sc_channel = (BaseSequentialStream *) nsp;
shell_cfgp->sc_commands = commands;
shellCreate(shell_cfgp, SHELL_WA_SIZE, NORMALPRIO - 1);
}
}
return RDY_OK;
}
Simple and impressive!