From ab12c9208b125b42786a69291a96959cd295b19d Mon Sep 17 00:00:00 2001 From: Leandro Lucarella Date: Fri, 9 Dec 2005 07:25:43 +0000 Subject: [PATCH] Copio el estado actual del proyecto como un ejemplo de echo server. --- pruebas/sdcc/8051_dp8390_echo_server/Makefile | 35 ++ pruebas/sdcc/8051_dp8390_echo_server/config.h | 10 + pruebas/sdcc/8051_dp8390_echo_server/debug.h | 45 ++ pruebas/sdcc/8051_dp8390_echo_server/dp8390.c | 469 ++++++++++++++++++ pruebas/sdcc/8051_dp8390_echo_server/dp8390.h | 236 +++++++++ pruebas/sdcc/8051_dp8390_echo_server/eth.c | 60 +++ pruebas/sdcc/8051_dp8390_echo_server/eth.h | 58 +++ .../sdcc/8051_dp8390_echo_server/etherled.Uv2 | 136 +++++ pruebas/sdcc/8051_dp8390_echo_server/ip.c | 169 +++++++ pruebas/sdcc/8051_dp8390_echo_server/ip.h | 71 +++ pruebas/sdcc/8051_dp8390_echo_server/leds.h | 33 ++ pruebas/sdcc/8051_dp8390_echo_server/main.c | 106 ++++ pruebas/sdcc/8051_dp8390_echo_server/netdev.h | 86 ++++ pruebas/sdcc/8051_dp8390_echo_server/reg51.h | 10 + .../sdcc/8051_dp8390_echo_server/reg51keil.h | 82 +++ .../sdcc/8051_dp8390_echo_server/reg51sdcc.h | 82 +++ .../sdcc/8051_dp8390_echo_server/startup.a51 | 157 ++++++ pruebas/sdcc/8051_dp8390_echo_server/types.h | 48 ++ pruebas/sdcc/8051_dp8390_echo_server/udp.c | 157 ++++++ pruebas/sdcc/8051_dp8390_echo_server/udp.h | 100 ++++ 20 files changed, 2150 insertions(+) create mode 100644 pruebas/sdcc/8051_dp8390_echo_server/Makefile create mode 100644 pruebas/sdcc/8051_dp8390_echo_server/config.h create mode 100644 pruebas/sdcc/8051_dp8390_echo_server/debug.h create mode 100644 pruebas/sdcc/8051_dp8390_echo_server/dp8390.c create mode 100644 pruebas/sdcc/8051_dp8390_echo_server/dp8390.h create mode 100644 pruebas/sdcc/8051_dp8390_echo_server/eth.c create mode 100644 pruebas/sdcc/8051_dp8390_echo_server/eth.h create mode 100644 pruebas/sdcc/8051_dp8390_echo_server/etherled.Uv2 create mode 100644 pruebas/sdcc/8051_dp8390_echo_server/ip.c create mode 100644 pruebas/sdcc/8051_dp8390_echo_server/ip.h create mode 100644 pruebas/sdcc/8051_dp8390_echo_server/leds.h create mode 100644 pruebas/sdcc/8051_dp8390_echo_server/main.c create mode 100644 pruebas/sdcc/8051_dp8390_echo_server/netdev.h create mode 100644 pruebas/sdcc/8051_dp8390_echo_server/reg51.h create mode 100644 pruebas/sdcc/8051_dp8390_echo_server/reg51keil.h create mode 100644 pruebas/sdcc/8051_dp8390_echo_server/reg51sdcc.h create mode 100644 pruebas/sdcc/8051_dp8390_echo_server/startup.a51 create mode 100644 pruebas/sdcc/8051_dp8390_echo_server/types.h create mode 100644 pruebas/sdcc/8051_dp8390_echo_server/udp.c create mode 100644 pruebas/sdcc/8051_dp8390_echo_server/udp.h diff --git a/pruebas/sdcc/8051_dp8390_echo_server/Makefile b/pruebas/sdcc/8051_dp8390_echo_server/Makefile new file mode 100644 index 0000000..1cdf384 --- /dev/null +++ b/pruebas/sdcc/8051_dp8390_echo_server/Makefile @@ -0,0 +1,35 @@ + +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 diff --git a/pruebas/sdcc/8051_dp8390_echo_server/config.h b/pruebas/sdcc/8051_dp8390_echo_server/config.h new file mode 100644 index 0000000..71f1727 --- /dev/null +++ b/pruebas/sdcc/8051_dp8390_echo_server/config.h @@ -0,0 +1,10 @@ +// vim: set et sw=4 sts=4 : + +#ifndef _CONFIG_H_ +#define _CONFIG_H_ + +#define NET_RECV_CALLBACK eth_recv + +#define + +#endif // _CONFIG_H_ diff --git a/pruebas/sdcc/8051_dp8390_echo_server/debug.h b/pruebas/sdcc/8051_dp8390_echo_server/debug.h new file mode 100644 index 0000000..9d70530 --- /dev/null +++ b/pruebas/sdcc/8051_dp8390_echo_server/debug.h @@ -0,0 +1,45 @@ +// 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_ diff --git a/pruebas/sdcc/8051_dp8390_echo_server/dp8390.c b/pruebas/sdcc/8051_dp8390_echo_server/dp8390.c new file mode 100644 index 0000000..5c472a5 --- /dev/null +++ b/pruebas/sdcc/8051_dp8390_echo_server/dp8390.c @@ -0,0 +1,469 @@ +// 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); +} + diff --git a/pruebas/sdcc/8051_dp8390_echo_server/dp8390.h b/pruebas/sdcc/8051_dp8390_echo_server/dp8390.h new file mode 100644 index 0000000..dfd19d6 --- /dev/null +++ b/pruebas/sdcc/8051_dp8390_echo_server/dp8390.h @@ -0,0 +1,236 @@ +// 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 diff --git a/pruebas/sdcc/8051_dp8390_echo_server/eth.c b/pruebas/sdcc/8051_dp8390_echo_server/eth.c new file mode 100644 index 0000000..ee40aac --- /dev/null +++ b/pruebas/sdcc/8051_dp8390_echo_server/eth.c @@ -0,0 +1,60 @@ +// 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(); +} + diff --git a/pruebas/sdcc/8051_dp8390_echo_server/eth.h b/pruebas/sdcc/8051_dp8390_echo_server/eth.h new file mode 100644 index 0000000..f65f5d2 --- /dev/null +++ b/pruebas/sdcc/8051_dp8390_echo_server/eth.h @@ -0,0 +1,58 @@ +// vim: set et sw=4 sts=4 : + +#ifndef _ETH_H_ +#define _ETH_H_ + +#include "types.h" + +/** @file + * Estructura de un frame ethernet. + * + *
+ * /--- 8 ----/--- 6 ----/--- 6 ----/- 2 --/- 46-1500 -/- 4 -/  bytes
+ * +----------+----------+----------+------+-----------+-----+
+ * | preamble | dst addr | src addr | type |   data    | crc |
+ * +----------+----------+----------+------+-----------+-----+
+ * 
+ * + * 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_ */ diff --git a/pruebas/sdcc/8051_dp8390_echo_server/etherled.Uv2 b/pruebas/sdcc/8051_dp8390_echo_server/etherled.Uv2 new file mode 100644 index 0000000..a07d2cb --- /dev/null +++ b/pruebas/sdcc/8051_dp8390_echo_server/etherled.Uv2 @@ -0,0 +1,136 @@ +### uVision2 Project, (C) Keil Software +### Do not modify ! + +Target (etherled), 0x0000 // Tools: 'MCS-51' + +Group (net) +Group (common) + +File 1,5,<.\netdev.h> 0x0 +File 1,5,<.\dp8390.h> 0x0 +File 1,1,<.\dp8390.c> 0x0 +File 1,5,<.\eth.h> 0x0 +File 1,1,<.\eth.c> 0x0 +File 1,5,<.\ip.h> 0x0 +File 1,1,<.\ip.c> 0x0 +File 1,5,<.\udp.h> 0x0 +File 1,1,<.\udp.c> 0x0 +File 2,1,<.\main.c> 0x0 +File 2,5,<.\reg51.h> 0x0 +File 2,2,<.\startup.a51> 0x0 +File 2,5,<.\types.h> 0x0 +File 2,5,<.\reg51keil.h> 0x0 + + +Options 1,0,0 // Target 'etherled' + Device (AT89S8252) + Vendor (Atmel) + Cpu (IRAM(0-0xFF) IROM(0-0x1FFF) CLOCK(24000000) MODA2) + FlashUt () + StupF ("LIB\STARTUP.A51" ("Standard 8051 Startup Code")) + FlashDR () + DevID () + Rgf (REG8252.H) + Mem () + C () + A () + RL () + OH () + DBC_IFX () + DBC_CMS () + DBC_AMS () + DBC_LMS () + UseEnv=0 + EnvBin () + EnvInc () + EnvLib () + EnvReg (ÿAtmel\) + OrgReg (ÿAtmel\) + TgStat=27 + OutDir (.\) + OutName (etherled) + GenApp=1 + GenLib=0 + GenHex=1 + Debug=1 + Browse=1 + LstDir (.\) + HexSel=0 + MG32K=0 + TGMORE=0 + RunUsr 0 0 <> + RunUsr 1 0 <> + BrunUsr 0 0 <> + BrunUsr 1 0 <> + SVCSID <> + MODEL5=0 + RTOS5=0 + ROMSZ5=2 + DHOLD5=0 + XHOLD5=0 + T51FL=208 + XT51FL=0 + CBANKS5=0 + XBANKS5=0 + RCB51 { 0,0,0,0,0,255,255,0,0 } + RXB51 { 0,0,0,0,0,0,0,0,0 } + 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 } + 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 } + IRO51 { 1,0,0,0,0,0,32,0,0 } + IRA51 { 0,0,0,0,0,0,1,0,0 } + XRA51 { 0,0,0,0,0,0,0,0,0 } + XRA512 { 0,0,0,0,0,0,0,0,0 } + IROM512 { 0,0,0,0,0,0,0,0,0 } + C51FL=21630224 + C51VA=0 + C51MSC () + C51DEF () + C51UDF () + INCC5 () + AX51FL=4 + AX51MSC () + AX51SET () + AX51RST () + INCA5 () + PropFld { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } + IncBld=1 + AlwaysBuild=0 + GenAsm=0 + AsmAsm=0 + PublicsOnly=0 + StopCode=3 + CustArgs () + LibMods () + BankNo=65535 + LX51FL=292 + LX51OVL () + LX51MSC () + LX51DWN () + LX51LFI () + LX51ASN () + LX51RES () + LX51CCL () + LX51UCL () + LX51CSC () + LX51UCS () + LX51COB () + LX51XDB () + LX51PDB () + LX51BIB () + LX51DAB () + LX51IDB () + LX51PRC () + LX51STK () + LX51COS () + LX51XDS () + LX51BIS () + LX51DAS () + LX51IDS () + OPTDL (S8051.DLL)()(DP51.DLL)(-p8252)(S8051.DLL)()(TP51.DLL)(-p8252) + OPTDBG 48125,0,()()()()()()()()()() ()()()() + FLASH1 { 0,0,0,0,0,0,0,0,255,255,255,255,0,0,0,0,0,0,0,0 } + FLASH2 () + FLASH3 () + FLASH4 () +EndOpt + diff --git a/pruebas/sdcc/8051_dp8390_echo_server/ip.c b/pruebas/sdcc/8051_dp8390_echo_server/ip.c new file mode 100644 index 0000000..f60c3d4 --- /dev/null +++ b/pruebas/sdcc/8051_dp8390_echo_server/ip.c @@ -0,0 +1,169 @@ +// 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(); +} + diff --git a/pruebas/sdcc/8051_dp8390_echo_server/ip.h b/pruebas/sdcc/8051_dp8390_echo_server/ip.h new file mode 100644 index 0000000..a40693a --- /dev/null +++ b/pruebas/sdcc/8051_dp8390_echo_server/ip.h @@ -0,0 +1,71 @@ +// 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_ */ diff --git a/pruebas/sdcc/8051_dp8390_echo_server/leds.h b/pruebas/sdcc/8051_dp8390_echo_server/leds.h new file mode 100644 index 0000000..ea8c083 --- /dev/null +++ b/pruebas/sdcc/8051_dp8390_echo_server/leds.h @@ -0,0 +1,33 @@ +// 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_ diff --git a/pruebas/sdcc/8051_dp8390_echo_server/main.c b/pruebas/sdcc/8051_dp8390_echo_server/main.c new file mode 100644 index 0000000..60ed5db --- /dev/null +++ b/pruebas/sdcc/8051_dp8390_echo_server/main.c @@ -0,0 +1,106 @@ +// 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(); + } +} + diff --git a/pruebas/sdcc/8051_dp8390_echo_server/netdev.h b/pruebas/sdcc/8051_dp8390_echo_server/netdev.h new file mode 100644 index 0000000..27bbb3b --- /dev/null +++ b/pruebas/sdcc/8051_dp8390_echo_server/netdev.h @@ -0,0 +1,86 @@ +// 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_ diff --git a/pruebas/sdcc/8051_dp8390_echo_server/reg51.h b/pruebas/sdcc/8051_dp8390_echo_server/reg51.h new file mode 100644 index 0000000..9f08bd6 --- /dev/null +++ b/pruebas/sdcc/8051_dp8390_echo_server/reg51.h @@ -0,0 +1,10 @@ +#ifndef _REG51_H_ +#define _REG51_H_ + +#ifdef SDCC +# include "reg51sdcc.h" +#else +# include "reg51keil.h" +#endif + +#endif /* _REG51_H_ */ diff --git a/pruebas/sdcc/8051_dp8390_echo_server/reg51keil.h b/pruebas/sdcc/8051_dp8390_echo_server/reg51keil.h new file mode 100644 index 0000000..ed182bf --- /dev/null +++ b/pruebas/sdcc/8051_dp8390_echo_server/reg51keil.h @@ -0,0 +1,82 @@ +#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_ */ diff --git a/pruebas/sdcc/8051_dp8390_echo_server/reg51sdcc.h b/pruebas/sdcc/8051_dp8390_echo_server/reg51sdcc.h new file mode 100644 index 0000000..ca1bbaf --- /dev/null +++ b/pruebas/sdcc/8051_dp8390_echo_server/reg51sdcc.h @@ -0,0 +1,82 @@ +#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_ */ diff --git a/pruebas/sdcc/8051_dp8390_echo_server/startup.a51 b/pruebas/sdcc/8051_dp8390_echo_server/startup.a51 new file mode 100644 index 0000000..d9a8780 --- /dev/null +++ b/pruebas/sdcc/8051_dp8390_echo_server/startup.a51 @@ -0,0 +1,157 @@ +$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 , STARTUP.OBJ +; +;------------------------------------------------------------------------------ +; +; 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 diff --git a/pruebas/sdcc/8051_dp8390_echo_server/types.h b/pruebas/sdcc/8051_dp8390_echo_server/types.h new file mode 100644 index 0000000..c3fac6a --- /dev/null +++ b/pruebas/sdcc/8051_dp8390_echo_server/types.h @@ -0,0 +1,48 @@ +#ifndef _TYPES_H_ +#define _TYPES_H_ + +#if 0 /* i386 */ +#include + +/** 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_ */ diff --git a/pruebas/sdcc/8051_dp8390_echo_server/udp.c b/pruebas/sdcc/8051_dp8390_echo_server/udp.c new file mode 100644 index 0000000..09b9bfc --- /dev/null +++ b/pruebas/sdcc/8051_dp8390_echo_server/udp.c @@ -0,0 +1,157 @@ +// 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; +} + diff --git a/pruebas/sdcc/8051_dp8390_echo_server/udp.h b/pruebas/sdcc/8051_dp8390_echo_server/udp.h new file mode 100644 index 0000000..d08d03f --- /dev/null +++ b/pruebas/sdcc/8051_dp8390_echo_server/udp.h @@ -0,0 +1,100 @@ +// vim: set et sw=4 sts=4 : + +#ifndef _UDP_H_ +#define _UDP_H_ + +#include "types.h" + +/** @file + * Datagrama UDP. + * + *
+ *  0      7 8     15 16    23 24    31  
+ * +--------+--------+--------+--------+ 
+ * |     Source      |   Destination   | 
+ * |      Port       |      Port       | 
+ * +--------+--------+--------+--------+ 
+ * |                 |                 | 
+ * |     Length      |    Checksum     | 
+ * +--------+--------+--------+--------+ 
+ * |                                     
+ * |          data octets ...            
+ * +---------------- ...                 
+ * 
+ * + * 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': + * + *
+ *  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)
+ * +---------------- ...                 
+ * 
+ */ + +/** 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_ */ -- 2.43.0