--- /dev/null
+
+CC=sdcc
+
+LD=sdcc
+
+all: el.hex
+
+reg51.h: reg51sdcc.h
+
+netdev.h: types.h
+
+dp8390.h: types.h reg51.h netdev.h
+
+dp8390.rel: dp8390.c dp8390.h
+ $(CC) $(CFLAGS) -c dp8390.c
+
+eth.rel: eth.c eth.h netdev.h types.h
+ $(CC) $(CFLAGS) -c eth.c
+
+ip.rel: ip.c ip.h netdev.h types.h
+ $(CC) $(CFLAGS) -c ip.c
+
+udp.rel: udp.c udp.h ip.h netdev.h types.h
+ $(CC) $(CFLAGS) -c udp.c
+
+main.rel: main.c netdev.h eth.h ip.h udp.h
+ $(CC) $(CFLAGS) -c main.c
+
+el.hex: main.rel dp8390.rel eth.rel ip.rel udp.rel
+ $(LD) $(LDFLAGS) -o el.hex main.rel dp8390.rel eth.rel ip.rel udp.rel
+
+clean:
+ @rm -vf el.hex *.rel *.asm *.lst *.map *.lnk *.mem *.sym
+
+.PHONY: clean
+++ /dev/null
-#include "eth.h"
-
-/** tipos de paquetes transportados soportados */
-enum { IP = 0x0800, ARP = 0x0806 };
-
-byte mac_addr_local[MAC_ADDR_SIZE];
-
-byte mac_addr_remote[MAC_ADDR_SIZE];
-
-bool eth_frame_arp;
-
-bool eth_read_frame_header()
-{
- /* variable para iterar */
- byte i;
- /* vemos si es para nosotros */
- for (i = 0; i < MAC_ADDR_SIZE; ++i)
- if (mac_addr_local[i] != net_getb())
- return false; /* no es para nosotros (drop) */
- /* obtenemos MAC de origen */
- for (i = 0; i < MAC_ADDR_SIZE; ++i)
- mac_addr_remote[i] = net_getb();
- /* obtenemos tipo de protocolo transportado por el frame, (sólo
- * aceptamos IP y ARP) */
- switch (net_getw())
- {
- case IP:
- eth_frame_arp = false;
- break;
- case ARP:
- eth_frame_arp = true;
- break;
- default:
- return false; /* drop */
- }
- return true;
-}
-
-void eth_write_frame_header()
-{
- /* variable para iterar */
- byte i;
- /* mandamos como MAC de destino la remota */
- for (i = 0; i < ETH_MAC_SIZE; ++i)
- net_putb(mac_addr_remote[i]);
- /* y como fuente la nuestra */
- for (i = 0; i < ETH_MAC_SIZE; ++i)
- net_putb(mac_addr_local[i]);
- /* escribimos el tipo de paquete que transporta el frame */
- net_putw(eth_frame_arp ? TYPE_ARP : TYPE_IP);
-}
-
+++ /dev/null
-#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;
-
-bool ip_proto_icmp;
-
-/* para calcular checksum */
-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;
- /* reseteamos checksum */
- checksum = 0;
- /* versión y tamaño de cabecera vienen en el 1er byte */
- h = net_getb();
- /* sólo soportamos versión 4 */
- if ((h >> 4) != 4)
- return false; /* drop */
- /* tamaño de cabecera */
- if ((h & 0x0F) != 5) /* no aceptamos opciones raras =) */
- return false; /* drop */
- /* ignoramos el TOS y vamos calculando checksum */
- sum(WORD(h, net_getb()));
- /* obtenemos tamaño del paquete */
- if (h = net_getb()) /* tiene más de 255 bytes (no lo soportamos) */
- return false; /* drop */
- ip_packet_len = net_getb(); /* 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(net_getw());
- /* 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 = net_getb();
- l = net_getb();
- if ((h & 0x3F) || l)
- return false; /* drop */
- /* 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 = net_getb();
- /* protocolo (sólo soportamos UDP e ICMP) */
- l = net_getb();
- switch (l)
- {
- case ICMP:
- ip_proto_icmp = true;
- break;
- case UDP:
- ip_proto_icmp = false;
- break;
- default:
- return false; /* drop */
- }
- /* sigo calculando checksum */
- sum(WORD(h, l));
- /* obtenemos checksum y seguimos el cálculo */
- sum(net_getw());
- /* obtenemos IP de origen (mientras seguimos calculando el checksum) */
- for (l = 0; l < IP_ADDR_SIZE; ++l)
- {
- ip_addr_remote[l] = net_getb();
- 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) */
- for (l = 0; l < IP_ADDR_SIZE; ++l)
- {
- if (ip_addr_local[l] != net_getb())
- return false; /* drop (no es para nosotros) */
- if (l % 2)
- sum(WORD(h, ip_addr_local[l]));
- else
- h = ip_addr_local[l];
- }
- /* verificamos checksum */
- if (~checksum)
- return false; /* checksum malo, drop */
- return true;
-}
-
-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;
- /* versión (4) y tamaño de cabecera (5 words de 4 bytes = 20 bytes) */
- net_putb(h = 0x45);
- /* TOS (0xc0 = Internetwork Control, 0x00 = normal) */
- l = ip_proto_icmp ? 0xc0 : 0x00;
- net_putb(l);
- sum(WORD(h, l)); /* actualizamos checksum */
- /* escribimos tamaño del paquete */
- net_putb(h = 0x00); /* nunca vamos a mandar algo de más de 255 bytes */
- net_putb(ip_packet_len);
- sum(WORD(h, ip_packet_len)); /* actualizamos checksum */
- /* identificación (sirve para reensamblar paquetes) */
- net_putw(id);
- sum(id); /* actualizamos checksum */
- /* pedimos que no se fragmente */
- net_putb(h = 0x40); /* Don't Fragment (DF) = 1 */
- net_putb(l = 0x00); /* offset de fragmento = 0 */
- sum(WORD(h, l)); /* actualizamos checksum */
- /* TTL de 64 saltos porque está de moda */
- net_putb(h = 0x40);
- /* protocolo (sólo soportamos UDP e ICMP) */
- l = ip_proto_icmp ? ICMP : UDP;
- net_putb(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 */
- net_putw(~checksum);
- /* ahora sí, continuamos poniendo las direcciones */
- /* ponemos como dirección IP de origen la nuestra */
- for (i = 0; i < IP_ADDR_SIZE; ++i)
- net_putb(ip_addr_local[i]);
- /* IP de destino, la remota */
- for (i = 0; i < IP_ADDR_SIZE; ++i)
- net_putb(ip_addr_remote[i]);
-}
-
+++ /dev/null
-#include "net.h"
-
-uint16 net_getw()
-{
- byte b = net_getb() << 8;
- return b + net_getb();
-}
-
-void net_putw(uint16 w)
-{
- net_putb(HIGH(w));
- net_putb(LOW(w));
-}
-
+++ /dev/null
-#ifndef _NET_H_
-#define _NET_H_
-
-#include "types.h"
-
-/** lee un byte de la placa de red */
-byte net_getb();
-
-/** escribe un byte a la placa de red */
-void net_putb(byte);
-
-/** lee un word de la placa de red */
-uint16 net_getw();
-
-/** escribe un word a la placa de red */
-void net_putw(uint16);
-
-#endif /* _NET_H_ */
+++ /dev/null
-#include "ip.h"
-#include "udp.h"
-
-uint16 udp_port_local;
-
-uint16 udp_port_remote;
-
-byte udp_dgram_len;
-
-/* para calcular checksum */
-uint16 checksum;
-
-/* agrega un word al checksum calculado */
-void udp_checksum_sum(uint16 w)
-{
- checksum += w;
- if (checksum < w) /* corrección de carry (hubo OV) */
- ++checksum;
-}
-
-/* indica si el checksum calculado está ok */
-bool udp_checksum_ok()
-{
- return ~checksum;
-}
-
-bool udp_read_dgram_header()
-{
- /* reseteamos checksum */
- checksum = 0;
- /* el UDP tiene un checksum que incluye parte de la cabecera IP */
- /* ip de origen */
- udp_checksum_sum(WORD(ip_addr_remote[0], ip_addr_remote[1]));
- udp_checksum_sum(WORD(ip_addr_remote[2], ip_addr_remote[3]));
- /* ip de destino */
- udp_checksum_sum(WORD(ip_addr_local[0], ip_addr_local[1]));
- udp_checksum_sum(WORD(ip_addr_local[2], ip_addr_local[3]));
- /* protocolo expresado en 16 bits (0x11 es UDP) */
- udp_checksum_sum(0x11);
- /* tamaño del paquete UDP (sin las cabeceras que son 20 bytes) */
- udp_checksum_sum(ip_packet_len - 20);
- /* de ahora en más todos los datos del checksum corresponden a UDP */
- /* puerto origen (remoto) */
- udp_port_remote = net_getw();
- /* agregamos puerto de origen al checksum */
- udp_checksum_sum(udp_port_remote);
- /* sólo aceptamos datagramas a nuestro puerto */
- if (net_getw() != udp_port_local)
- return false; /* drop */
- /* agregamos puerto de destino al checksum */
- udp_checksum_sum(udp_port_local);
- /* tamaño del datagrama */
- if (net_getb()) /* no soportamos más de 255 bytes */
- return false; /* drop */
- udp_dgram_len = net_getb();
- /* agregamos tamaño al checksum */
- udp_checksum_sum(udp_dgram_len);
- /* agregamos checksum al checksum */
- udp_checksum_sum(net_getw());
- /* 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 */
- return true;
-}
-
-void udp_write_dgram_header()
-{
- /* puerto origen */
- net_putw(udp_port_local);
- /* puerto destino */
- net_putw(udp_port_remote);
- /* tamaño del datagrama */
- net_putb(0x00); /* parte alta en 0 porque no soportamos más de 255 */
- net_putb(udp_dgram_len);
- /* indicamos que no se usa checksum */
- net_putw(0x0000);
- return true;
-}
-
--- /dev/null
+// vim: set et sw=4 sts=4 :
+
+#ifndef _CONFIG_H_
+#define _CONFIG_H_
+
+#define NET_RECV_CALLBACK eth_recv
+
+#define
+
+#endif // _CONFIG_H_
--- /dev/null
+// vim: set et sw=4 sts=4 :
+
+#include "eth.h"
+#include "dp8390.h"
+
+#ifdef DEBUG
+void sleep(unsigned char);
+#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
+#endif
+
+/// Datos persistentes del módulo
+static union
+{
+ byte send_len; ///> Tamaño del frame que será enviado
+ byte next_pkt; ///> Próximo frame a obtener
+}
+persistent;
+
+/// 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()
+{
+ // 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
+ eth_addr_local[0] = read_reg(RDMA);
+ read_reg(RDMA); // Ignoramos porque viene como un word
+ eth_addr_local[1] = read_reg(RDMA);
+ read_reg(RDMA); // Ignoramos porque viene como un word
+ eth_addr_local[2] = read_reg(RDMA);
+ read_reg(RDMA); // Ignoramos porque viene como un word
+ eth_addr_local[3] = read_reg(RDMA);
+ read_reg(RDMA); // Ignoramos porque viene como un word
+ eth_addr_local[4] = read_reg(RDMA);
+ read_reg(RDMA); // Ignoramos porque viene como un word
+ eth_addr_local[5] = 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
+ write_reg(PAR0, eth_addr_local[0]);
+ write_reg(PAR1, eth_addr_local[1]);
+ write_reg(PAR2, eth_addr_local[2]);
+ write_reg(PAR3, eth_addr_local[3]);
+ write_reg(PAR4, eth_addr_local[4]);
+ write_reg(PAR5, eth_addr_local[5]);
+
+ // 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
+ * @param len Tamaño del frame a enviar
+ */
+void netdev_send_start()
+{
+ persistent.send_len = 0;
+ // Wait until pending transmit operation completes.
+ while(read_reg(CR) & TXP) continue;
+
+ // Set remote DMA start address registers to indicate where to load packet.
+ write_reg(RSAR0, 0u);
+ write_reg(RSAR1, TX_PAGE_START);
+
+ // Set remote DMA byte count registers to indicate length of packet load.
+ write_reg(RBCR0, MAX_PACKET_LEN); // Tamaño máximo en principio
+ write_reg(RBCR1, 0u);
+
+ // Initiate DMA transfer of uip_buf & uip_appdata buffers to RTL8019AS.
+ write_reg(CR, WRITE | STA);
+}
+
+/** 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_send_byte(byte b)
+{
+ persistent.send_len++;
+ 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_send_word(uint16 w)
+{
+ persistent.send_len += 2;
+ write_reg(RDMA, HIGH(w));
+ write_reg(RDMA, LOW(w));
+}
+
+/** Finaliza el envío del frame
+ * @precond netdev_send_start() debe haber sido ejecutada
+ */
+void netdev_send_end()
+{
+ // Abort/ complete DMA operation.
+ ABORT_DMA(START);
+
+ // 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 (persistent.send_len < MIN_PACKET_LEN)
+ {
+ persistent.send_len = MIN_PACKET_LEN;
+ }
+
+ // Set transmit byte count registers to indicate packet length.
+ write_reg(TBCR0, LOW(persistent.send_len));
+ write_reg(TBCR1, 0u);
+
+ // Issue command for RTL8019AS to transmit packet from it's local buffer.
+ write_reg(CR, START | TXP);
+}
+
+/** Comienza la lectura de un nuevo frame
+ * @return Cantidad de bytes del frame leído
+ */
+byte netdev_recv_start()
+{
+ // Check if the rx buffer has overflowed.
+ if (read_reg(ISR) & OVW)
+ {
+ byte current;
+
+ // 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);
+
+ if (read_reg(BNRY) == current)
+ {
+#ifdef DEBUG
+ leds1 = ~0x01;
+ leds2 = ~read_reg(ISR);
+ sleep(5);
+ leds1 = ~0x02;
+ leds2 = ~read_reg(BNRY);
+ sleep(5);
+ leds1 = ~0x04;
+ leds2 = ~current;
+ sleep(5);
+#endif
+ reset();
+ }
+ return 0u;
+ }
+ // Check if there is a packet in the rx buffer.
+ else if (read_reg(ISR) & PRX)
+ {
+ struct buf_hdr_t
+ {
+ byte status; // Estado del frame recibido
+ byte next; // Offset del próximo frame
+ uint16 len; // Tamaño del frame
+ }
+ buf_hdr;
+ byte current;
+ byte bnry;
+
+ // Retrieve packet header. (status, next_ptr, length_l, length_h)
+
+ // Set remote DMA start address registers to packet header.
+ bnry = read_reg(BNRY) + 1;
+ if (bnry >= RX_PAGE_STOP)
+ bnry = RX_PAGE_START;
+ write_reg(RSAR0, 0u);
+ write_reg(RSAR1, bnry);
+
+ // 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(bnry == current)
+ {
+ // Clear packet received interrupt flag.
+ write_reg(ISR, PRX | RXE);
+ return 0u;
+ }
+
+ // Set remote DMA byte count registers to packet header length.
+ write_reg(RBCR0, sizeof(struct buf_hdr_t));
+ write_reg(RBCR1, 0u);
+
+ // Clear remote DMA complete interrupt status register bit.
+ write_reg(ISR, RDC);
+
+ // Initiate DMA transfer of packet header.
+ write_reg(CR, READ);
+
+ // Packet status.
+ buf_hdr.status = read_reg(RDMA);
+
+ // Save next packet pointer.
+ buf_hdr.next = persistent.next_pkt = read_reg(RDMA);
+
+ // Retrieve packet data length and subtract CRC bytes.
+ buf_hdr.len = read_reg(RDMA) - sizeof(struct buf_hdr_t);
+
+ // Si es muy grande, muy chico o hubo error, lo descartamos
+ if ((buf_hdr.len < MIN_PACKET_LEN) || (buf_hdr.len > MAX_PACKET_LEN)
+ || ((buf_hdr.status & 0x0F) != RXSOK)
+ || read_reg(RDMA)) // Parte alta del tamaño
+ {
+ ABORT_DMA(START); // Termina DMA
+ write_reg(BNRY, buf_hdr.next - 1); // Pasa al próximo frame
+ return 0;
+ }
+
+ // Abort/ complete DMA operation.
+ ABORT_DMA(START);
+
+ // Set remote DMA start address registers to packet data.
+ write_reg(RSAR0, sizeof(struct buf_hdr_t));
+ write_reg(RSAR1, bnry);
+
+ // Set remote DMA byte count registers to packet data length.
+ write_reg(RBCR0, buf_hdr.len);
+ write_reg(RBCR1, 0u);
+
+ // Initiate DMA transfer of packet data.
+ write_reg(CR, READ);
+
+ return buf_hdr.len;
+ }
+ return 0;
+}
+
+/** Lee un byte del buffer de la placa de red
+ * @precond netdev_recv_start() debe haber sido ejecutada
+ */
+byte netdev_recv_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_recv_word()
+{
+ uint16 w = netdev_recv_byte() << 8;
+ return w + netdev_recv_byte();
+}
+
+/** Finaliza la lectura del frame
+ * @precond netdev_recv_start() debe haber sido ejecutada
+ */
+void netdev_recv_end()
+{
+ // Abort/ complete DMA operation.
+ ABORT_DMA(START);
+
+ // Advance boundary pointer to next packet start.
+ write_reg(BNRY, persistent.next_pkt - 1);
+}
+
--- /dev/null
+// vim: set et sw=4 sts=4 :
+
+#ifndef _DP8390_H_
+#define _DP8390_H_
+
+#include "reg51.h"
+#include "types.h"
+#include "netdev.h"
+
+// Configuración de puertos para comunicarse con la placa de red
+#define DATA_PORT P2 // Adjust this to suit hardware.
+#define DATA_PORT_MASK 0xFF // Máscara para leer del puerto
+#define ADDR_PORT P1 // Adjust this to suit hardware.
+#define ADDR_PORT_MASK 0x1F // Máscara de direcciones (para no cambiar
+ // bits que no se usan)
+#ifdef SDCC
+sbit at 0xB2 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
+
+// Configuración de paǵinas de buffers
+#define TX_PAGE_START 0x40 // 0x4000 Tx buffer: 6 * 256 = 1536 bytes
+#define RX_PAGE_START 0x46 // 0x4600 Rx buffer: 26 * 256 = 6656 bytes
+#define RX_PAGE_STOP 0x60 // 0x6000
+
+// 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 =)
+
+// 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 0x40 // Page 1
+ #define PAGE2 0x80 // Page 2
+ #define PAGE3 0xC0 // Page 3 (Reserved!)
+ #define ABORT 0x20 // Abort/Complete DMA
+ #define READ 0x0A // Remote Read (RD0 | STA)
+ #define WRITE 0x12 // Remote Write (RD1 | STA)
+ #define SENDPKT 0x1A // Send Packet (RD0 | RD1 | STA)
+ #define STOP 0x21 // STP | ABORT
+ #define START 0x22 // STA | ABORT
+
+#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 0x02 // Loopback mode 1
+ #define MODE2 0x04 // Loopback mode 2
+ #define MODE3 0x06 // 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 PAR0 REG_BASE + 0x01 // Physical address register 0
+#define PAR1 REG_BASE + 0x02 // Physical address register 1
+#define PAR2 REG_BASE + 0x03 // Physical address register 2
+#define PAR3 REG_BASE + 0x04 // Physical address register 3
+#define PAR4 REG_BASE + 0x05 // Physical address register 4
+#define PAR5 REG_BASE + 0x06 // Physical address register 5
+#define CURR REG_BASE + 0x07 // Current receive buffer page
+#define MAR0 REG_BASE + 0x08
+#define MAR1 REG_BASE + 0x09
+#define MAR2 REG_BASE + 0x0A
+#define MAR3 REG_BASE + 0x0B
+#define MAR4 REG_BASE + 0x0C
+#define MAR5 REG_BASE + 0x0D
+#define MAR6 REG_BASE + 0x0E
+#define MAR7 REG_BASE + 0x0F
+
+// Page 2 read only registers.
+// Each previously defined in page 0 write only.
+//#define PSTART REG_BASE + 0x01
+//#define PSTOP REG_BASE + 0x02
+//#define TPSR REG_BASE + 0x04
+//#define RCR REG_BASE + 0x0C
+//#define TCR REG_BASE + 0x0D
+//#define DCR REG_BASE + 0x0E
+//#define IMR REG_BASE + 0x0F
+
+// Page 3 read/write registers.
+#define _9346CR REG_BASE + 0x01 // 9346 EEPROM command register
+ // 9346 EEPROM command register bits
+ #define EEM1 0x80 // RTL8019AS operating mode bit 1
+ #define EEM0 0x40 // RTL8019AS operating mode bit 0
+ #define EECS 0x08 // 9346 EEPROM chip select bit
+ #define EESK 0x04 // 9346 EEPROM serial clock bit
+ #define EEDI 0x02 // 9346 EEPROM data input bit
+ #define EEDO 0x01 // 9346 EEPROM data output bit
+#define BPAGE REG_BASE + 0x02
+#define CONFIG1 REG_BASE + 0x04 // RTL9019AS config register 1
+ // RTL9019AS config register 1 bits
+ #define IRQEN 0x80 // IRQ enable bit (WR protected)
+ #define IRQS2 0x40 // IRQ line select bit 2
+ #define IRQS1 0x20 // IRQ line select bit 1
+ #define IRQS0 0x10 // IRQ line select bit 0
+ #define IOS3 0x08 // I/O base address select bit 3
+ #define IOS2 0x04 // I/O base address select bit 2
+ #define IOS1 0x02 // I/O base address select bit 1
+ #define IOS0 0x01 // I/O base address select bit 0
+#define CONFIG2 REG_BASE + 0x05 //
+ // RTL9019AS config register 2 bits
+ #define PL1 0x80 // Network medium type select bit 1
+ #define PL0 0x40 // Network medium type select bit 0
+ #define BSELB 0x20 // Boot ROM disable (WR protected)
+ #define BS4 0x10 // Boot ROM configuration bit 4
+ #define BS3 0x08 // Boot ROM configuration bit 3
+ #define BS2 0x04 // Boot ROM configuration bit 2
+ #define BS1 0x02 // Boot ROM configuration bit 1
+ #define BS0 0x01 // Boot ROM configuration bit 0
+#define CONFIG3 REG_BASE + 0x06 // RTL9019AS config register 3
+ // RTL9019AS config register 3 bits
+ #define PNP 0x80 // Plug & play mode indicator bit
+ #define FUDUP 0x40 // Full duplex mode select bit
+ #define LEDS1 0x20 // LED output select bit 1
+ #define LEDS0 0x10 // LED output select bit 0
+ #define SLEEP 0x04 // Sleep mode select bit
+ #define PWRDN 0x02 // Power down mode select bit
+ #define ACTIVEB 0x01 // Inverse of bit 0, PNP active reg
+
+// Page 3 read only registers.
+#define CONFIG0 REG_BASE + 0x03 // RTL9019AS config register 0
+ // RTL9019AS config register 0 bits
+ #define VERID1 0x80 // RTL9019AS version ID bit 1 (R/W)
+ #define VERID0 0x40 // RTL9019AS version ID bit 0 (R/W)
+ #define AUI 0x20 // AUI input pin state bit
+ #define PNPJP 0x10 // PNP jumper pin state bit
+ #define JP 0x08 // JP input pin state bit
+ #define BNC 0x04 // Thinnet mode indication bit
+#define CSNSAV REG_BASE + 0x08
+#define INTR REG_BASE + 0x0B
+#define CONFIG4 REG_BASE + 0x0D
+
+// Page 3 write only registers.
+#define TEST REG_BASE + 0x07
+#define HLTCLK REG_BASE + 0x09
+#define FMWP REG_BASE + 0x0C
+
+
+// Bits del byte de status del frame recibido
+#define RXSOK 0x01 /* Received a good packet */
+#define RXSCRC 0x02 /* CRC error */
+#define RXSFAE 0x04 /* frame alignment error */
+#define RXSFO 0x08 /* FIFO overrun */
+#define RXSMPA 0x10 /* missed pkt */
+#define RXSPHY 0x20 /* physical/multicast address */
+#define RXSDIS 0x40 /* receiver disable. set in monitor mode */
+#define RXSDEF 0x80 /* deferring */
+
+#endif
--- /dev/null
+// vim: set et sw=4 sts=4 :
+
+#include "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;
+ /* descarto MAC de destino, acepto broadcasts */
+ for (i = 0; i < ETH_ADDR_SIZE; ++i)
+ netdev_recv_byte();
+ /* obtenemos MAC de origen */
+ for (i = 0; i < ETH_ADDR_SIZE; ++i)
+ eth_addr_remote[i] = netdev_recv_byte();
+ /* obtenemos tipo de protocolo transportado por el frame, (sólo
+ * aceptamos IP y ARP) */
+ switch (netdev_recv_word())
+ {
+ case IP:
+ eth_proto = ETH_IP;
+ break;
+ case ARP:
+ eth_proto = ETH_ARP;
+ break;
+ default:
+ return false; /* drop */
+ }
+ return true;
+}
+
+void eth_write_frame_header()
+{
+ /* variable para iterar */
+ byte i;
+ /* mandamos como MAC de destino la remota */
+ for (i = 0; i < ETH_ADDR_SIZE; ++i)
+ netdev_send_byte(eth_addr_remote[i]);
+ /* y como fuente la nuestra */
+ for (i = 0; i < ETH_ADDR_SIZE; ++i)
+ netdev_send_byte(eth_addr_local[i]);
+ /* escribimos el tipo de paquete que transporta el frame */
+ netdev_send_word((eth_proto == ETH_IP) ? IP : ARP);
+}
+
+// vim: set et sw=4 sts=4 :
+
#ifndef _ETH_H_
#define _ETH_H_
#include "types.h"
-#include "net.h"
/** @file
* Estructura de un frame ethernet.
*/
/** Tamaño de dirección MAC (en bytes) */
-#define MAC_ADDR_SIZE 6
+#define ETH_ADDR_SIZE 6
+
+/** Tipos de frame ethernet */
+typedef enum { ETH_IP, ETH_ARP } eth_proto_t;
/** Dirección MAC nuestra */
-extern byte mac_addr_local[ETH_MAC_SIZE];
+extern byte eth_addr_local[ETH_ADDR_SIZE];
/** Dirección MAC de destino */
-extern byte mac_addr_remote[ETH_MAC_SIZE];
+extern byte eth_addr_remote[ETH_ADDR_SIZE];
-/** Indica si el frame transporta ARP (si no transporta IP) */
-extern bool eth_frame_arp; /* FIXME: debería ser un bit manejado con ASM supongo */
+/** Indica que protocolo está transportando el frame */
+extern eth_proto_t eth_proto;
/**
* Lee la cabecera del frame ethernet.
--- /dev/null
+### uVision2 Project, (C) Keil Software\r
+### Do not modify !\r
+\r
+Target (etherled), 0x0000 // Tools: 'MCS-51'\r
+\r
+Group (net)\r
+Group (common)\r
+\r
+File 1,5,<.\netdev.h><netdev.h> 0x0 \r
+File 1,5,<.\dp8390.h><dp8390.h> 0x0 \r
+File 1,1,<.\dp8390.c><dp8390.c> 0x0 \r
+File 1,5,<.\eth.h><eth.h> 0x0 \r
+File 1,1,<.\eth.c><eth.c> 0x0 \r
+File 1,5,<.\ip.h><ip.h> 0x0 \r
+File 1,1,<.\ip.c><ip.c> 0x0 \r
+File 1,5,<.\udp.h><udp.h> 0x0 \r
+File 1,1,<.\udp.c><udp.c> 0x0 \r
+File 2,1,<.\main.c><main.c> 0x0 \r
+File 2,5,<.\reg51.h><reg51.h> 0x0 \r
+File 2,2,<.\startup.a51><startup.a51> 0x0 \r
+File 2,5,<.\types.h><types.h> 0x0 \r
+\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=25\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,0,0,1,0 }\r
+ RXB51 { 0,0,0,0,0,0,0,0,0 }\r
+ OCM51 { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }\r
+ OCR51 { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }\r
+ IRO51 { 1,0,0,0,0,0,32,0,0 }\r
+ IRA51 { 0,0,0,0,0,0,1,0,0 }\r
+ XRA51 { 0,0,0,0,0,0,0,0,0 }\r
+ XRA512 { 0,0,0,0,0,0,0,0,0 }\r
+ IROM512 { 0,0,0,0,0,0,0,0,0 }\r
+ C51FL=21630224\r
+ C51VA=0\r
+ C51MSC ()\r
+ C51DEF ()\r
+ C51UDF ()\r
+ INCC5 ()\r
+ AX51FL=4\r
+ AX51MSC ()\r
+ AX51SET ()\r
+ AX51RST ()\r
+ INCA5 ()\r
+ PropFld { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }\r
+ IncBld=1\r
+ AlwaysBuild=0\r
+ GenAsm=0\r
+ AsmAsm=0\r
+ PublicsOnly=0\r
+ StopCode=3\r
+ CustArgs ()\r
+ LibMods ()\r
+ BankNo=65535\r
+ LX51FL=292\r
+ LX51OVL ()\r
+ LX51MSC ()\r
+ LX51DWN ()\r
+ LX51LFI ()\r
+ LX51ASN ()\r
+ LX51RES ()\r
+ LX51CCL ()\r
+ LX51UCL ()\r
+ LX51CSC ()\r
+ LX51UCS ()\r
+ LX51COB ()\r
+ LX51XDB ()\r
+ LX51PDB ()\r
+ LX51BIB ()\r
+ LX51DAB ()\r
+ LX51IDB ()\r
+ LX51PRC ()\r
+ LX51STK ()\r
+ LX51COS ()\r
+ LX51XDS ()\r
+ LX51BIS ()\r
+ LX51DAS ()\r
+ LX51IDS ()\r
+ OPTDL (S8051.DLL)()(DP51.DLL)(-p8252)(S8051.DLL)()(TP51.DLL)(-p8252)\r
+ OPTDBG 48125,0,()()()()()()()()()() ()()()()\r
+ FLASH1 { 0,0,0,0,0,0,0,0,255,255,255,255,0,0,0,0,0,0,0,0 }\r
+ FLASH2 ()\r
+ FLASH3 ()\r
+ FLASH4 ()\r
+EndOpt\r
+\r
--- /dev/null
+// vim: set et sw=4 sts=4 :
+
+#include "netdev.h"
+#include "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;
+ /* reseteamos checksum */
+ checksum = 0;
+ /* versión y tamaño de cabecera vienen en el 1er byte */
+ h = netdev_recv_byte();
+ /* sólo soportamos versión 4 */
+ if ((h >> 4) != 4)
+ return false; /* drop */
+ /* tamaño de cabecera */
+ if ((h & 0x0F) != 5) /* no aceptamos opciones raras =) */
+ return false; /* drop */
+ /* ignoramos el TOS y vamos calculando checksum */
+ sum(WORD(h, netdev_recv_byte()));
+ /* obtenemos tamaño del paquete */
+ if (h = netdev_recv_byte()) /* tiene más de 255 bytes (no lo soportamos) */
+ return false; /* drop */
+ ip_packet_len = netdev_recv_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_recv_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_recv_byte();
+ l = netdev_recv_byte();
+ if ((h & 0x3F) || l)
+ return false; /* drop */
+ /* 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_recv_byte();
+ /* protocolo (sólo soportamos UDP e ICMP) */
+ l = netdev_recv_byte();
+ switch (l)
+ {
+ case ICMP:
+ ip_proto = IP_ICMP;
+ break;
+ case UDP:
+ ip_proto = IP_UDP;
+ break;
+ default:
+ return false; /* drop */
+ }
+ /* sigo calculando checksum */
+ sum(WORD(h, l));
+ /* obtenemos checksum y seguimos el cálculo */
+ sum(netdev_recv_word());
+ /* obtenemos IP de origen (mientras seguimos calculando el checksum) */
+ for (l = 0; l < IP_ADDR_SIZE; ++l)
+ {
+ ip_addr_remote[l] = netdev_recv_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_recv_byte())
+ return false; /* drop (no es para nosotros) */
+ if (l % 2)
+ sum(WORD(h, ip_addr_local[l]));
+ else
+ h = ip_addr_local[l];
+ }
+ /* verificamos checksum */
+ if ((uint16)~checksum)
+ return false; /* checksum malo, drop */
+ return true;
+}
+
+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;
+ /* versión (4) y tamaño de cabecera (5 words de 4 bytes = 20 bytes) */
+ netdev_send_byte(h = 0x45);
+ /* TOS (0xc0 = Internetwork Control, 0x00 = normal) */
+ l = (ip_proto == IP_ICMP) ? 0xc0 : 0x00;
+ netdev_send_byte(l);
+ sum(WORD(h, l)); /* actualizamos checksum */
+ /* escribimos tamaño del paquete */
+ netdev_send_byte(h = 0x00); /* nunca vamos a mandar algo de más de 255 bytes */
+ netdev_send_byte(ip_packet_len);
+ sum(WORD(h, ip_packet_len)); /* actualizamos checksum */
+ /* identificación (sirve para reensamblar paquetes) */
+ netdev_send_word(id);
+ sum(id); /* actualizamos checksum */
+ /* pedimos que no se fragmente */
+ netdev_send_byte(h = 0x40); /* Don't Fragment (DF) = 1 */
+ netdev_send_byte(l = 0x00); /* offset de fragmento = 0 */
+ sum(WORD(h, l)); /* actualizamos checksum */
+ /* TTL de 64 saltos porque está de moda */
+ netdev_send_byte(h = 0x40);
+ /* protocolo (sólo soportamos UDP e ICMP) */
+ l = (ip_proto == IP_ICMP) ? ICMP : UDP;
+ netdev_send_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_send_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_send_byte(ip_addr_local[l]);
+ /* IP de destino, la remota */
+ for (l = 0; l < IP_ADDR_SIZE; ++l)
+ netdev_send_byte(ip_addr_remote[l]);
+}
+
+// vim: set et sw=4 sts=4 :
+
#ifndef _IP_H_
#define _IP_H_
#include "types.h"
-#include "net.h"
/** @file
* Paquete IP.
/** Tamaño de dirección IP (en bytes) */
#define IP_ADDR_SIZE 4
+/** 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];
extern byte ip_packet_len;
/** Indica si el paquete es ICMP (si no es UDP) */
-bool ip_proto_icmp;
+extern ip_proto_t ip_proto;
/** Lee la cabecera del paquete IP.
*
--- /dev/null
+// vim: set et sw=4 sts=4 :
+
+#include "netdev.h"
+#include "eth.h"
+#include "ip.h"
+#include "udp.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 \
+ { \
+ leds0 = ~LOW(word); \
+ leds1 = ~HIGH(word); \
+ } \
+ while (0)
+
+
+void sleep(unsigned char times)
+{
+ unsigned int i;
+ unsigned char j;
+ for (i = 0; i < 0xffff; ++i)
+ for (j = 0; j < times; ++j);
+}
+
+void main(void)
+{
+ // Apagamos todos los leds
+ leds(0x0000);
+
+ // 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 = 9000;
+
+ while (1) // Forever
+ {
+ uint16 len = netdev_recv_start();
+ if (!len) // no recibimos nada
+ {
+ netdev_recv_end();
+ continue; // vamos de nuevo!
+ }
+
+ // Parseamos cabecera ethernet
+ if (!eth_read_frame_header()) // No es un buen header
+ {
+ // Tenemos algo!
+ netdev_recv_end(); // Tiramos el paquete
+ continue; // Vamos de nuevo!
+ }
+
+ // Vemos que protocolo transporta
+ switch (eth_proto)
+ {
+ case ETH_ARP: // FIXME, implementar ARP!
+ netdev_recv_end(); // Tiramos el paquete
+ continue; // Vamos de nuevo!
+
+ case ETH_IP:
+ // Parseamos cabecera IP
+ if (!ip_read_packet_header()) // No es un buen header
+ {
+ netdev_recv_end(); // Tiramos el paquete
+ continue; // Vamos de nuevo!
+ }
+
+ // Vemos que protocolo transporta
+ switch (ip_proto)
+ {
+ case IP_ICMP: // FIXME, implementar ICMP!
+ netdev_recv_end(); // Tiramos el paquete
+ continue; // Vamos de nuevo!
+
+ case IP_UDP:
+ // Parseamos cabecera UDP
+ if (!udp_read_dgram_header()) // No es un buen header
+ {
+ netdev_recv_end(); // Tiramos el paquete
+ continue; // Vamos de nuevo!
+ }
+
+ // TODO
+ // Nuestro protocolo, por ahora un simple echo!
+ for (len = 8; len < udp_dgram_len; len += 2) // 8 por la cabecera UDP
+ {
+ leds(netdev_recv_word());
+ sleep(5);
+ }
+ }
+ }
+ }
+}
+
--- /dev/null
+// vim: set et sw=4 sts=4 :
+
+#ifndef _NETDEV_H_
+#define _NETDEV_H_
+
+#include "types.h"
+
+/** Inicializa dispositivo de red
+ * @return true si se inicializó correctamente, false si no
+ */
+bool netdev_init();
+
+/** Comienza el envío de un nuevo frame
+ * @param len Tamaño del frame a enviar
+ */
+void netdev_send_start();
+
+/** 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_send_byte(byte 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_send_word(uint16 w);
+
+/** Finaliza el envío del frame
+ * @precond netdev_send_start() debe haber sido ejecutada
+ */
+void netdev_send_end();
+
+/** Comienza la lectura de un nuevo frame
+ * @return Cantidad de bytes del frame leído
+ */
+byte netdev_recv_start();
+
+/** Lee un byte del buffer de la placa de red
+ * @precond netdev_recv_start() debe haber sido ejecutada
+ */
+byte netdev_recv_byte();
+
+/** Lee un word del buffer de la placa de red
+ * @precond netdev_recv_start() debe haber sido ejecutada
+ */
+uint16 netdev_recv_word();
+
+/** Finaliza la lectura del frame
+ * @precond netdev_recv_start() debe haber sido ejecutada
+ */
+void netdev_recv_end();
+
+#endif // _NETDEV_H_
--- /dev/null
+#ifndef _REG51_H_
+#define _REG51_H_
+
+#ifdef SDCC
+# include "reg51sdcc.h"
+#else
+# include "reg51keil.h"
+#endif
+
+#endif /* _REG51_H_ */
--- /dev/null
+$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
#ifndef _TYPES_H_
#define _TYPES_H_
-/** valores posibles de un booleano */
-enum { false = 0, true = 1 };
-
-/** convierte 2 bytes (high, low) en un word */
-#define WORD(high, low) ((high << 8) + low)
-
-/** convierte un word en 2 bytes */
-#define UNPACK(word, high, low) (high = (word >> 8), low = word)
-
-/** obtiene parte alta de un word */
-#define HIGH(word) (word >> 8)
-
-/** obtiene parte baja de un word */
-#define LOW(word) (word)
-
-#if 1 /* i386 */
+#if 0 /* i386 */
+#include <stdint.h>
/** booleano */
typedef unsigned char bool;
/** entero sin signo de 8 bits */
-typedef unsigned char byte;
+typedef uint8_t byte;
/** entero sin signo de 16 bits */
-typedef unsigned short uint16;
+typedef uint16_t uint16;
#endif
-#if 0 /* 8051 */
-
-/** booleano */
-typedef unsigned char bool;
+#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))
+
+/** convierte un word en 2 bytes */
+#define UNPACK(word, high, low) (high = (byte)(word >> 8), \
+ low = (byte)word & 0xFF)
+
+/** obtiene parte alta de un word */
+#define HIGH(word) ((byte)(word >> 8))
+
+/** obtiene parte baja de un word */
+#define LOW(word) ((byte)(word & 0xFF))
+
#endif /* _TYPES_H_ */
--- /dev/null
+// vim: set et sw=4 sts=4 :
+
+#include "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;
+
+/* 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()
+{
+ uint16 p;
+ /* reseteamos checksum */
+ checksum = 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_recv_word();
+ /* agregamos puerto de origen al checksum */
+ sum(udp_port_remote);
+ /* sólo aceptamos datagramas a nuestro puerto */
+ p = netdev_recv_word();
+ if (p != udp_port_local)
+ return false; /* drop */
+ /* agregamos puerto de destino al checksum */
+ sum(udp_port_local);
+ /* tamaño del datagrama */
+ if (netdev_recv_byte()) /* no soportamos más de 255 bytes */
+ return false; /* drop */
+ udp_dgram_len = netdev_recv_byte();
+ /* agregamos tamaño al checksum */
+ sum(udp_dgram_len);
+ /* agregamos checksum al checksum */
+ sum(netdev_recv_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 */
+ return true;
+}
+
+uint16 udp_read_word()
+{
+ uint16 w = netdev_recv_word();
+ sum(w);
+ return w;
+}
+
+bool udp_checksum_ok()
+{
+ return (uint16)~checksum;
+}
+
+void udp_write_dgram_header()
+{
+ /* reseteamos checksum */
+ checksum = 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_send_word(udp_port_local);
+ sum(udp_port_local);
+ /* puerto destino */
+ netdev_send_word(udp_port_remote);
+ sum(udp_port_remote);
+ /* tamaño del datagrama */
+ netdev_send_byte(0x00); /* parte alta en 0 porque no soportamos más de 255 */
+ netdev_send_byte(udp_dgram_len);
+ sum(WORD(0x00, udp_dgram_len));
+ /* indicamos que no se usa checksum */
+ netdev_send_word(0x0000);
+ sum(0x0000);
+}
+
+void udp_write_word(uint16 w)
+{
+ sum(w);
+ netdev_send_word(w);
+}
+
+void udp_write_checksum()
+{
+ // XXX TODO FIXME
+ return;
+}
+
+// vim: set et sw=4 sts=4 :
+
#ifndef _UDP_H_
#define _UDP_H_
#include "types.h"
-#include "net.h"
/** @file
* Datagrama UDP.
* es 8.
*
* El checksum se calcula utilizando algunos datos de la capa inferior e incluye
- * a los datos. Entonces es calculado sobre el siguiente paquete 'virtual':
+ * 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
* | Length | Checksum |
* +--------+--------+--------+--------+
* |
- * | data octets ...
+ * | data octets ... (padding)
* +---------------- ...
* </pre>
*/
*/
bool udp_read_dgram_header();
+/** Recibe un word del payload UDP chequeando el checksum. */
+uint16 udp_read_word();
+
+/* 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
*/
void udp_write_dgram_header();
+/** Escribe un word al payload UDP chequeando el checksum. */
+void udp_write_word(uint16 w);
+
+/* Escribe el checksum calculado al frame a enviar */
+void udp_write_checksum();
+
#endif /* _UDP_H_ */