1
0
mirror of git://projects.qi-hardware.com/ben-wpan.git synced 2024-11-05 03:24:05 +02:00

tools/dirtpan/: rewritten for simultaneous reception and transmission

Note that the PHY is still half-duplex, but we don't need to insist
on the entire packet reception/transmission to finish before letting
the peer have its say.
This commit is contained in:
Werner Almesberger 2011-05-12 16:26:38 -03:00
parent 707af5f4da
commit 7e2c576f7b

View File

@ -11,6 +11,7 @@
*/ */
#include <stdarg.h>
#include <stdint.h> #include <stdint.h>
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h> #include <stdio.h>
@ -58,18 +59,12 @@ enum packet_type {
#define T_ACK_MS 50 #define T_ACK_MS 50
static enum state {
s_idle,
s_tx,
s_rx,
} state = s_idle;
static int tun, net; static int tun, net;
static uint8_t packet[MAX_PACKET+1]; static uint8_t rx_packet[MAX_PACKET], tx_packet[MAX_PACKET+1];
static void *pos; static void *rx_pos, *tx_pos;
static int left; static int rx_left, tx_left;
static int my_seq = 0, peer_seq; static int txing = 0, rxing = 0;
static int rx_seq, tx_seq = 0;
static int retries; static int retries;
static int debug = 0; static int debug = 0;
@ -79,23 +74,8 @@ static int debug = 0;
static void debug_label(const char *label) static void debug_label(const char *label)
{ {
const char *t; fprintf(stderr, "%s(%c%c)",
label, txing ? 'T' : '-', rxing ? 'R' : '-');
switch (state) {
case s_idle:
t = "--";
break;
case s_tx:
t = "tx";
break;
case s_rx:
t = "rx";
break;
default:
t = "???";
break;
}
fprintf(stderr, "%s(%s)", label, t);
} }
@ -163,36 +143,73 @@ static void debug_timeout(const char *label)
/* ----- Timers ------------------------------------------------------------ */ /* ----- Timers ------------------------------------------------------------ */
static struct timeval t0; static struct timeval t_reass, t_ack;
static void start_timer(void) static void start_timer(struct timeval *t, int ms)
{ {
gettimeofday(&t0, NULL); assert(!t->tv_sec && !t->tv_usec);
gettimeofday(t, NULL);
t->tv_usec += 1000*ms;
while (t->tv_usec >= 1000000) {
t->tv_sec++;
t->tv_usec -= 1000000;
}
} }
static struct timeval *timeout(int ms) static void stop_timer(struct timeval *t)
{ {
static struct timeval t; assert(t->tv_sec || t->tv_usec);
t->tv_sec = 0;
t->tv_usec = 0;
}
gettimeofday(&t, NULL);
t.tv_sec -= t0.tv_sec;
t.tv_usec -= t0.tv_usec;
t.tv_usec += 1000*ms;
while (t.tv_usec < 0) { static const struct timeval *next_timer(int n, ...)
t.tv_sec--; {
t.tv_usec += 1000000; va_list ap;
const struct timeval *next = NULL;
const struct timeval *t;
va_start(ap, n);
while (n--) {
t = va_arg(ap, const struct timeval *);
if (!t->tv_sec && !t->tv_usec)
continue;
if (next) {
if (next->tv_sec < t->tv_sec)
continue;
if (next->tv_sec == t->tv_sec &&
next->tv_usec < t->tv_usec)
continue;
}
next = t;
} }
while (t.tv_usec >= 1000000) { va_end(ap);
t.tv_sec++; return next;
t.tv_usec -= 1000000; }
}
if (t.tv_sec < 0)
t.tv_sec = t.tv_usec = 0;
return &t;
static struct timeval *timer_delta(const struct timeval *t)
{
static struct timeval d;
if (!t)
return NULL;
gettimeofday(&d, NULL);
d.tv_sec = t->tv_sec-d.tv_sec;
d.tv_usec = t->tv_usec-d.tv_usec;
while (d.tv_usec < 0) {
d.tv_sec--;
d.tv_usec += 1000000;
}
if (d.tv_sec < 0)
d.tv_sec = d.tv_usec = 0;
return &d;
} }
@ -201,7 +218,7 @@ static struct timeval *timeout(int ms)
static inline int send_size(void) static inline int send_size(void)
{ {
return left > MAX_FRAG ? MAX_FRAG : left; return tx_left > MAX_FRAG ? MAX_FRAG : tx_left;
} }
@ -228,11 +245,11 @@ static void send_frame(void *buf, int size)
static void send_more(void) static void send_more(void)
{ {
uint8_t *p = pos-1; uint8_t *p = tx_pos-1;
*p = (pos == packet+1 ? pt_first : pt_next) | (my_seq ? SEQ : 0); *p = (tx_pos == tx_packet+1 ? pt_first : pt_next) | (tx_seq ? SEQ : 0);
send_frame(p, send_size()+1); send_frame(p, send_size()+1);
start_timer(); start_timer(&t_ack, T_ACK_MS);
} }
@ -261,75 +278,67 @@ static void rx_pck(void *buf, int size)
type = ctrl & PT_MASK; type = ctrl & PT_MASK;
seq = !!(ctrl & SEQ); seq = !!(ctrl & SEQ);
if (type == pt_first || type == pt_next) switch (type) {
case pt_first:
send_ack(seq); send_ack(seq);
switch (state) { if (rxing) {
case s_tx: stop_timer(&t_reass);
if (type == pt_first) { rxing = 0;
/* }
* @@@ Not optimal - we break the tie but lose a break;
* perfectly good frame. case pt_next:
*/ send_ack(seq);
state = s_idle; if (!rxing)
return;
if (seq == rx_seq)
return; /* retransmission */
break;
case pt_ack:
if (!txing)
return;
if (seq != tx_seq)
return;
stop_timer(&t_ack);
tx_pos += send_size();
tx_left -= send_size();
if (!tx_left) {
txing = 0;
return; return;
} }
if (type != pt_ack) tx_seq = !tx_seq;
return;
if (seq != my_seq)
return;
pos += send_size();
left -= send_size();
if (!left) {
state = s_idle;
return;
}
my_seq = !my_seq;
retries = 0; retries = 0;
send_more(); send_more();
return; return;
case s_rx:
if (type == pt_first) {
start_timer();
state = s_idle;
break;
}
if (type != pt_next)
return;
if (seq == peer_seq)
return; /* retransmission */
goto recv_more;
case s_idle:
if (type == pt_first)
break;
if (type == pt_next)
return;
return;
default: default:
abort(); abort();
} }
if (size < 5) if (!rxing) {
return; if (size < 5)
left = p[3] << 8 | p[4]; return;
if (left > MAX_PACKET+1) rx_left = p[3] << 8 | p[4];
return; if (rx_left > MAX_PACKET)
state = s_rx; return;
pos = packet; start_timer(&t_reass, T_REASS_MS);
rxing = 1;
rx_pos = rx_packet;
}
recv_more: if (rx_left < size-1) {
if (left < size-1) { stop_timer(&t_reass);
state = s_idle; rxing = 0;
return; return;
} }
memcpy(pos, buf+1, size-1); memcpy(rx_pos, buf+1, size-1);
pos += size-1; rx_pos += size-1;
left -= size-1; rx_left -= size-1;
peer_seq = seq; rx_seq = seq;
if (!left) { if (!rx_left) {
debug_ip("<-", packet, pos-(void *) packet); debug_ip("<-", rx_packet, rx_pos-(void *) rx_packet);
write_buf(tun, packet, pos-(void *) packet); write_buf(tun, rx_packet, rx_pos-(void *) rx_packet);
state = s_idle; stop_timer(&t_reass);
rxing = 0;
} }
} }
@ -339,17 +348,17 @@ static void tx_pck(void *buf, int size)
const uint8_t *p = buf; const uint8_t *p = buf;
debug_ip(">-", buf, size); debug_ip(">-", buf, size);
assert(state == s_idle); assert(!txing);
state = s_tx; txing = 1;
pos = packet+1; tx_pos = tx_packet+1;
left = p[2] << 8 | p[3]; tx_left = p[2] << 8 | p[3];
assert(left <= MAX_PACKET); assert(tx_left <= MAX_PACKET);
assert(left == size); assert(tx_left == size);
/* /*
* We could avoid the memcpy by reading directly into "packet" * @@@ We could avoid the memcpy by reading directly into "tx_packet"
*/ */
memcpy(pos, buf, size); memcpy(tx_pos, buf, size);
my_seq = !my_seq; tx_seq = !tx_seq;
retries = 0; retries = 0;
send_more(); send_more();
} }
@ -358,8 +367,10 @@ static void tx_pck(void *buf, int size)
static void ack_timeout(void) static void ack_timeout(void)
{ {
debug_timeout("ACK-TO"); debug_timeout("ACK-TO");
assert(txing);
stop_timer(&t_ack);
if (++retries == MAX_TRIES) if (++retries == MAX_TRIES)
state = s_idle; txing = 0;
else else
send_more(); send_more();
} }
@ -368,7 +379,9 @@ static void ack_timeout(void)
static void reass_timeout(void) static void reass_timeout(void)
{ {
debug_timeout("REASS-TO"); debug_timeout("REASS-TO");
state = s_idle; assert(rxing);
stop_timer(&t_reass);
rxing = 0;
} }
@ -378,43 +391,32 @@ static void reass_timeout(void)
static void event(void) static void event(void)
{ {
uint8_t buf[MAX_PACKET]; uint8_t buf[MAX_PACKET];
struct timeval *to; const struct timeval *to;
fd_set rset; fd_set rset;
int res; int res;
ssize_t got; ssize_t got;
FD_ZERO(&rset); FD_ZERO(&rset);
FD_SET(net, &rset); FD_SET(net, &rset);
switch (state) {
case s_idle: /* only accept more work if we're idle */
if (!txing && !rxing)
FD_SET(tun, &rset); FD_SET(tun, &rset);
to = NULL;
break; to = next_timer(2, &t_reass, &t_ack);
case s_rx:
to = timeout(T_REASS_MS); res = select(net > tun ? net+1 : tun+1, &rset, NULL, NULL,
break; timer_delta(to));
case s_tx:
to = timeout(T_ACK_MS);
break;
default:
abort();
}
res = select(net > tun ? net+1 : tun+1, &rset, NULL, NULL, to);
if (res < 0) { if (res < 0) {
perror("select"); perror("select");
return; return;
} }
if (!res) { if (!res) {
switch (state) { assert(to);
case s_rx: if (to == &t_reass)
reass_timeout(); reass_timeout();
break; else
case s_tx:
ack_timeout(); ack_timeout();
break;
default:
abort();
}
} }
if (FD_ISSET(tun, &rset)) { if (FD_ISSET(tun, &rset)) {
got = read(tun, buf, sizeof(buf)); got = read(tun, buf, sizeof(buf));