diff --git a/litex/soc/software/bios/boot.c b/litex/soc/software/bios/boot.c index 5faa649354..8166249c26 100755 --- a/litex/soc/software/bios/boot.c +++ b/litex/soc/software/bios/boot.c @@ -21,6 +21,8 @@ #include "sfl.h" #include "boot.h" +#include + #include #include @@ -29,6 +31,7 @@ #include #include +#include #include #include @@ -538,34 +541,82 @@ static void netboot_from_bin(const char * filename, unsigned int ip, unsigned sh } #endif +void bootp(void) +{ + uint32_t client_ip; + uint32_t server_ip; + char bootp_filename[64]; + int ret; + + printf("Requesting ip...\n"); + udp_start(macadr, IPTOINT(local_ip[0], local_ip[1], local_ip[2], local_ip[3])); + ret = bootp_get(macadr, &client_ip, &server_ip, (char *) &bootp_filename, sizeof(bootp_filename), 1); + if (ret == 0) { + printf("Local IP: "); + print_ip(client_ip); + printf("\n"); + printf("Remote IP: "); + print_ip(server_ip); + printf("\n"); + } + printf("BOOTP done.\n"); +} + void netboot(int nb_params, char **params) { - unsigned int ip; char * filename = NULL; + char bootp_name[64]; + uint8_t bootp_len; + uint8_t bootp_ret; + uint32_t client_ip; + uint32_t server_ip; if (nb_params > 0 ) filename = params[0]; printf("Booting from network...\n"); - printf("Local IP: %d.%d.%d.%d\n", local_ip[0], local_ip[1], local_ip[2], local_ip[3]); - printf("Remote IP: %d.%d.%d.%d\n", remote_ip[0], remote_ip[1], remote_ip[2], remote_ip[3]); + server_ip = IPTOINT(remote_ip[0], remote_ip[1], remote_ip[2], remote_ip[3]); + if(udp_get_ip() == 0) + udp_start(macadr, IPTOINT(local_ip[0], local_ip[1], local_ip[2], local_ip[3])); - ip = IPTOINT(remote_ip[0], remote_ip[1], remote_ip[2], remote_ip[3]); - udp_start(macadr, IPTOINT(local_ip[0], local_ip[1], local_ip[2], local_ip[3])); + bootp_ret = bootp_get(macadr, &client_ip, &server_ip, (char *) &bootp_name, sizeof(bootp_name), 0); + bootp_len = strlen(bootp_name); - if (filename) { + if(bootp_ret == 0) + printf("Received configuration via BOOTP.\n"); + printf("Local IP: "); + print_ip(client_ip); + printf("\n"); + printf("Remote IP: "); + print_ip(server_ip); + printf("\n"); + + if(bootp_ret == 0 && bootp_len > 0 && bootp_len < sizeof(bootp_name)) { +#ifdef MAIN_RAM_BASE + if(bootp_name[bootp_len - 4] == '.' && bootp_name[bootp_len - 3] == 'b' && + bootp_name[bootp_len - 2] == 'i' && bootp_name[bootp_len - 1] == 'n'){ + printf("Booting from %s...\n", bootp_name); + netboot_from_bin(bootp_name, server_ip, TFTP_SERVER_PORT); + } else { +#else + if(1){ +#endif + printf("Booting from %s (JSON)...\n", bootp_name); + netboot_from_json(bootp_name, server_ip, TFTP_SERVER_PORT); + } + } else if (filename) { printf("Booting from %s (JSON)...\n", filename); - netboot_from_json(filename, ip, TFTP_SERVER_PORT); + netboot_from_json(filename, server_ip, TFTP_SERVER_PORT); } else { /* Boot from boot.json */ printf("Booting from boot.json...\n"); - netboot_from_json("boot.json", ip, TFTP_SERVER_PORT); + netboot_from_json("boot.json", server_ip, TFTP_SERVER_PORT); #ifdef MAIN_RAM_BASE /* Boot from boot.bin */ printf("Booting from boot.bin...\n"); - netboot_from_bin("boot.bin", ip, TFTP_SERVER_PORT); + netboot_from_bin("boot.bin", server_ip, TFTP_SERVER_PORT); #endif } diff --git a/litex/soc/software/bios/boot.h b/litex/soc/software/bios/boot.h index 338312d090..59746764b0 100644 --- a/litex/soc/software/bios/boot.h +++ b/litex/soc/software/bios/boot.h @@ -8,6 +8,7 @@ void set_mac_addr(const char * mac_address); void __attribute__((noreturn)) boot(unsigned long r1, unsigned long r2, unsigned long r3, unsigned long addr); int serialboot(void); void netboot(int nb_params, char **params); +void bootp(void); void flashboot(void); void romboot(void); void sdcardboot(void); diff --git a/litex/soc/software/bios/cmds/cmd_boot.c b/litex/soc/software/bios/cmds/cmd_boot.c index 0a9b0df63e..f7d49ed50d 100644 --- a/litex/soc/software/bios/cmds/cmd_boot.c +++ b/litex/soc/software/bios/cmds/cmd_boot.c @@ -114,6 +114,7 @@ define_command(serialboot, serialboot, "Boot from Serial (SFL)", BOOT_CMDS); */ #ifdef CSR_ETHMAC_BASE define_command(netboot, netboot, "Boot via Ethernet (TFTP)", BOOT_CMDS); +define_command(bootp, bootp, "Request IP (BOOTP)", BOOT_CMDS); #endif /** diff --git a/litex/soc/software/bios/helpers.c b/litex/soc/software/bios/helpers.c index 6b384ec0e6..fbff7ae202 100644 --- a/litex/soc/software/bios/helpers.c +++ b/litex/soc/software/bios/helpers.c @@ -15,6 +15,12 @@ extern unsigned int _ftext, _edata_rom; +void print_ip(uint32_t ip) +{ + printf("%d.%d.%d.%d", (uint8_t)(ip >> 24), (uint8_t)(ip >> 16), + (uint8_t)(ip >> 8), (uint8_t)ip); +} + #define NUMBER_OF_BYTES_ON_A_LINE 16 void dump_bytes(unsigned int *ptr, int count, unsigned long addr) { diff --git a/litex/soc/software/bios/helpers.h b/litex/soc/software/bios/helpers.h index 23853afce1..2e9541b095 100644 --- a/litex/soc/software/bios/helpers.h +++ b/litex/soc/software/bios/helpers.h @@ -1,6 +1,7 @@ #ifndef __HELPERS_H__ #define __HELPERS_H__ +void print_ip(uint32_t ip); void dump_bytes(unsigned int *ptr, int count, unsigned long addr); void crcbios(void); int get_param(char *buf, char **cmd, char **params); diff --git a/litex/soc/software/libliteeth/Makefile b/litex/soc/software/libliteeth/Makefile index c3cf0b98f5..c26b3fb059 100644 --- a/litex/soc/software/libliteeth/Makefile +++ b/litex/soc/software/libliteeth/Makefile @@ -1,7 +1,7 @@ include ../include/generated/variables.mak include $(SOC_DIRECTORY)/software/common.mak -OBJECTS=udp.o tftp.o mdio.o +OBJECTS=udp.o bootp.o tftp.o mdio.o all: libliteeth.a diff --git a/litex/soc/software/libliteeth/bootp.c b/litex/soc/software/libliteeth/bootp.c new file mode 100644 index 0000000000..01f4567384 --- /dev/null +++ b/litex/soc/software/libliteeth/bootp.c @@ -0,0 +1,193 @@ +// This file is Copyright (c) 2024 Matthias Breithaupt + +// License: BSD + +#include +#include +#include + +#include + +#include +#include +#include + +#define PORT_SERVER 67 +#define PORT_CLIENT 68 + +#define OP_BOOTREQUEST 1 +#define OP_BOOTREPLY 2 + +#define HTYPE_ETHERNET 1 + +#define HLEN_ETHERNET 6 + +#define FLAG_BROADCAST 0x8000 + +#define MAGIC_COOKIE 0x63825363 + +typedef struct { + uint8_t op; + uint8_t htype; + uint8_t hlen; + uint8_t hops; + uint32_t xid; + uint16_t secs; + uint16_t flags; + uint32_t ciaddr; + uint32_t yiaddr; + uint32_t siaddr; + uint32_t giaddr; + uint8_t chaddr[6]; + uint8_t pad[10]; + uint8_t sname[64]; + uint8_t file[128]; + uint32_t cookie; + uint8_t vend[60]; +} __attribute__((packed)) bootp_message; + +static unsigned int seed = 0; + +static void seed_from_mac(const unsigned char* macaddr) +{ + seed = macaddr[2] << 24 | macaddr[3] << 16 | macaddr[4] << 8 | macaddr[5]; +} + +static uint32_t rand32(void) +{ +#ifdef CSR_TIMER0_UPTIME_CYCLES_ADDR + timer0_uptime_latch_write(1); + seed = timer0_uptime_cycles_read(); +#endif + uint32_t ret = lfsr(32, seed); + seed = ret; + return ret; +} + +static int format_request(uint8_t *buf, uint32_t xid, const unsigned char* macaddr) +{ + uint16_t flags = FLAG_BROADCAST; + uint16_t uptime = 0; +#ifdef CSR_TIMER0_UPTIME_CYCLES_ADDR + timer0_uptime_latch_write(1); + uptime = timer0_uptime_cycles_read()/CONFIG_CLOCK_FREQUENCY +#endif + + bootp_message *msg = (bootp_message*) buf; + msg->op = OP_BOOTREQUEST; + msg->htype = HTYPE_ETHERNET; + msg->hlen = HLEN_ETHERNET; + msg->hops = 0; + msg->xid = htonl(xid); + msg->secs = htons(uptime); + msg->flags = htons(flags); + msg->ciaddr = 0; + msg->yiaddr = 0; + msg->siaddr = 0; + msg->giaddr = 0; + memcpy(msg->chaddr, macaddr, 6); + memset(msg->pad, 0, sizeof(msg->pad)); + memset(msg->sname, 0, sizeof(msg->sname)); + memset(msg->file, 0, sizeof(msg->file)); + msg->cookie = htonl(MAGIC_COOKIE); + memset(msg->vend, 0, sizeof(msg->vend)); + return 300; +} + +static uint32_t xid; +static uint8_t response_received; + +static unsigned char mymac[6]; +static uint32_t client_ip; +static uint32_t server_ip; +static char filename[128]; + +static uint8_t got_ip; + +static void rx_callback(uint32_t src_ip, uint16_t src_port, + uint16_t dst_port, void *buf, unsigned int length) +{ + bootp_message *msg; + if(length < 300) return; + msg = (bootp_message*) buf; + if(dst_port != PORT_CLIENT) return; + if(msg->op != OP_BOOTREPLY) return; + if(msg->htype != HTYPE_ETHERNET) return; + if(msg->hlen != HLEN_ETHERNET) return; + if(msg->hops != 0) return; + if(ntohl(msg->xid) != xid) return; + if(memcmp(msg->chaddr, mymac, 6) != 0) return; + client_ip = ntohl(msg->yiaddr); + server_ip = ntohl(msg->siaddr); + memcpy(filename, msg->file, sizeof(filename)); + filename[sizeof(filename) - 1] = 0; + response_received = 1; +} + +int bootp_get(const unsigned char *macaddr, uint32_t *_client_ip, + uint32_t *_server_ip, char *_filename, size_t _len_filename, + uint8_t force) +{ + int len, tries, i; + int ret = -1; + uint8_t *packet_data; + uint8_t len_filename = 128; + + if (_len_filename < 128) { + len_filename = _len_filename; + } + if (got_ip && !force) { + ret = 0; + goto copy; + } + response_received = 0; + memcpy(mymac, macaddr, 6); + + client_ip = udp_get_ip(); + udp_set_ip(IPTOINT(0, 0, 0, 0)); + +#ifndef CSR_TIMER0_UPTIME_CYCLES_ADDR + if(seed == 0) { + seed_from_mac(macaddr); + } +#endif + + udp_set_broadcast_callback((udp_callback) rx_callback); + + tries = 3; + + while(1) { + xid = rand32(); + + packet_data = udp_get_tx_buffer(); + len = format_request(packet_data, xid, macaddr); + + udp_set_broadcast(); + udp_send(PORT_CLIENT, PORT_SERVER, len); + for(i=0;i<100000;i++) { + udp_service(); + if(response_received) break; + } + if(response_received) break; + tries--; + if(tries == 0) { + ret = -1; + goto end; + } + } + + ret = 0; + got_ip = 1; +copy: + *_server_ip = server_ip; + len = strlen(filename); + memcpy(_filename, filename, len_filename); + _filename[len_filename - 1] = 0; + +end: + *_client_ip = client_ip; + udp_set_broadcast_callback(NULL); + udp_set_ip(client_ip); + + return ret; +} diff --git a/litex/soc/software/libliteeth/bootp.h b/litex/soc/software/libliteeth/bootp.h new file mode 100644 index 0000000000..26d4fa2c97 --- /dev/null +++ b/litex/soc/software/libliteeth/bootp.h @@ -0,0 +1,21 @@ +#ifndef __BOOTP_H +#define __BOOTP_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +int bootp_get(const unsigned char *macaddr, uint32_t *client_ip, + uint32_t *server_ip, char *filename, size_t len_filename, + uint8_t force); + +int bootp_has_ip(void); + +#ifdef __cplusplus +} +#endif + +#endif /* __BOOTP_H */ + diff --git a/litex/soc/software/libliteeth/udp.c b/litex/soc/software/libliteeth/udp.c index 881b4f542e..a149f700be 100644 --- a/litex/soc/software/libliteeth/udp.c +++ b/litex/soc/software/libliteeth/udp.c @@ -32,14 +32,14 @@ struct ethernet_header { #ifndef HW_PREAMBLE_CRC - unsigned char preamble[8]; + uint8_t preamble[8]; #endif - unsigned char destmac[6]; - unsigned char srcmac[6]; - unsigned short ethertype; + uint8_t destmac[6]; + uint8_t srcmac[6]; + uint16_t ethertype; } __attribute__((packed)); -static void fill_eth_header(struct ethernet_header *h, const unsigned char *destmac, const unsigned char *srcmac, unsigned short ethertype) +static void fill_eth_header(struct ethernet_header *h, const uint8_t *destmac, const uint8_t *srcmac, uint16_t ethertype) { int i; @@ -67,16 +67,16 @@ static void fill_eth_header(struct ethernet_header *h, const unsigned char *dest #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]; + uint16_t hwtype; + uint16_t proto; + uint8_t hwsize; + uint8_t protosize; + uint16_t opcode; + uint8_t sender_mac[6]; + uint32_t sender_ip; + uint8_t target_mac[6]; + uint32_t target_ip; + uint8_t padding[18]; } __attribute__((packed)); #define IP_IPV4 0x45 @@ -85,23 +85,23 @@ struct arp_frame { #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; + uint8_t version; + uint8_t diff_services; + uint16_t total_length; + uint16_t identification; + uint16_t fragment_offset; + uint8_t ttl; + uint8_t proto; + uint16_t checksum; + uint32_t src_ip; + uint32_t dst_ip; } __attribute__((packed)); struct udp_header { - unsigned short src_port; - unsigned short dst_port; - unsigned short length; - unsigned short checksum; + uint16_t src_port; + uint16_t dst_port; + uint16_t length; + uint16_t checksum; } __attribute__((packed)); struct udp_frame { @@ -120,15 +120,15 @@ struct ethernet_frame { typedef union { struct ethernet_frame frame; - unsigned char raw[ETHMAC_SLOT_SIZE]; + uint8_t raw[ETHMAC_SLOT_SIZE]; } ethernet_buffer; -static unsigned int rxslot; -static unsigned int rxlen; +static uint32_t rxslot; +static uint32_t rxlen; static ethernet_buffer *rxbuffer; -static unsigned int txslot; -static unsigned int txlen; +static uint32_t txslot; +static uint32_t txlen; static ethernet_buffer *txbuffer; static void send_packet(void) @@ -138,7 +138,7 @@ static void send_packet(void) /* fill txbuffer */ #ifndef HW_PREAMBLE_CRC - unsigned int crc; + uint32_t crc; crc = crc32(&txbuffer->raw[8], txlen-8); txbuffer->raw[txlen ] = (crc & 0xff); txbuffer->raw[txlen+1] = (crc & 0xff00) >> 8; @@ -165,15 +165,20 @@ static void send_packet(void) txbuffer = (ethernet_buffer *)(ETHMAC_BASE + ETHMAC_SLOT_SIZE * (ETHMAC_RX_SLOTS + txslot)); } -static unsigned char my_mac[6]; -static unsigned int my_ip; +static uint8_t my_mac[6]; +static uint32_t my_ip; -void udp_set_ip(unsigned int ip) +void udp_set_ip(uint32_t ip) { my_ip = ip; } -void udp_set_mac(const unsigned char *macaddr) +uint32_t udp_get_ip(void) +{ + return my_ip; +} + +void udp_set_mac(const uint8_t *macaddr) { int i; for(i=0;i<6;i++) @@ -181,8 +186,16 @@ void udp_set_mac(const unsigned char *macaddr) } /* ARP cache - one entry only */ -static unsigned char cached_mac[6]; -static unsigned int cached_ip; +static uint8_t cached_mac[6]; +static uint32_t cached_ip; + +void udp_set_broadcast(void) +{ + int i; + for(i=0;i<6;i++) + cached_mac[i] = 0xFF; + cached_ip = IPTOINT(255, 255, 255, 255); +} static void process_arp(void) { @@ -229,9 +242,9 @@ static void process_arp(void) } } -static const unsigned char broadcast[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; +static const uint8_t broadcast[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; -int udp_arp_resolve(unsigned int ip) +int udp_arp_resolve(uint32_t ip) { struct arp_frame *arp; int i; @@ -279,16 +292,16 @@ int udp_arp_resolve(unsigned int ip) return 0; } -static unsigned short ip_checksum(unsigned int r, void *buffer, unsigned int length, int complete) +static uint16_t ip_checksum(uint32_t r, void *buffer, uint32_t length, int complete) { - unsigned char *ptr; - unsigned int i; + uint8_t *ptr; + uint32_t i; - ptr = (unsigned char *)buffer; + ptr = (uint8_t *)buffer; length >>= 1; for(i=0;i> 16) @@ -308,17 +321,17 @@ void *udp_get_tx_buffer(void) } struct pseudo_header { - unsigned int src_ip; - unsigned int dst_ip; - unsigned char zero; - unsigned char proto; - unsigned short length; + uint32_t src_ip; + uint32_t dst_ip; + uint8_t zero; + uint8_t proto; + uint16_t length; } __attribute__((packed)); -int udp_send(unsigned short src_port, unsigned short dst_port, unsigned int length) +int udp_send(uint16_t src_port, uint16_t dst_port, uint32_t length) { struct pseudo_header h; - unsigned int r; + uint32_t 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)) @@ -366,6 +379,7 @@ int udp_send(unsigned short src_port, unsigned short dst_port, unsigned int leng } static udp_callback rx_callback; +static udp_callback bx_callback; static void process_ip(void) { @@ -379,12 +393,22 @@ static void process_ip(void) // check disabled for QEMU compatibility //if(ntohs(rxbuffer->frame.contents.udp.ip.fragment_offset) != IP_DONT_FRAGMENT) return; if(udp_ip->ip.proto != IP_PROTO_UDP) return; - if(ntohl(udp_ip->ip.dst_ip) != my_ip) return; if(ntohs(udp_ip->udp.length) < sizeof(struct udp_header)) return; - - if(rx_callback) - rx_callback(ntohl(udp_ip->ip.src_ip), ntohs(udp_ip->udp.src_port), ntohs(udp_ip->udp.dst_port), - udp_ip->payload, ntohs(udp_ip->udp.length)-sizeof(struct udp_header)); + if(ntohl(udp_ip->ip.dst_ip) != my_ip) { + if(ntohl(udp_ip->ip.dst_ip) == IPTOINT(255, 255, 255, 255) && bx_callback) { + bx_callback(ntohl(udp_ip->ip.src_ip), ntohs(udp_ip->udp.src_port), ntohs(udp_ip->udp.dst_port), + udp_ip->payload, ntohs(udp_ip->udp.length)-sizeof(struct udp_header)); + } + } else { + + if(rx_callback) { + rx_callback(ntohl(udp_ip->ip.src_ip), ntohs(udp_ip->udp.src_port), ntohs(udp_ip->udp.dst_port), + udp_ip->payload, ntohs(udp_ip->udp.length)-sizeof(struct udp_header)); + } else if(bx_callback) { + bx_callback(ntohl(udp_ip->ip.src_ip), ntohs(udp_ip->udp.src_port), ntohs(udp_ip->udp.dst_port), + udp_ip->payload, ntohs(udp_ip->udp.length)-sizeof(struct udp_header)); + } + } } void udp_set_callback(udp_callback callback) @@ -392,6 +416,11 @@ void udp_set_callback(udp_callback callback) rx_callback = callback; } +void udp_set_broadcast_callback(udp_callback callback) +{ + bx_callback = callback; +} + static void process_frame(void) { flush_cpu_dcache(); @@ -412,12 +441,12 @@ static void process_frame(void) #endif #ifndef HW_PREAMBLE_CRC - unsigned int received_crc; - unsigned int computed_crc; - 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]); + uint32_t received_crc; + uint32_t computed_crc; + received_crc = ((uint32_t)rxbuffer->raw[rxlen-1] << 24) + |((uint32_t)rxbuffer->raw[rxlen-2] << 16) + |((uint32_t)rxbuffer->raw[rxlen-3] << 8) + |((uint32_t)rxbuffer->raw[rxlen-4]); computed_crc = crc32(&rxbuffer->raw[8], rxlen-12); if(received_crc != computed_crc) return; @@ -428,12 +457,11 @@ static void process_frame(void) else if(ntohs(rxbuffer->frame.eth_header.ethertype) == ETHERTYPE_IP) process_ip(); } -void udp_start(const unsigned char *macaddr, unsigned int ip) +void udp_start(const uint8_t *macaddr, uint32_t ip) { int i; ethmac_sram_reader_ev_pending_write(ETHMAC_EV_SRAM_READER); ethmac_sram_writer_ev_pending_write(ETHMAC_EV_SRAM_WRITER); - udp_set_ip(ip); udp_set_mac(macaddr); @@ -448,6 +476,7 @@ void udp_start(const unsigned char *macaddr, unsigned int ip) rxslot = 0; rxbuffer = (ethernet_buffer *)(ETHMAC_BASE + ETHMAC_SLOT_SIZE * rxslot); rx_callback = (udp_callback)0; + bx_callback = (udp_callback)0; } void udp_service(void) diff --git a/litex/soc/software/libliteeth/udp.h b/litex/soc/software/libliteeth/udp.h index 854065179d..f36946b274 100644 --- a/litex/soc/software/libliteeth/udp.h +++ b/litex/soc/software/libliteeth/udp.h @@ -12,15 +12,18 @@ extern "C" { #define UDP_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 udp_set_ip(unsigned int ip); -void udp_set_mac(const unsigned char *macaddr); -void udp_start(const unsigned char *macaddr, unsigned int ip); -int udp_arp_resolve(unsigned int ip); +typedef void (*udp_callback)(uint32_t src_ip, uint16_t src_port, uint16_t dst_port, void *data, uint32_t length); + +void udp_set_ip(uint32_t ip); +uint32_t udp_get_ip(void); +void udp_set_mac(const uint8_t *macaddr); +void udp_set_broadcast(void); +void udp_start(const uint8_t *macaddr, uint32_t ip); +int udp_arp_resolve(uint32_t ip); void *udp_get_tx_buffer(void); -int udp_send(unsigned short src_port, unsigned short dst_port, unsigned int length); +int udp_send(uint16_t src_port, uint16_t dst_port, uint32_t length); void udp_set_callback(udp_callback callback); +void udp_set_broadcast_callback(udp_callback callback); void udp_service(void); void eth_init(void);