--- /dev/null
+
+CC=sdcc
+LD=sdcc
+CFLAGS=-DDEBUG
+
+all: el.hex
+
+reg51.h: reg51sdcc.h
+
+netdev.h: types.h
+
+dp8390.h: types.h reg51.h netdev.h
+
+dp8390.rel: dp8390.c dp8390.h
+ $(CC) $(CFLAGS) -c dp8390.c
+
+eth.rel: eth.c eth.h netdev.h types.h
+ $(CC) $(CFLAGS) -c eth.c
+
+ip.rel: ip.c ip.h netdev.h types.h
+ $(CC) $(CFLAGS) -c ip.c
+
+udp.rel: udp.c udp.h ip.h netdev.h types.h
+ $(CC) $(CFLAGS) -c udp.c
+
+main.rel: main.c netdev.h eth.h ip.h udp.h
+ $(CC) $(CFLAGS) -c main.c
+
+el.hex: main.rel dp8390.rel eth.rel ip.rel udp.rel
+ $(LD) $(LDFLAGS) -o el.hex main.rel dp8390.rel eth.rel ip.rel udp.rel
+
+clean:
+ @rm -vf el.hex *.rel *.asm *.lst *.map *.lnk *.mem *.sym
+
+.PHONY: clean
--- /dev/null
+// vim: set et sw=4 sts=4 :
+
+#ifndef _CONFIG_H_
+#define _CONFIG_H_
+
+#define NET_RECV_CALLBACK eth_recv
+
+#define
+
+#endif // _CONFIG_H_
--- /dev/null
+// vim: set et sw=4 sts=4 :
+
+#ifndef _DEBUG_H_
+#define _DEBUG_H_
+
+#ifdef DEBUG
+
+#include "types.h"
+#include "leds.h"
+
+#define sleep(t) \
+ do \
+ { \
+ uint16 i; \
+ byte j; \
+ for (i = 0; i < 0xffff; ++i) \
+ for (j = 0; j < t; ++j); \
+ } \
+ while (0)
+
+#define print(w) \
+ do \
+ { \
+ leds(w); \
+ sleep(8); \
+ } \
+ while (0)
+
+#define printb(bh, bl) \
+ do \
+ { \
+ ledsb(bh, bl); \
+ sleep(8); \
+ } \
+ while (0)
+
+#else // NO DEBUG
+
+#define sleep(t) ;
+#define print(w) ;
+#define printb(b1, b2) ;
+
+#endif // DEBUG
+
+#endif // _DEBUG_H_
--- /dev/null
+// vim: set et sw=4 sts=4 :
+
+#include "debug.h"
+#include "eth.h"
+#include "dp8390.h"
+
+/** Tamaño del frame */
+byte netdev_len;
+
+// Próximo frame a obtener
+static struct
+{
+ byte next_buf;
+ byte curr_buf;
+ byte curr_off;
+}
+recv_state;
+
+/// Tamaño de la cabecera de los buffers de la placa de red
+#define BUF_HDR_SIZE 4
+
+/// Cambia de página sin modificar los demás bits del CR
+#define SELECT_REG_PAGE(page) \
+ do \
+ { \
+ write_reg(CR, read_reg(CR) & ~(PS1 | PS0)); \
+ write_reg(CR, read_reg(CR) | (page << 6)); \
+ } \
+ while (0)
+
+/// Aborta (o completa) el DMA limpiando el ISR
+#define ABORT_DMA(flags) \
+ do \
+ { \
+ write_reg(CR, flags); \
+ write_reg(ISR, RDC); \
+ } \
+ while (0)
+
+
+static void write_reg(unsigned char reg, unsigned char wr_data)
+{
+ // Select register address.
+ ADDR_PORT &= ~ADDR_PORT_MASK;
+ ADDR_PORT |= reg;
+
+ // Output register data to port.
+ DATA_PORT = wr_data;
+
+ // Clock register data into RTL8019AS.
+ // IOR & IOW are both active low.
+ NICE = 0;
+ IOW = 0;
+ IOW = 1;
+ NICE = 1;
+
+ // Set register data port as input again.
+ DATA_PORT = DATA_PORT_MASK;
+}
+
+
+static unsigned char read_reg(unsigned char reg)
+{
+ // Select register address.
+ ADDR_PORT &= ~ADDR_PORT_MASK;
+ ADDR_PORT |= reg;
+
+ // Enable register data output from RTL8019AS.
+ NICE = 0;
+ IOR = 0;
+
+ // Read register data from port.
+ reg = DATA_PORT;
+
+ // Disable register data output from RTL8019AS.
+ IOR = 1;
+ NICE = 1;
+
+ return reg;
+}
+
+/** Resetea placa de red en caso de buffer overflow */
+static void reset()
+{
+ bit retransmit = read_reg(CR) & TXP;
+
+ // If the receive buffer ring has overflowed we dump the whole
+ // thing and start over. There is no way of knowing whether the
+ // data it contains is uncorrupted, or will cause us grief.
+
+ // Stop RTL8019AS and abort DMA operation.
+ write_reg(CR, STOP);
+
+ // Wait for controller to halt after any current tx completes.
+ while(!(read_reg(ISR) & RST)) continue;
+
+ // Reset remote byte count registers.
+ write_reg(RBCR0, 0x00);
+ write_reg(RBCR1, 0x00);
+
+ // Check whether currently transmitting a packet.
+ if(retransmit)
+ {
+ // If neither a successful transmission nor a tx abort error
+ // has occured, then flag current tx packet for resend.
+ if(read_reg(ISR) & (PTX | TXE))
+ {
+ retransmit = 0;
+ }
+ }
+
+ // Set transmit configuration register to loopback internally.
+ write_reg(TCR, MODE1);
+
+ // Restart the RTL8019AS.
+ write_reg(CR, START);
+
+ // Re-initialise last receive buffer read pointer.
+ write_reg(BNRY, RX_PAGE_START);
+
+ // Select RTL8019AS register page 1.
+ SELECT_REG_PAGE(1);
+
+ // Re-initialise current packet receive buffer page pointer.
+ write_reg(CURR, RX_PAGE_START + 1);
+
+ // Select RTL8019AS register page 0.
+ SELECT_REG_PAGE(0);
+
+ // Clear rx buffer overflow & packet received interrupt flags.
+ write_reg(ISR, PRX | OVW);
+
+ // Re-itialise transmit configuration reg for normal operation.
+ write_reg(TCR, MODE0);
+
+ if(retransmit)
+ {
+ // Retransmit packet in RTL8019AS local tx buffer.
+ write_reg(CR, START | TXP);
+ }
+}
+
+
+/** Inicializa dispositivo de red
+ * @return true si se inicializó correctamente, false si no
+ */
+bool netdev_init()
+{
+ byte i;
+
+ // Set IOR & IOW as they're active low.
+ IOR = 1;
+ IOW = 1;
+ NICE = 1;
+
+ // Set register data port as input.
+ DATA_PORT = DATA_PORT_MASK;
+
+ // Configure RTL8019AS ethernet controller.
+
+ // Keil startup code takes 4ms to execute (18.432MHz, X1 mode).
+ // That leaves plenty of time for the RTL8019AS to read it's
+ // configuration in from the 9346 EEPROM before we get here.
+
+ // Select RTL8019AS register page 0.
+ SELECT_REG_PAGE(0);
+
+ // Check if RTL8019AS fully reset.
+ if(!(read_reg(ISR) & RST))
+ {
+ return 0;
+ }
+
+ // Stop RTL8019AS, select page 0 and abort DMA operation.
+ write_reg(CR, STOP);
+
+ // Initialise data configuration register.
+ // FIFO threshold 8 bytes, no loopback, don't use auto send packet.
+ write_reg(DCR, FT1 | LS);
+
+ // Reset remote byte count registers.
+ write_reg(RBCR0, 0u);
+ write_reg(RBCR1, 0u);
+
+ // Receive configuration register to monitor mode.
+ write_reg(RCR, MON);
+
+ // Initialise transmit configuration register to loopback internally.
+ write_reg(TCR, MODE1);
+
+ // Clear interrupt status register bits by writing 1 to each.
+ write_reg(ISR, ALL);
+
+ // Mask all interrupts in mask register.
+ write_reg(IMR, NONE);
+
+ // Obtengo MAC de la placa
+ write_reg(RBCR0, 12u); // Vamos a leer 12 bytes (2 x 6)
+ write_reg(RBCR1, 0u);
+ write_reg(RSAR0, 0u); // En la dirección 0x0000
+ write_reg(RSAR1, 0u);
+ write_reg(CR, READ); // Comienza lectura
+ for (i = 0; i < ETH_ADDR_SIZE; ++i)
+ {
+ eth_addr_local[i] = read_reg(RDMA);
+ read_reg(RDMA); // Ignoramos porque viene como un word
+ }
+
+ // Abort/ complete DMA operation.
+ ABORT_DMA(STOP);
+
+ // Set transmit page start.
+ write_reg(TPSR, TX_PAGE_START);
+
+ // Set receive buffer page start.
+ write_reg(PSTART, RX_PAGE_START);
+
+ // Initialise last receive buffer read pointer.
+ write_reg(BNRY, RX_PAGE_START);
+
+ // Set receive buffer page stop.
+ write_reg(PSTOP, RX_PAGE_STOP);
+
+ // Select RTL8019AS register page 1.
+ SELECT_REG_PAGE(1);
+
+ // Initialise current packet receive buffer page pointer
+ write_reg(CURR, RX_PAGE_START + 1);
+
+ // Set physical address
+ for (i = 0; i < ETH_ADDR_SIZE; ++i)
+ write_reg(PAR_BASE + i, eth_addr_local[i]);
+
+ // Restart RTL8019AS.
+ write_reg(CR, START);
+
+ // Initialise transmit configuration register for normal operation.
+ write_reg(TCR, MODE0);
+
+ // Receive configuration register to accept broadcast packets.
+ write_reg(RCR, AB);
+
+ return 1;
+}
+
+
+/** Comienza el envío de un nuevo frame */
+void netdev_send_start()
+{
+ // Wait until pending transmit operation completes.
+ while (read_reg(CR) & TXP) continue;
+ write_reg(ISR, PTX); // Limpio bit de interrupción
+
+ // Set remote DMA start address registers to indicate where to load packet.
+ write_reg(RSAR0, 0u);
+ write_reg(RSAR1, TX_PAGE_START);
+}
+
+/** Finaliza el envío del frame
+ * @precond netdev_send_start() debe haber sido ejecutada
+ * @precond se copiaron datos al dispositivo para enviar
+ * @param len Cantidad de bytes a transmitir
+ */
+void netdev_send_end(byte len)
+{
+ // Set transmit page start to indicate packet start.
+ write_reg(TPSR, TX_PAGE_START);
+
+ // Ethernet packets must be > 60 bytes, otherwise are rejected as runts.
+ if (len < MIN_PACKET_LEN)
+ len = MIN_PACKET_LEN;
+
+ // Set transmit byte count registers to indicate packet length.
+ write_reg(TBCR0, len);
+ write_reg(TBCR1, 0u);
+
+ // Issue command for RTL8019AS to transmit packet from it's local buffer.
+ write_reg(CR, START | TXP);
+}
+
+void netdev_write_start(byte len)
+{
+ // Set remote DMA byte count registers to indicate length of packet load.
+ write_reg(RBCR0, len);
+ write_reg(RBCR1, 0u);
+
+ // Initiate DMA transfer of uip_buf & uip_appdata buffers to RTL8019AS.
+ write_reg(CR, WRITE);
+}
+
+void netdev_write_start_at(byte offset, byte len)
+{
+ // Set remote DMA start address registers to packet data.
+ write_reg(RSAR0, offset);
+ write_reg(RSAR1, TX_PAGE_START);
+
+ // Set remote DMA byte count registers to indicate length of packet load.
+ write_reg(RBCR0, len);
+ write_reg(RBCR1, 0u);
+
+ // Initiate DMA transfer of uip_buf & uip_appdata buffers to RTL8019AS.
+ write_reg(CR, WRITE);
+}
+
+/** Escribe un byte al buffer de la placa de red para ser enviado
+ * @precond netdev_send_start() debe haber sido ejecutada
+ * @param b Byte a enviar
+ */
+void netdev_write_byte(byte b)
+{
+ write_reg(RDMA, b);
+}
+
+/** Escribe un word al buffer de la placa de red para ser enviado
+ * @precond netdev_send_start() debe haber sido ejecutada
+ * @param w Word a enviar
+ */
+void netdev_write_word(uint16 w)
+{
+ write_reg(RDMA, HIGH(w));
+ write_reg(RDMA, LOW(w));
+}
+
+void netdev_write_end()
+{
+ // Abort/ complete DMA operation.
+ ABORT_DMA(START);
+}
+
+/** Comienza la lectura de un nuevo frame
+ * @return Cantidad de bytes a recibir
+ */
+byte netdev_recv_start()
+{
+ // Check if the rx buffer has overflowed.
+ if (read_reg(ISR) & OVW)
+ {
+ byte current;
+
+ SELECT_REG_PAGE(1);
+ current = read_reg(CURR);
+ SELECT_REG_PAGE(0);
+
+ // Hack: a veces reporta mal el flag de OVW, así que verificamos que
+ // relamente haya habido overflow.
+ if (read_reg(BNRY) == current)
+ {
+ printb(read_reg(ISR), 0x01);
+ printb(read_reg(BNRY), 0x02);
+ printb(current, 0x04);
+ printb(0x00, 0x00);
+ reset();
+ }
+ return 0;
+ }
+ // Check if there is a packet in the rx buffer.
+ else if (read_reg(ISR) & PRX)
+ {
+ byte status;
+ byte len;
+ byte current;
+
+ // Retrieve packet header. (status, next_ptr, length_l, length_h)
+
+ // Obtiene el buffer a leer actualmente
+ recv_state.curr_buf = read_reg(BNRY) + 1;
+ if (recv_state.curr_buf >= RX_PAGE_STOP)
+ recv_state.curr_buf = RX_PAGE_START;
+
+ // Select RTL8019AS register page 1.
+ SELECT_REG_PAGE(1);
+
+ // Retrieve current receive buffer page
+ current = read_reg(CURR);
+
+ // Select RTL8019AS register page 1.
+ SELECT_REG_PAGE(0);
+
+ // Check if last packet has been removed from rx buffer.
+ if(recv_state.curr_buf == current)
+ {
+ // Clear packet received interrupt flag.
+ write_reg(ISR, PRX | RXE);
+ return 0;
+ }
+
+ // Set remote DMA byte count registers to packet header length.
+ recv_state.curr_off = 0;
+ netdev_read_start(BUF_HDR_SIZE);
+
+ // Packet status.
+ status = netdev_read_byte();
+
+ // Save next packet pointer.
+ recv_state.next_buf = netdev_read_byte() - 1;
+
+ // Retrieve packet data length and subtract CRC bytes.
+ len = netdev_read_byte() - BUF_HDR_SIZE;
+
+ // Si es muy grande, muy chico o hubo error, lo descartamos
+ if ((len < MIN_PACKET_LEN) || (len > MAX_PACKET_LEN)
+ || ((status & 0x0F) != RXSOK)
+ || netdev_read_byte()) // Parte alta del tamaño
+ {
+ // Terminamos DMA y pasamos al próximo frame
+ netdev_read_end();
+ write_reg(BNRY, recv_state.next_buf);
+ return 0;
+ }
+
+ // Abort/ complete DMA operation.
+ netdev_read_end();
+
+ return len;
+ }
+
+ return 0;
+}
+
+/** Finaliza la recepción del frame
+ * @precond netdev_recv_start() debe haber sido ejecutada
+ */
+void netdev_recv_end()
+{
+ // Pasa el próximo frame
+ write_reg(BNRY, recv_state.next_buf);
+}
+
+void netdev_read_start(byte len)
+{
+ // Set remote DMA start address registers to packet data.
+ write_reg(RSAR0, recv_state.curr_off);
+ write_reg(RSAR1, recv_state.curr_buf);
+ recv_state.curr_off += len;
+
+ // Set remote DMA byte count registers to packet data length.
+ write_reg(RBCR0, len);
+ write_reg(RBCR1, 0);
+
+ // Initiate DMA transfer of packet data.
+ write_reg(CR, READ);
+}
+
+/** Lee un byte del buffer de la placa de red
+ * @precond netdev_recv_start() debe haber sido ejecutada
+ */
+byte netdev_read_byte()
+{
+ return read_reg(RDMA);
+}
+
+/** Lee un word del buffer de la placa de red
+ * @precond netdev_recv_start() debe haber sido ejecutada
+ */
+uint16 netdev_read_word()
+{
+ uint16 w = read_reg(RDMA) << 8;
+ return w + read_reg(RDMA);
+}
+
+/** Finaliza la lectura del frame
+ * @precond netdev_recv_start() debe haber sido ejecutada
+ */
+void netdev_read_end()
+{
+ // Completa DMA
+ ABORT_DMA(START);
+}
+
--- /dev/null
+// vim: set et sw=4 sts=4 :
+
+#ifndef _DP8390_H_
+#define _DP8390_H_
+
+#include "reg51.h"
+#include "types.h"
+#include "netdev.h"
+
+// Configuración de puertos para comunicarse con la placa de red
+#define DATA_PORT P2 // Adjust this to suit hardware.
+#define DATA_PORT_MASK 0xFF // Máscara para leer del puerto
+#define ADDR_PORT P1 // Adjust this to suit hardware.
+#define ADDR_PORT_MASK 0x1F // Máscara de direcciones (para no cambiar
+ // bits que no se usan)
+#ifdef SDCC
+sbit at 0xB4 IOW; // ISA slot pin B13, RTL8019AS pin 30, active low
+sbit at 0xB5 IOR; // ISA slot pin B14, RTL8019AS pin 29, active low
+sbit at 0xB2 NICE; // A7, usado para activar placa de red
+#else
+#define CTRL_PORT P3 // Adjust this to suit hardware.
+sbit IOW = CTRL_PORT^4; // ISA slot pin B13, RTL8019AS pin 30, active low
+sbit IOR = CTRL_PORT^5; // ISA slot pin B14, RTL8019AS pin 29, active low
+sbit NICE = CTRL_PORT^2; // A7, usado para activar placa de red
+#endif
+
+// Límites de tamaño de paquete
+#define MIN_PACKET_LEN 60u // Mínimo permitido por 802.3
+#define MAX_PACKET_LEN 128u // Mínimo permitido por nuestra escasa memoria =)
+
+// Configuración de paǵinas de buffers
+#define TX_PAGE_START 0x40 // 0x4000 Tx buffer: 256 bytes (usamos 128)
+#define RX_PAGE_START 0x41 // 0x4600 Rx buffer: 31 * 256 = 7936 bytes
+#define RX_PAGE_STOP 0x60 // 0x6000
+
+// Register base address
+#define REG_BASE 0x0000 // Hardwired to 0x0300
+
+// Registers common to all pages.
+#define CR REG_BASE + 0x00 // Control register
+ // Control register bits
+ #define PS1 0x80 // Page select bit 1
+ #define PS0 0x40 // Page select bit 0
+ #define RD2 0x20 // Remote DMA control bit 2
+ #define RD1 0x10 // Remote DMA control bit 1
+ #define RD0 0x08 // Remote DMA control bit 0
+ #define TXP 0x04 // Transmit packet bit
+ #define STA 0x02 // Start bit (a flag only)
+ #define STP 0x01 // Stop bit transceiver ctrl
+ // Shortcuts
+ #define PAGE0 0x00 // Page 0
+ #define PAGE1 PS0 // Page 1
+ #define PAGE2 PS1 // Page 2
+ #define PAGE3 (PS0 | PS1) // Page 3 (Reserved!)
+ #define ABORT RD2 // Abort/Complete DMA
+ #define READ (RD0 | STA) // Remote Read
+ #define WRITE (RD1 | STA) // Remote Write
+ #define SENDPKT (RD0 | RD1 | STA) // Send Packet
+ #define STOP (ABORT | STP) // Para la placa de red
+ #define START (ABORT | STA) // Inicia la placa de red
+
+#define RDMA 0x10 // Remote DMA port
+#define RESET 0x18 // Reset port
+
+// Page 0 read/write registers.
+#define BNRY REG_BASE + 0x03 // Boundary register
+#define ISR REG_BASE + 0x07 // Interrupt status register
+ // Interrupt status register bits
+ #define RST 0x80 // Reset state indicator bit
+ #define RDC 0x40 // Remote DMA complete bit
+ #define CNT 0x20 // Network tally counter MSB set
+ #define OVW 0x10 // Receive buffer exhausted
+ #define TXE 0x08 // Transmit abort error bit
+ #define RXE 0x04 // Receive error report bit
+ #define PTX 0x02 // Successful packet transmit
+ #define PRX 0x01 // Successful packet receive
+ // Shortcuts
+ #define ALL 0xFF // Todos los bits
+ #define NONE 0x00 // Ninguno
+
+// Page 0 read only registers.
+#define CLDA0 REG_BASE + 0x01
+#define CLDA1 REG_BASE + 0x02
+#define TSR REG_BASE + 0x04
+#define NCR REG_BASE + 0x05
+#define FIFO REG_BASE + 0x06
+#define CRDA0 REG_BASE + 0x08
+#define CRDA1 REG_BASE + 0x09
+#define CONFIGA REG_BASE + 0x0A
+#define CONFIGB REG_BASE + 0x0B
+#define RSR REG_BASE + 0x0C
+#define CNTR0 REG_BASE + 0x0D
+#define CNTR1 REG_BASE + 0x0E
+#define CNTR2 REG_BASE + 0x0F
+
+// Page 0 write only registers.
+#define PSTART REG_BASE + 0x01 // Receive page start register
+#define PSTOP REG_BASE + 0x02 // Receive page stop register
+#define TPSR REG_BASE + 0x04 // Transmit page start register
+#define TBCR0 REG_BASE + 0x05 // Transmit byte count register 0
+#define TBCR1 REG_BASE + 0x06 // Transmit byte count register 1
+#define RSAR0 REG_BASE + 0x08 // Remote start address register 0
+#define RSAR1 REG_BASE + 0x09 // Remote start address register 0
+#define RBCR0 REG_BASE + 0x0A // Remote byte count register 0
+#define RBCR1 REG_BASE + 0x0B // Remote byte count register 1
+#define RCR REG_BASE + 0x0C // Receive configuration register
+ // Receive configuration register bits (write in page 0, read in page 2)
+ #define MON 0x20 // Monitor mode select bit
+ #define PRO 0x10 // Promiscuous mode select bit
+ #define AM 0x08 // Multicast packet accept bit
+ #define AB 0x04 // Broadcast packet accept bit
+ #define AR 0x02 // Runt packet accept bit
+ #define SEP 0x01 // Error packet accept bit
+#define TCR REG_BASE + 0x0D // Transmit configuration register
+ // Transmit configuration register bits
+ #define OFST 0x10 // Collision offset enable bit
+ #define ATD 0x08 // Auto transmit disable select bit
+ #define LB1 0x04 // Loopback mode select bit 1
+ #define LB0 0x02 // Loopback mode select bit 0
+ #define CRC 0x01 // CRC generation inhibit bit
+ // Shortcuts
+ #define MODE0 0x00 // Loopback mode 0
+ #define MODE1 LB0 // Loopback mode 1
+ #define MODE2 LB1 // Loopback mode 2
+ #define MODE3 (LB0 | LB1) // Loopback mode 3
+#define DCR REG_BASE + 0x0E // Data configuration register
+ // Data configuration register bits (write in page 0, read in page 2)
+ #define FT1 0x40 // FIFO threshold select bit 1
+ #define FT0 0x20 // FIFO threshold select bit 0
+ #define ARM 0x10 // Auto-initialise remote
+ #define LS 0x08 // Loopback select bit
+ #define LAS 0x04 // Set to 0 (pwrup = 1)
+ #define BOS 0x02 // Byte order select bit
+ #define WTS 0x01 // Word transfer select bit
+#define IMR REG_BASE + 0x0F // Interrupt mask register
+ // Interrupt mask register bits
+ // Each enable bit correspons with an interrupt flag in ISR
+
+// Page 1 read/write registers.
+#define PAR_BASE REG_BASE + 0x01 // Physical address register base address
+#define PAR0 REG_BASE + 0x01 // Physical address register 0
+#define PAR1 REG_BASE + 0x02 // Physical address register 1
+#define PAR2 REG_BASE + 0x03 // Physical address register 2
+#define PAR3 REG_BASE + 0x04 // Physical address register 3
+#define PAR4 REG_BASE + 0x05 // Physical address register 4
+#define PAR5 REG_BASE + 0x06 // Physical address register 5
+#define CURR REG_BASE + 0x07 // Current receive buffer page
+#define MAR0 REG_BASE + 0x08
+#define MAR1 REG_BASE + 0x09
+#define MAR2 REG_BASE + 0x0A
+#define MAR3 REG_BASE + 0x0B
+#define MAR4 REG_BASE + 0x0C
+#define MAR5 REG_BASE + 0x0D
+#define MAR6 REG_BASE + 0x0E
+#define MAR7 REG_BASE + 0x0F
+
+// Page 2 read only registers.
+// Each previously defined in page 0 write only.
+//#define PSTART REG_BASE + 0x01
+//#define PSTOP REG_BASE + 0x02
+//#define TPSR REG_BASE + 0x04
+//#define RCR REG_BASE + 0x0C
+//#define TCR REG_BASE + 0x0D
+//#define DCR REG_BASE + 0x0E
+//#define IMR REG_BASE + 0x0F
+
+// Page 3 read/write registers.
+#define _9346CR REG_BASE + 0x01 // 9346 EEPROM command register
+ // 9346 EEPROM command register bits
+ #define EEM1 0x80 // RTL8019AS operating mode bit 1
+ #define EEM0 0x40 // RTL8019AS operating mode bit 0
+ #define EECS 0x08 // 9346 EEPROM chip select bit
+ #define EESK 0x04 // 9346 EEPROM serial clock bit
+ #define EEDI 0x02 // 9346 EEPROM data input bit
+ #define EEDO 0x01 // 9346 EEPROM data output bit
+#define BPAGE REG_BASE + 0x02
+#define CONFIG1 REG_BASE + 0x04 // RTL9019AS config register 1
+ // RTL9019AS config register 1 bits
+ #define IRQEN 0x80 // IRQ enable bit (WR protected)
+ #define IRQS2 0x40 // IRQ line select bit 2
+ #define IRQS1 0x20 // IRQ line select bit 1
+ #define IRQS0 0x10 // IRQ line select bit 0
+ #define IOS3 0x08 // I/O base address select bit 3
+ #define IOS2 0x04 // I/O base address select bit 2
+ #define IOS1 0x02 // I/O base address select bit 1
+ #define IOS0 0x01 // I/O base address select bit 0
+#define CONFIG2 REG_BASE + 0x05 //
+ // RTL9019AS config register 2 bits
+ #define PL1 0x80 // Network medium type select bit 1
+ #define PL0 0x40 // Network medium type select bit 0
+ #define BSELB 0x20 // Boot ROM disable (WR protected)
+ #define BS4 0x10 // Boot ROM configuration bit 4
+ #define BS3 0x08 // Boot ROM configuration bit 3
+ #define BS2 0x04 // Boot ROM configuration bit 2
+ #define BS1 0x02 // Boot ROM configuration bit 1
+ #define BS0 0x01 // Boot ROM configuration bit 0
+#define CONFIG3 REG_BASE + 0x06 // RTL9019AS config register 3
+ // RTL9019AS config register 3 bits
+ #define PNP 0x80 // Plug & play mode indicator bit
+ #define FUDUP 0x40 // Full duplex mode select bit
+ #define LEDS1 0x20 // LED output select bit 1
+ #define LEDS0 0x10 // LED output select bit 0
+ #define SLEEP 0x04 // Sleep mode select bit
+ #define PWRDN 0x02 // Power down mode select bit
+ #define ACTIVEB 0x01 // Inverse of bit 0, PNP active reg
+
+// Page 3 read only registers.
+#define CONFIG0 REG_BASE + 0x03 // RTL9019AS config register 0
+ // RTL9019AS config register 0 bits
+ #define VERID1 0x80 // RTL9019AS version ID bit 1 (R/W)
+ #define VERID0 0x40 // RTL9019AS version ID bit 0 (R/W)
+ #define AUI 0x20 // AUI input pin state bit
+ #define PNPJP 0x10 // PNP jumper pin state bit
+ #define JP 0x08 // JP input pin state bit
+ #define BNC 0x04 // Thinnet mode indication bit
+#define CSNSAV REG_BASE + 0x08
+#define INTR REG_BASE + 0x0B
+#define CONFIG4 REG_BASE + 0x0D
+
+// Page 3 write only registers.
+#define TEST REG_BASE + 0x07
+#define HLTCLK REG_BASE + 0x09
+#define FMWP REG_BASE + 0x0C
+
+
+// Bits del byte de status del frame recibido
+#define RXSOK 0x01 /* Received a good packet */
+#define RXSCRC 0x02 /* CRC error */
+#define RXSFAE 0x04 /* frame alignment error */
+#define RXSFO 0x08 /* FIFO overrun */
+#define RXSMPA 0x10 /* missed pkt */
+#define RXSPHY 0x20 /* physical/multicast address */
+#define RXSDIS 0x40 /* receiver disable. set in monitor mode */
+#define RXSDEF 0x80 /* deferring */
+
+#endif
--- /dev/null
+// vim: set et sw=4 sts=4 :
+
+#include "debug.h"
+#include "netdev.h"
+#include "eth.h"
+
+/** tipos de paquetes transportados soportados */
+enum { IP = 0x0800, ARP = 0x0806 };
+
+byte eth_addr_local[ETH_ADDR_SIZE];
+
+byte eth_addr_remote[ETH_ADDR_SIZE];
+
+eth_proto_t eth_proto;
+
+bool eth_read_frame_header()
+{
+ /* variable para iterar */
+ byte i;
+ netdev_read_start(ETH_HEADER_SIZE);
+ /* descarto MAC de destino, acepto broadcasts */
+ for (i = 0; i < ETH_ADDR_SIZE; ++i)
+ netdev_read_byte();
+ /* obtenemos MAC de origen */
+ for (i = 0; i < ETH_ADDR_SIZE; ++i)
+ eth_addr_remote[i] = netdev_read_byte();
+ /* obtenemos tipo de protocolo transportado por el frame, (sólo
+ * aceptamos IP y ARP) */
+ switch (netdev_read_word())
+ {
+ case IP:
+ eth_proto = ETH_IP;
+ break;
+ case ARP:
+ eth_proto = ETH_ARP;
+ break;
+ default:
+ netdev_read_end();
+ return false; /* drop */
+ }
+ netdev_read_end();
+ return true;
+}
+
+void eth_write_frame_header()
+{
+ /* variable para iterar */
+ byte i;
+ netdev_write_start(ETH_HEADER_SIZE);
+ /* mandamos como MAC de destino la remota */
+ for (i = 0; i < ETH_ADDR_SIZE; ++i)
+ netdev_write_byte(eth_addr_remote[i]);
+ /* y como fuente la nuestra */
+ for (i = 0; i < ETH_ADDR_SIZE; ++i)
+ netdev_write_byte(eth_addr_local[i]);
+ /* escribimos el tipo de paquete que transporta el frame */
+ netdev_write_word((eth_proto == ETH_IP) ? IP : ARP);
+ netdev_write_end();
+}
+
--- /dev/null
+// vim: set et sw=4 sts=4 :
+
+#ifndef _ETH_H_
+#define _ETH_H_
+
+#include "types.h"
+
+/** @file
+ * Estructura de un frame ethernet.
+ *
+ * <pre>
+ * /--- 8 ----/--- 6 ----/--- 6 ----/- 2 --/- 46-1500 -/- 4 -/ bytes
+ * +----------+----------+----------+------+-----------+-----+
+ * | preamble | dst addr | src addr | type | data | crc |
+ * +----------+----------+----------+------+-----------+-----+
+ * </pre>
+ *
+ * type es 0x0800 para IP y 0x0806 para ARP, los únicos dos protocolos que
+ * soportamos sobre ethernet.
+ * El preamble y el crc los pone la placa de red automáticamente.
+ */
+
+/** Tamaño de dirección MAC (en bytes) */
+#define ETH_ADDR_SIZE 6
+
+/** Tamaño de cabecera ethernet */
+#define ETH_HEADER_SIZE 14
+
+/** Tipos de frame ethernet */
+typedef enum { ETH_IP, ETH_ARP } eth_proto_t;
+
+/** Dirección MAC nuestra */
+extern byte eth_addr_local[ETH_ADDR_SIZE];
+
+/** Dirección MAC de destino */
+extern byte eth_addr_remote[ETH_ADDR_SIZE];
+
+/** Indica que protocolo está transportando el frame */
+extern eth_proto_t eth_proto;
+
+/**
+ * Lee la cabecera del frame ethernet.
+ *
+ * Deja en mac_addr_remote la MAC de origen y eth_frame_arp en 1 si es ARP.
+ * Si devuelve false (0) hay un error o es un frame no soportado, por lo que
+ * hay que descartarlo.
+ */
+bool eth_read_frame_header();
+
+/**
+ * Escribe la cabecera del frame ethernet.
+ *
+ * Pone como destino a mac_addr_remote, como origen a mac_addr_local y como
+ * tipo ARP si eth_frame_arp está en 1 (si no el tipo es IP).
+ */
+void eth_write_frame_header();
+
+#endif /* _ETH_H_ */
--- /dev/null
+### uVision2 Project, (C) Keil Software\r
+### Do not modify !\r
+\r
+Target (etherled), 0x0000 // Tools: 'MCS-51'\r
+\r
+Group (net)\r
+Group (common)\r
+\r
+File 1,5,<.\netdev.h><netdev.h> 0x0 \r
+File 1,5,<.\dp8390.h><dp8390.h> 0x0 \r
+File 1,1,<.\dp8390.c><dp8390.c> 0x0 \r
+File 1,5,<.\eth.h><eth.h> 0x0 \r
+File 1,1,<.\eth.c><eth.c> 0x0 \r
+File 1,5,<.\ip.h><ip.h> 0x0 \r
+File 1,1,<.\ip.c><ip.c> 0x0 \r
+File 1,5,<.\udp.h><udp.h> 0x0 \r
+File 1,1,<.\udp.c><udp.c> 0x0 \r
+File 2,1,<.\main.c><main.c> 0x0 \r
+File 2,5,<.\reg51.h><reg51.h> 0x0 \r
+File 2,2,<.\startup.a51><startup.a51> 0x0 \r
+File 2,5,<.\types.h><types.h> 0x0 \r
+File 2,5,<.\reg51keil.h><reg51keil.h> 0x0 \r
+\r
+\r
+Options 1,0,0 // Target 'etherled'\r
+ Device (AT89S8252)\r
+ Vendor (Atmel)\r
+ Cpu (IRAM(0-0xFF) IROM(0-0x1FFF) CLOCK(24000000) MODA2)\r
+ FlashUt ()\r
+ StupF ("LIB\STARTUP.A51" ("Standard 8051 Startup Code"))\r
+ FlashDR ()\r
+ DevID ()\r
+ Rgf (REG8252.H)\r
+ Mem ()\r
+ C ()\r
+ A ()\r
+ RL ()\r
+ OH ()\r
+ DBC_IFX ()\r
+ DBC_CMS ()\r
+ DBC_AMS ()\r
+ DBC_LMS ()\r
+ UseEnv=0\r
+ EnvBin ()\r
+ EnvInc ()\r
+ EnvLib ()\r
+ EnvReg (ÿAtmel\)\r
+ OrgReg (ÿAtmel\)\r
+ TgStat=27\r
+ OutDir (.\)\r
+ OutName (etherled)\r
+ GenApp=1\r
+ GenLib=0\r
+ GenHex=1\r
+ Debug=1\r
+ Browse=1\r
+ LstDir (.\)\r
+ HexSel=0\r
+ MG32K=0\r
+ TGMORE=0\r
+ RunUsr 0 0 <>\r
+ RunUsr 1 0 <>\r
+ BrunUsr 0 0 <>\r
+ BrunUsr 1 0 <>\r
+ SVCSID <>\r
+ MODEL5=0\r
+ RTOS5=0\r
+ ROMSZ5=2\r
+ DHOLD5=0\r
+ XHOLD5=0\r
+ T51FL=208\r
+ XT51FL=0\r
+ CBANKS5=0\r
+ XBANKS5=0\r
+ RCB51 { 0,0,0,0,0,255,255,0,0 }\r
+ RXB51 { 0,0,0,0,0,0,0,0,0 }\r
+ OCM51 { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }\r
+ OCR51 { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }\r
+ IRO51 { 1,0,0,0,0,0,32,0,0 }\r
+ IRA51 { 0,0,0,0,0,0,1,0,0 }\r
+ XRA51 { 0,0,0,0,0,0,0,0,0 }\r
+ XRA512 { 0,0,0,0,0,0,0,0,0 }\r
+ IROM512 { 0,0,0,0,0,0,0,0,0 }\r
+ C51FL=21630224\r
+ C51VA=0\r
+ C51MSC ()\r
+ C51DEF ()\r
+ C51UDF ()\r
+ INCC5 ()\r
+ AX51FL=4\r
+ AX51MSC ()\r
+ AX51SET ()\r
+ AX51RST ()\r
+ INCA5 ()\r
+ PropFld { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }\r
+ IncBld=1\r
+ AlwaysBuild=0\r
+ GenAsm=0\r
+ AsmAsm=0\r
+ PublicsOnly=0\r
+ StopCode=3\r
+ CustArgs ()\r
+ LibMods ()\r
+ BankNo=65535\r
+ LX51FL=292\r
+ LX51OVL ()\r
+ LX51MSC ()\r
+ LX51DWN ()\r
+ LX51LFI ()\r
+ LX51ASN ()\r
+ LX51RES ()\r
+ LX51CCL ()\r
+ LX51UCL ()\r
+ LX51CSC ()\r
+ LX51UCS ()\r
+ LX51COB ()\r
+ LX51XDB ()\r
+ LX51PDB ()\r
+ LX51BIB ()\r
+ LX51DAB ()\r
+ LX51IDB ()\r
+ LX51PRC ()\r
+ LX51STK ()\r
+ LX51COS ()\r
+ LX51XDS ()\r
+ LX51BIS ()\r
+ LX51DAS ()\r
+ LX51IDS ()\r
+ OPTDL (S8051.DLL)()(DP51.DLL)(-p8252)(S8051.DLL)()(TP51.DLL)(-p8252)\r
+ OPTDBG 48125,0,()()()()()()()()()() ()()()()\r
+ FLASH1 { 0,0,0,0,0,0,0,0,255,255,255,255,0,0,0,0,0,0,0,0 }\r
+ FLASH2 ()\r
+ FLASH3 ()\r
+ FLASH4 ()\r
+EndOpt\r
+\r
--- /dev/null
+// vim: set et sw=4 sts=4 :
+
+#include "netdev.h"
+#include "debug.h"
+#include "ip.h"
+
+/** protocolos soportados */
+enum { ICMP = 0x01, UDP = 0x11 };
+
+byte ip_addr_local[IP_ADDR_SIZE];
+
+byte ip_addr_remote[IP_ADDR_SIZE];
+
+byte ip_packet_len;
+
+ip_proto_t ip_proto;
+
+/* para calcular checksum */
+static uint16 checksum;
+
+/* agrega un word al checksum calculado */
+static void sum(uint16 w)
+{
+ checksum += w;
+ if (checksum < w) /* corrección de carry (hubo OV) */
+ ++checksum;
+}
+
+bool ip_read_packet_header()
+{
+ /* variables utilitarias (iterar y/o buffer) */
+ byte h, l;
+ bit ok = true;
+ netdev_read_start(IP_HEADER_SIZE);
+ /* reseteamos checksum */
+ checksum = 0;
+ /* versión y tamaño de cabecera vienen en el 1er byte */
+ h = netdev_read_byte();
+ /* sólo soportamos versión 4 */
+ if ((h >> 4) != 4)
+ ok = false;
+ /* tamaño de cabecera */
+ if ((h & 0x0F) != 5) /* no aceptamos opciones raras =) */
+ ok = false;
+ /* ignoramos el TOS y vamos calculando checksum */
+ sum(WORD(h, netdev_read_byte()));
+ /* obtenemos tamaño del paquete */
+ if (h = netdev_read_byte()) /* tiene más de 255 bytes (no lo soportamos) */
+ ok = false;
+ ip_packet_len = netdev_read_byte(); /* hasta 255 bytes tienen los nuestros */
+ /* vamos calculando checksum */
+ sum(WORD(h, ip_packet_len));
+ /* ignoramos identificación (2 bytes) y vamos calculando checksum */
+ sum(netdev_read_word());
+ /* si tiene prendido el bit de MF (More Fragments, bit 5 del byte, bit 2
+ * de los flags de la cabecera) o si tiene un offset de fragmento (bits
+ * del 4 al 0 y todos los bits del byte siguiente), dropeamos (no
+ * soportamos fragmentación) */
+ h = netdev_read_byte();
+ l = netdev_read_byte();
+ if ((h & 0x3F) || l)
+ ok = false;
+ /* seguimos calculando checksum */
+ sum(WORD(h, l));
+ /* no le damos bola al TTL (no le vamos a hacer lío si ya llegó hasta
+ * acá el pobre =) */
+ h = netdev_read_byte();
+ /* protocolo (sólo soportamos UDP e ICMP) */
+ l = netdev_read_byte();
+ switch (l)
+ {
+ case ICMP:
+ ip_proto = IP_ICMP;
+ break;
+ case UDP:
+ ip_proto = IP_UDP;
+ break;
+ default:
+ ok = false;
+ }
+ /* sigo calculando checksum */
+ sum(WORD(h, l));
+ /* obtenemos checksum y seguimos el cálculo */
+ sum(netdev_read_word());
+ /* obtenemos IP de origen (mientras seguimos calculando el checksum) */
+ for (l = 0; l < IP_ADDR_SIZE; ++l)
+ {
+ ip_addr_remote[l] = netdev_read_byte();
+ if (l % 2)
+ sum(WORD(h, ip_addr_remote[l]));
+ else
+ h = ip_addr_remote[l];
+ }
+ /* vemos si el paquete es para nosotros (ningún soportar broadcast =)
+ * (mientras seguimos calculando el checksum) */
+ // TODO si soportamos DHCP hay que aceptar broadcasts!
+ for (l = 0; l < IP_ADDR_SIZE; ++l)
+ {
+ if (ip_addr_local[l] != netdev_read_byte())
+ ok = false;
+ if (l % 2)
+ sum(WORD(h, ip_addr_local[l]));
+ else
+ h = ip_addr_local[l];
+ }
+ /* verificamos checksum */
+ if ((uint16)~checksum)
+ ok = false;
+ netdev_read_end();
+ return ok;
+}
+
+void ip_write_packet_header()
+{
+ /* variables utilitarias (iterar y/o buffer) */
+ byte h, l;
+ /* identificador del paquete IP (incrementa con cada paquete) */
+ static uint16 id;
+ /* reseteamos checksum */
+ checksum = 0;
+ netdev_write_start(IP_HEADER_SIZE);
+ /* versión (4) y tamaño de cabecera (5 words de 4 bytes = 20 bytes) */
+ netdev_write_byte(h = 0x45);
+ /* TOS (0xc0 = Internetwork Control, 0x00 = normal) */
+ l = (ip_proto == IP_ICMP) ? 0xc0 : 0x00;
+ netdev_write_byte(l);
+ sum(WORD(h, l)); /* actualizamos checksum */
+ /* escribimos tamaño del paquete */
+ netdev_write_byte(h = 0x00); /* nunca vamos a mandar algo de más de 255 bytes */
+ netdev_write_byte(ip_packet_len);
+ sum(WORD(h, ip_packet_len)); /* actualizamos checksum */
+ /* identificación (sirve para reensamblar paquetes) */
+ netdev_write_word(++id);
+ sum(id); /* actualizamos checksum */
+ /* pedimos que no se fragmente */
+ netdev_write_byte(h = 0x40); /* Don't Fragment (DF) = 1 */
+ netdev_write_byte(l = 0x00); /* offset de fragmento = 0 */
+ sum(WORD(h, l)); /* actualizamos checksum */
+ /* TTL de 64 saltos porque está de moda */
+ netdev_write_byte(h = 0x40);
+ /* protocolo (sólo soportamos UDP e ICMP) */
+ l = (ip_proto == IP_ICMP) ? ICMP : UDP;
+ netdev_write_byte(l);
+ sum(WORD(h, l)); /* actualizamos checksum */
+ /* checksum: antes de poder escribir el checksum hay que terminar de
+ * calcularlo según las direcciones IP de origen y destino, así que eso
+ * hacemos */
+ for (l = 0; l < IP_ADDR_SIZE; ++l) /* origen = local */
+ if (l % 2)
+ sum(WORD(h, ip_addr_local[l]));
+ else
+ h = ip_addr_local[l];
+ for (l = 0; l < IP_ADDR_SIZE; ++l) /* destino = remota */
+ if (l % 2)
+ sum(WORD(h, ip_addr_remote[l]));
+ else
+ h = ip_addr_remote[l];
+ /* ahora sí grabamos el checksum */
+ netdev_write_word(~checksum);
+ /* ahora sí, continuamos poniendo las direcciones */
+ /* ponemos como dirección IP de origen la nuestra */
+ for (l = 0; l < IP_ADDR_SIZE; ++l)
+ netdev_write_byte(ip_addr_local[l]);
+ /* IP de destino, la remota */
+ for (l = 0; l < IP_ADDR_SIZE; ++l)
+ netdev_write_byte(ip_addr_remote[l]);
+ netdev_write_end();
+}
+
--- /dev/null
+// vim: set et sw=4 sts=4 :
+
+#ifndef _IP_H_
+#define _IP_H_
+
+#include "types.h"
+
+/** @file
+ * Paquete IP.
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |Version| IHL |Type of Service| Total Length |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Identification |Flags| Fragment Offset |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Time to Live | Protocol | Header Checksum |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Source Address |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Destination Address |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Options | Padding |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Data |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ * Nosotros sólo implementamos la versión 4, sin opciones, con tamaño de paquete
+ * como máximo de 255 bytes, sin fragmentación y sólo los protocolos UDP e ICMP
+ * (este último sólo para responder al ping). Todos los paquetes que no cumplan
+ * con estas restricciones son descartados.
+ */
+
+/** Tamaño de dirección IP (en bytes) */
+#define IP_ADDR_SIZE 4
+
+/** Tamaño de cabecera IP (en bytes) */
+#define IP_HEADER_SIZE 20
+
+/** Tipos de paquete IP */
+typedef enum { IP_UDP, IP_ICMP } ip_proto_t;
+
+/** Dirección IP nuestra */
+extern byte ip_addr_local[IP_ADDR_SIZE];
+
+/** Dirección IP de destino */
+extern byte ip_addr_remote[IP_ADDR_SIZE];
+
+/** Tamaño del paquete IP */
+extern byte ip_packet_len;
+
+/** Indica si el paquete es ICMP (si no es UDP) */
+extern ip_proto_t ip_proto;
+
+/** Lee la cabecera del paquete IP.
+ *
+ * Deja en ip_addr_remote la ip de origen.
+ * Si devuelve false (0) es que hubo un error o es un paquete no soportado, por
+ * lo que hay que descartarlo.
+ */
+bool ip_read_packet_header();
+
+/** Escribe la cabecera del paquete IP.
+ *
+ * Pone como destino a ip_addr_remote, como origen a ip_addr_local, el protocolo
+ * lo especifica según ip_proto_icmp y como tamaño pone ip_packet_len.
+ */
+void ip_write_packet_header();
+
+#endif /* _IP_H_ */
--- /dev/null
+// vim: set et sw=4 sts=4 :
+
+#ifndef _LEDS_H_
+#define _LEDS_H_
+
+#include "types.h"
+
+#ifdef SDCC
+static xdata at 0x0080 byte leds0;
+static xdata at 0x00c0 byte leds1;
+#else
+static byte xdata leds0 _at_ 0x0080;
+static byte xdata leds1 _at_ 0x00c0;
+#endif
+
+#define leds(word) \
+ do \
+ { \
+ uint16 w = word; \
+ leds0 = ~LOW(w); \
+ leds1 = ~HIGH(w); \
+ } \
+ while (0)
+
+#define ledsb(bh, bl) \
+ do \
+ { \
+ leds0 = ~(bl); \
+ leds1 = ~(bh); \
+ } \
+ while (0)
+
+#endif // _LEDS_H_
--- /dev/null
+// vim: set et sw=4 sts=4 :
+
+#include "debug.h"
+#include "leds.h"
+#include "netdev.h"
+#include "eth.h"
+#include "ip.h"
+#include "udp.h"
+
+void main(void)
+{
+ // Apagamos todos los leds
+ leds(0);
+
+ // Inicializamos dispositivo de red
+ if (!netdev_init())
+ {
+ leds(0xFFFF);
+ while(1); // Si falla init nos quedamos bobos
+ }
+
+ // Inicializo IP
+ ip_addr_local[0] = 10;
+ ip_addr_local[1] = 10;
+ ip_addr_local[2] = 10;
+ ip_addr_local[3] = 100;
+
+ // Inicializo puerto UDP
+ udp_port_local = 9876;
+
+ while (1) // Forever
+ {
+ byte buf[64]; //XXX
+ byte i; //XXX
+ byte len;
+
+ len = netdev_recv_start();
+ if (!len) // no recibimos nada (válido)
+ continue; // Probamos de nuevo
+
+ // Tenemos algo!
+ //print(0x2);
+
+ // Parseamos cabecera ethernet
+ if (!eth_read_frame_header()) // No es un buen header
+ goto drop; // Tiramos el paquete
+ //print(0x4);
+
+ // Vemos que protocolo transporta
+ switch (eth_proto)
+ {
+ case ETH_ARP: // TODO: implementar ARP!
+ goto drop; // Tiramos el paquete
+
+ case ETH_IP:
+ //print(0x8);
+ // Parseamos cabecera IP
+ if (!ip_read_packet_header()) // No es un buen header
+ goto drop; // Tiramos el paquete
+ //print(0x10);
+
+ // Vemos que protocolo transporta
+ switch (ip_proto)
+ {
+ case IP_ICMP: // TODO: implementar ICMP!
+ goto drop; // Tiramos el paquete
+
+ case IP_UDP:
+ //print(0x20);
+ // Parseamos cabecera UDP
+ if (!udp_read_dgram_header()) // No es un buen header
+ goto drop; // Tiramos el paquete
+
+ //printb(udp_dgram_len, 0x40);
+ // TODO
+ // Nuestro protocolo, por ahora un simple echo!
+ len = udp_dgram_len - UDP_HEADER_SIZE;
+ netdev_read_start(len);
+ for (i = 0; i < len; ++i)
+ buf[i] = udp_read_byte();
+ netdev_read_end();
+ if (!udp_checksum_ok())
+ goto drop;
+ netdev_recv_end();
+
+ // Respuesta
+ netdev_send_start();
+ eth_write_frame_header();
+ //udp_dgram_len = UDP_HEADER_SIZE+len;
+ //ip_packet_len = IP_HEADER_SIZE+udp_dgram_len;
+ ip_write_packet_header();
+ udp_write_dgram_header();
+ netdev_write_start(len);
+ for (i = 0; i < len; ++i)
+ udp_write_byte(buf[i]);
+ netdev_write_end();
+ udp_write_checksum(ETH_HEADER_SIZE+IP_HEADER_SIZE);
+ netdev_send_end(ETH_HEADER_SIZE+IP_HEADER_SIZE+udp_dgram_len);
+ }
+ }
+ continue;
+drop:
+ netdev_recv_end();
+ }
+}
+
--- /dev/null
+// vim: set et sw=4 sts=4 :
+
+#ifndef _NETDEV_H_
+#define _NETDEV_H_
+
+#include "types.h"
+
+/** Tamaño del frame */
+extern byte netdev_len;
+
+/** Inicializa dispositivo de red
+ * @return true si se inicializó correctamente, false si no
+ */
+bool netdev_init();
+
+/** Comienza el envío de un nuevo frame */
+void netdev_send_start();
+
+/** Finaliza el envío del frame
+ * @precond netdev_send_start() debe haber sido ejecutada
+ * @precond se copiaron datos al dispositivo para enviar
+ * @param len Cantidad de bytes a transmitir
+ */
+void netdev_send_end(byte len);
+
+/** Comienza una escritura a los buffers del dispositivo de red
+ * @param len Cantidad de bytes a escribir
+ */
+void netdev_write_start(byte len);
+
+/** Comienza una escritura a los buffers del dispositivo de red a partir de un
+ * offset dado.
+ * @param offset Offset en donde comenzar a escribir
+ * @param len Cantidad de bytes a escribir
+ */
+void netdev_write_start_at(byte offset, byte len);
+
+/** Escribe un byte al buffer de la placa de red para ser enviado
+ * @precond netdev_write_start() debe haber sido ejecutada
+ * @param b Byte a enviar
+ */
+void netdev_write_byte(byte b);
+
+/** Escribe un word al buffer de la placa de red para ser enviado
+ * @precond netdev_write_start() debe haber sido ejecutada
+ * @param w Word a enviar
+ */
+void netdev_write_word(uint16 w);
+
+/** Finaliza una transferencia al dispositivo de red
+ * @precond netdev_write_start() fue llamada y se ecribió la cantidad de bytes
+ * en ella especificada
+ */
+void netdev_write_end();
+
+/** Comienza la recepción de un nuevo frame
+ * @return Cantidad de bytes a recibir
+ */
+byte netdev_recv_start();
+
+/** Finaliza la lectura del frame
+ * @precond netdev_recv_start() debe haber sido ejecutada
+ */
+void netdev_recv_end();
+
+/** Comienza a leer bytes del dispositivo de red
+ * @param len Cantidad de bytes a leer
+ */
+void netdev_read_start(byte len);
+
+/** Lee un byte del buffer de la placa de red
+ * @precond netdev_recv_start() debe haber sido ejecutada
+ */
+byte netdev_read_byte();
+
+/** Lee un word del buffer de la placa de red
+ * @precond netdev_recv_start() debe haber sido ejecutada
+ */
+uint16 netdev_read_word();
+
+/** Finaliza la lectura del frame
+ * @precond netdev_recv_start() debe haber sido ejecutada
+ */
+void netdev_read_end();
+
+#endif // _NETDEV_H_
--- /dev/null
+#ifndef _REG51_H_
+#define _REG51_H_
+
+#ifdef SDCC
+# include "reg51sdcc.h"
+#else
+# include "reg51keil.h"
+#endif
+
+#endif /* _REG51_H_ */
--- /dev/null
+#ifndef _REG51KEIL_H_
+#define _REG51KEIL_H_
+
+/* BYTE Register */
+sfr P0 = 0x80;
+sfr P1 = 0x90;
+sfr P2 = 0xA0;
+sfr P3 = 0xB0;
+sfr PSW = 0xD0;
+sfr ACC = 0xE0;
+sfr B = 0xF0;
+sfr SP = 0x81;
+sfr DPL = 0x82;
+sfr DPH = 0x83;
+sfr PCON = 0x87;
+sfr TCON = 0x88;
+sfr TMOD = 0x89;
+sfr TL0 = 0x8A;
+sfr TL1 = 0x8B;
+sfr TH0 = 0x8C;
+sfr TH1 = 0x8D;
+sfr IE = 0xA8;
+sfr IP = 0xB8;
+sfr SCON = 0x98;
+sfr SBUF = 0x99;
+
+/* BIT Register */
+/* PSW */
+sbit CY = 0xD7;
+sbit AC = 0xD6;
+sbit F0 = 0xD5;
+sbit RS1 = 0xD4;
+sbit RS0 = 0xD3;
+sbit OV = 0xD2;
+sbit P = 0xD0;
+
+/* TCON */
+sbit TF1 = 0x8F;
+sbit TR1 = 0x8E;
+sbit TF0 = 0x8D;
+sbit TR0 = 0x8C;
+sbit IE1 = 0x8B;
+sbit IT1 = 0x8A;
+sbit IE0 = 0x89;
+sbit IT0 = 0x88;
+
+/* IE */
+sbit EA = 0xAF;
+sbit ES = 0xAC;
+sbit ET1 = 0xAB;
+sbit EX1 = 0xAA;
+sbit ET0 = 0xA9;
+sbit EX0 = 0xA8;
+
+/* IP */
+sbit PS = 0xBC;
+sbit PT1 = 0xBB;
+sbit PX1 = 0xBA;
+sbit PT0 = 0xB9;
+sbit PX0 = 0xB8;
+
+/* P3 */
+sbit RD = 0xB7;
+sbit WR = 0xB6;
+sbit T1 = 0xB5;
+sbit T0 = 0xB4;
+sbit INT1 = 0xB3;
+sbit INT0 = 0xB2;
+sbit TXD = 0xB1;
+sbit RXD = 0xB0;
+
+/* SCON */
+sbit SM0 = 0x9F;
+sbit SM1 = 0x9E;
+sbit SM2 = 0x9D;
+sbit REN = 0x9C;
+sbit TB8 = 0x9B;
+sbit RB8 = 0x9A;
+sbit TI = 0x99;
+sbit RI = 0x98;
+
+#endif /* _REG51KEIL_H_ */
--- /dev/null
+#ifndef _REG51SDCC_H_
+#define _REG51SDCC_H_
+
+/* BYTE Register */
+sfr at 0x80 P0;
+sfr at 0x90 P1;
+sfr at 0xA0 P2;
+sfr at 0xB0 P3;
+sfr at 0xD0 PSW;
+sfr at 0xE0 ACC;
+sfr at 0xF0 B;
+sfr at 0x81 SP;
+sfr at 0x82 DPL;
+sfr at 0x83 DPH;
+sfr at 0x87 PCON;
+sfr at 0x88 TCON;
+sfr at 0x89 TMOD;
+sfr at 0x8A TL0;
+sfr at 0x8B TL1;
+sfr at 0x8C TH0;
+sfr at 0x8D TH1;
+sfr at 0xA8 IE;
+sfr at 0xB8 IP;
+sfr at 0x98 SCON;
+sfr at 0x99 SBUF;
+
+/* BIT Register */
+/* PSW */
+sbit at 0xD7 CY;
+sbit at 0xD6 AC;
+sbit at 0xD5 F0;
+sbit at 0xD4 RS1;
+sbit at 0xD3 RS0;
+sbit at 0xD2 OV;
+sbit at 0xD0 P;
+
+/* TCON */
+sbit at 0x8F TF1;
+sbit at 0x8E TR1;
+sbit at 0x8D TF0;
+sbit at 0x8C TR0;
+sbit at 0x8B IE1;
+sbit at 0x8A IT1;
+sbit at 0x89 IE0;
+sbit at 0x88 IT0;
+
+/* IE */
+sbit at 0xAF EA;
+sbit at 0xAC ES;
+sbit at 0xAB ET1;
+sbit at 0xAA EX1;
+sbit at 0xA9 ET0;
+sbit at 0xA8 EX0;
+
+/* IP */
+sbit at 0xBC PS;
+sbit at 0xBB PT1;
+sbit at 0xBA PX1;
+sbit at 0xB9 PT0;
+sbit at 0xB8 PX0;
+
+/* P3 */
+sbit at 0xB7 RD;
+sbit at 0xB6 WR;
+sbit at 0xB5 T1;
+sbit at 0xB4 T0;
+sbit at 0xB3 INT1;
+sbit at 0xB2 INT0;
+sbit at 0xB1 TXD;
+sbit at 0xB0 RXD;
+
+/* SCON */
+sbit at 0x9F SM0;
+sbit at 0x9E SM1;
+sbit at 0x9D SM2;
+sbit at 0x9C REN;
+sbit at 0x9B TB8;
+sbit at 0x9A RB8;
+sbit at 0x99 TI;
+sbit at 0x98 RI;
+
+#endif /* _REG51SDCC_H_ */
--- /dev/null
+$NOMOD51
+;------------------------------------------------------------------------------
+; This file is part of the C51 Compiler package
+; Copyright (c) 1988-2002 Keil Elektronik GmbH and Keil Software, Inc.
+;------------------------------------------------------------------------------
+; STARTUP.A51: This code is executed after processor reset.
+;
+; To translate this file use A51 with the following invocation:
+;
+; A51 STARTUP.A51
+;
+; To link the modified STARTUP.OBJ file to your application use the following
+; BL51 invocation:
+;
+; BL51 <your object file list>, STARTUP.OBJ <controls>
+;
+;------------------------------------------------------------------------------
+;
+; User-defined Power-On Initialization of Memory
+;
+; With the following EQU statements the initialization of memory
+; at processor reset can be defined:
+;
+; ; the absolute start-address of IDATA memory is always 0
+IDATALEN EQU 80H ; the length of IDATA memory in bytes.
+;
+XDATASTART EQU 0H ; the absolute start-address of XDATA memory
+XDATALEN EQU 400H ; the length of XDATA memory in bytes.
+;
+PDATASTART EQU 0H ; the absolute start-address of PDATA memory
+PDATALEN EQU 0H ; the length of PDATA memory in bytes.
+;
+; Notes: The IDATA space overlaps physically the DATA and BIT areas of the
+; 8051 CPU. At minimum the memory space occupied from the C51
+; run-time routines must be set to zero.
+;------------------------------------------------------------------------------
+;
+; Reentrant Stack Initilization
+;
+; The following EQU statements define the stack pointer for reentrant
+; functions and initialized it:
+;
+; Stack Space for reentrant functions in the SMALL model.
+IBPSTACK EQU 0 ; set to 1 if small reentrant is used.
+IBPSTACKTOP EQU 0FFH+1 ; set top of stack to highest location+1.
+;
+; Stack Space for reentrant functions in the LARGE model.
+XBPSTACK EQU 0 ; set to 1 if large reentrant is used.
+XBPSTACKTOP EQU 0FFFFH+1; set top of stack to highest location+1.
+;
+; Stack Space for reentrant functions in the COMPACT model.
+PBPSTACK EQU 0 ; set to 1 if compact reentrant is used.
+PBPSTACKTOP EQU 0FFFFH+1; set top of stack to highest location+1.
+;
+;------------------------------------------------------------------------------
+;
+; Page Definition for Using the Compact Model with 64 KByte xdata RAM
+;
+; The following EQU statements define the xdata page used for pdata
+; variables. The EQU PPAGE must conform with the PPAGE control used
+; in the linker invocation.
+;
+PPAGEENABLE EQU 0 ; set to 1 if pdata object are used.
+;
+PPAGE EQU 0 ; define PPAGE number.
+;
+PPAGE_SFR DATA 0A0H ; SFR that supplies uppermost address byte
+; (most 8051 variants use P2 as uppermost address byte)
+;
+;------------------------------------------------------------------------------
+
+; Standard SFR Symbols
+ACC DATA 0E0H
+B DATA 0F0H
+SP DATA 81H
+DPL DATA 82H
+DPH DATA 83H
+
+ NAME ?C_STARTUP
+
+
+?C_C51STARTUP SEGMENT CODE
+?STACK SEGMENT IDATA
+
+ RSEG ?STACK
+ DS 1
+
+ EXTRN CODE (?C_START)
+ PUBLIC ?C_STARTUP
+
+ CSEG AT 0
+?C_STARTUP: LJMP STARTUP1
+
+ RSEG ?C_C51STARTUP
+
+STARTUP1:
+
+IF IDATALEN <> 0
+ MOV R0,#IDATALEN - 1
+ CLR A
+IDATALOOP: MOV @R0,A
+ DJNZ R0,IDATALOOP
+ENDIF
+
+IF XDATALEN <> 0
+ MOV DPTR,#XDATASTART
+ MOV R7,#LOW (XDATALEN)
+ IF (LOW (XDATALEN)) <> 0
+ MOV R6,#(HIGH (XDATALEN)) +1
+ ELSE
+ MOV R6,#HIGH (XDATALEN)
+ ENDIF
+ CLR A
+XDATALOOP: MOVX @DPTR,A
+ INC DPTR
+ DJNZ R7,XDATALOOP
+ DJNZ R6,XDATALOOP
+ENDIF
+
+IF PPAGEENABLE <> 0
+ MOV PPAGE_SFR,#PPAGE
+ENDIF
+
+IF PDATALEN <> 0
+ MOV R0,#LOW (PDATASTART)
+ MOV R7,#LOW (PDATALEN)
+ CLR A
+PDATALOOP: MOVX @R0,A
+ INC R0
+ DJNZ R7,PDATALOOP
+ENDIF
+
+IF IBPSTACK <> 0
+EXTRN DATA (?C_IBP)
+
+ MOV ?C_IBP,#LOW IBPSTACKTOP
+ENDIF
+
+IF XBPSTACK <> 0
+EXTRN DATA (?C_XBP)
+
+ MOV ?C_XBP,#HIGH XBPSTACKTOP
+ MOV ?C_XBP+1,#LOW XBPSTACKTOP
+ENDIF
+
+IF PBPSTACK <> 0
+EXTRN DATA (?C_PBP)
+ MOV ?C_PBP,#LOW PBPSTACKTOP
+ENDIF
+
+ MOV SP,#?STACK-1
+; This code is required if you use L51_BANK.A51 with Banking Mode 4
+; EXTRN CODE (?B_SWITCH0)
+; CALL ?B_SWITCH0 ; init bank mechanism to code bank 0
+ LJMP ?C_START
+
+ END
--- /dev/null
+#ifndef _TYPES_H_
+#define _TYPES_H_
+
+#if 0 /* i386 */
+#include <stdint.h>
+
+/** booleano */
+typedef unsigned char bool;
+
+/** entero sin signo de 8 bits */
+typedef uint8_t byte;
+
+/** entero sin signo de 16 bits */
+typedef uint16_t uint16;
+
+#endif
+
+#if 1 /* 8051 */
+
+/** entero sin signo de 8 bits */
+typedef unsigned char byte;
+
+/** entero sin signo de 16 bits */
+typedef unsigned int uint16;
+
+#ifdef SDCC
+/** booleano */
+typedef byte bool;
+#else
+/** booleano */
+typedef bit bool;
+#endif
+
+#endif
+
+/** valores posibles de un booleano */
+enum { false = 0, true = 1 };
+
+/** convierte 2 bytes (high, low) en un word */
+#define WORD(high, low) ((uint16)((uint16)((high) << 8) + (uint16)(low)))
+
+/** obtiene parte alta de un word */
+#define HIGH(word) ((byte)((word) >> 8))
+
+/** obtiene parte baja de un word */
+#define LOW(word) ((byte)((word) & 0xFF))
+
+#endif /* _TYPES_H_ */
--- /dev/null
+// vim: set et sw=4 sts=4 :
+
+#include "debug.h"
+#include "netdev.h"
+#include "ip.h"
+#include "udp.h"
+
+uint16 udp_port_local;
+
+uint16 udp_port_remote;
+
+byte udp_dgram_len;
+
+/* para calcular checksum */
+static uint16 checksum;
+static byte byte_count;
+static byte last_byte;
+
+/* agrega un word al checksum calculado */
+static void sum(uint16 w)
+{
+ checksum += w;
+ if (checksum < w) /* corrección de carry (hubo OV) */
+ ++checksum;
+}
+
+bool udp_read_dgram_header()
+{
+ byte tmp;
+ bit ok = true;
+ netdev_read_start(UDP_HEADER_SIZE);
+ /* reseteamos checksum */
+ checksum = 0;
+ byte_count = 0;
+ /* el UDP tiene un checksum que incluye parte de la cabecera IP */
+ /* ip de origen */
+ sum(WORD(ip_addr_remote[0], ip_addr_remote[1]));
+ sum(WORD(ip_addr_remote[2], ip_addr_remote[3]));
+ /* ip de destino */
+ sum(WORD(ip_addr_local[0], ip_addr_local[1]));
+ sum(WORD(ip_addr_local[2], ip_addr_local[3]));
+ /* protocolo expresado en 16 bits (0x11 es UDP) */
+ sum(0x0011);
+ /* tamaño del paquete UDP (sin las cabeceras que son 20 bytes) */
+ sum(ip_packet_len - 20);
+ /* de ahora en más todos los datos del checksum corresponden a UDP */
+ /* puerto origen (remoto) */
+ udp_port_remote = netdev_read_word();
+ /* agregamos puerto de origen al checksum */
+ sum(udp_port_remote);
+ /* sólo aceptamos datagramas a nuestro puerto */
+ if (netdev_read_word() != udp_port_local)
+ ok = false; /* drop */
+ /* agregamos puerto de destino al checksum */
+ sum(udp_port_local);
+ /* tamaño del datagrama */
+ if (tmp = netdev_read_byte()) /* no soportamos más de 255 bytes */
+ ok = false; /* drop */
+ udp_dgram_len = netdev_read_byte(); /* parte baja */
+ if (udp_dgram_len < 8) /* no puede ser más chico que sus cabeceras */
+ ok = false; /* drop */
+ /* agregamos tamaño al checksum */
+ sum(WORD(tmp, udp_dgram_len));
+ /* agregamos checksum al checksum */
+ sum(netdev_read_word());
+ /* falta agregar el cuerpo del mensaje para verificar la suma
+ * esto debe hacerlo el protocolo que sigue para poder seguir obteniendo
+ * los datos de la placa de red byte a byte */
+ netdev_read_end();
+ return true;
+}
+
+byte udp_read_byte()
+{
+ byte b;
+ if (byte_count % 2) // impar, tengo 2, sumo
+ {
+ b = netdev_read_byte();
+ sum(WORD(last_byte, b));
+ }
+ else // par, guardo para sumar cuando tenga 2
+ {
+ b = netdev_read_byte();
+ last_byte = b;
+ }
+ ++byte_count;
+ return b;
+}
+
+bool udp_checksum_ok()
+{
+ // Verifico si falta sumar algo (UDP debe sumar de a un word)
+ if (byte_count == (udp_dgram_len - UDP_HEADER_SIZE))
+ sum(WORD(last_byte, 0x00)); // Relleno el byte que falta con 0x00
+ return !(uint16)~checksum;
+}
+
+void udp_write_dgram_header()
+{
+ netdev_write_start(UDP_HEADER_SIZE);
+ /* reseteamos checksum */
+ checksum = 0;
+ byte_count = 0;
+ /* el UDP tiene un checksum que incluye parte de la cabecera IP */
+ /* ip de origen */
+ sum(WORD(ip_addr_remote[0], ip_addr_remote[1]));
+ sum(WORD(ip_addr_remote[2], ip_addr_remote[3]));
+ /* ip de destino */
+ sum(WORD(ip_addr_local[0], ip_addr_local[1]));
+ sum(WORD(ip_addr_local[2], ip_addr_local[3]));
+ /* protocolo expresado en 16 bits (0x11 es UDP) */
+ sum(0x0011);
+ /* tamaño del paquete UDP (IP sin las cabeceras, que son 20 bytes) */
+ sum(ip_packet_len - 20); // FIXME
+ /* puerto origen */
+ netdev_write_word(udp_port_local);
+ sum(udp_port_local);
+ /* puerto destino */
+ netdev_write_word(udp_port_remote);
+ sum(udp_port_remote);
+ /* tamaño del datagrama */
+ netdev_write_byte(0x00); /* parte alta en 0 porque no soportamos más de 255 */
+ netdev_write_byte(udp_dgram_len);
+ sum(WORD(0x00, udp_dgram_len));
+ /* indicamos que no se usa checksum */
+ netdev_write_word(0x0000);
+ sum(0x0000);
+ netdev_write_end();
+}
+
+void udp_write_byte(byte b)
+{
+ if (byte_count % 2) // impar, tengo 2, sumo
+ {
+ netdev_write_byte(b);
+ sum(WORD(last_byte, b));
+ }
+ else // par, guardo para sumar cuando tenga 2
+ {
+ netdev_write_byte(b);
+ last_byte = b;
+ }
+ ++byte_count;
+}
+
+void udp_write_checksum(byte offset)
+{
+ // Verifico si falta sumar algo (UDP debe sumar de a un word)
+ if (byte_count == (udp_dgram_len - UDP_HEADER_SIZE))
+ sum(WORD(last_byte, 0x00)); // Relleno el byte que falta con 0x00
+ // Escribo checksum en el buffer de la placa de red
+ netdev_write_start_at(offset + 6, 2); // 6 bytes de offset hasta el checksum
+ netdev_write_word((uint16)~checksum); // Guardo checksum
+ netdev_write_end();
+ return;
+}
+
--- /dev/null
+// vim: set et sw=4 sts=4 :
+
+#ifndef _UDP_H_
+#define _UDP_H_
+
+#include "types.h"
+
+/** @file
+ * Datagrama UDP.
+ *
+ * <pre>
+ * 0 7 8 15 16 23 24 31
+ * +--------+--------+--------+--------+
+ * | Source | Destination |
+ * | Port | Port |
+ * +--------+--------+--------+--------+
+ * | | |
+ * | Length | Checksum |
+ * +--------+--------+--------+--------+
+ * |
+ * | data octets ...
+ * +---------------- ...
+ * </pre>
+ *
+ * Aceptamos sólo datagramas UDP que vayan a el puerto de nuestra aplicación y
+ * cuyo tamaño sea menor a 255. El resto es descartado.
+ * El Length es tanto de la cabecera como datos, por lo tanto el tamaño mínimo
+ * es 8.
+ *
+ * El checksum se calcula utilizando algunos datos de la capa inferior e incluye
+ * a los datos (completando con 0 al final si no es múltiplo de 2). Entonces es
+ * calculado sobre el siguiente paquete 'virtual':
+ *
+ * <pre>
+ * 0 7 8 15 16 23 24 31
+ * +--------+--------+--------+--------+
+ * | ip source address |
+ * +--------+--------+--------+--------+
+ * | ip destination address |
+ * +--------+--------+--------+--------+
+ * | zero |protocol| UDP length |
+ * +--------+--------+--------+--------+
+ * | Source | Destination |
+ * | Port | Port |
+ * +--------+--------+--------+--------+
+ * | | |
+ * | Length | Checksum |
+ * +--------+--------+--------+--------+
+ * |
+ * | data octets ... (padding)
+ * +---------------- ...
+ * </pre>
+ */
+
+/** Tamaño de la cabecera UDP */
+#define UDP_HEADER_SIZE 8
+
+/** Puerto UDP nuestro */
+extern uint16 udp_port_local;
+
+/** Puerto UDP de destino */
+extern uint16 udp_port_remote;
+
+/** Tamaño del datagrama UDP (no soportamos más de 255) */
+extern byte udp_dgram_len;
+
+/** Lee la cabecera del datagrama UDP.
+ *
+ * Deja en udp_port_remote el puerto de origen.
+ * Si devuelve false (0) es que hubo un error o es un datagrama no soportado,
+ * por lo que hay que descartarlo.
+ */
+bool udp_read_dgram_header();
+
+/** Recibe un word del payload UDP chequeando el checksum.
+ * @precond Hay que llamar antes a netdev_read_start()
+ */
+byte udp_read_byte();
+
+/* Indica si el checksum calculado está ok */
+bool udp_checksum_ok();
+
+/** Escribe la cabecera del datagrama UDP.
+ *
+ * Pone como puerto destino a udp_port_remote, como origen udp_port_local y como
+ * tamaño a udp_dgram_len (en la parte baja y 0 en la parte alta).
+ */
+void udp_write_dgram_header();
+
+/** Escribe un word al payload UDP chequeando el checksum.
+ * @precond Hay que llamar antes a netdev_write_start()
+ */
+void udp_write_byte(byte b);
+
+/* Escribe el checksum calculado al frame a enviar
+ * @param offset Offset a partir de donde están las cabeceras UDP.
+ */
+void udp_write_checksum(byte offset);
+
+#endif /* _UDP_H_ */