Files
vt1200/harness/udpstream/sendraw.py
T
arti f251672d5d udpstream: payload sends and receives raw Ethernet with its own LANCE driver
lance_bcast.c drives the chip entirely on its own -- init block, PADR/LADRF,
eight TX and eight RX descriptors, INIT/IDON/STRT, TDMD, completion polling --
and test-bcast.sh runs it in MAME and reports:

  TXD=6  RXF=5  ET=0x88B5  source MAC 0xC6FC  ok=1  PASS

The tap capture separates the directions: six 60-byte 0x88b5 broadcasts from
08:00:2b:16:4b:5a (our payload) and 49 from the host. RXF counts host frames
(early stop at five) and the recorded source MAC is the host's.

Fixes behind it: per-frame TX descriptor (the chip advances its own ring
position), cfg-net needed for a tap to exist at all, pkill -x instead of -f,
and two bit-vs-byte addressing bugs.
2026-10-30 16:21:02 +02:00

66 lines
2.4 KiB
Python

#!/usr/bin/env python3
"""sendraw.py -- send raw Ethernet frames from the host.
Used to exercise the VT1200 payload's receive path without a protocol stack:
the payload counts frames and records the last ethertype and source MAC on its
panel, so a single broadcast frame is enough to prove RX works.
# MAME: the bridge floods to the enslaved tap
sudo python3 harness/udpstream/sendraw.py --iface vt1200 --count 3
# real unit: the host's own NIC
sudo python3 harness/udpstream/sendraw.py --iface eth0 --dst ff:ff:ff:ff:ff:ff
Options default to a 60-byte broadcast frame with ethertype 0x88B5, which is the
one the payload's panel shows in its ET row.
"""
import argparse
import socket
import struct
import sys
import time
def parse_mac(s):
parts = s.split(":")
if len(parts) != 6:
raise argparse.ArgumentTypeError("expected aa:bb:cc:dd:ee:ff")
return bytes(int(p, 16) for p in parts)
def main():
ap = argparse.ArgumentParser(description=__doc__,
formatter_class=argparse.RawDescriptionHelpFormatter)
ap.add_argument("--iface", required=True, help="interface to send on")
ap.add_argument("--dst", type=parse_mac, default=parse_mac("ff:ff:ff:ff:ff:ff"),
help="destination MAC (default broadcast)")
ap.add_argument("--src", type=parse_mac, default=None,
help="source MAC (default: the interface's own)")
ap.add_argument("--ethertype", type=lambda v: int(v, 0), default=0x88B5)
ap.add_argument("--size", type=int, default=60, help="frame size from dst onwards")
ap.add_argument("--count", type=int, default=1)
ap.add_argument("--gap", type=float, default=0.2, help="seconds between frames")
args = ap.parse_args()
src = args.src
if src is None:
with open("/sys/class/net/%s/address" % args.iface) as f:
src = parse_mac(f.read().strip())
size = max(args.size, 14)
body = bytes((i & 0xFF) for i in range(size - 14))
frame = args.dst + src + struct.pack(">H", args.ethertype) + body
s = socket.socket(socket.AF_PACKET, socket.SOCK_RAW)
s.bind((args.iface, 0))
for i in range(args.count):
s.send(frame)
print("sent %d B on %s: %s -> %s ethertype 0x%04x"
% (len(frame), args.iface, src.hex(":"), args.dst.hex(":"), args.ethertype))
time.sleep(args.gap)
return 0
if __name__ == "__main__":
sys.exit(main())