]> git.llucax.com Git - z.facultad/66.09/etherled.git/commitdiff
Copio el estado actual del proyecto como un ejemplo de echo server.
authorLeandro Lucarella <llucax@gmail.com>
Fri, 9 Dec 2005 07:25:43 +0000 (07:25 +0000)
committerLeandro Lucarella <llucax@gmail.com>
Fri, 9 Dec 2005 07:25:43 +0000 (07:25 +0000)
20 files changed:
pruebas/sdcc/8051_dp8390_echo_server/Makefile [new file with mode: 0644]
pruebas/sdcc/8051_dp8390_echo_server/config.h [new file with mode: 0644]
pruebas/sdcc/8051_dp8390_echo_server/debug.h [new file with mode: 0644]
pruebas/sdcc/8051_dp8390_echo_server/dp8390.c [new file with mode: 0644]
pruebas/sdcc/8051_dp8390_echo_server/dp8390.h [new file with mode: 0644]
pruebas/sdcc/8051_dp8390_echo_server/eth.c [new file with mode: 0644]
pruebas/sdcc/8051_dp8390_echo_server/eth.h [new file with mode: 0644]
pruebas/sdcc/8051_dp8390_echo_server/etherled.Uv2 [new file with mode: 0644]
pruebas/sdcc/8051_dp8390_echo_server/ip.c [new file with mode: 0644]
pruebas/sdcc/8051_dp8390_echo_server/ip.h [new file with mode: 0644]
pruebas/sdcc/8051_dp8390_echo_server/leds.h [new file with mode: 0644]
pruebas/sdcc/8051_dp8390_echo_server/main.c [new file with mode: 0644]
pruebas/sdcc/8051_dp8390_echo_server/netdev.h [new file with mode: 0644]
pruebas/sdcc/8051_dp8390_echo_server/reg51.h [new file with mode: 0644]
pruebas/sdcc/8051_dp8390_echo_server/reg51keil.h [new file with mode: 0644]
pruebas/sdcc/8051_dp8390_echo_server/reg51sdcc.h [new file with mode: 0644]
pruebas/sdcc/8051_dp8390_echo_server/startup.a51 [new file with mode: 0644]
pruebas/sdcc/8051_dp8390_echo_server/types.h [new file with mode: 0644]
pruebas/sdcc/8051_dp8390_echo_server/udp.c [new file with mode: 0644]
pruebas/sdcc/8051_dp8390_echo_server/udp.h [new file with mode: 0644]

diff --git a/pruebas/sdcc/8051_dp8390_echo_server/Makefile b/pruebas/sdcc/8051_dp8390_echo_server/Makefile
new file mode 100644 (file)
index 0000000..1cdf384
--- /dev/null
@@ -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 (file)
index 0000000..71f1727
--- /dev/null
@@ -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 (file)
index 0000000..9d70530
--- /dev/null
@@ -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 (file)
index 0000000..5c472a5
--- /dev/null
@@ -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 (file)
index 0000000..dfd19d6
--- /dev/null
@@ -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 (file)
index 0000000..ee40aac
--- /dev/null
@@ -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 (file)
index 0000000..f65f5d2
--- /dev/null
@@ -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.
+ *
+ * <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_ */
diff --git a/pruebas/sdcc/8051_dp8390_echo_server/etherled.Uv2 b/pruebas/sdcc/8051_dp8390_echo_server/etherled.Uv2
new file mode 100644 (file)
index 0000000..a07d2cb
--- /dev/null
@@ -0,0 +1,136 @@
+### 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
diff --git a/pruebas/sdcc/8051_dp8390_echo_server/ip.c b/pruebas/sdcc/8051_dp8390_echo_server/ip.c
new file mode 100644 (file)
index 0000000..f60c3d4
--- /dev/null
@@ -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 (file)
index 0000000..a40693a
--- /dev/null
@@ -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 (file)
index 0000000..ea8c083
--- /dev/null
@@ -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 (file)
index 0000000..60ed5db
--- /dev/null
@@ -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 (file)
index 0000000..27bbb3b
--- /dev/null
@@ -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 (file)
index 0000000..9f08bd6
--- /dev/null
@@ -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 (file)
index 0000000..ed182bf
--- /dev/null
@@ -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 (file)
index 0000000..ca1bbaf
--- /dev/null
@@ -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 (file)
index 0000000..d9a8780
--- /dev/null
@@ -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 <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
diff --git a/pruebas/sdcc/8051_dp8390_echo_server/types.h b/pruebas/sdcc/8051_dp8390_echo_server/types.h
new file mode 100644 (file)
index 0000000..c3fac6a
--- /dev/null
@@ -0,0 +1,48 @@
+#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_ */
diff --git a/pruebas/sdcc/8051_dp8390_echo_server/udp.c b/pruebas/sdcc/8051_dp8390_echo_server/udp.c
new file mode 100644 (file)
index 0000000..09b9bfc
--- /dev/null
@@ -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 (file)
index 0000000..d08d03f
--- /dev/null
@@ -0,0 +1,100 @@
+// 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_ */