From: Sebastien Bourdeauducq Date: Tue, 30 Apr 2013 22:12:13 +0000 (+0200) Subject: software: put network code in a library X-Git-Tag: 24jan2021_ls180~2965 X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=2e3c2611a660daf5a846f5eba844ab65f688f7cf;p=litex.git software: put network code in a library --- diff --git a/software/bios/Makefile b/software/bios/Makefile index 77f60c97..6e464112 100644 --- a/software/bios/Makefile +++ b/software/bios/Makefile @@ -1,7 +1,7 @@ M2DIR=../.. include $(M2DIR)/software/common.mak -OBJECTS=crt0.o isr.o sdram.o main.o microudp.o tftp.o boot-helper.o boot.o dataflow.o +OBJECTS=crt0.o isr.o sdram.o main.o boot-helper.o boot.o dataflow.o all: bios.bin @@ -19,9 +19,10 @@ bios-rescue.elf: linker-rescue.ld $(OBJECTS) libs %.elf: $(LD) $(LDFLAGS) -T $< -N -o $@ $(OBJECTS) \ + -L$(M2DIR)/software/libnet \ -L$(M2DIR)/software/libbase \ -L$(M2DIR)/software/libcompiler-rt \ - -lbase -lcompiler-rt + -lnet -lbase -lcompiler-rt chmod -x $@ main.o: main.c @@ -36,6 +37,7 @@ main.o: main.c libs: make -C $(M2DIR)/software/libcompiler-rt make -C $(M2DIR)/software/libbase + make -C $(M2DIR)/software/libnet flash: bios.bin m1nor bios.bin diff --git a/software/bios/boot.c b/software/bios/boot.c index 6d703f82..93d2f279 100644 --- a/software/bios/boot.c +++ b/software/bios/boot.c @@ -11,8 +11,8 @@ #include -#include "microudp.h" -#include "tftp.h" +#include +#include #include "boot.h" extern int rescue; diff --git a/software/bios/main.c b/software/bios/main.c index 8b0939fc..146882b3 100644 --- a/software/bios/main.c +++ b/software/bios/main.c @@ -11,11 +11,11 @@ #include #include +#include #include "sdram.h" #include "dataflow.h" #include "boot.h" -#include "microudp.h" enum { CSR_IE = 1, CSR_IM, CSR_IP, CSR_ICC, CSR_DCC, CSR_CC, CSR_CFG, CSR_EBA, diff --git a/software/bios/microudp.c b/software/bios/microudp.c deleted file mode 100644 index 1b263260..00000000 --- a/software/bios/microudp.c +++ /dev/null @@ -1,400 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include - -#include "microudp.h" - -#define ETHERTYPE_ARP 0x0806 -#define ETHERTYPE_IP 0x0800 - -struct ethernet_header { - unsigned char preamble[8]; - unsigned char destmac[6]; - unsigned char srcmac[6]; - unsigned short ethertype; -} __attribute__((packed)); - -static void fill_eth_header(struct ethernet_header *h, const unsigned char *destmac, const unsigned char *srcmac, unsigned short ethertype) -{ - int i; - - for(i=0;i<7;i++) - h->preamble[i] = 0x55; - h->preamble[7] = 0xd5; - for(i=0;i<6;i++) - h->destmac[i] = destmac[i]; - for(i=0;i<6;i++) - h->srcmac[i] = srcmac[i]; - h->ethertype = ethertype; -} - -#define ARP_HWTYPE_ETHERNET 0x0001 -#define ARP_PROTO_IP 0x0800 - -#define ARP_OPCODE_REQUEST 0x0001 -#define ARP_OPCODE_REPLY 0x0002 - -struct arp_frame { - unsigned short hwtype; - unsigned short proto; - unsigned char hwsize; - unsigned char protosize; - unsigned short opcode; - unsigned char sender_mac[6]; - unsigned int sender_ip; - unsigned char target_mac[6]; - unsigned int target_ip; - unsigned char padding[18]; -} __attribute__((packed)); - -#define IP_IPV4 0x45 -#define IP_DONT_FRAGMENT 0x4000 -#define IP_TTL 64 -#define IP_PROTO_UDP 0x11 - -struct ip_header { - unsigned char version; - unsigned char diff_services; - unsigned short total_length; - unsigned short identification; - unsigned short fragment_offset; - unsigned char ttl; - unsigned char proto; - unsigned short checksum; - unsigned int src_ip; - unsigned int dst_ip; -} __attribute__((packed)); - -struct udp_header { - unsigned short src_port; - unsigned short dst_port; - unsigned short length; - unsigned short checksum; -} __attribute__((packed)); - -struct udp_frame { - struct ip_header ip; - struct udp_header udp; - char payload[]; -} __attribute__((packed)); - -struct ethernet_frame { - struct ethernet_header eth_header; - union { - struct arp_frame arp; - struct udp_frame udp; - } contents; -} __attribute__((packed)); - -typedef union { - struct ethernet_frame frame; - unsigned char raw[1532]; -} ethernet_buffer; - - -static unsigned int rxlen; -static ethernet_buffer *rxbuffer; -static ethernet_buffer *rxbuffer0; -static ethernet_buffer *rxbuffer1; -static unsigned int txlen; -static ethernet_buffer *txbuffer; - -static void send_packet(void) -{ - unsigned int crc; - - crc = crc32(&txbuffer->raw[8], txlen-8); - txbuffer->raw[txlen ] = (crc & 0xff); - txbuffer->raw[txlen+1] = (crc & 0xff00) >> 8; - txbuffer->raw[txlen+2] = (crc & 0xff0000) >> 16; - txbuffer->raw[txlen+3] = (crc & 0xff000000) >> 24; - txlen += 4; - minimac_tx_count_write(txlen); - minimac_tx_start_write(1); - while(!(minimac_ev_pending_read() & MINIMAC_EV_TX)); - minimac_ev_pending_write(MINIMAC_EV_TX); -} - -static unsigned char my_mac[6]; -static unsigned int my_ip; - -/* ARP cache - one entry only */ -static unsigned char cached_mac[6]; -static unsigned int cached_ip; - -static void process_arp(void) -{ - const struct arp_frame *rx_arp = &rxbuffer->frame.contents.arp; - struct arp_frame *tx_arp = &txbuffer->frame.contents.arp; - - if(rxlen < 68) return; - if(rx_arp->hwtype != ARP_HWTYPE_ETHERNET) return; - if(rx_arp->proto != ARP_PROTO_IP) return; - if(rx_arp->hwsize != 6) return; - if(rx_arp->protosize != 4) return; - if(rx_arp->opcode == ARP_OPCODE_REPLY) { - if(rx_arp->sender_ip == cached_ip) { - int i; - for(i=0;i<6;i++) - cached_mac[i] = rx_arp->sender_mac[i]; - } - return; - } - if(rx_arp->opcode == ARP_OPCODE_REQUEST) { - if(rx_arp->target_ip == my_ip) { - int i; - - fill_eth_header(&txbuffer->frame.eth_header, - rx_arp->sender_mac, - my_mac, - ETHERTYPE_ARP); - txlen = 68; - tx_arp->hwtype = ARP_HWTYPE_ETHERNET; - tx_arp->proto = ARP_PROTO_IP; - tx_arp->hwsize = 6; - tx_arp->protosize = 4; - tx_arp->opcode = ARP_OPCODE_REPLY; - tx_arp->sender_ip = my_ip; - for(i=0;i<6;i++) - tx_arp->sender_mac[i] = my_mac[i]; - tx_arp->target_ip = rx_arp->sender_ip; - for(i=0;i<6;i++) - tx_arp->target_mac[i] = rx_arp->sender_mac[i]; - send_packet(); - } - return; - } -} - -static const unsigned char broadcast[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; - -int microudp_arp_resolve(unsigned int ip) -{ - struct arp_frame *arp = &txbuffer->frame.contents.arp; - int i; - int tries; - int timeout; - - if(cached_ip == ip) { - for(i=0;i<6;i++) - if(cached_mac[i]) return 1; - } - cached_ip = ip; - for(i=0;i<6;i++) - cached_mac[i] = 0; - - for(tries=0;tries<5;tries++) { - /* Send an ARP request */ - fill_eth_header(&txbuffer->frame.eth_header, - broadcast, - my_mac, - ETHERTYPE_ARP); - txlen = 68; - arp->hwtype = ARP_HWTYPE_ETHERNET; - arp->proto = ARP_PROTO_IP; - arp->hwsize = 6; - arp->protosize = 4; - arp->opcode = ARP_OPCODE_REQUEST; - arp->sender_ip = my_ip; - for(i=0;i<6;i++) - arp->sender_mac[i] = my_mac[i]; - arp->target_ip = ip; - for(i=0;i<6;i++) - arp->target_mac[i] = 0; - send_packet(); - - /* Do we get a reply ? */ - for(timeout=0;timeout<2000000;timeout++) { - microudp_service(); - for(i=0;i<6;i++) - if(cached_mac[i]) return 1; - } - } - - return 0; -} - -static unsigned short ip_checksum(unsigned int r, void *buffer, unsigned int length, int complete) -{ - unsigned char *ptr; - unsigned int i; - - ptr = (unsigned char *)buffer; - length >>= 1; - - for(i=0;i> 16) - r = (r & 0xffff) + (r >> 16); - - if(complete) { - r = ~r; - r &= 0xffff; - if(r == 0) r = 0xffff; - } - return r; -} - -void *microudp_get_tx_buffer(void) -{ - return txbuffer->frame.contents.udp.payload; -} - -struct pseudo_header { - unsigned int src_ip; - unsigned int dst_ip; - unsigned char zero; - unsigned char proto; - unsigned short length; -} __attribute__((packed)); - -int microudp_send(unsigned short src_port, unsigned short dst_port, unsigned int length) -{ - struct pseudo_header h; - unsigned int r; - - if((cached_mac[0] == 0) && (cached_mac[1] == 0) && (cached_mac[2] == 0) - && (cached_mac[3] == 0) && (cached_mac[4] == 0) && (cached_mac[5] == 0)) - return 0; - - txlen = length + sizeof(struct ethernet_header) + sizeof(struct udp_frame) + 8; - if(txlen < 72) txlen = 72; - - fill_eth_header(&txbuffer->frame.eth_header, - cached_mac, - my_mac, - ETHERTYPE_IP); - - txbuffer->frame.contents.udp.ip.version = IP_IPV4; - txbuffer->frame.contents.udp.ip.diff_services = 0; - txbuffer->frame.contents.udp.ip.total_length = length + sizeof(struct udp_frame); - txbuffer->frame.contents.udp.ip.identification = 0; - txbuffer->frame.contents.udp.ip.fragment_offset = IP_DONT_FRAGMENT; - txbuffer->frame.contents.udp.ip.ttl = IP_TTL; - h.proto = txbuffer->frame.contents.udp.ip.proto = IP_PROTO_UDP; - txbuffer->frame.contents.udp.ip.checksum = 0; - h.src_ip = txbuffer->frame.contents.udp.ip.src_ip = my_ip; - h.dst_ip = txbuffer->frame.contents.udp.ip.dst_ip = cached_ip; - txbuffer->frame.contents.udp.ip.checksum = ip_checksum(0, &txbuffer->frame.contents.udp.ip, - sizeof(struct ip_header), 1); - - txbuffer->frame.contents.udp.udp.src_port = src_port; - txbuffer->frame.contents.udp.udp.dst_port = dst_port; - h.length = txbuffer->frame.contents.udp.udp.length = length + sizeof(struct udp_header); - txbuffer->frame.contents.udp.udp.checksum = 0; - - h.zero = 0; - r = ip_checksum(0, &h, sizeof(struct pseudo_header), 0); - if(length & 1) { - txbuffer->frame.contents.udp.payload[length] = 0; - length++; - } - r = ip_checksum(r, &txbuffer->frame.contents.udp.udp, - sizeof(struct udp_header)+length, 1); - txbuffer->frame.contents.udp.udp.checksum = r; - - send_packet(); - - return 1; -} - -static udp_callback rx_callback; - -static void process_ip(void) -{ - if(rxlen < (sizeof(struct ethernet_header)+sizeof(struct udp_frame))) return; - /* We don't verify UDP and IP checksums and rely on the Ethernet checksum solely */ - if(rxbuffer->frame.contents.udp.ip.version != IP_IPV4) return; - // check disabled for QEMU compatibility - //if(rxbuffer->frame.contents.udp.ip.diff_services != 0) return; - if(rxbuffer->frame.contents.udp.ip.total_length < sizeof(struct udp_frame)) return; - // check disabled for QEMU compatibility - //if(rxbuffer->frame.contents.udp.ip.fragment_offset != IP_DONT_FRAGMENT) return; - if(rxbuffer->frame.contents.udp.ip.proto != IP_PROTO_UDP) return; - if(rxbuffer->frame.contents.udp.ip.dst_ip != my_ip) return; - if(rxbuffer->frame.contents.udp.udp.length < sizeof(struct udp_header)) return; - - if(rx_callback) - rx_callback(rxbuffer->frame.contents.udp.ip.src_ip, rxbuffer->frame.contents.udp.udp.src_port, rxbuffer->frame.contents.udp.udp.dst_port, rxbuffer->frame.contents.udp.payload, rxbuffer->frame.contents.udp.udp.length-sizeof(struct udp_header)); -} - -void microudp_set_callback(udp_callback callback) -{ - rx_callback = callback; -} - -static void process_frame(void) -{ - int i; - unsigned int received_crc; - unsigned int computed_crc; - - flush_cpu_dcache(); - for(i=0;i<7;i++) - if(rxbuffer->frame.eth_header.preamble[i] != 0x55) return; - if(rxbuffer->frame.eth_header.preamble[7] != 0xd5) return; - received_crc = ((unsigned int)rxbuffer->raw[rxlen-1] << 24) - |((unsigned int)rxbuffer->raw[rxlen-2] << 16) - |((unsigned int)rxbuffer->raw[rxlen-3] << 8) - |((unsigned int)rxbuffer->raw[rxlen-4]); - computed_crc = crc32(&rxbuffer->raw[8], rxlen-12); - if(received_crc != computed_crc) return; - - rxlen -= 4; /* strip CRC here to be consistent with TX */ - if(rxbuffer->frame.eth_header.ethertype == ETHERTYPE_ARP) process_arp(); - else if(rxbuffer->frame.eth_header.ethertype == ETHERTYPE_IP) process_ip(); -} - -void microudp_start(unsigned char *macaddr, unsigned int ip) -{ - int i; - - minimac_ev_pending_write(MINIMAC_EV_RX0 | MINIMAC_EV_RX1 | MINIMAC_EV_TX); - - rxbuffer0 = (ethernet_buffer *)MINIMAC_RX0_BASE; - rxbuffer1 = (ethernet_buffer *)MINIMAC_RX1_BASE; - txbuffer = (ethernet_buffer *)MINIMAC_TX_BASE; - - for(i=0;i<6;i++) - my_mac[i] = macaddr[i]; - my_ip = ip; - - cached_ip = 0; - for(i=0;i<6;i++) - cached_mac[i] = 0; - - rx_callback = (udp_callback)0; -} - -void microudp_service(void) -{ - if(minimac_ev_pending_read() & MINIMAC_EV_RX0) { - rxlen = minimac_rx_count_0_read(); - rxbuffer = rxbuffer0; - process_frame(); - minimac_ev_pending_write(MINIMAC_EV_RX0); - } - if(minimac_ev_pending_read() & MINIMAC_EV_RX1) { - rxlen = minimac_rx_count_1_read(); - rxbuffer = rxbuffer1; - process_frame(); - minimac_ev_pending_write(MINIMAC_EV_RX1); - } -} - -void ethreset(void) -{ - minimac_phy_reset_write(0); - busy_wait(2); - /* that pesky ethernet PHY needs two resets at times... */ - minimac_phy_reset_write(1); - busy_wait(2); - minimac_phy_reset_write(0); - busy_wait(2); -} diff --git a/software/bios/microudp.h b/software/bios/microudp.h deleted file mode 100644 index 79a1b0e5..00000000 --- a/software/bios/microudp.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef __MICROUDP_H -#define __MICROUDP_H - -#define IPTOINT(a, b, c, d) ((a << 24)|(b << 16)|(c << 8)|d) - -#define MICROUDP_BUFSIZE (5*1532) - -typedef void (*udp_callback)(unsigned int src_ip, unsigned short src_port, unsigned short dst_port, void *data, unsigned int length); - -void microudp_start(unsigned char *macaddr, unsigned int ip); -int microudp_arp_resolve(unsigned int ip); -void *microudp_get_tx_buffer(void); -int microudp_send(unsigned short src_port, unsigned short dst_port, unsigned int length); -void microudp_set_callback(udp_callback callback); -void microudp_service(void); - -void ethreset(void); - -#endif /* __MICROUDP_H */ diff --git a/software/bios/tftp.c b/software/bios/tftp.c deleted file mode 100644 index a2e1bfe1..00000000 --- a/software/bios/tftp.c +++ /dev/null @@ -1,216 +0,0 @@ -#include -#include - -#include "microudp.h" -#include "tftp.h" - -#define PORT_OUT 69 -#define PORT_IN 7642 - -enum { - TFTP_RRQ = 1, /* Read request */ - TFTP_WRQ = 2, /* Write request */ - TFTP_DATA = 3, /* Data */ - TFTP_ACK = 4, /* Acknowledgment */ - TFTP_ERROR = 5, /* Error */ -}; - -#define BLOCK_SIZE 512 /* block size in bytes */ - - -static int format_request(uint8_t *buf, uint16_t op, const char *filename) -{ - int len = strlen(filename); - - *buf++ = op >> 8; /* Opcode */ - *buf++ = op; - memcpy(buf, filename, len); - buf += len; - *buf++ = 0x00; - *buf++ = 'o'; - *buf++ = 'c'; - *buf++ = 't'; - *buf++ = 'e'; - *buf++ = 't'; - *buf++ = 0x00; - return 9+strlen(filename); -} - -static int format_ack(uint8_t *buf, uint16_t block) -{ - *buf++ = 0x00; /* Opcode: Ack */ - *buf++ = TFTP_ACK; - *buf++ = (block & 0xff00) >> 8; - *buf++ = (block & 0x00ff); - return 4; -} - -static int format_data(uint8_t *buf, uint16_t block, const void *data, int len) -{ - *buf++ = 0x00; /* Opcode: Data*/ - *buf++ = TFTP_DATA; - *buf++ = (block & 0xff00) >> 8; - *buf++ = (block & 0x00ff); - memcpy(buf, data, len); - return len+4; -} - -static uint8_t *packet_data; -static int total_length; -static int transfer_finished; -static uint8_t *dst_buffer; -static int last_ack; /* signed, so we can use -1 */ -static uint16_t data_port; - -static void rx_callback(uint32_t src_ip, uint16_t src_port, - uint16_t dst_port, void *_data, unsigned int length) -{ - uint8_t *data = _data; - uint16_t opcode; - uint16_t block; - int i; - int offset; - - if(length < 4) return; - if(dst_port != PORT_IN) return; - opcode = data[0] << 8 | data[1]; - block = data[2] << 8 | data[3]; - if(opcode == TFTP_ACK) { /* Acknowledgement */ - data_port = src_port; - last_ack = block; - return; - } - if(block < 1) return; - if(opcode == TFTP_DATA) { /* Data */ - length -= 4; - offset = (block-1)*BLOCK_SIZE; - for(i=0;i 0) || transfer_finished) break; - } - if((total_length > 0) || transfer_finished) break; - tries--; - if(tries == 0) { - microudp_set_callback(NULL); - return -1; - } - } - - length_before = total_length; - while(!transfer_finished) { - if(length_before != total_length) { - i = 12000000; - length_before = total_length; - } - if(i-- == 0) { - microudp_set_callback(NULL); - return -1; - } - microudp_service(); - } - - microudp_set_callback(NULL); - - return total_length; -} - -int tftp_put(uint32_t ip, const char *filename, const void *buffer, int size) -{ - int len, send; - int tries; - int i; - int block = 0, sent = 0; - - if(!microudp_arp_resolve(ip)) - return -1; - - microudp_set_callback(rx_callback); - - packet_data = microudp_get_tx_buffer(); - - total_length = 0; - transfer_finished = 0; - tries = 5; - while(1) { - len = format_request(packet_data, TFTP_WRQ, filename); - microudp_send(PORT_IN, PORT_OUT, len); - for(i=0;i<2000000;i++) { - last_ack = -1; - microudp_service(); - if(last_ack == block) - goto send_data; - if(transfer_finished) - goto fail; - } - tries--; - if(tries == 0) - goto fail; - } - -send_data: - do { - block++; - send = sent+BLOCK_SIZE > size ? size-sent : BLOCK_SIZE; - tries = 5; - while(1) { - len = format_data(packet_data, block, buffer, send); - microudp_send(PORT_IN, data_port, len); - for(i=0;i<12000000;i++) { - microudp_service(); - if(transfer_finished) - goto fail; - if(last_ack == block) - goto next; - } - if (!--tries) - goto fail; - } -next: - sent += send; - buffer += send; - } while (send == BLOCK_SIZE); - - microudp_set_callback(NULL); - - return sent; - -fail: - microudp_set_callback(NULL); - return -1; -} diff --git a/software/bios/tftp.h b/software/bios/tftp.h deleted file mode 100644 index 7babb2d7..00000000 --- a/software/bios/tftp.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef __TFTP_H -#define __TFTP_H - -#include - -int tftp_get(uint32_t ip, const char *filename, void *buffer); -int tftp_put(uint32_t ip, const char *filename, const void *buffer, int size); - -#endif /* __TFTP_H */ - diff --git a/software/include/net/microudp.h b/software/include/net/microudp.h new file mode 100644 index 00000000..79a1b0e5 --- /dev/null +++ b/software/include/net/microudp.h @@ -0,0 +1,19 @@ +#ifndef __MICROUDP_H +#define __MICROUDP_H + +#define IPTOINT(a, b, c, d) ((a << 24)|(b << 16)|(c << 8)|d) + +#define MICROUDP_BUFSIZE (5*1532) + +typedef void (*udp_callback)(unsigned int src_ip, unsigned short src_port, unsigned short dst_port, void *data, unsigned int length); + +void microudp_start(unsigned char *macaddr, unsigned int ip); +int microudp_arp_resolve(unsigned int ip); +void *microudp_get_tx_buffer(void); +int microudp_send(unsigned short src_port, unsigned short dst_port, unsigned int length); +void microudp_set_callback(udp_callback callback); +void microudp_service(void); + +void ethreset(void); + +#endif /* __MICROUDP_H */ diff --git a/software/include/net/tftp.h b/software/include/net/tftp.h new file mode 100644 index 00000000..7babb2d7 --- /dev/null +++ b/software/include/net/tftp.h @@ -0,0 +1,10 @@ +#ifndef __TFTP_H +#define __TFTP_H + +#include + +int tftp_get(uint32_t ip, const char *filename, void *buffer); +int tftp_put(uint32_t ip, const char *filename, const void *buffer, int size); + +#endif /* __TFTP_H */ + diff --git a/software/libnet/Makefile b/software/libnet/Makefile new file mode 100644 index 00000000..21af8d44 --- /dev/null +++ b/software/libnet/Makefile @@ -0,0 +1,24 @@ +M2DIR=../.. +include $(M2DIR)/software/common.mak + +OBJECTS=microudp.o tftp.o + +all: libnet.a + +# pull in dependency info for *existing* .o files +-include $(OBJECTS:.o=.d) + +libnet.a: $(OBJECTS) + $(AR) clr libnet.a $(OBJECTS) + $(RANLIB) libnet.a + +%.o: %.c + $(compile-dep) + +%.o: %.S + $(assemble) + +.PHONY: clean + +clean: + rm -f $(OBJECTS) $(OBJECTS:.o=.ts) $(OBJECTS:.o=.d) libnet.a .*~ *~ diff --git a/software/libnet/microudp.c b/software/libnet/microudp.c new file mode 100644 index 00000000..55257d83 --- /dev/null +++ b/software/libnet/microudp.c @@ -0,0 +1,400 @@ +#include +#include +#include +#include +#include +#include +#include + +#include + +#define ETHERTYPE_ARP 0x0806 +#define ETHERTYPE_IP 0x0800 + +struct ethernet_header { + unsigned char preamble[8]; + unsigned char destmac[6]; + unsigned char srcmac[6]; + unsigned short ethertype; +} __attribute__((packed)); + +static void fill_eth_header(struct ethernet_header *h, const unsigned char *destmac, const unsigned char *srcmac, unsigned short ethertype) +{ + int i; + + for(i=0;i<7;i++) + h->preamble[i] = 0x55; + h->preamble[7] = 0xd5; + for(i=0;i<6;i++) + h->destmac[i] = destmac[i]; + for(i=0;i<6;i++) + h->srcmac[i] = srcmac[i]; + h->ethertype = ethertype; +} + +#define ARP_HWTYPE_ETHERNET 0x0001 +#define ARP_PROTO_IP 0x0800 + +#define ARP_OPCODE_REQUEST 0x0001 +#define ARP_OPCODE_REPLY 0x0002 + +struct arp_frame { + unsigned short hwtype; + unsigned short proto; + unsigned char hwsize; + unsigned char protosize; + unsigned short opcode; + unsigned char sender_mac[6]; + unsigned int sender_ip; + unsigned char target_mac[6]; + unsigned int target_ip; + unsigned char padding[18]; +} __attribute__((packed)); + +#define IP_IPV4 0x45 +#define IP_DONT_FRAGMENT 0x4000 +#define IP_TTL 64 +#define IP_PROTO_UDP 0x11 + +struct ip_header { + unsigned char version; + unsigned char diff_services; + unsigned short total_length; + unsigned short identification; + unsigned short fragment_offset; + unsigned char ttl; + unsigned char proto; + unsigned short checksum; + unsigned int src_ip; + unsigned int dst_ip; +} __attribute__((packed)); + +struct udp_header { + unsigned short src_port; + unsigned short dst_port; + unsigned short length; + unsigned short checksum; +} __attribute__((packed)); + +struct udp_frame { + struct ip_header ip; + struct udp_header udp; + char payload[]; +} __attribute__((packed)); + +struct ethernet_frame { + struct ethernet_header eth_header; + union { + struct arp_frame arp; + struct udp_frame udp; + } contents; +} __attribute__((packed)); + +typedef union { + struct ethernet_frame frame; + unsigned char raw[1532]; +} ethernet_buffer; + + +static unsigned int rxlen; +static ethernet_buffer *rxbuffer; +static ethernet_buffer *rxbuffer0; +static ethernet_buffer *rxbuffer1; +static unsigned int txlen; +static ethernet_buffer *txbuffer; + +static void send_packet(void) +{ + unsigned int crc; + + crc = crc32(&txbuffer->raw[8], txlen-8); + txbuffer->raw[txlen ] = (crc & 0xff); + txbuffer->raw[txlen+1] = (crc & 0xff00) >> 8; + txbuffer->raw[txlen+2] = (crc & 0xff0000) >> 16; + txbuffer->raw[txlen+3] = (crc & 0xff000000) >> 24; + txlen += 4; + minimac_tx_count_write(txlen); + minimac_tx_start_write(1); + while(!(minimac_ev_pending_read() & MINIMAC_EV_TX)); + minimac_ev_pending_write(MINIMAC_EV_TX); +} + +static unsigned char my_mac[6]; +static unsigned int my_ip; + +/* ARP cache - one entry only */ +static unsigned char cached_mac[6]; +static unsigned int cached_ip; + +static void process_arp(void) +{ + const struct arp_frame *rx_arp = &rxbuffer->frame.contents.arp; + struct arp_frame *tx_arp = &txbuffer->frame.contents.arp; + + if(rxlen < 68) return; + if(rx_arp->hwtype != ARP_HWTYPE_ETHERNET) return; + if(rx_arp->proto != ARP_PROTO_IP) return; + if(rx_arp->hwsize != 6) return; + if(rx_arp->protosize != 4) return; + if(rx_arp->opcode == ARP_OPCODE_REPLY) { + if(rx_arp->sender_ip == cached_ip) { + int i; + for(i=0;i<6;i++) + cached_mac[i] = rx_arp->sender_mac[i]; + } + return; + } + if(rx_arp->opcode == ARP_OPCODE_REQUEST) { + if(rx_arp->target_ip == my_ip) { + int i; + + fill_eth_header(&txbuffer->frame.eth_header, + rx_arp->sender_mac, + my_mac, + ETHERTYPE_ARP); + txlen = 68; + tx_arp->hwtype = ARP_HWTYPE_ETHERNET; + tx_arp->proto = ARP_PROTO_IP; + tx_arp->hwsize = 6; + tx_arp->protosize = 4; + tx_arp->opcode = ARP_OPCODE_REPLY; + tx_arp->sender_ip = my_ip; + for(i=0;i<6;i++) + tx_arp->sender_mac[i] = my_mac[i]; + tx_arp->target_ip = rx_arp->sender_ip; + for(i=0;i<6;i++) + tx_arp->target_mac[i] = rx_arp->sender_mac[i]; + send_packet(); + } + return; + } +} + +static const unsigned char broadcast[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + +int microudp_arp_resolve(unsigned int ip) +{ + struct arp_frame *arp = &txbuffer->frame.contents.arp; + int i; + int tries; + int timeout; + + if(cached_ip == ip) { + for(i=0;i<6;i++) + if(cached_mac[i]) return 1; + } + cached_ip = ip; + for(i=0;i<6;i++) + cached_mac[i] = 0; + + for(tries=0;tries<5;tries++) { + /* Send an ARP request */ + fill_eth_header(&txbuffer->frame.eth_header, + broadcast, + my_mac, + ETHERTYPE_ARP); + txlen = 68; + arp->hwtype = ARP_HWTYPE_ETHERNET; + arp->proto = ARP_PROTO_IP; + arp->hwsize = 6; + arp->protosize = 4; + arp->opcode = ARP_OPCODE_REQUEST; + arp->sender_ip = my_ip; + for(i=0;i<6;i++) + arp->sender_mac[i] = my_mac[i]; + arp->target_ip = ip; + for(i=0;i<6;i++) + arp->target_mac[i] = 0; + send_packet(); + + /* Do we get a reply ? */ + for(timeout=0;timeout<2000000;timeout++) { + microudp_service(); + for(i=0;i<6;i++) + if(cached_mac[i]) return 1; + } + } + + return 0; +} + +static unsigned short ip_checksum(unsigned int r, void *buffer, unsigned int length, int complete) +{ + unsigned char *ptr; + unsigned int i; + + ptr = (unsigned char *)buffer; + length >>= 1; + + for(i=0;i> 16) + r = (r & 0xffff) + (r >> 16); + + if(complete) { + r = ~r; + r &= 0xffff; + if(r == 0) r = 0xffff; + } + return r; +} + +void *microudp_get_tx_buffer(void) +{ + return txbuffer->frame.contents.udp.payload; +} + +struct pseudo_header { + unsigned int src_ip; + unsigned int dst_ip; + unsigned char zero; + unsigned char proto; + unsigned short length; +} __attribute__((packed)); + +int microudp_send(unsigned short src_port, unsigned short dst_port, unsigned int length) +{ + struct pseudo_header h; + unsigned int r; + + if((cached_mac[0] == 0) && (cached_mac[1] == 0) && (cached_mac[2] == 0) + && (cached_mac[3] == 0) && (cached_mac[4] == 0) && (cached_mac[5] == 0)) + return 0; + + txlen = length + sizeof(struct ethernet_header) + sizeof(struct udp_frame) + 8; + if(txlen < 72) txlen = 72; + + fill_eth_header(&txbuffer->frame.eth_header, + cached_mac, + my_mac, + ETHERTYPE_IP); + + txbuffer->frame.contents.udp.ip.version = IP_IPV4; + txbuffer->frame.contents.udp.ip.diff_services = 0; + txbuffer->frame.contents.udp.ip.total_length = length + sizeof(struct udp_frame); + txbuffer->frame.contents.udp.ip.identification = 0; + txbuffer->frame.contents.udp.ip.fragment_offset = IP_DONT_FRAGMENT; + txbuffer->frame.contents.udp.ip.ttl = IP_TTL; + h.proto = txbuffer->frame.contents.udp.ip.proto = IP_PROTO_UDP; + txbuffer->frame.contents.udp.ip.checksum = 0; + h.src_ip = txbuffer->frame.contents.udp.ip.src_ip = my_ip; + h.dst_ip = txbuffer->frame.contents.udp.ip.dst_ip = cached_ip; + txbuffer->frame.contents.udp.ip.checksum = ip_checksum(0, &txbuffer->frame.contents.udp.ip, + sizeof(struct ip_header), 1); + + txbuffer->frame.contents.udp.udp.src_port = src_port; + txbuffer->frame.contents.udp.udp.dst_port = dst_port; + h.length = txbuffer->frame.contents.udp.udp.length = length + sizeof(struct udp_header); + txbuffer->frame.contents.udp.udp.checksum = 0; + + h.zero = 0; + r = ip_checksum(0, &h, sizeof(struct pseudo_header), 0); + if(length & 1) { + txbuffer->frame.contents.udp.payload[length] = 0; + length++; + } + r = ip_checksum(r, &txbuffer->frame.contents.udp.udp, + sizeof(struct udp_header)+length, 1); + txbuffer->frame.contents.udp.udp.checksum = r; + + send_packet(); + + return 1; +} + +static udp_callback rx_callback; + +static void process_ip(void) +{ + if(rxlen < (sizeof(struct ethernet_header)+sizeof(struct udp_frame))) return; + /* We don't verify UDP and IP checksums and rely on the Ethernet checksum solely */ + if(rxbuffer->frame.contents.udp.ip.version != IP_IPV4) return; + // check disabled for QEMU compatibility + //if(rxbuffer->frame.contents.udp.ip.diff_services != 0) return; + if(rxbuffer->frame.contents.udp.ip.total_length < sizeof(struct udp_frame)) return; + // check disabled for QEMU compatibility + //if(rxbuffer->frame.contents.udp.ip.fragment_offset != IP_DONT_FRAGMENT) return; + if(rxbuffer->frame.contents.udp.ip.proto != IP_PROTO_UDP) return; + if(rxbuffer->frame.contents.udp.ip.dst_ip != my_ip) return; + if(rxbuffer->frame.contents.udp.udp.length < sizeof(struct udp_header)) return; + + if(rx_callback) + rx_callback(rxbuffer->frame.contents.udp.ip.src_ip, rxbuffer->frame.contents.udp.udp.src_port, rxbuffer->frame.contents.udp.udp.dst_port, rxbuffer->frame.contents.udp.payload, rxbuffer->frame.contents.udp.udp.length-sizeof(struct udp_header)); +} + +void microudp_set_callback(udp_callback callback) +{ + rx_callback = callback; +} + +static void process_frame(void) +{ + int i; + unsigned int received_crc; + unsigned int computed_crc; + + flush_cpu_dcache(); + for(i=0;i<7;i++) + if(rxbuffer->frame.eth_header.preamble[i] != 0x55) return; + if(rxbuffer->frame.eth_header.preamble[7] != 0xd5) return; + received_crc = ((unsigned int)rxbuffer->raw[rxlen-1] << 24) + |((unsigned int)rxbuffer->raw[rxlen-2] << 16) + |((unsigned int)rxbuffer->raw[rxlen-3] << 8) + |((unsigned int)rxbuffer->raw[rxlen-4]); + computed_crc = crc32(&rxbuffer->raw[8], rxlen-12); + if(received_crc != computed_crc) return; + + rxlen -= 4; /* strip CRC here to be consistent with TX */ + if(rxbuffer->frame.eth_header.ethertype == ETHERTYPE_ARP) process_arp(); + else if(rxbuffer->frame.eth_header.ethertype == ETHERTYPE_IP) process_ip(); +} + +void microudp_start(unsigned char *macaddr, unsigned int ip) +{ + int i; + + minimac_ev_pending_write(MINIMAC_EV_RX0 | MINIMAC_EV_RX1 | MINIMAC_EV_TX); + + rxbuffer0 = (ethernet_buffer *)MINIMAC_RX0_BASE; + rxbuffer1 = (ethernet_buffer *)MINIMAC_RX1_BASE; + txbuffer = (ethernet_buffer *)MINIMAC_TX_BASE; + + for(i=0;i<6;i++) + my_mac[i] = macaddr[i]; + my_ip = ip; + + cached_ip = 0; + for(i=0;i<6;i++) + cached_mac[i] = 0; + + rx_callback = (udp_callback)0; +} + +void microudp_service(void) +{ + if(minimac_ev_pending_read() & MINIMAC_EV_RX0) { + rxlen = minimac_rx_count_0_read(); + rxbuffer = rxbuffer0; + process_frame(); + minimac_ev_pending_write(MINIMAC_EV_RX0); + } + if(minimac_ev_pending_read() & MINIMAC_EV_RX1) { + rxlen = minimac_rx_count_1_read(); + rxbuffer = rxbuffer1; + process_frame(); + minimac_ev_pending_write(MINIMAC_EV_RX1); + } +} + +void ethreset(void) +{ + minimac_phy_reset_write(0); + busy_wait(2); + /* that pesky ethernet PHY needs two resets at times... */ + minimac_phy_reset_write(1); + busy_wait(2); + minimac_phy_reset_write(0); + busy_wait(2); +} diff --git a/software/libnet/tftp.c b/software/libnet/tftp.c new file mode 100644 index 00000000..2bf0eac6 --- /dev/null +++ b/software/libnet/tftp.c @@ -0,0 +1,216 @@ +#include +#include + +#include +#include + +#define PORT_OUT 69 +#define PORT_IN 7642 + +enum { + TFTP_RRQ = 1, /* Read request */ + TFTP_WRQ = 2, /* Write request */ + TFTP_DATA = 3, /* Data */ + TFTP_ACK = 4, /* Acknowledgment */ + TFTP_ERROR = 5, /* Error */ +}; + +#define BLOCK_SIZE 512 /* block size in bytes */ + + +static int format_request(uint8_t *buf, uint16_t op, const char *filename) +{ + int len = strlen(filename); + + *buf++ = op >> 8; /* Opcode */ + *buf++ = op; + memcpy(buf, filename, len); + buf += len; + *buf++ = 0x00; + *buf++ = 'o'; + *buf++ = 'c'; + *buf++ = 't'; + *buf++ = 'e'; + *buf++ = 't'; + *buf++ = 0x00; + return 9+strlen(filename); +} + +static int format_ack(uint8_t *buf, uint16_t block) +{ + *buf++ = 0x00; /* Opcode: Ack */ + *buf++ = TFTP_ACK; + *buf++ = (block & 0xff00) >> 8; + *buf++ = (block & 0x00ff); + return 4; +} + +static int format_data(uint8_t *buf, uint16_t block, const void *data, int len) +{ + *buf++ = 0x00; /* Opcode: Data*/ + *buf++ = TFTP_DATA; + *buf++ = (block & 0xff00) >> 8; + *buf++ = (block & 0x00ff); + memcpy(buf, data, len); + return len+4; +} + +static uint8_t *packet_data; +static int total_length; +static int transfer_finished; +static uint8_t *dst_buffer; +static int last_ack; /* signed, so we can use -1 */ +static uint16_t data_port; + +static void rx_callback(uint32_t src_ip, uint16_t src_port, + uint16_t dst_port, void *_data, unsigned int length) +{ + uint8_t *data = _data; + uint16_t opcode; + uint16_t block; + int i; + int offset; + + if(length < 4) return; + if(dst_port != PORT_IN) return; + opcode = data[0] << 8 | data[1]; + block = data[2] << 8 | data[3]; + if(opcode == TFTP_ACK) { /* Acknowledgement */ + data_port = src_port; + last_ack = block; + return; + } + if(block < 1) return; + if(opcode == TFTP_DATA) { /* Data */ + length -= 4; + offset = (block-1)*BLOCK_SIZE; + for(i=0;i 0) || transfer_finished) break; + } + if((total_length > 0) || transfer_finished) break; + tries--; + if(tries == 0) { + microudp_set_callback(NULL); + return -1; + } + } + + length_before = total_length; + while(!transfer_finished) { + if(length_before != total_length) { + i = 12000000; + length_before = total_length; + } + if(i-- == 0) { + microudp_set_callback(NULL); + return -1; + } + microudp_service(); + } + + microudp_set_callback(NULL); + + return total_length; +} + +int tftp_put(uint32_t ip, const char *filename, const void *buffer, int size) +{ + int len, send; + int tries; + int i; + int block = 0, sent = 0; + + if(!microudp_arp_resolve(ip)) + return -1; + + microudp_set_callback(rx_callback); + + packet_data = microudp_get_tx_buffer(); + + total_length = 0; + transfer_finished = 0; + tries = 5; + while(1) { + len = format_request(packet_data, TFTP_WRQ, filename); + microudp_send(PORT_IN, PORT_OUT, len); + for(i=0;i<2000000;i++) { + last_ack = -1; + microudp_service(); + if(last_ack == block) + goto send_data; + if(transfer_finished) + goto fail; + } + tries--; + if(tries == 0) + goto fail; + } + +send_data: + do { + block++; + send = sent+BLOCK_SIZE > size ? size-sent : BLOCK_SIZE; + tries = 5; + while(1) { + len = format_data(packet_data, block, buffer, send); + microudp_send(PORT_IN, data_port, len); + for(i=0;i<12000000;i++) { + microudp_service(); + if(transfer_finished) + goto fail; + if(last_ack == block) + goto next; + } + if (!--tries) + goto fail; + } +next: + sent += send; + buffer += send; + } while (send == BLOCK_SIZE); + + microudp_set_callback(NULL); + + return sent; + +fail: + microudp_set_callback(NULL); + return -1; +}