commit cff122808a91b5db9d8c5e498240d8eb1808cd5b Author: mlalondesvn Date: Fri Sep 14 10:20:15 2007 +0000 initial import of the ENC28J60 soft for the Arduino :: Ethduino git-svn-id: svn+ssh://oldsvn/home/mlalondesvn/svn/Ethduino@1 b05466c9-153a-0410-ad00-ea1d4d8a27b5 diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..a7d45a2 --- /dev/null +++ b/Makefile @@ -0,0 +1,375 @@ +# Arduino makefile +# +# This makefile allows you to build sketches from the command line +# without the Arduino environment (or Java). +# Previous work on this file was done by mellis, eighthave and probably +# many others. +# +# Last State: 28.08.2007 | 15:00 CEST +# -- oli.keller ][ gmail.com +# +# This is a fork of the original makefiles shipped with Arduino 0009. +# It tries to reassemble the preprocessing which the Arduino IDE does. +# This makefile even allows to create an automatic reset, +# like the Arduino IDE does on an Arduino Diecimila board! +# No no, you don't have to push that tiny button again and again... ;) +# +# You should only have to check the first six variables below for +# propper settings. That's it. +# +# +# Detailed instructions for using the makefile: +# +# 1. Copy this file into the folder with your sketch. There should be a +# file with the ending .pde (e.g. foo.pde) +# +# 2. Below, modify the line containing "TARGET" to refer to the name of +# of your program's file without an extension (e.g. TARGET = foo). +# +# 3. Modify the line containg "INSTALL_DIR" to point to the directory that +# contains the Arduino installation (for Arduino installations in +# Mac OSX, this could be /Applications/arduino-0009 ). +# +# 4. Modify the line containing "PORT" to refer to the filename +# representing the USB or serial connection to your Arduino board +# (e.g. PORT = /dev/tty.USB0). If the exact name of this file +# changes, you can use * as a wildcard (e.g. PORT = /dev/tty.USB*). +# +# 5. Set the line containing "MCU" to match your board's processor. +# Older one's are atmega8 based, newer ones like Arduino Mini, Bluetooth +# or Diecimila have the atmega168. +# +# 6. At the command line, change to the directory containing your +# program's file and the makefile. +# +# 7. Type "make" and press enter to compile/verify your program. +# In TextMate you can just hit Command-R to trigger make. +# +# 8. Type "make upload", reset your Arduino board, and press enter to +# upload your program to the Arduino board. +# +# $Id$ + +############################################################################### +# +# project specific settings +# +TARGET = main +ARDUINO_VERSION = 0009 +INSTALL_DIR = /Applications/arduino-$(ARDUINO_VERSION) +############################################################################### + + +############################################################################### +# +# serial uploader settings +# +PORT = /dev/tty.usbserial-A* +UPLOAD_RATE = 19200 +AVRDUDE_PROGRAMMER = stk500 + +# HINT: If you want to use the automatic reset feature which comes with Arduino +# Diecimila, put the follwoing in your avrdude.conf: +# (Use the systemwide $(INSTALL_DIR)/tools/avr/etc/avrdude.conf or create a +# local $HOME/.avrduderc file) +# +# programmer +# id = "arduino"; +# desc = "Arduino Serial Bootloader"; +# type = stk500; +# reset = 7; +# # since Arduino Diecimila the serial DTR line can be used to trigger a reset! +# ; +# +# After this you can specify AVRDUDE_PROGRAMMER = arduino, above. +# On older boards you can manually ad this reset feature. Wire a cable from the +# FTDI 232 Chip's DTR pin (the number differs from package to package, see datasheet) +# to the RESET line of the ATmega. Inbetween this connection must be a 100nF capacitor. +##################################################################################### + + +##################################################################################### +# +# hardware dependent settings +# +MCU = atmega168 +F_CPU = 16000000 +# F_CPU sets the clock speed in Hz. So far its 16MHz on all standard boards. +##################################################################################### + + + + + +##################################################################################### +# +# Below here nothing should be changed... +# +##################################################################################### + + + + + +ARDUINO = $(INSTALL_DIR)/lib/targets/arduino +#AVR_TOOLS_PATH = $(INSTALL_DIR)/tools/avr/bin +AVR_TOOLS_PATH = /usr/local/bin + +# +# Arduino & Wiring libraries +############################ +SRC = $(ARDUINO)/pins_arduino.c $(ARDUINO)/wiring.c \ +$(ARDUINO)/wiring_analog.c $(ARDUINO)/wiring_digital.c \ +$(ARDUINO)/wiring_pulse.c $(ARDUINO)/wiring_serial.c \ +$(ARDUINO)/wiring_shift.c $(ARDUINO)/WInterrupts.c +CXXSRC = $(ARDUINO)/HardwareSerial.cpp $(ARDUINO)/WRandom.cpp + +CXXSRC+= programStrings.cpp + +# +# Procyon AVRLib libraries +########################## +# Base +CXXSRC+= debug.cpp +#CXXSRC+= buffer.cpp +#CXXSRC+= timer.cpp +#CXXSRC+= timerx8.cpp +#CXXSRC+= timer128.cpp + +# Net +CXXSRC+= netstack.cpp +CXXSRC+= ip.cpp +CXXSRC+= arp.cpp +CXXSRC+= icmp.cpp +CXXSRC+= net.cpp +# CXXSRC+= dhcp.cpp + +# Driver +CXXSRC+=enc28j60.cpp + +################## +# End of libraries +FORMAT = ihex + +# Name of this Makefile (used for "make depend"). +MAKEFILE = Makefile + +# Debugging format. +# Native formats for AVR-GCC's -g are stabs [default], or dwarf-2. +# AVR (extended) COFF requires stabs, plus an avr-objcopy run. +DEBUG = stabs + +OPT = s + +# Place -D or -U options here +CDEFS = -DF_CPU=$(F_CPU) +CXXDEFS = -DF_CPU=$(F_CPU) + +# Place -I options here +CINCS = -I$(ARDUINO) +CXXINCS = -I$(ARDUINO) + +# Compiler flag to set the C Standard level. +# c89 - "ANSI" C +# gnu89 - c89 plus GCC extensions +# c99 - ISO C99 standard (not yet fully implemented) +# gnu99 - c99 plus GCC extensions +CSTANDARD = -std=gnu99 +CDEBUG = -g$(DEBUG) +CWARN = -Wall -Wstrict-prototypes +CTUNING = -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums +#CEXTRA = -Wa,-adhlns=$(<:.c=.lst) + +CFLAGS = $(CDEBUG) $(CDEFS) $(CINCS) -O$(OPT) $(CWARN) $(CSTANDARD) $(CEXTRA) +CXXFLAGS = $(CDEFS) $(CINCS) -O$(OPT) +#ASFLAGS = -Wa,-adhlns=$(<:.S=.lst),-gstabs +LDFLAGS = -lm + + +# Programming support using avrdude. Settings and variables. +AVRDUDE_PORT = $(PORT) +AVRDUDE_WRITE_FLASH = -U flash:w:applet/$(TARGET).hex +#AVRDUDE_FLAGS = -V -F -C $(INSTALL_DIR)/tools/avr/etc/avrdude.conf -p $(MCU) -P $(AVRDUDE_PORT) -c $(AVRDUDE_PROGRAMMER) -b $(UPLOAD_RATE) +AVRDUDE_FLAGS = -F -C $(INSTALL_DIR)/tools/avr/etc/avrdude.conf -p $(MCU) -P $(AVRDUDE_PORT) -c $(AVRDUDE_PROGRAMMER) -b $(UPLOAD_RATE) + +# Program settings +CC = $(AVR_TOOLS_PATH)/avr-gcc +CXX = $(AVR_TOOLS_PATH)/avr-g++ +OBJCOPY = $(AVR_TOOLS_PATH)/avr-objcopy +OBJDUMP = $(AVR_TOOLS_PATH)/avr-objdump +AR = $(AVR_TOOLS_PATH)/avr-ar +SIZE = $(AVR_TOOLS_PATH)/avr-size +NM = $(AVR_TOOLS_PATH)/avr-nm +# AVRDUDE = $(AVR_TOOLS_PATH)/avrdude +#CC = avr-gcc +#CXX = avr-g++ +#OBJCOPY = avr-objcopy +#OBJDUMP = avr-objdump +#AR = avr-ar +#SIZE = avr-size +#NM = avr-nm +AVRDUDE = avrdude +REMOVE = rm -f +MV = mv -f + +# Define all object files. +OBJ = $(SRC:.c=.o) $(CXXSRC:.cpp=.o) $(ASRC:.S=.o) + +# Define all listing files. +LST = $(ASRC:.S=.lst) $(CXXSRC:.cpp=.lst) $(SRC:.c=.lst) + +# Combine all necessary flags and optional flags. +# Add target processor to flags. +ALL_CFLAGS = -mmcu=$(MCU) -I. $(CFLAGS) +ALL_CXXFLAGS = -mmcu=$(MCU) -I. $(CXXFLAGS) +ALL_ASFLAGS = -mmcu=$(MCU) -I. -x assembler-with-cpp $(ASFLAGS) + + +# Default target. +all: applet_files build sizeafter + +build: elf hex bin s + +applet_files: $(TARGET).pde + # + # Here is the "preprocessing". + # It creates a .cpp file based with the same name as the .pde file. + # On top of the new .cpp file comes the WProgram.h header. + # At the end there is a generic main() function attached. + # The ne .cpp file will be compiled. Errors during compile will + # refer to this new, automatically generated, file. + # Not the original .pde file you actually edit... + # + test -d applet || mkdir applet + echo '#include "WProgram.h"' > applet/$(TARGET).cpp + echo '#include "HardwareSerial.h"' >> applet/$(TARGET).cpp + cat $(TARGET).pde >> applet/$(TARGET).cpp + + echo >> applet/$(TARGET).cpp + echo >> applet/$(TARGET).cpp + echo '/**' >> applet/$(TARGET).cpp + echo ' * Main program function' >> applet/$(TARGET).cpp + echo ' * (Autogenerated!)' >> applet/$(TARGET).cpp + echo '**/' >> applet/$(TARGET).cpp + echo 'int main(void)' >> applet/$(TARGET).cpp + echo '{' >> applet/$(TARGET).cpp + echo ' init();' >> applet/$(TARGET).cpp + echo >> applet/$(TARGET).cpp + echo ' // Call user setup' >> applet/$(TARGET).cpp + echo ' setup();' >> applet/$(TARGET).cpp + echo >> applet/$(TARGET).cpp + echo ' // Call user main loop' >> applet/$(TARGET).cpp + echo ' for (;;)' >> applet/$(TARGET).cpp + echo ' loop();' >> applet/$(TARGET).cpp + echo ' init();' >> applet/$(TARGET).cpp + echo ' return 0;' >> applet/$(TARGET).cpp + echo '}' >> applet/$(TARGET).cpp + +elf: applet/$(TARGET).elf +hex: applet/$(TARGET).hex +bin: applet/$(TARGET).bin +eep: applet/$(TARGET).eep +lss: applet/$(TARGET).lss +s: applet/$(TARGET).s + +# Program the device. +upload: applet/$(TARGET).hex + $(AVRDUDE) $(AVRDUDE_FLAGS) $(AVRDUDE_WRITE_FLASH) + + +# Display size of file. +HEXSIZE = $(SIZE) --target=$(FORMAT) applet/$(TARGET).hex +ELFSIZE = $(SIZE) applet/$(TARGET).elf +sizebefore: + @if [ -f applet/$(TARGET).elf ]; then echo; echo $(MSG_SIZE_BEFORE); $(HEXSIZE); echo; fi + +sizeafter: + @if [ -f applet/$(TARGET).elf ]; then echo; echo $(MSG_SIZE_AFTER); $(HEXSIZE); echo; fi + + +# Convert ELF to COFF for use in debugging / simulating in AVR Studio or VMLAB. +COFFCONVERT=$(OBJCOPY) --debugging \ +--change-section-address .data-0x800000 \ +--change-section-address .bss-0x800000 \ +--change-section-address .noinit-0x800000 \ +--change-section-address .eeprom-0x810000 + + +coff: applet/$(TARGET).elf + $(COFFCONVERT) -O coff-avr applet/$(TARGET).elf $(TARGET).cof + + +extcoff: $(TARGET).elf + $(COFFCONVERT) -O coff-ext-avr applet/$(TARGET).elf $(TARGET).cof + + +.SUFFIXES: .elf .hex .eep .lss .sym .bin .s + +.elf.hex: + $(OBJCOPY) -O $(FORMAT) -R .eeprom $< $@ + +.elf.eep: + -$(OBJCOPY) -j .eeprom --set-section-flags=.eeprom="alloc,load" \ + --change-section-lma .eeprom=0 -O $(FORMAT) $< $@ + +# Create extended listing file from ELF output file. +.elf.lss: + $(OBJDUMP) -h -S $< > $@ + +# Create a symbol table from ELF output file. +.elf.sym: + $(NM) -n $< > $@ + +.elf.bin: + $(OBJCOPY) -O $(FORMAT) applet/$(TARGET).elf applet/$(TARGET).bin + +# Link: create ELF output file from library. +applet/$(TARGET).elf: $(TARGET).pde applet/core.a + $(CC) $(ALL_CFLAGS) -o $@ applet/$(TARGET).cpp -L. applet/core.a $(LDFLAGS) + +applet/core.a: $(OBJ) + @for i in $(OBJ); do echo $(AR) rcs applet/core.a $$i; $(AR) rcs applet/core.a $$i; done + +# Compile: create object files from C++ source files. +.cpp.o: + $(CXX) -c $(ALL_CXXFLAGS) $< -o $@ + +# Compile: create object files from C source files. +.c.o: + $(CC) -c $(ALL_CFLAGS) $< -o $@ + + +# Compile: create assembler files from C++ source files. +.cpp.s: + $(CXX) -S $(ALL_CXXFLAGS) $< -o $@ + +# Compile: create assembler files from C source files. +.c.s: + $(CC) -S $(ALL_CFLAGS) $< -o $@ + + +# Assemble: create object files from assembler source files. +.S.o: + $(CC) -c $(ALL_ASFLAGS) $< -o $@ + + + +# Target: clean project. +clean: + $(REMOVE) applet/$(TARGET).hex applet/$(TARGET).eep applet/$(TARGET).cof applet/$(TARGET).elf \ + applet/$(TARGET).s applet/$(TARGET).cpp applet/$(TARGET).bin \ + applet/$(TARGET).map applet/$(TARGET).sym applet/$(TARGET).lss applet/core.a \ + $(OBJ) $(LST) $(SRC:.c=.s) $(SRC:.c=.d) $(CXXSRC:.cpp=.s) $(CXXSRC:.cpp=.d) + +depend: + if grep '^# DO NOT DELETE' $(MAKEFILE) >/dev/null; \ + then \ + sed -e '/^# DO NOT DELETE/,$$d' $(MAKEFILE) > \ + $(MAKEFILE).$$$$ && \ + $(MV) $(MAKEFILE).$$$$ $(MAKEFILE); \ + fi + echo '# DO NOT DELETE THIS LINE -- make depend depends on it.' \ + >> $(MAKEFILE); \ + $(CC) -M -mmcu=$(MCU) $(CDEFS) $(CINCS) $(SRC) $(ASRC) >> $(MAKEFILE) + +.PHONY: all build elf hex eep lss sym program coff extcoff clean depend applet_files sizebefore sizeafter diff --git a/arp.cpp b/arp.cpp new file mode 100644 index 0000000..4561a24 --- /dev/null +++ b/arp.cpp @@ -0,0 +1,235 @@ +/*! \file arp.c \brief ARP Protocol Library. */ +//***************************************************************************** +// +// File Name : 'arp.c' +// Title : ARP Protocol Library +// Author : Pascal Stang +// Created : 9/7/2004 +// Revised : 7/3/2005 +// Version : 0.1 +// Target MCU : Atmel AVR series +// Editor Tabs : 4 +// +//***************************************************************************** + +#include "net.h" +#include "nic.h" +#include "arp.h" + +#include "HardwareSerial.h" + +#ifdef ARP_DEBUG_PRINT +#include "debug.h" +#include +#include "programStrings.h" +#include "HardwareSerial.h" +#endif + +// global variables + +/// Single ARP table entry/record +struct ArpEntry +{ + uint32_t ipaddr; ///< remote-note IP address + struct netEthAddr ethaddr; ///< remote-node ethernet (hardware/mac) address + uint8_t time; ///< time to live (in ARP table); this is decremented by arpTimer() +}; + +struct ArpEntry ArpMyAddr; ///< my local interface information (IP and MAC address) +struct ArpEntry ArpTable[ARP_TABLE_SIZE]; ///< ARP table of matched IP<->MAC associations + + +void arpInit(void) +{ + u08 i; + // initialize all ArpTable elements to unused + for(i=0; iarp ); + #endif + + // for now, we just reply to requests + // need to add ARP cache + if( (packet->arp.dipaddr == HTONL(ArpMyAddr.ipaddr)) && + (packet->arp.opcode == htons(ARP_OPCODE_REQUEST)) ) + { + // in ARP header + // copy sender's address info to dest. fields + packet->arp.dhwaddr = packet->arp.shwaddr; + packet->arp.dipaddr = packet->arp.sipaddr; + // fill in our information + packet->arp.shwaddr = ArpMyAddr.ethaddr; + packet->arp.sipaddr = HTONL(ArpMyAddr.ipaddr); + // change op to reply + packet->arp.opcode = htons(ARP_OPCODE_REPLY); + + // in ethernet header + packet->eth.dest = packet->eth.src; + packet->eth.src = ArpMyAddr.ethaddr; + + #ifdef ARP_DEBUG + SPrint_P(PSTR("Sending ARP Reply\r\n")); + arpPrintHeader( &packet->arp ); + #endif + + // send reply! + nicSend(len, (unsigned char*)packet); + } +} + +void arpIpIn(struct netEthIpHeader* packet) +{ + int8_t index; + + // check if sender is already present in arp table + index = arpMatchIp(HTONL(packet->ip.srcipaddr)); + if(index != -1) + { + // sender's IP address found, update ARP entry + ArpTable[index].ethaddr = packet->eth.src; + // and we're done + return; + } + + // sender was not present in table, + // must add in empty/expired slot + for(index=0; indexeth.src; + ArpTable[index].ipaddr = HTONL(packet->ip.srcipaddr); + ArpTable[index].time = ARP_CACHE_TIME_TO_LIVE; + // and we're done + return; + } + } + + // no space in table, we give up +} + +void arpIpOut(struct netEthIpHeader* packet, uint32_t phyDstIp) +{ + int index; + // check if destination is already present in arp table + // use the physical dstIp if it's provided, otherwise the dstIp in packet + if(phyDstIp) + index = arpMatchIp(phyDstIp); + else + index = arpMatchIp(HTONL(packet->ip.destipaddr)); + // fill in ethernet info + if(index != -1) + { + // ARP entry present, fill eth address(es) + packet->eth.src = ArpMyAddr.ethaddr; + packet->eth.dest = ArpTable[index].ethaddr; + packet->eth.type = HTONS(ETHTYPE_IP); + } + else + { + // not in table, must send ARP request + packet->eth.src = ArpMyAddr.ethaddr; + // MUST CHANGE, but for now, send this one broadcast + packet->eth.dest.addr[0] = 0xFF; + packet->eth.dest.addr[1] = 0xFF; + packet->eth.dest.addr[2] = 0xFF; + packet->eth.dest.addr[3] = 0xFF; + packet->eth.dest.addr[4] = 0xFF; + packet->eth.dest.addr[5] = 0xFF; + packet->eth.type = HTONS(ETHTYPE_IP); + } +} + +void arpTimer(void) +{ + int index; + // this function meant to be called on a regular time interval + + // decrement time-to-live for all entries + for(index=0; index 6 + debugPrintHexTable(60, (unsigned char*)&packet); +#endif + // print operation type + SPrint_P(PSTR("Operation : ")); + if(packet->opcode == htons(ARP_OPCODE_REQUEST)) + SPrint_P(PSTR("REQUEST")); + else if(packet->opcode == htons(ARP_OPCODE_REPLY)) + SPrint_P(PSTR("REPLY")); + else + SPrint_P(PSTR("UNKNOWN")); + Serial.println(); + // print source hardware address + SPrint_P(PSTR("SrcHwAddr : ")); netPrintEthAddr(&packet->shwaddr); Serial.println(); + // print source protocol address + SPrint_P(PSTR("SrcProtoAddr: ")); netPrintIPAddr(HTONL(packet->sipaddr)); Serial.println(); + // print target hardware address + SPrint_P(PSTR("DstHwAddr : ")); netPrintEthAddr(&packet->dhwaddr); Serial.println(); + // print target protocol address + SPrint_P(PSTR("DstProtoAddr: ")); netPrintIPAddr(HTONL(packet->dipaddr)); Serial.println(); +} + + +void arpPrintTable(void) +{ + uint8_t i; + + // print ARP table + SPrint_P(PSTR("Time Eth Address IP Address\r\n")); + SPrint_P(PSTR("---------------------------------------\r\n")); + for(i=0; iMAC mapping is not in the table. +/// +/// \note This code is currently below version 1.0, and therefore is considered +/// to be lacking in some functionality or documentation, or may not be fully +/// tested. Nonetheless, you can expect most functions to work. +/// +// This code is distributed under the GNU Public License +// which can be found at http://www.gnu.org/licenses/gpl.txt +//***************************************************************************** +//@{ + +#ifndef ARP_H +#define ARP_H + +#include "net.h" + +#ifndef ARP_TABLE_SIZE +#define ARP_TABLE_SIZE 8 +#endif + +#ifndef ARP_CACHE_TIME_TO_LIVE +#define ARP_CACHE_TIME_TO_LIVE 100 +#endif + +#define ARP_DEBUG +#define ARP_DEBUG_PRINT + + +/*! Initialize ARP system. + Clears ARP table and prepares it for use. This is typically done + once at program initialization. */ +void arpInit(void); + +/*! Set IP and Ethernet hardware/MAC address. + This must be done before valid replies can be generated for ARP + requests. Typically done once at program initialization. */ +void arpSetAddress(struct netEthAddr* myeth, uint32_t myip); + +/*! Processes incoming ARP packets. + This function is to be called when an ARP type packet has arrived + over the network. If the packet type is an ARP request for us, + an ARP reply will be generated and sent. */ +void arpArpIn(unsigned int len, struct netEthArpHeader* packet); + +/*! Process incoming IP packets to harvest IP<->MAC relationships. + This function should be called when IP packets are received over the + network. It does nothing more than harvest the IP<->MAC address + relationships from the ethernet and IP header of the packet. The + packet is not changed nor processed. Nothing is sent on the network. + Use of this command is not required, but it is a good way to + automatically fill the ARP table with information about nodes that are + active on the network. + + \warning On very busy or heavily populated netorks, this can quickly + fill the ARP table with unnecessary entries, and/or cause some CPU load. +*/ +void arpIpIn(struct netEthIpHeader* packet); + +/*! Process outgoing IP packet to fill in ethernet header information. + To be sent on a network, an IP packet must have the correct ethernet + header information appended to the front. This function will fill + in this information. + + A physical destination IP address argument is needed to support + sending to a gateway (i.e. when a packet is destined for a node that + is not on this network, IP addressing is as usual, but we phyiscally + send the packet to the gateway's ethernet address/interface). + + \warning Technically, if an IP<->MAC address mapping is not in the + ARP table, then the IP packet should be held while an ARP request is + made, and the reply received. However, in single-threaded ram-limited + embedded systems, such a holdup is unacceptable. This function instead + sends the packet as an ethernet broadcast if a mapping cannot be found. + + \todo Send the packet broadcast AND send an ARP request, if a mapping + is not found. +*/ +void arpIpOut(struct netEthIpHeader* packet, uint32_t phyDstIp); + +/*! Periodic ARP cache maintenance. + This function is to be called once per second and will slowly + expire old ARP cache entries. */ +void arpTimer(void); + + +/*! Check if this IP address is present in the ARP cache. Internal function. + If IP address is found, function returns index of entry. If not found, + returns -1. */ +int arpMatchIp(uint32_t ipaddr); + +//! Print diagnotic information about ARP packet. +void arpPrintHeader(struct netArpHeader* packet); +//! Print diagnotic information about ARP cache. +void arpPrintTable(void); + +#endif +//@} diff --git a/avrlibdefs.h b/avrlibdefs.h new file mode 100644 index 0000000..0ac646f --- /dev/null +++ b/avrlibdefs.h @@ -0,0 +1,81 @@ +/*! \file avrlibdefs.h \brief AVRlib global defines and macros. */ +//***************************************************************************** +// +// File Name : 'avrlibdefs.h' +// Title : AVRlib global defines and macros include file +// Author : Pascal Stang +// Created : 7/12/2001 +// Revised : 9/30/2002 +// Version : 1.1 +// Target MCU : Atmel AVR series +// Editor Tabs : 4 +// +// Description : This include file is designed to contain items useful to all +// code files and projects, regardless of specific implementation. +// +// This code is distributed under the GNU Public License +// which can be found at http://www.gnu.org/licenses/gpl.txt +// +//***************************************************************************** + + +#ifndef AVRLIBDEFS_H +#define AVRLIBDEFS_H + +#include "wiring.h" +#include "wiring_private.h" + +// Code compatibility to new AVR-libc +// outb(), inb(), inw(), outw(), BV(), sbi(), cbi(), sei(), cli() + +#ifndef outb + #define outb(addr, data) addr = (data) +#endif +#ifndef inb + #define inb(addr) (addr) +#endif +#ifndef outw + #define outw(addr, data) addr = (data) +#endif +#ifndef inw + #define inw(addr) (addr) +#endif +#ifndef BV + #define BV(bit) (1<<(bit)) +#endif +#ifndef cbi + #define cbi(reg,bit) reg &= ~(BV(bit)) +#endif +#ifndef sbi + #define sbi(reg,bit) reg |= (BV(bit)) +#endif +#ifndef cli + #define cli() __asm__ __volatile__ ("cli" ::) +#endif +#ifndef sei + #define sei() __asm__ __volatile__ ("sei" ::) +#endif + +// use this for packed structures +// (this is seldom necessary on an 8-bit architecture like AVR, +// but can assist in code portability to AVR) +#ifndef GNUC_PACKED + #define GNUC_PACKED __attribute__((packed)) +#endif + +// port address helpers +#ifndef DDR + #define DDR(x) ((x)-1) // address of data direction register of port x +#endif +#ifndef PIN + #define PIN(x) ((x)-2) // address of input register of port x +#endif + +#ifndef MIN + #define MIN min +#endif +#ifndef MAX + #define MAX max +#endif + +#endif diff --git a/avrlibtypes.h b/avrlibtypes.h new file mode 100644 index 0000000..1faac5b --- /dev/null +++ b/avrlibtypes.h @@ -0,0 +1,88 @@ +/*! \file avrlibtypes.h \brief AVRlib global types and typedefines. */ +//***************************************************************************** +// +// File Name : 'avrlibtypes.h' +// Title : AVRlib global types and typedefines include file +// Author : Pascal Stang +// Created : 7/12/2001 +// Revised : 9/30/2002 +// Version : 1.0 +// Target MCU : Atmel AVR series +// Editor Tabs : 4 +// +// Description : Type-defines required and used by AVRlib. Most types are also +// generally useful. +// +// This code is distributed under the GNU Public License +// which can be found at http://www.gnu.org/licenses/gpl.txt +// +//***************************************************************************** + + +#ifndef AVRLIBTYPES_H +#define AVRLIBTYPES_H + +#ifndef Wiring_h +#include "wiring.h" +#endif + +#ifndef WIN32 + // true/false defines + #define FALSE 0 + #define TRUE -1 +#endif + +// datatype definitions macros +typedef unsigned char u08; +typedef signed char s08; +typedef unsigned short u16; +typedef signed short s16; +typedef unsigned long u32; +typedef signed long s32; +typedef unsigned long long u64; +typedef signed long long s64; + +/* use inttypes.h instead +// C99 standard integer type definitions +typedef unsigned char uint8_t; +typedef signed char int8_t; +typedef unsigned short uint16_t; +typedef signed short int16_t; +typedef unsigned long uint32_t; +typedef signed long int32_t; +typedef unsigned long uint64_t; +typedef signed long int64_t; +*/ +// maximum value that can be held +// by unsigned data types (8,16,32bits) +#define MAX_U08 255 +#define MAX_U16 65535 +#define MAX_U32 4294967295 + +// maximum values that can be held +// by signed data types (8,16,32bits) +#define MIN_S08 -128 +#define MAX_S08 127 +#define MIN_S16 -32768 +#define MAX_S16 32767 +#define MIN_S32 -2147483648 +#define MAX_S32 2147483647 + +#ifndef WIN32 + // more type redefinitions + typedef unsigned char BOOL; +// typedef unsigned char BYTE; + typedef unsigned int WORD; + typedef unsigned long DWORD; + + typedef unsigned char UCHAR; + typedef unsigned int UINT; + typedef unsigned short USHORT; + typedef unsigned long ULONG; + + typedef char CHAR; + typedef int INT; + typedef long LONG; +#endif + +#endif diff --git a/buffer.cpp b/buffer.cpp new file mode 100644 index 0000000..85ec225 --- /dev/null +++ b/buffer.cpp @@ -0,0 +1,148 @@ +/*! \file buffer.c \brief Multipurpose byte buffer structure and methods. */ +//***************************************************************************** +// +// File Name : 'buffer.c' +// Title : Multipurpose byte buffer structure and methods +// Author : Pascal Stang - Copyright (C) 2001-2002 +// Created : 9/23/2001 +// Revised : 9/23/2001 +// Version : 1.0 +// Target MCU : any +// Editor Tabs : 4 +// +// This code is distributed under the GNU Public License +// which can be found at http://www.gnu.org/licenses/gpl.txt +// +//***************************************************************************** + +#include "buffer.h" +#include "avr/io.h" + +#ifndef CRITICAL_SECTION_START +#define CRITICAL_SECTION_START unsigned char _sreg = SREG; cli() +#define CRITICAL_SECTION_END SREG = _sreg +#endif + +// global variables + +// initialization + +void bufferInit(cBuffer* buffer, unsigned char *start, unsigned short size) +{ + // begin critical section + CRITICAL_SECTION_START; + // set start pointer of the buffer + buffer->dataptr = start; + buffer->size = size; + // initialize index and length + buffer->dataindex = 0; + buffer->datalength = 0; + // end critical section + CRITICAL_SECTION_END; +} + +// access routines +unsigned char bufferGetFromFront(cBuffer* buffer) +{ + unsigned char data = 0; + // begin critical section + CRITICAL_SECTION_START; + // check to see if there's data in the buffer + if(buffer->datalength) + { + // get the first character from buffer + data = buffer->dataptr[buffer->dataindex]; + // move index down and decrement length + buffer->dataindex++; + if(buffer->dataindex >= buffer->size) + { + buffer->dataindex -= buffer->size; + } + buffer->datalength--; + } + // end critical section + CRITICAL_SECTION_END; + // return + return data; +} + +void bufferDumpFromFront(cBuffer* buffer, unsigned short numbytes) +{ + // begin critical section + CRITICAL_SECTION_START; + // dump numbytes from the front of the buffer + // are we dumping less than the entire buffer? + if(numbytes < buffer->datalength) + { + // move index down by numbytes and decrement length by numbytes + buffer->dataindex += numbytes; + if(buffer->dataindex >= buffer->size) + { + buffer->dataindex -= buffer->size; + } + buffer->datalength -= numbytes; + } + else + { + // flush the whole buffer + buffer->datalength = 0; + } + // end critical section + CRITICAL_SECTION_END; +} + +unsigned char bufferGetAtIndex(cBuffer* buffer, unsigned short index) +{ + // begin critical section + CRITICAL_SECTION_START; + // return character at index in buffer + unsigned char data = buffer->dataptr[(buffer->dataindex+index)%(buffer->size)]; + // end critical section + CRITICAL_SECTION_END; + return data; +} + +unsigned char bufferAddToEnd(cBuffer* buffer, unsigned char data) +{ + // begin critical section + CRITICAL_SECTION_START; + // make sure the buffer has room + if(buffer->datalength < buffer->size) + { + // save data byte at end of buffer + buffer->dataptr[(buffer->dataindex + buffer->datalength) % buffer->size] = data; + // increment the length + buffer->datalength++; + // end critical section + CRITICAL_SECTION_END; + // return success + return -1; + } + // end critical section + CRITICAL_SECTION_END; + // return failure + return 0; +} + +unsigned short bufferIsNotFull(cBuffer* buffer) +{ + // begin critical section + CRITICAL_SECTION_START; + // check to see if the buffer has room + // return true if there is room + unsigned short bytesleft = (buffer->size - buffer->datalength); + // end critical section + CRITICAL_SECTION_END; + return bytesleft; +} + +void bufferFlush(cBuffer* buffer) +{ + // begin critical section + CRITICAL_SECTION_START; + // flush contents of the buffer + buffer->datalength = 0; + // end critical section + CRITICAL_SECTION_END; +} + diff --git a/buffer.h b/buffer.h new file mode 100644 index 0000000..88525d3 --- /dev/null +++ b/buffer.h @@ -0,0 +1,74 @@ +/*! \file buffer.h \brief Multipurpose byte buffer structure and methods. */ +//***************************************************************************** +// +// File Name : 'buffer.h' +// Title : Multipurpose byte buffer structure and methods +// Author : Pascal Stang - Copyright (C) 2001-2002 +// Created : 9/23/2001 +// Revised : 11/16/2002 +// Version : 1.1 +// Target MCU : any +// Editor Tabs : 4 +// +/// \ingroup general +/// \defgroup buffer Circular Byte-Buffer Structure and Function Library (buffer.c) +/// \code #include "buffer.h" \endcode +/// \par Overview +/// This byte-buffer structure provides an easy and efficient way to store +/// and process a stream of bytes.  You can create as many buffers as you +/// like (within memory limits), and then use this common set of functions to +/// access each buffer.  The buffers are designed for FIFO operation (first +/// in, first out).  This means that the first byte you put in the buffer +/// will be the first one you get when you read out the buffer.  Supported +/// functions include buffer initialize, get byte from front of buffer, add +/// byte to end of buffer, check if buffer is full, and flush buffer.  The +/// buffer uses a circular design so no copying of data is ever necessary. +/// This buffer is not dynamically allocated, it has a user-defined fixed +/// maximum size.  This buffer is used in many places in the avrlib code. +// +// This code is distributed under the GNU Public License +// which can be found at http://www.gnu.org/licenses/gpl.txt +// +//***************************************************************************** +//@{ + +#ifndef BUFFER_H +#define BUFFER_H + +// structure/typdefs + +//! cBuffer structure +typedef struct struct_cBuffer +{ + unsigned char *dataptr; ///< the physical memory address where the buffer is stored + unsigned short size; ///< the allocated size of the buffer + unsigned short datalength; ///< the length of the data currently in the buffer + unsigned short dataindex; ///< the index into the buffer where the data starts +} cBuffer; + +// function prototypes + +//! initialize a buffer to start at a given address and have given size +void bufferInit(cBuffer* buffer, unsigned char *start, unsigned short size); + +//! get the first byte from the front of the buffer +unsigned char bufferGetFromFront(cBuffer* buffer); + +//! dump (discard) the first numbytes from the front of the buffer +void bufferDumpFromFront(cBuffer* buffer, unsigned short numbytes); + +//! get a byte at the specified index in the buffer (kind of like array access) +// ** note: this does not remove the byte that was read from the buffer +unsigned char bufferGetAtIndex(cBuffer* buffer, unsigned short index); + +//! add a byte to the end of the buffer +unsigned char bufferAddToEnd(cBuffer* buffer, unsigned char data); + +//! check if the buffer is full/not full (returns zero value if full) +unsigned short bufferIsNotFull(cBuffer* buffer); + +//! flush (clear) the contents of the buffer +void bufferFlush(cBuffer* buffer); + +#endif +//@} diff --git a/debug.cpp b/debug.cpp new file mode 100644 index 0000000..a36c34d --- /dev/null +++ b/debug.cpp @@ -0,0 +1,99 @@ +/*! \file debug.c \brief Debugging function library. */ +//***************************************************************************** +// +// File Name : 'debug.c' +// Title : Helpful debugging functions +// Author : Pascal Stang - Copyright (C) 2003 +// Created : 2003-03-13 +// Revised : 2003-03-13 +// Version : 0.1 +// Target MCU : Atmel AVR series +// Editor Tabs : 4 +// +// Description : This file contains a set of functions which may be useful +// for general debugging. +// +// This code is distributed under the GNU Public License +// which can be found at http://www.gnu.org/licenses/gpl.txt +// +//***************************************************************************** + +#include +#include + +#include "debug.h" +#include "HardwareSerial.h" + +#include +#include "programStrings.h" + +// global variables + +// functions + +// Print a part of memory as a formatted hex table with ascii translation +void debugPrintHexTable(u16 length, u08 *buffer) +{ + u08 i; + u16 j; + u08 *buf; + u08 s; + + buf = buffer; + + // print the low order address indicies and ASCII header + SPrint_P(PSTR(" 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 0123456789ABCDEF\r\n")); + SPrint_P(PSTR(" ----------------------------------------------- ---- ASCII -----\r\n")); + + // print the data + for(j=0; j<((length+15)>>4); j++) + { + // print the high order address index for this line + Serial.print(j<<4); + Serial.print(' '); + + // print the hex data + for(i=0; i<0x10; i++) + { + // be nice and print only up to the exact end of the data + if( ((j<<4)+i) < length) + { + // print hex byte + Serial.print(buf[(j<<4)+i], HEX); + Serial.print(' '); + } + else + { + // we're past the end of the data's length + // print spaces + SPrint_P(PSTR(" ")); + } + } + + // leave some space + Serial.print(' '); + + // print the ascii data + for(i=0; i<0x10; i++) + { + // be nice and print only up to the exact end of the data + if( ((j<<4)+i) < length) + { + // get the character + s = buf[(j<<4)+i]; + // make sure character is printable + if(s >= 0x20) + Serial.print(s, HEX); + else + Serial.print('.'); + } + else + { + // we're past the end of the data's length + // print a space + Serial.print(' '); + } + } + Serial.println(); + } +} diff --git a/debug.h b/debug.h new file mode 100644 index 0000000..0703611 --- /dev/null +++ b/debug.h @@ -0,0 +1,35 @@ +/*! \file debug.h \brief Debugging function library. */ +//***************************************************************************** +// +// File Name : 'debug.h' +// Title : Helpful debugging functions +// Author : Pascal Stang - Copyright (C) 2003 +// Created : 2003-03-13 +// Revised : 2003-03-13 +// Version : 0.1 +// Target MCU : Atmel AVR series +// Editor Tabs : 4 +// +// Description : This file contains a set of functions which may be useful +// for general debugging. +// +// This code is distributed under the GNU Public License +// which can be found at http://www.gnu.org/licenses/gpl.txt +// +//***************************************************************************** + +#ifndef DEBUG_H +#define DEBUG_H + +#include "avrlibdefs.h" +#include "avrlibtypes.h" + +// defines + +// function prototypes + +//! Print a part of memory as a formatted hex table with ascii translation +void debugPrintHexTable(u16 length, u08 *buffer); + + +#endif diff --git a/dhcp.cpp b/dhcp.cpp new file mode 100644 index 0000000..61034b5 --- /dev/null +++ b/dhcp.cpp @@ -0,0 +1,300 @@ +/*! \file dhcp.c \brief DHCP Protocol Library. */ +//***************************************************************************** +// +// File Name : 'dhcp.c' +// Title : DHCP Protocol Library +// Author : Pascal Stang +// Created : 9/17/2005 +// Revised : 9/17/2005 +// Version : 0.1 +// Target MCU : Atmel AVR series +// Editor Tabs : 4 +// +//***************************************************************************** + +#include "net.h" +#include "nic.h" +#include "ip.h" +#include "netstack.h" + +#include "dhcp.h" + +#include "HardwareSerial.h" + +// global variables +uint32_t DhcpServerIP; ///< IP address of the DHCP server that offered lease +uint32_t DhcpTransactID; ///< Unique transaction ID that identifies DHCP request/replies +uint32_t DhcpLeaseTime; ///< Number of seconds left in DHCP lease + +void dhcpInit(void) +{ + uint8_t macaddr[6]; + + // get interface mac address + nicGetMacAddress(macaddr); + // set transaction ID based on mac address + DhcpTransactID = *((uint32_t*)&macaddr); + // reset lease time + DhcpLeaseTime = 0; +} + +void dhcpIn(unsigned int len, struct netDhcpHeader* packet) +{ + uint8_t msgtype; + uint32_t sid; + uint8_t* optptr; + uint32_t val; + uint32_t netmask; + uint32_t gateway; + + #if NET_DEBUG >= 3 + dhcpPrintHeader(packet); + #endif + + // check that this is a reply, and for me + if((packet->bootp.op != BOOTP_OP_BOOTREPLY) || (packet->bootp.xid != DhcpTransactID)) + return; + + // process incoming packet + // check reply type + dhcpGetOption(packet->options, DHCP_OPT_DHCPMSGTYPE, 1, &msgtype); + #if NET_DEBUG >= 2 + SPrint_P(PSTR("DHCP: Received msgtype = %d\r\n", msgtype); + #endif + + if(msgtype == DHCP_MSG_DHCPOFFER) + { + // get DHCP server ID + dhcpGetOption(packet->options, DHCP_OPT_SERVERID, 4, &sid); + #ifdef DHCP_DEBUG + SPrint_P(PSTR("DHCP: Got offer from server ")); netPrintIPAddr(htonl(sid)); Serial.println(); + #endif + + // build DHCP request (on top of this reply) + packet->bootp.op = BOOTP_OP_BOOTREQUEST; // request type + // set operation + val = DHCP_MSG_DHCPREQUEST; + optptr = dhcpSetOption(packet->options, DHCP_OPT_DHCPMSGTYPE, 1, &val); + // set the server ID + optptr = dhcpSetOption(optptr, DHCP_OPT_SERVERID, 4, &sid); + // request the IP previously offered + optptr = dhcpSetOption(optptr, DHCP_OPT_REQUESTEDIP, 4, &packet->bootp.yiaddr); + // request additional information + ((uint8_t*)&val)[0] = DHCP_OPT_NETMASK; + ((uint8_t*)&val)[1] = DHCP_OPT_ROUTERS; + ((uint8_t*)&val)[2] = DHCP_OPT_DNSSERVERS; + ((uint8_t*)&val)[3] = DHCP_OPT_DOMAINNAME; + optptr = dhcpSetOption(optptr, DHCP_OPT_PARAMREQLIST, 4, &val); + + #ifdef DHCP_DEBUG + SPrint_P(PSTR("DHCP: Sending request in response to offer\r\n")); + #endif + // send DHCP request + DhcpServerIP = htonl(sid); + udpSend(DhcpServerIP, DHCP_UDP_SERVER_PORT, DHCP_HEADER_LEN+3+6+6+6+1, (uint8_t*)packet); + + } + else if(msgtype == DHCP_MSG_DHCPACK) + { + // get netmask + dhcpGetOption(packet->options, DHCP_OPT_NETMASK, 4, &val); + netmask = htonl(val); + // get gateway + dhcpGetOption(packet->options, DHCP_OPT_ROUTERS, 4, &val); + gateway = htonl(val); + // get gateway + dhcpGetOption(packet->options, DHCP_OPT_LEASETIME, 4, &val); + DhcpLeaseTime = htonl(val); + + // assign new network info + ipSetConfig(htonl(packet->bootp.yiaddr), netmask, gateway); + + #ifdef DHCP_DEBUG + SPrint_P(PSTR("DHCP: Got request ACK, bind complete\r\n")); + //debugPrintHexTable(len-DHCP_HEADER_LEN, (packet->options)); + // print info + ipPrintConfig(ipGetConfig()); + SPrint_P(PSTR("LeaseTm : ")); Serial.print(DhcpLeaseTime); Serial.println(); + #endif + } +} + +void dhcpRequest(void) +{ + struct netDhcpHeader* packet; + uint32_t val; + + packet = (struct netDhcpHeader*)&netstackGetBuffer()[ETH_HEADER_LEN+IP_HEADER_LEN+UDP_HEADER_LEN]; + + // build BOOTP/DHCP header + packet->bootp.op = BOOTP_OP_BOOTREQUEST; // request type + packet->bootp.htype = BOOTP_HTYPE_ETHERNET; + packet->bootp.hlen = BOOTP_HLEN_ETHERNET; + packet->bootp.ciaddr = htonl(ipGetConfig()->ip); + packet->bootp.yiaddr = HTONL(0l); + packet->bootp.siaddr = HTONL(0l); + packet->bootp.giaddr = HTONL(0l); + nicGetMacAddress(&packet->bootp.chaddr[0]); // fill client hardware address + packet->bootp.xid = DhcpTransactID; + packet->bootp.flags = HTONS(1); + + // build DHCP request + // begin with magic cookie + packet->cookie = 0x63538263; + // set operation + val = DHCP_MSG_DHCPDISCOVER; + dhcpSetOption(packet->options, DHCP_OPT_DHCPMSGTYPE, 1, &val); + + #ifdef DHCP_DEBUG + SPrint_P(PSTR("DHCP: Sending Query\r\n")); + //dhcpPrintHeader(packet); + #endif + + // send request + udpSend(0xFFFFFFFF, DHCP_UDP_SERVER_PORT, DHCP_HEADER_LEN+3+1, (uint8_t*)packet); +} + +void dhcpRelease(void) +{ + struct netDhcpHeader* packet; + uint32_t val; + uint8_t* optptr; + + packet = (struct netDhcpHeader*)&netstackGetBuffer()[ETH_HEADER_LEN+IP_HEADER_LEN+UDP_HEADER_LEN]; + + // build BOOTP/DHCP header + packet->bootp.op = BOOTP_OP_BOOTREQUEST; // request type + packet->bootp.htype = BOOTP_HTYPE_ETHERNET; + packet->bootp.hlen = BOOTP_HLEN_ETHERNET; + packet->bootp.ciaddr = htonl(ipGetConfig()->ip); + packet->bootp.yiaddr = HTONL(0l); + packet->bootp.siaddr = HTONL(0l); + packet->bootp.giaddr = HTONL(0l); + nicGetMacAddress(&packet->bootp.chaddr[0]); // fill client hardware address + packet->bootp.xid = DhcpTransactID; // set trans ID (use part of MAC address) + packet->bootp.flags = HTONS(1); + + // build DHCP request + // begin with magic cookie + packet->cookie = 0x63538263; + // set operation + val = DHCP_MSG_DHCPRELEASE; + optptr = dhcpSetOption(packet->options, DHCP_OPT_DHCPMSGTYPE, 1, &val); + // set the server ID + val = htonl(DhcpServerIP); + optptr = dhcpSetOption(optptr, DHCP_OPT_SERVERID, 4, &val); + // request the IP previously offered + optptr = dhcpSetOption(optptr, DHCP_OPT_REQUESTEDIP, 4, &packet->bootp.ciaddr); + + #ifdef DHCP_DEBUG + SPrint_P(PSTR("DHCP: Sending Release to ")); netPrintIPAddr(DhcpServerIP); Serial.println(); + //dhcpPrintHeader(packet); + #endif + + // send release + udpSend(DhcpServerIP, DHCP_UDP_SERVER_PORT, DHCP_HEADER_LEN+3+6+6+1, (uint8_t*)packet); + + // deconfigure ip addressing + ipSetConfig(0,0,0); + DhcpLeaseTime = 0; +} + +void dhcpTimer(void) +{ + // this function to be called once per second + + // decrement lease time + if(DhcpLeaseTime) + DhcpLeaseTime--; +} + +uint8_t dhcpGetOption(uint8_t* options, uint8_t optcode, uint8_t optlen, void* optvalptr) +{ + uint8_t i; + + // parse for desired option + for (;;) + { + // skip pad characters + if(*options == DHCP_OPT_PAD) + options++; + // break if end reached + else if(*options == DHCP_OPT_END) + break; + // check for desired option + else if(*options == optcode) + { + // found desired option + // limit size to actual option length + optlen = MIN(optlen, *(options+1)); + //if(*(options+1) < optlen) + // optlen = *(options+1); + + // copy contents of option + for(i=0; ibootp.op) + { + case BOOTP_OP_BOOTREQUEST: SPrint_P(PSTR("BOOTREQUEST")); break; + case BOOTP_OP_BOOTREPLY: SPrint_P(PSTR("BOOTREPLY")); break; + default: SPrint_P(PSTR("UNKNOWN")); break; + } + Serial.println(); + // print transaction ID + SPrint_P(PSTR("XID : 0x")); Serial.printu32(packet->bootp.xid); Serial.println(); + // print client IP address + SPrint_P(PSTR("ClIpAddr: ")); netPrintIPAddr(htonl(packet->bootp.ciaddr)); Serial.println(); + // print 'your' IP address + SPrint_P(PSTR("YrIpAddr: ")); netPrintIPAddr(htonl(packet->bootp.yiaddr)); Serial.println(); + // print server IP address + SPrint_P(PSTR("SvIpAddr: ")); netPrintIPAddr(htonl(packet->bootp.siaddr)); Serial.println(); + // print gateway IP address + SPrint_P(PSTR("GwIpAddr: ")); netPrintIPAddr(htonl(packet->bootp.giaddr)); Serial.println(); + // print client hardware address + SPrint_P(PSTR("ClHwAddr: ")); netPrintEthAddr((struct netEthAddr*)packet->bootp.chaddr); Serial.println(); +} + +#endif diff --git a/dhcp.h b/dhcp.h new file mode 100644 index 0000000..4e28ea0 --- /dev/null +++ b/dhcp.h @@ -0,0 +1,155 @@ +/*! \file dhcp.h \brief DHCP Protocol Library. */ +//***************************************************************************** +// +// File Name : 'dhcp.h' +// Title : DHCP Protocol Library +// Author : Pascal Stang +// Created : 9/17/2005 +// Revised : 9/17/2005 +// Version : 0.1 +// Target MCU : Atmel AVR series +// Editor Tabs : 4 +// +/// \ingroup network +/// \defgroup dhcp DHCP Protocol Library (dhcp.c) +/// \code #include "net/dhcp.h" \endcode +/// \par Description +/// This library provides a limited implementation of DHCP (Dynamic Host +/// Configuration Protocol) as described in RFC2131. DHCP allows a +/// network device to automatically obtain an IP address and other network +/// configuration settings from a DHCP server. +/// +/// \note This code is currently below version 1.0, and therefore is considered +/// to be lacking in some functionality or documentation, or may not be fully +/// tested. Nonetheless, you can expect most functions to work. +/// +// This code is distributed under the GNU Public License +// which can be found at http://www.gnu.org/licenses/gpl.txt +//***************************************************************************** +//@{ + +#ifndef DHCP_H +#define DHCP_H + +#include "net.h" + +//#define DHCP_DEBUG_PRINT +//#define DHCP_DEBUG + +/// Bootp Header (DHCP is transported by BOOTP/UDP/IP) +struct netBootpHeader +{ + uint8_t op; ///< Message op-code / message type + uint8_t htype; ///< Hardware address type (Ethernet=1) + uint8_t hlen; ///< Hardware address length (Ethernet=6 byte MAC addr) + uint8_t hops; ///< hop count (client set to zero) + uint32_t xid; ///< Transaction ID (randomly chosen by client, must remain same) + uint16_t secs; ///< Seconds elapsed since DHCP negotiation began (filled by client) + uint16_t flags; ///< Flags + uint32_t ciaddr; ///< Client IP address (filled only if already bound, renewing, or rebinding) + uint32_t yiaddr; ///< 'Your' IP address (client) + uint32_t siaddr; ///< Server IP address + uint32_t giaddr; ///< Gateway IP address + uint8_t chaddr[16]; ///< Client Hardware Address + uint8_t sname[64]; ///< Server Host Name + uint8_t file[128]; ///< Boot file name (null-term string) +} GNUC_PACKED; + +#define BOOTP_HEADER_LEN 236 ///< length of BOOTP header not including options + +#define BOOTP_OP_BOOTREQUEST 1 ///< BOOTP Request operation (message from client to server) +#define BOOTP_OP_BOOTREPLY 2 ///< BOOTP Reply operation (message from server to client) + +#define BOOTP_HTYPE_ETHERNET 1 +#define BOOTP_HLEN_ETHERNET 6 + +/// DHCP Header +struct netDhcpHeader +{ + struct netBootpHeader bootp; ///< BOOTP header + uint32_t cookie; ///< magic cookie value + uint8_t options[]; ///< DHCP options +} GNUC_PACKED; + +#define DHCP_HEADER_LEN 240 ///< length of DHCP header not including options + +#define DHCP_UDP_SERVER_PORT 67 ///< UDP port where DHCP requests should be sent +#define DHCP_UDP_CLIENT_PORT 68 ///< UDP port clients will receive DHCP replies + + +#define DHCP_OPT_PAD 0 ///< token padding value (make be skipped) +#define DHCP_OPT_NETMASK 1 ///< subnet mask client should use (4 byte mask) +#define DHCP_OPT_ROUTERS 3 ///< routers client should use (IP addr list) +#define DHCP_OPT_TIMESERVERS 4 ///< time servers client should use (IP addr list) +#define DHCP_OPT_NAMESERVERS 5 ///< name servers client should use (IP addr list) +#define DHCP_OPT_DNSSERVERS 6 ///< DNS servers client should use (IP addr list) +#define DHCP_OPT_HOSTNAME 12 ///< host name client should use (string) +#define DHCP_OPT_DOMAINNAME 15 ///< domain name client should use (string) +#define DHCP_OPT_REQUESTEDIP 50 ///< IP address requested by client (IP address) +#define DHCP_OPT_LEASETIME 51 ///< DHCP Lease Time (uint32 seconds) +#define DHCP_OPT_DHCPMSGTYPE 53 ///< DHCP message type (1 byte) +#define DHCP_OPT_SERVERID 54 ///< Server Identifier (IP address) +#define DHCP_OPT_PARAMREQLIST 55 ///< Paramerter Request List (n OPT codes) +#define DHCP_OPT_RENEWALTIME 58 ///< DHCP Lease Renewal Time (uint32 seconds) +#define DHCP_OPT_REBINDTIME 59 ///< DHCP Lease Rebinding Time (uint32 seconds) +#define DHCP_OPT_END 255 ///< token end value (marks end of options list) + +#define DHCP_MSG_DHCPDISCOVER 1 ///< DISCOVER is broadcast by client to solicit OFFER from any/all DHCP servers. +#define DHCP_MSG_DHCPOFFER 2 ///< OFFER(s) are made to client by server to offer IP address and config info. +#define DHCP_MSG_DHCPREQUEST 3 ///< REQUEST is made my client in response to best/favorite OFFER message. +#define DHCP_MSG_DHCPDECLINE 4 ///< DECLINE may be sent by client to server to indicate IP already in use. +#define DHCP_MSG_DHCPACK 5 ///< ACK is sent to client by server in confirmation of REQUEST, contains config and IP. +#define DHCP_MSG_DHCPNAK 6 ///< NAK is sent to client by server to indicate problem with REQUEST. +#define DHCP_MSG_DHCPRELEASE 7 ///< RELEASE is sent by client to server to relinquish DHCP lease on IP address, etc. +#define DHCP_MSG_DHCPINFORM 8 ///< INFORM is sent by client to server to request config info, IP address configured locally. + + +/*! Initialize DHCP system. + Prepares DHCP for use and initializes lease time to zero. */ +void dhcpInit(void); + +/*! Processes incoming DHCP packets from UDP port 68. + This function is to be called by the stack when a DHCP packet + arrives over the network. The DHCP packet will be parsed, handled, + and a response will be generated and sent if needed. When the DHCP + process completes, the IP addressing will be automatically updated. */ +void dhcpIn(unsigned int len, struct netDhcpHeader* packet); + +/*! Request DHCP assigned network parameters. + This function begins the DHCP process. The remainder of operations + are handled in dhcpIn(). */ +void dhcpRequest(void); + +/*! Release DHCP lease and assigned network parameters. + This function releases the DHCP assigned address and allows the + DHCP server to reallocate it. */ +void dhcpRelease(void); + +/*! Periodic DHCP maintenance. + This function is to be called once per second and will + expire the DHCP lease. */ +void dhcpTimer(void); + +/*! Get a DHCP option from the option list. + \param options is a pointer to the options field of a DHCP packet. + \param optcode is the desired option number to retrieve. + \param optlen is the maximum data length that should be retrieved (less data will be retrieved if option is shorter). + \param optvalptr is a pointer to where the option value will be stored. + \return actual length of the option data, as stored in the options list. */ +uint8_t dhcpGetOption(uint8_t* options, uint8_t optcode, uint8_t optlen, void* optvalptr); + +/*! Set a DHCP option in the option list. + \param options is a pointer to the options field of a DHCP packet. + \param optcode is the option number to write. + \param optlen is the data length of the option value. + \param optvalptr is a pointer to the option data to be read. + \return pointer to write location of the next option. */ +uint8_t* dhcpSetOption(uint8_t* options, uint8_t optcode, uint8_t optlen, uint32_t* optvalptr); + +/*! Print diagnotic information about BOOTP/DHCP packet. +*/ +void dhcpPrintHeader(struct netDhcpHeader* packet); + +#endif +//@} + diff --git a/enc28j60.cpp b/enc28j60.cpp new file mode 100644 index 0000000..59b3ed3 --- /dev/null +++ b/enc28j60.cpp @@ -0,0 +1,713 @@ +/*! \file enc28j60.c \brief Microchip ENC28J60 Ethernet Interface Driver. */ +//***************************************************************************** +// +// File Name : 'enc28j60.c' +// Title : Microchip ENC28J60 Ethernet Interface Driver +// Author : Pascal Stang (c)2005 +// Created : 9/22/2005 +// Revised : 9/22/2005 +// Version : 0.1 +// Target MCU : Atmel AVR series +// Editor Tabs : 4 +// +// Description : This driver provides initialization and transmit/receive +// functions for the Microchip ENC28J60 10Mb Ethernet Controller and PHY. +// This chip is novel in that it is a full MAC+PHY interface all in a 28-pin +// chip, using an SPI interface to the host processor. +// +//***************************************************************************** +#include "wiring.h" +#include +#include "avr/io.h" + +#include "avrlibdefs.h" +#include "avrlibtypes.h" + +#include "HardwareSerial.h" + +#include "programStrings.h" + +// include configuration +#include "enc28j60conf.h" +#include "enc28j60.h" + +#ifdef SPDR0 + #define SPDR SPDR0 + #define SPCR SPCR0 + #define SPSR SPSR0 + + #define SPIF SPIF0 + #define MSTR MSTR0 + #define CPOL CPOL0 + #define DORD DORD0 + #define SPR0 SPR00 + #define SPR1 SPR01 + #define SPI2X SPI2X0 + #define SPE SPE0 +#endif + +u08 Enc28j60Bank; +u16 NextPacketPtr; + +void nicInit(void) +{ + enc28j60Init(); +} + +void nicSend(unsigned int len, unsigned char* packet) +{ + enc28j60PacketSend(len, packet); +} + +unsigned int nicPoll(unsigned int maxlen, unsigned char* packet) +{ + return enc28j60PacketReceive(maxlen, packet); +} + +void nicGetMacAddress(uint8_t* macaddr) +{ + // read MAC address registers + // NOTE: MAC address in ENC28J60 is byte-backward + *macaddr++ = enc28j60Read(MAADR5); + *macaddr++ = enc28j60Read(MAADR4); + *macaddr++ = enc28j60Read(MAADR3); + *macaddr++ = enc28j60Read(MAADR2); + *macaddr++ = enc28j60Read(MAADR1); + *macaddr++ = enc28j60Read(MAADR0); +} + +void nicSetMacAddress(uint8_t* macaddr) +{ + // write MAC address + // NOTE: MAC address in ENC28J60 is byte-backward + enc28j60Write(MAADR5, *macaddr++); + enc28j60Write(MAADR4, *macaddr++); + enc28j60Write(MAADR3, *macaddr++); + enc28j60Write(MAADR2, *macaddr++); + enc28j60Write(MAADR1, *macaddr++); + enc28j60Write(MAADR0, *macaddr++); +} + +void nicRegDump(void) +{ + enc28j60RegDump(); +} + +void nicHardReset(void) +{ +#ifndef ENC28J60_HARD_RESET_DISABLED + enc28j60hardReset(); +#endif +} + +void nicSoftReset(void) +{ + enc28j60softReset(); +} + +void nicReboot(void) +{ + enc28j60Reboot(); +} + +void enc28j60Reboot(void) +{ +#ifndef ENC28J60_HARD_RESET_DISABLED + enc28j60hardReset(); +#else + delayMicroseconds(10); +#endif + + enc28j60softReset(); +} + +void enc28j60softReset(void) +{ + // perform system reset + enc28j60WriteOp(ENC28J60_SOFT_RESET, 0, ENC28J60_SOFT_RESET); + delayMicroseconds(50); + + // check CLKRDY bit to see if reset is complete + //while(!(enc28j60Read(ESTAT) & ESTAT_CLKRDY)); + // Errata workaround #1, CLKRDY check is unreliable, delay 1 mS instead + delay(1); +} + +#ifndef ENC28J60_HARD_RESET_DISABLED +void enc28j60hardReset(void) +{ + // HW reset + sbi(ENC28J60_CONTROL_DDR, ENC28J60_CONTROL_HRESET); + + cbi(ENC28J60_CONTROL_PORT, ENC28J60_CONTROL_HRESET); // Down + delayMicroseconds(10); + sbi(ENC28J60_CONTROL_PORT, ENC28J60_CONTROL_HRESET); // Up + + delay(1); +} +#endif + +uint8_t enc28j60ReadOp(uint8_t op, uint8_t address) +{ + uint8_t data; + + // assert CS + cbi(ENC28J60_CONTROL_PORT, ENC28J60_CONTROL_CS); + + // issue read command + SPDR = op | (address & ADDR_MASK); + while(!(SPSR & (1<>5); + Enc28j60Bank = (address & BANK_MASK); + } +} + +uint8_t enc28j60Read(uint8_t address) +{ + // set the bank + enc28j60SetBank(address); + // do the read + return enc28j60ReadOp(ENC28J60_READ_CTRL_REG, address); +} + +void enc28j60Write(uint8_t address, uint8_t data) +{ + // set the bank + enc28j60SetBank(address); + // do the write + enc28j60WriteOp(ENC28J60_WRITE_CTRL_REG, address, data); +} + +uint16_t enc28j60PhyRead(uint8_t address) +{ + uint16_t data; + + // Set the right address and start the register read operation + enc28j60Write(MIREGADR, address); + enc28j60Write(MICMD, MICMD_MIIRD); + + // wait until the PHY read completes + while(enc28j60Read(MISTAT) & MISTAT_BUSY); + + // quit reading + enc28j60Write(MICMD, 0x00); + + // get data value + data = enc28j60Read(MIRDH); + data <<= 8; + data |= enc28j60Read(MIRDL); + + // return the data + return data; +} + +void enc28j60PhyWrite(uint8_t address, uint16_t data) +{ + // set the PHY register address + enc28j60Write(MIREGADR, address); + + // write the PHY data + enc28j60Write(MIWRL, data); + enc28j60Write(MIWRH, data>>8); + + // wait until the PHY write completes + while(enc28j60Read(MISTAT) & MISTAT_BUSY); +} + +void enc28j60Init(void) +{ + /** + * Enable ENC28J560 Control ports + **/ + // Enable CS and pull it high as CS is inverted + sbi(ENC28J60_CONTROL_DDR, ENC28J60_CONTROL_CS); // CS as output + sbi(ENC28J60_CONTROL_PORT, ENC28J60_CONTROL_CS); // CS high + + // Enable Hard reset and pull it high as RESET is inverted +#ifndef ENC28J60_HARD_RESET_DISABLED + sbi(ENC28J60_CONTROL_DDR, ENC28J60_CONTROL_HRESET); // HR as output + cbi(ENC28J60_CONTROL_PORT, ENC28J60_CONTROL_HRESET); // HR low +#endif + + /** + * setup SPI I/O pins + **/ + sbi(ENC28J60_CONTROL_DDR, ENC28J60_SPI_SCK); // set SCK as output + cbi(ENC28J60_CONTROL_PORT, ENC28J60_SPI_SCK); // set SCK lo + sbi(ENC28J60_CONTROL_DDR, ENC28J60_SPI_MOSI); // set MOSI as output + cbi(ENC28J60_CONTROL_DDR, ENC28J60_SPI_MISO); // set MISO as input + + sbi(ENC28J60_CONTROL_PORT, ENC28J60_SPI_SS); // SS must be output for Master mode to work + + // master mode + sbi(SPCR, MSTR); + // select clock phase positive-going in middle of data + cbi(SPCR, CPOL); + // Data order MSB first + cbi(SPCR,DORD); + // switch to f/4 2X = f/2 bitrate + cbi(SPCR, SPR0); + cbi(SPCR, SPR1); + sbi(SPSR, SPI2X); + // enable SPI + sbi(SPCR, SPE); + delay(1); + +#ifdef DEBUG_ENC_INIT + SPrintln_P(PSTR("SPI Enabled")); +#endif + // Reboot the ENC28J60 + enc28j60Reboot(); + +#ifdef DEBUG_ENC_INIT + SPrintln_P(PSTR("PHY reboot done")); +#endif + +#ifdef ENC28J60_LAMPS_MODE + SPrintln_P(PSTR("Custom lamps")); + enc28j60PhyWrite(PHLCON, ENC28J60_LAMPS_MODE); +#else + + // Errata #9 correction + if (enc28j60Read(MACON3) & MACON3_FULDPX) + { + SPrintln_P(PSTR("Full duplex lamps")); + enc28j60PhyWrite(PHLCON, PHLCON_DEFAULT); + } else { + SPrintln_P(PSTR("Half duplex lamps")); + enc28j60PhyWrite(PHLCON, PHLCON_DEFAULT_HD); + } +#endif + +#ifdef DEBUG_ENC_INIT + SPrintln_P(PSTR("Lamps set")); +#endif + // do bank 0 stuff + // initialize receive buffer + // 16-bit transfers, must write low byte first + + // set receive buffer start address + NextPacketPtr = RXSTART_INIT; + enc28j60Write(ERXSTL, RXSTART_INIT&0xFF); + enc28j60Write(ERXSTH, RXSTART_INIT>>8); + + // set receive pointer address + enc28j60Write(ERXRDPTL, RXSTART_INIT&0xFF); + enc28j60Write(ERXRDPTH, RXSTART_INIT>>8); + + // set receive buffer end + // ERXND defaults to 0x1FFF (end of ram) + enc28j60Write(ERXNDL, RXSTOP_INIT&0xFF); + enc28j60Write(ERXNDH, RXSTOP_INIT>>8); + + // set transmit buffer start + // ETXST defaults to 0x0000 (beginnging of ram) + enc28j60Write(ETXSTL, TXSTART_INIT&0xFF); + enc28j60Write(ETXSTH, TXSTART_INIT>>8); +/* +// Needs register implementation +#ifdef ENC28J60_PACKET_FILTER_ENABLED + // do bank 1 stuff, packet filter: + // For broadcast packets we allow only ARP packtets + // All other packets should be unicast only for our mac (MAADR) + // + // The pattern to match on is therefore + // Type ETH.DST + // ARP BROADCAST + // 06 08 -- ff ff ff ff ff ff -> ip checksum for theses bytes=f7f9 + // in binary these poitions are:11 0000 0011 1111 + // This is hex 303F->EPMM0=0x3f,EPMM1=0x30 + //enc28j60Write(ERXFCON, ERXFCON_UCEN|ERXFCON_CRCEN|ERXFCON_PMEN); + enc28j60Write(ERXFCON, ERXFCON_UCEN|ERXFCON_PMEN); + enc28j60Write(EPMM0, 0x3f); + enc28j60Write(EPMM1, 0x30); + enc28j60Write(EPMCSL, 0xf9); + enc28j60Write(EPMCSH, 0xf7); +#endif +*/ + // do bank 2 stuff + // enable MAC receive + enc28j60Write(MACON1, MACON1_MARXEN|MACON1_TXPAUS|MACON1_RXPAUS); + // bring MAC out of reset + enc28j60Write(MACON2, 0x00); + // enable automatic padding and CRC operations + enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, MACON3, MACON3_PADCFG0|MACON3_TXCRCEN|MACON3_FRMLNEN); +// enc28j60Write(MACON3, MACON3_PADCFG0|MACON3_TXCRCEN|MACON3_FRMLNEN); + + // set inter-frame gap (non-back-to-back) + enc28j60Write(MAIPGL, 0x12); + enc28j60Write(MAIPGH, 0x0C); + // set inter-frame gap (back-to-back) + enc28j60Write(MABBIPG, 0x12); + // Set the maximum packet size which the controller will accept + enc28j60Write(MAMXFLL, MAX_FRAMELEN&0xFF); + enc28j60Write(MAMXFLH, MAX_FRAMELEN>>8); + + // do bank 3 stuff + // write MAC address + // NOTE: MAC address in ENC28J60 is byte-backward + enc28j60Write(MAADR5, ENC28J60_MAC0); + enc28j60Write(MAADR4, ENC28J60_MAC1); + enc28j60Write(MAADR3, ENC28J60_MAC2); + enc28j60Write(MAADR2, ENC28J60_MAC3); + enc28j60Write(MAADR1, ENC28J60_MAC4); + enc28j60Write(MAADR0, ENC28J60_MAC5); + + // no loopback of transmitted frames + enc28j60PhyWrite(PHCON2, PHCON2_HDLDIS); + + // switch to bank 0 + enc28j60SetBank(ECON1); + // enable interrutps + enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, EIE, EIE_INTIE|EIE_PKTIE); + // enable packet reception + enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_RXEN); + +#ifdef DEBUG_ENC_INIT + SPrintln_P(PSTR("Banks robbed")); +#endif +/* + enc28j60PhyWrite(PHLCON, 0x0AA2); + + // setup duplex ---------------------- + + // Disable receive logic and abort any packets currently being transmitted + enc28j60WriteOp(ENC28J60_BIT_FIELD_CLR, ECON1, ECON1_TXRTS|ECON1_RXEN); + + { + uint16_t temp; + // Set the PHY to the proper duplex mode + temp = enc28j60PhyRead(PHCON1); + temp &= ~PHCON1_PDPXMD; + enc28j60PhyWrite(PHCON1, temp); + // Set the MAC to the proper duplex mode + temp = enc28j60Read(MACON3); + temp &= ~MACON3_FULDPX; + enc28j60Write(MACON3, temp); + } + + // Set the back-to-back inter-packet gap time to IEEE specified + // requirements. The meaning of the MABBIPG value changes with the duplex + // state, so it must be updated in this function. + // In full duplex, 0x15 represents 9.6us; 0x12 is 9.6us in half duplex + //enc28j60Write(MABBIPG, DuplexState ? 0x15 : 0x12); + + // Reenable receive logic + enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_RXEN); + + // setup duplex ---------------------- +*/ +} + +#define ETHERNET_MIN_PACKET_LENGTH 0x3C +#define ETHERNET_HEADER_LENGTH 0x0E + +#define IP_TCP_HEADER_LENGTH 40 +#define TOTAL_HEADER_LENGTH (IP_TCP_HEADER_LENGTH+ETHERNET_HEADER_LENGTH) + + +void enc28j60PacketSend(uint16_t len, uint8_t* packet) +{ +/* + // dump packet + int i,j; + for (i=0;i>8); + + // Set the TXND pointer to correspond to the packet size given + enc28j60Write(ETXNDL, (TXSTART_INIT+len)); + enc28j60Write(ETXNDH, (TXSTART_INIT+len)>>8); + + // write per-packet control byte + enc28j60WriteOp(ENC28J60_WRITE_BUF_MEM, 0, 0x00); + + // TODO, fix this up +/* + if( uip_len <= TOTAL_HEADER_LENGTH ) + { + // copy the packet into the transmit buffer + enc28j60WriteBuffer(len, packet); + } + else + { + len -= TOTAL_HEADER_LENGTH; + enc28j60WriteBuffer(TOTAL_HEADER_LENGTH, packet); + enc28j60WriteBuffer(len, (unsigned char *)uip_appdata); + } +*/ + // copy the packet into the transmit buffer + enc28j60WriteBuffer(len, packet); + + // send the contents of the transmit buffer onto the network + enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_TXRTS); + + // if( (enc28j60Read(EIR) & EIR_TXERIF) ){ + // enc28j60WriteOp(ENC28J60_BIT_FIELD_CLR, ECON1, ECON1_TXRTS); + // } +} + +uint16_t enc28j60PacketReceive(uint16_t maxlen, uint8_t* packet) +{ + uint16_t rxstat; + uint16_t len; + + // check if a packet has been received and buffered + if( !(enc28j60Read(EIR) & EIR_PKTIF) ) + { + // Errata workaround #4, PKTIF is not reliable + // double check by looking at EPKTCNT + if (enc28j60Read(EPKTCNT) == 0) + return 0; + } + + // Set the read pointer to the start of the received packet + enc28j60Write(ERDPTL, (NextPacketPtr)); + enc28j60Write(ERDPTH, (NextPacketPtr)>>8); + + // read the next packet pointer + NextPacketPtr = enc28j60ReadOp(ENC28J60_READ_BUF_MEM, 0); + NextPacketPtr |= enc28j60ReadOp(ENC28J60_READ_BUF_MEM, 0)<<8; + + // read the packet length + len = enc28j60ReadOp(ENC28J60_READ_BUF_MEM, 0); + len |= enc28j60ReadOp(ENC28J60_READ_BUF_MEM, 0)<<8; + + // read the receive status + rxstat = enc28j60ReadOp(ENC28J60_READ_BUF_MEM, 0); + rxstat |= enc28j60ReadOp(ENC28J60_READ_BUF_MEM, 0)<<8; + + // limit retrieve length + // (we reduce the MAC-reported length by 4 to remove the CRC) + len = MIN(len, maxlen); + + // copy the packet from the receive buffer + enc28j60ReadBuffer(len, packet); + + // Move the RX read pointer to the start of the next received packet + // This frees the memory we just read out + // Errata workaround #11. Make sure ERXRDPT is odd + // + { + uint16_t rs,re; + rs = enc28j60Read(ERXSTH); + rs <<= 8; + rs |= enc28j60Read(ERXSTL); + re = enc28j60Read(ERXNDH); + re <<= 8; + re |= enc28j60Read(ERXNDL); + if (NextPacketPtr - 1 < rs || NextPacketPtr - 1 > re) + { + enc28j60Write(ERXRDPTL, (re)); + enc28j60Write(ERXRDPTH, (re)>>8); + } + else + { + enc28j60Write(ERXRDPTL, (NextPacketPtr-1)); + enc28j60Write(ERXRDPTH, (NextPacketPtr-1)>>8); + } + } + + // decrement the packet counter indicate we are done with this packet + enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, ECON2, ECON2_PKTDEC); + + return len; +} + +void enc28j60ReceiveOverflowRecover(void) +{ + // receive buffer overflow handling procedure + + // recovery completed +} + +void enc28j60RegDump(void) +{ +// unsigned char macaddr[6]; +// result = ax88796Read(TR); + +// SPrint_P(PSTR("Media State: ")); +// if(!(result & AUTOD)) +// SPrint_P(PSTR("Autonegotiation\r\n")); +// else if(result & RST_B) +// SPrint_P(PSTR("PHY in Reset \r\n")); +// else if(!(result & RST_10B)) +// SPrint_P(PSTR("10BASE-T \r\n")); +// else if(!(result & RST_TXB)) +// SPrint_P(PSTR("100BASE-T \r\n")); + + SPrint_P(PSTR("RevID: ")); delay(5); + Serial.println(enc28j60Read(EREVID), HEX); delay(5); + + SPrint_P(PSTR("Cntrl: ECON1 ECON2 ESTAT EIR EIE\r\n")); delay(5); + SPrint_P(PSTR(" ")); delay(5); + Serial.print(enc28j60Read(ECON1), HEX); delay(5); + SPrint_P(PSTR(" ")); delay(5); + Serial.print(enc28j60Read(ECON2), HEX); delay(5); + SPrint_P(PSTR(" ")); delay(5); + Serial.print(enc28j60Read(ESTAT), HEX); delay(5); + SPrint_P(PSTR(" ")); delay(5); + Serial.print(enc28j60Read(EIR), HEX); delay(5); + SPrint_P(PSTR(" ")); delay(5); + Serial.print(enc28j60Read(EIE), HEX); delay(5); + Serial.println(); delay(5); + + SPrint_P(PSTR("MAC : MACON1 MACON2 MACON3 MACON4 MAC-Address\r\n")); delay(5); + SPrint_P(PSTR(" ")); delay(5); + Serial.print(enc28j60Read(MACON1), HEX); delay(5); + SPrint_P(PSTR(" ")); delay(5); + Serial.print(enc28j60Read(MACON2), HEX); delay(5); + SPrint_P(PSTR(" ")); delay(5); + Serial.print(enc28j60Read(MACON3), HEX); delay(5); + SPrint_P(PSTR(" ")); delay(5); + Serial.print(enc28j60Read(MACON4), HEX); delay(5); + SPrint_P(PSTR(" ")); delay(5); + Serial.print(enc28j60Read(MAADR5), HEX); delay(5); + Serial.print(enc28j60Read(MAADR4), HEX); delay(5); + Serial.print(enc28j60Read(MAADR3), HEX); delay(5); + Serial.print(enc28j60Read(MAADR2), HEX); delay(5); + Serial.print(enc28j60Read(MAADR1), HEX); delay(5); + Serial.print(enc28j60Read(MAADR0), HEX); delay(5); + Serial.println(); delay(5); + + SPrint_P(PSTR("Rx : ERXST ERXND ERXWRPT ERXRDPT ERXFCON EPKTCNT MAMXFL\r\n")); delay(5); + SPrint_P(PSTR(" ")); delay(5); + Serial.print(enc28j60Read(ERXSTH), HEX); delay(5); + Serial.print(enc28j60Read(ERXSTL), HEX); delay(5); + SPrint_P(PSTR(" ")); delay(5); + Serial.print(enc28j60Read(ERXNDH), HEX); delay(5); + Serial.print(enc28j60Read(ERXNDL), HEX); delay(5); + SPrint_P(PSTR(" ")); delay(5); + Serial.print(enc28j60Read(ERXWRPTH), HEX); delay(5); + Serial.print(enc28j60Read(ERXWRPTL), HEX); delay(5); + SPrint_P(PSTR(" ")); delay(5); + Serial.print(enc28j60Read(ERXRDPTH), HEX); delay(5); + Serial.print(enc28j60Read(ERXRDPTL), HEX); delay(5); + SPrint_P(PSTR(" ")); delay(5); + Serial.print(enc28j60Read(ERXFCON), HEX); delay(5); + SPrint_P(PSTR(" ")); delay(5); + Serial.print(enc28j60Read(EPKTCNT), HEX); delay(5); + SPrint_P(PSTR(" ")); delay(5); + Serial.print(enc28j60Read(MAMXFLH), HEX); delay(5); + Serial.print(enc28j60Read(MAMXFLL), HEX); delay(5); + Serial.println(); delay(5); + + SPrint_P(PSTR("Tx : ETXST ETXND MACLCON1 MACLCON2 MAPHSUP\r\n")); delay(5); + SPrint_P(PSTR(" ")); delay(5); + Serial.print(enc28j60Read(ETXSTH), HEX); delay(5); + Serial.print(enc28j60Read(ETXSTL), HEX); delay(5); + SPrint_P(PSTR(" ")); delay(5); + Serial.print(enc28j60Read(ETXNDH), HEX); delay(5); + Serial.print(enc28j60Read(ETXNDL), HEX); delay(5); + SPrint_P(PSTR(" ")); delay(5); + Serial.print(enc28j60Read(MACLCON1), HEX); delay(5); + SPrint_P(PSTR(" ")); delay(5); + Serial.print(enc28j60Read(MACLCON2), HEX); delay(5); + SPrint_P(PSTR(" ")); delay(5); + Serial.print(enc28j60Read(MAPHSUP), HEX); delay(5); + Serial.println(); delay(5); + + //delay_ms(25); delay(5); + delay(25); +} + + + diff --git a/enc28j60.h b/enc28j60.h new file mode 100644 index 0000000..46a19d6 --- /dev/null +++ b/enc28j60.h @@ -0,0 +1,338 @@ +/*! \file enc28j60.h \brief Microchip ENC28J60 Ethernet Interface Driver. */ +//***************************************************************************** +// +// File Name : 'enc28j60.h' +// Title : Microchip ENC28J60 Ethernet Interface Driver +// Author : Pascal Stang (c)2005 +// Created : 9/22/2005 +// Revised : 9/22/2005 +// Version : 0.1 +// Target MCU : Atmel AVR series +// Editor Tabs : 4 +// +/// \ingroup network +/// \defgroup enc28j60 Microchip ENC28J60 Ethernet Interface Driver (enc28j60.c) +/// \code #include "net/enc28j60.h" \endcode +/// \par Overview +/// This driver provides initialization and transmit/receive +/// functions for the Microchip ENC28J60 10Mb Ethernet Controller and PHY. +/// This chip is novel in that it is a full MAC+PHY interface all in a 28-pin +/// chip, using an SPI interface to the host processor. +/// +// +//***************************************************************************** +//@{ + +#ifndef ENC28J60_H +#define ENC28J60_H + +//#define DEBUG_ENC_INIT + +#include "avrlibdefs.h" +#include "avrlibtypes.h" + +#ifndef nop +#define nop() asm volatile ("nop") +#endif + +// ENC28J60 Control Registers +// Control register definitions are a combination of address, +// bank number, and Ethernet/MAC/PHY indicator bits. +// - Register address (bits 0-4) +// - Bank number (bits 5-6) +// - MAC/PHY indicator (bit 7) +#define ADDR_MASK 0x1F +#define BANK_MASK 0x60 +#define SPRD_MASK 0x80 +// All-bank registers +#define EIE 0x1B +#define EIR 0x1C +#define ESTAT 0x1D +#define ECON2 0x1E +#define ECON1 0x1F + +// Bank 0 registers +#define ERDPTL (0x00|0x00) +#define ERDPTH (0x01|0x00) +#define EWRPTL (0x02|0x00) +#define EWRPTH (0x03|0x00) +#define ETXSTL (0x04|0x00) +#define ETXSTH (0x05|0x00) +#define ETXNDL (0x06|0x00) +#define ETXNDH (0x07|0x00) +#define ERXSTL (0x08|0x00) +#define ERXSTH (0x09|0x00) +#define ERXNDL (0x0A|0x00) +#define ERXNDH (0x0B|0x00) +#define ERXRDPTL (0x0C|0x00) +#define ERXRDPTH (0x0D|0x00) +#define ERXWRPTL (0x0E|0x00) +#define ERXWRPTH (0x0F|0x00) +#define EDMASTL (0x10|0x00) +#define EDMASTH (0x11|0x00) +#define EDMANDL (0x12|0x00) +#define EDMANDH (0x13|0x00) +#define EDMADSTL (0x14|0x00) +#define EDMADSTH (0x15|0x00) +#define EDMACSL (0x16|0x00) +#define EDMACSH (0x17|0x00) +// Bank 1 registers +#define EHT0 (0x00|0x20) +#define EHT1 (0x01|0x20) +#define EHT2 (0x02|0x20) +#define EHT3 (0x03|0x20) +#define EHT4 (0x04|0x20) +#define EHT5 (0x05|0x20) +#define EHT6 (0x06|0x20) +#define EHT7 (0x07|0x20) +#define EPMM0 (0x08|0x20) +#define EPMM1 (0x09|0x20) +#define EPMM2 (0x0A|0x20) +#define EPMM3 (0x0B|0x20) +#define EPMM4 (0x0C|0x20) +#define EPMM5 (0x0D|0x20) +#define EPMM6 (0x0E|0x20) +#define EPMM7 (0x0F|0x20) +#define EPMCSL (0x10|0x20) +#define EPMCSH (0x11|0x20) +#define EPMOL (0x14|0x20) +#define EPMOH (0x15|0x20) +#define EWOLIE (0x16|0x20) +#define EWOLIR (0x17|0x20) +#define ERXFCON (0x18|0x20) +#define EPKTCNT (0x19|0x20) +// Bank 2 registers +#define MACON1 (0x00|0x40|0x80) +#define MACON2 (0x01|0x40|0x80) +#define MACON3 (0x02|0x40|0x80) +#define MACON4 (0x03|0x40|0x80) +#define MABBIPG (0x04|0x40|0x80) +#define MAIPGL (0x06|0x40|0x80) +#define MAIPGH (0x07|0x40|0x80) +#define MACLCON1 (0x08|0x40|0x80) +#define MACLCON2 (0x09|0x40|0x80) +#define MAMXFLL (0x0A|0x40|0x80) +#define MAMXFLH (0x0B|0x40|0x80) +#define MAPHSUP (0x0D|0x40|0x80) +#define MICON (0x11|0x40|0x80) +#define MICMD (0x12|0x40|0x80) +#define MIREGADR (0x14|0x40|0x80) +#define MIWRL (0x16|0x40|0x80) +#define MIWRH (0x17|0x40|0x80) +#define MIRDL (0x18|0x40|0x80) +#define MIRDH (0x19|0x40|0x80) + +// Bank 3 registers +#define MAADR1 (0x00|0x60|0x80) +#define MAADR0 (0x01|0x60|0x80) +#define MAADR3 (0x02|0x60|0x80) +#define MAADR2 (0x03|0x60|0x80) +#define MAADR5 (0x04|0x60|0x80) +#define MAADR4 (0x05|0x60|0x80) +#define EBSTSD (0x06|0x60) +#define EBSTCON (0x07|0x60) +#define EBSTCSL (0x08|0x60) +#define EBSTCSH (0x09|0x60) +#define MISTAT (0x0A|0x60|0x80) +#define EREVID (0x12|0x60) /* 0x312 */ +#define ECOCON (0x15|0x60) +#define EFLOCON (0x17|0x60) +#define EPAUSL (0x18|0x60) +#define EPAUSH (0x19|0x60) + +// PHY registers +#define PHCON1 0x00 +#define PHSTAT1 0x01 +#define PHHID1 0x02 +#define PHHID2 0x03 +#define PHCON2 0x10 +#define PHSTAT2 0x11 +#define PHIE 0x12 +#define PHIR 0x13 +#define PHLCON 0x14 + +// ENC28J60 ERXFCON Register Bit Definitions +#define ERXFCON_UCEN 0x80 +#define ERXFCON_ANDOR 0x40 +#define ERXFCON_CRCEN 0x20 +#define ERXFCON_PMEN 0x10 +#define ERXFCON_MPEN 0x08 +#define ERXFCON_HTEN 0x04 +#define ERXFCON_MCEN 0x02 +#define ERXFCON_BCEN 0x01 +// ENC28J60 EIE Register Bit Definitions +#define EIE_INTIE 0x80 +#define EIE_PKTIE 0x40 +#define EIE_DMAIE 0x20 +#define EIE_LINKIE 0x10 +#define EIE_TXIE 0x08 +#define EIE_WOLIE 0x04 +#define EIE_TXERIE 0x02 +#define EIE_RXERIE 0x01 +// ENC28J60 EIR Register Bit Definitions +#define EIR_PKTIF 0x40 +#define EIR_DMAIF 0x20 +#define EIR_LINKIF 0x10 +#define EIR_TXIF 0x08 +#define EIR_WOLIF 0x04 +#define EIR_TXERIF 0x02 +#define EIR_RXERIF 0x01 +// ENC28J60 ESTAT Register Bit Definitions +#define ESTAT_INT 0x80 +#define ESTAT_LATECOL 0x10 +#define ESTAT_RXBUSY 0x04 +#define ESTAT_TXABRT 0x02 +#define ESTAT_CLKRDY 0x01 +// ENC28J60 ECON2 Register Bit Definitions +#define ECON2_AUTOINC 0x80 +#define ECON2_PKTDEC 0x40 +#define ECON2_PWRSV 0x20 +#define ECON2_VRPS 0x08 +// ENC28J60 ECON1 Register Bit Definitions +#define ECON1_TXRST 0x80 +#define ECON1_RXRST 0x40 +#define ECON1_DMAST 0x20 +#define ECON1_CSUMEN 0x10 +#define ECON1_TXRTS 0x08 +#define ECON1_RXEN 0x04 +#define ECON1_BSEL1 0x02 +#define ECON1_BSEL0 0x01 +// ENC28J60 MACON1 Register Bit Definitions +#define MACON1_LOOPBK 0x10 +#define MACON1_TXPAUS 0x08 +#define MACON1_RXPAUS 0x04 +#define MACON1_PASSALL 0x02 +#define MACON1_MARXEN 0x01 +// ENC28J60 MACON2 Register Bit Definitions +#define MACON2_MARST 0x80 +#define MACON2_RNDRST 0x40 +#define MACON2_MARXRST 0x08 +#define MACON2_RFUNRST 0x04 +#define MACON2_MATXRST 0x02 +#define MACON2_TFUNRST 0x01 +// ENC28J60 MACON3 Register Bit Definitions +#define MACON3_PADCFG2 0x80 +#define MACON3_PADCFG1 0x40 +#define MACON3_PADCFG0 0x20 +#define MACON3_TXCRCEN 0x10 +#define MACON3_PHDRLEN 0x08 +#define MACON3_HFRMLEN 0x04 +#define MACON3_FRMLNEN 0x02 +#define MACON3_FULDPX 0x01 +// ENC28J60 MICMD Register Bit Definitions +#define MICMD_MIISCAN 0x02 +#define MICMD_MIIRD 0x01 +// ENC28J60 MISTAT Register Bit Definitions +#define MISTAT_NVALID 0x04 +#define MISTAT_SCAN 0x02 +#define MISTAT_BUSY 0x01 +// ENC28J60 PHY PHCON1 Register Bit Definitions +#define PHCON1_PRST 0x8000 +#define PHCON1_PLOOPBK 0x4000 +#define PHCON1_PPWRSV 0x0800 +#define PHCON1_PDPXMD 0x0100 +// ENC28J60 PHY PHSTAT1 Register Bit Definitions +#define PHSTAT1_PFDPX 0x1000 +#define PHSTAT1_PHDPX 0x0800 +#define PHSTAT1_LLSTAT 0x0004 +#define PHSTAT1_JBSTAT 0x0002 +// ENC28J60 PHY PHCON2 Register Bit Definitions +#define PHCON2_FRCLINK 0x4000 +#define PHCON2_TXDIS 0x2000 +#define PHCON2_JABBER 0x0400 +#define PHCON2_HDLDIS 0x0100 + +// ENC28J60 Packet Control Byte Bit Definitions +#define PKTCTRL_PHUGEEN 0x08 +#define PKTCTRL_PPADEN 0x04 +#define PKTCTRL_PCRCEN 0x02 +#define PKTCTRL_POVERRIDE 0x01 + +// Default lamps register values +// 0x3000 | 0xD00 | 0x50 | +// LEDA: Display link status and transmit/receive activity LEDB: Display duplex status +#define PHLCON_DEFAULT ((0x0C<<0x03)|(0x08<<0x0D)|(0x04<<0x05)|0x06) /* 0x3D56 */ +// LEDA: Display link status and transmit/receive activity LEDB: Display collision activity +#define PHLCON_DEFAULT_HD ((0x0C<<0x03)|(0x08<<0x0D)|(0x04<<0x03)|0x06) /* 0x3D36 */ + +// SPI operation codes +#define ENC28J60_READ_CTRL_REG 0x00 +#define ENC28J60_READ_BUF_MEM 0x3A +#define ENC28J60_WRITE_CTRL_REG 0x40 +#define ENC28J60_WRITE_BUF_MEM 0x7A +#define ENC28J60_BIT_FIELD_SET 0x80 +#define ENC28J60_BIT_FIELD_CLR 0xA0 +#define ENC28J60_SOFT_RESET 0xFF + +// buffer boundaries applied to internal 8K ram +// entire available packet buffer space is allocated +// Includes Errata #3 Module: Memory (Ethernet Buffer) +#define TXSTART_INIT 0x0000 // start TX buffer at 0 +#define RXSTART_INIT 0x0600 // give TX buffer space for one full ethernet frame (~1500 bytes) +#define RXSTOP_INIT 0x1FFF // receive buffer gets the rest + +#define MAX_FRAMELEN 1518 // maximum ethernet frame length +//#define MAX_FRAMELEN 500 + +// Ethernet constants +#define ETHERNET_MIN_PACKET_LENGTH 0x3C +//#define ETHERNET_HEADER_LENGTH 0x0E + +// functions +#include "nic.h" + +// setup ports for I/O +//! do a ENC28J60 read operation +u08 enc28j60ReadOp(u08 op, u08 address); +//! do a ENC28J60 write operation +void enc28j60WriteOp(u08 op, u08 address, u08 data); +//! read the packet buffer memory +void enc28j60ReadBuffer(u16 len, u08* data); +//! write the packet buffer memory +void enc28j60WriteBuffer(uint16_t len, uint8_t* data); +//! set the register bank for register at address +void enc28j60SetBank(uint8_t address); +//! read ax88796 register +uint8_t enc28j60Read(uint8_t address); +//! write ax88796 register +void enc28j60Write(u08 address, u08 data); +//! read a PHY register +uint16_t enc28j60PhyRead(uint8_t address); +//! write a PHY register +void enc28j60PhyWrite(uint8_t address, uint16_t data); + +//! initialize the ethernet interface for transmit/receive +void enc28j60Init(void); + +//! Packet transmit function. +/// Sends a packet on the network. It is assumed that the packet is headed by a valid ethernet header. +/// \param len Length of packet in bytes. +/// \param packet Pointer to packet data. +void enc28j60PacketSend(unsigned int len, unsigned char* packet); + +//! Packet receive function. +/// Gets a packet from the network receive buffer, if one is available. +/// The packet will by headed by an ethernet header. +/// \param maxlen The maximum acceptable length of a retrieved packet. +/// \param packet Pointer where packet data should be stored. +/// \return Packet length in bytes if a packet was retrieved, zero otherwise. +unsigned int enc28j60PacketReceive(unsigned int maxlen, unsigned char* packet); + +//! execute procedure for recovering from a receive overflow +/// this should be done when the receive memory fills up with packets +void enc28j60ReceiveOverflowRecover(void); + +//! formatted print of important ENC28J60 registers +void enc28j60RegDump(void); + +// ! Hard reset function +void enc28j60hardReset(void); + +// ! Hard reset function +void enc28j60softReset(void); + +// ! (Re)boot cycle +void enc28j60Reboot(void); + +#endif +//@} diff --git a/enc28j60conf.h b/enc28j60conf.h new file mode 100644 index 0000000..ca91c5b --- /dev/null +++ b/enc28j60conf.h @@ -0,0 +1,102 @@ +/*! \file enc28j60conf.h \brief Microchip ENC28J60 Ethernet Interface Driver Configuration. */ +//***************************************************************************** +// +// File Name : 'enc28j60conf.h' +// Title : Microchip ENC28J60 Ethernet Interface Driver Configuration +// Author : Pascal Stang +// Created : 10/5/2004 +// Revised : 8/22/2005 +// Version : 0.1 +// Target MCU : Atmel AVR series +// Editor Tabs : 4 +// +// Description : This driver provides initialization and transmit/receive +// functions for the ENC28J60 10Mb Ethernet Controller and PHY. +// +// This code is distributed under the GNU Public License +// which can be found at http://www.gnu.org/licenses/gpl.txt +// +//***************************************************************************** + +#ifndef ENC28J60CONF_H +#define ENC28J60CONF_H + +// Check for preference overrides +#include "main.h" + +// ENC28J60 SPI port +#define ENC28J60_SPI_PORT PORTB +#define ENC28J60_SPI_DDR DDRB + +// ENC28J60 control port +#define ENC28J60_CONTROL_PORT PORTB +#define ENC28J60_CONTROL_DDR DDRB + +// ENC28J60 port pins +//#ifdef __AVR_ATmega8__ || __AVR_ATmega168__ + #define ENC28J60_SPI_SCK 5 + #define ENC28J60_SPI_MOSI 3 + #define ENC28J60_SPI_MISO 4 + #define ENC28J60_SPI_SS 2 + + #define ENC28J60_CONTROL_CS 2 + #define ENC28J60_CONTROL_HRESET 1 +// #elif __AVR_ATmega128__ +// #define ENC28J60_SPI_SCK 1 +// #define ENC28J60_SPI_MOSI 2 +// #define ENC28J60_SPI_MISO 3 +// #define ENC28J60_SPI_SS 0 +// +// #define ENC28J60_CONTROL_CS 0 +// #define ENC28J60_CONTROL_HRESET 4 +// #else +// #define ENC28J60_SPI_SCK 7 +// #define ENC28J60_SPI_MOSI 5 +// #define ENC28J60_SPI_MISO 6 +// #define ENC28J60_SPI_SS 4 +// +// #define ENC28J60_CONTROL_CS 4 +// #define ENC28J60_CONTROL_HRESET 3 +// #endif + +/** + * If commented, the ENC will receive all "loose packets", + * this can be a lot for our little ĀµC so we will only + * accept packets that are sent to our MAC, except for ARP. +**/ +#define ENC28J60_PACKET_FILTER_ENABLED /* Unimplemented */ + +#define ENC28J60_LAMPS_MODE 0x3476 /* Default (with Errata #9 correction) applied if this is undefied */ +/** + * Lamps reference + * 0x3D56 # Default full duplex: LEDA: Display link status and transmit/receive activity LEDB: Display duplex status + * 0x3D36 # Default half duplex: LEDA: Display link status and transmit/receive activity LEDB: Display collision activity + * 0x3476 # Prefered half duplex: LEDA: Link status LEDB: Rx/Tx acitity + * 0x3996 # All lamps off + * 0x3886 # All lamps on +**/ + +// Use this if you want to disable the hard reset (can be useful if you your the ENC's CLKOUT) +//#define ENC28J60_HARD_RESET_DISABLED + +// Use this if you will run the ĀµC using CLKOUT +//#define ENC28J60_USE_CLKOUT /* Unimplemented */ + +// MAC address for this interface +#ifdef ETHADDR0 +#define ENC28J60_MAC0 ETHADDR0 +#define ENC28J60_MAC1 ETHADDR1 +#define ENC28J60_MAC2 ETHADDR2 +#define ENC28J60_MAC3 ETHADDR3 +#define ENC28J60_MAC4 ETHADDR4 +#define ENC28J60_MAC5 ETHADDR5 +#else +#define ENC28J60_MAC0 'C' +#define ENC28J60_MAC1 '0' +#define ENC28J60_MAC2 'F' +#define ENC28J60_MAC3 'F' +#define ENC28J60_MAC4 'E' +#define ENC28J60_MAC5 'E' +#endif + +#endif diff --git a/icmp.cpp b/icmp.cpp new file mode 100644 index 0000000..882b3e3 --- /dev/null +++ b/icmp.cpp @@ -0,0 +1,98 @@ +/*! \file icmp.c \brief ICMP Protocol Library. */ +//***************************************************************************** +// +// File Name : 'icmp.c' +// Title : ICMP (Internet Control Message Protocol) Protocol Library +// Author : Pascal Stang +// Created : 9/10/2004 +// Revised : 7/3/2005 +// Version : 0.1 +// Target MCU : Atmel AVR series +// Editor Tabs : 4 +// +//***************************************************************************** + +#include "net.h" +#include "nic.h" +#include "arp.h" +#include "icmp.h" + +#include "HardwareSerial.h" +#include "debug.h" + +#ifdef ICMP_DEBUG_PRINT +#include +#include "programStrings.h" +#include "HardwareSerial.h" +#endif +//extern void nicSend(unsigned int len, unsigned char* packet); + +// global variables + + +// functions +void icmpInit(void) +{ +} + +void icmpIpIn(icmpip_hdr* packet) +{ + // check ICMP type + switch(packet->icmp.type) + { + case ICMP_TYPE_ECHOREQUEST: + // echo request + icmpEchoRequest(packet); + break; + default: + break; + } +} + +void icmpEchoRequest(icmpip_hdr* packet) +{ + uint32_t tempIp; + + // change type to reply + packet->icmp.type = ICMP_TYPE_ECHOREPLY; + // recalculate checksum + packet->icmp.icmpchksum = 0; + packet->icmp.icmpchksum = netChecksum((netIpHeader*)&packet->icmp, htons(packet->ip.len)-IP_HEADER_LEN); + // return to sender + tempIp = packet->ip.destipaddr; + packet->ip.destipaddr = packet->ip.srcipaddr; + packet->ip.srcipaddr = tempIp; + // add ethernet routing + arpIpOut((struct netEthIpHeader*)(((u08*)packet)-ETH_HEADER_LEN), 0); + + // debugging + #if NET_DEBUG >= 2 + icmpPrintHeader(packet); + debugPrintHexTable(htons(packet->ip.len), (u08*)packet); + #endif + + // send it (packet->ip.len+ETH_HEADER_LEN + nicSend(htons(packet->ip.len)+ETH_HEADER_LEN, (((u08*)packet)-ETH_HEADER_LEN)); +} + +#ifdef ICMP_DEBUG_PRINT +void icmpPrintHeader(icmpip_hdr* packet) +{ + SPrint_P(PSTR("ICMP Packet:\r\n")); + // print source IP address + SPrint_P(PSTR("SrcIpAddr: ")); netPrintIPAddr(htonl(packet->ip.srcipaddr)); Serial.println(); + // print dest IP address + SPrint_P(PSTR("DstIpAddr: ")); netPrintIPAddr(htonl(packet->ip.destipaddr)); Serial.println(); + // print type + SPrint_P(PSTR("Type : ")); + switch(packet->icmp.type) + { + case ICMP_TYPE_ECHOREQUEST: SPrint_P(PSTR("ECHO REQUEST")); break; + case ICMP_TYPE_ECHOREPLY: SPrint_P(PSTR("ECHO REPLY")); break; + default: SPrint_P(PSTR("UNKNOWN")); break; + } + Serial.println(); + // print code + SPrint_P(PSTR("Code : 0x")); Serial.print(packet->icmp.icode); Serial.println(); +} +#endif diff --git a/icmp.h b/icmp.h new file mode 100644 index 0000000..75d6250 --- /dev/null +++ b/icmp.h @@ -0,0 +1,49 @@ +/*! \file icmp.h \brief ICMP Protocol Library. */ +//***************************************************************************** +// +// File Name : 'icmp.h' +// Title : ICMP (Internet Control Message Protocol) Protocol Library +// Author : Pascal Stang +// Created : 9/10/2004 +// Revised : 7/3/2005 +// Version : 0.1 +// Target MCU : Atmel AVR series +// Editor Tabs : 4 +// +/// \ingroup network +/// \defgroup icmp ICMP Protocol Library (icmp.c) +/// \code #include "net/icmp.h" \endcode +/// \par Description +/// ICMP (Internet Control Message Protocol) has many functions on the +/// internet, including the handling of ECHO (ping) requests, relaying +/// network route status, passing connection status messages, etc. +/// +/// This library currently handles only ICMP ECHO requests (ping), but +/// may be expanded to include other useful ICMP operations as needed. +// +// This code is distributed under the GNU Public License +// which can be found at http://www.gnu.org/licenses/gpl.txt +//***************************************************************************** +//@{ + +#ifndef ICMP_H +#define ICMP_H + +#include "net.h" + +#define ICMP_DEBUG_PRINT + +//! Initialize ICMP protocol library. +void icmpInit(void); + +//! Incoming IP packets of protocol ICMP should be passed to this function. +void icmpIpIn(icmpip_hdr* packet); + +//! Forms and sends a reply in response to an ICMP ECHO request. +void icmpEchoRequest(icmpip_hdr* packet); + +//! Print ICMP packet information. +void icmpPrintHeader(icmpip_hdr* packet); + +#endif +//@} diff --git a/ip.cpp b/ip.cpp new file mode 100644 index 0000000..c4ff8c2 --- /dev/null +++ b/ip.cpp @@ -0,0 +1,149 @@ +/*! \file ip.c \brief IP (Internet Protocol) Library. */ +//***************************************************************************** +// +// File Name : 'ip.c' +// Title : IP (Internet Protocol) Library +// Author : Pascal Stang +// Created : 8/30/2004 +// Revised : 7/3/2005 +// Version : 0.1 +// Target MCU : Atmel AVR series +// Editor Tabs : 4 +// +//***************************************************************************** + + +#include +#include "HardwareSerial.h" + +#include +#include "programStrings.h" + +#include "debug.h" + +#include "net.h" +#include "nic.h" +#include "arp.h" +#include "ip.h" + +struct ipConfig IpMyConfig; ///< Local IP address/config structure + + +void ipSetConfig(uint32_t myIp, uint32_t netmask, uint32_t gatewayIp) +{ + struct netEthAddr ethaddr; + + // set local addressing + IpMyConfig.ip = myIp; + IpMyConfig.netmask = netmask; + IpMyConfig.gateway = gatewayIp; + + // set ARP association + nicGetMacAddress(ethaddr.addr); + arpSetAddress(ðaddr, myIp); +} + +struct ipConfig* ipGetConfig(void) +{ + return &IpMyConfig; +} + +// deprecated +/* +uint32_t ipGetMyAddress(void) +{ + return IpMyConfig.ip; +} +*/ + +void ipSend(uint32_t dstIp, uint8_t protocol, uint16_t len, uint8_t* data) +{ + // make pointer to ethernet/IP header + struct netEthIpHeader* ethIpHeader; + + // move data pointer to make room for headers + data -= ETH_HEADER_LEN+IP_HEADER_LEN; + ethIpHeader = (struct netEthIpHeader*)data; + +#ifdef NET_BUG > 6 + debugPrintHexTable(len+ETH_HEADER_LEN+IP_HEADER_LEN, data); +#endif + // adjust length to add IP header + len += IP_HEADER_LEN; + + // fill IP header + ethIpHeader->ip.destipaddr = HTONL(dstIp); + ethIpHeader->ip.srcipaddr = HTONL(IpMyConfig.ip); + ethIpHeader->ip.proto = protocol; + ethIpHeader->ip.len = htons(len); + ethIpHeader->ip.vhl = 0x45; + ethIpHeader->ip.tos = 0; + ethIpHeader->ip.ipid = 0; + ethIpHeader->ip.ipoffset = 0; + ethIpHeader->ip.ttl = IP_TIME_TO_LIVE; + ethIpHeader->ip.ipchksum = 0; + + // calculate and apply IP checksum + // DO THIS ONLY AFTER ALL CHANGES HAVE BEEN MADE TO IP HEADER + ethIpHeader->ip.ipchksum = netChecksum(ðIpHeader->ip, IP_HEADER_LEN); + + // add ethernet routing + // check if we need to send to gateway + if( (dstIp & IpMyConfig.netmask) == (IpMyConfig.ip & IpMyConfig.netmask) ) + { + arpIpOut(ethIpHeader,0); // local send + #ifdef NET_DEBUG + SPrint_P(PSTR("Sending IP packet on local net\r\n")); + #endif + } + else + { + arpIpOut(ethIpHeader,IpMyConfig.gateway); // gateway send + #ifdef NET_DEBUG + SPrint_P(PSTR("Sending IP packet to gateway\r\n")); + #endif + } + + // adjust length to add ethernet header + len += ETH_HEADER_LEN; + + // debug +#if NET_DEBUG > 6 + debugPrintHexTable(ETH_HEADER_LEN, &data[0]); + debugPrintHexTable(len-ETH_HEADER_LEN, &data[ETH_HEADER_LEN]); +#endif + + // send it + nicSend(len, data); +} + +void udpSend(uint32_t dstIp, uint16_t dstPort, uint16_t len, uint8_t* data) +{ + // make pointer to UDP header + struct netUdpHeader* udpHeader; + + // move data pointer to make room for UDP header + data -= UDP_HEADER_LEN; + udpHeader = (struct netUdpHeader*)data; + + // adjust length to add UDP header + len += UDP_HEADER_LEN; + + // fill UDP header + udpHeader->destport = HTONS(dstPort); + udpHeader->srcport = HTONS(dstPort); + udpHeader->udplen = htons(len); + udpHeader->udpchksum = 0; + +#if NET_DEBUG > 6 + debugPrintHexTable(UDP_HEADER_LEN, (uint8_t*)udpHeader); +#endif + ipSend(dstIp, IP_PROTO_UDP, len, (uint8_t*)udpHeader); +} + +void ipPrintConfig(struct ipConfig* config) +{ + SPrint_P(PSTR("IP Addr : ")); netPrintIPAddr(config->ip); Serial.println(); + SPrint_P(PSTR("Netmask : ")); netPrintIPAddr(config->netmask); Serial.println(); + SPrint_P(PSTR("Gateway : ")); netPrintIPAddr(config->gateway); Serial.println(); +} diff --git a/ip.h b/ip.h new file mode 100644 index 0000000..6a90b41 --- /dev/null +++ b/ip.h @@ -0,0 +1,70 @@ +/*! \file ip.h \brief IP (Internet Protocol) Library. */ +//***************************************************************************** +// +// File Name : 'ip.h' +// Title : IP (Internet Protocol) Library +// Author : Pascal Stang +// Created : 8/30/2004 +// Revised : 7/3/2005 +// Version : 0.1 +// Target MCU : Atmel AVR series +// Editor Tabs : 4 +// +/// \ingroup network +/// \defgroup ip IP (Internet Protocol) Library (ip.c) +/// \code #include "net/ip.h" \endcode +/// \par Description +/// The IP (Internet Protocol) library provide support for sending IP and +/// IP-related packets. It's not clear if additional features are needed +/// or will be added, or even if this is the proper way to facilitate IP +/// packet operations. +// +// This code is distributed under the GNU Public License +// which can be found at http://www.gnu.org/licenses/gpl.txt +//***************************************************************************** +//@{ + +#ifndef IP_H +#define IP_H + +#include "net.h" +#include "arp.h" + +#include + +struct ipConfig ///< IP addressing/configuration structure +{ + uint32_t ip; ///< IP address + uint32_t netmask; ///< netmask + uint32_t gateway; ///< gateway IP address +}; + +#define IP_TIME_TO_LIVE 128 ///< default Time-To-Live (TTL) value to use in IP headers + +//! Set our IP address and routing information. +/// The myIp value will be used in the source field of IP packets. +/// Use this function to set and reset the system IP address. +void ipSetConfig(uint32_t myIp, uint32_t netmask, uint32_t gatewayIp); + +//! Get our local IP address. +/// Returns current IP address value. +//uint32_t ipGetMyAddress(void); + +//! Get our local IP configuration. +/// Returns pointer to current IP address/configuration. +struct ipConfig* ipGetConfig(void); + +//! Print IP configuration +/// +void ipPrintConfig(struct ipConfig* config); + + +//! Send an IP packet. +void ipSend(uint32_t dstIp, uint8_t protocol, uint16_t len, uint8_t* data); + +//! Send a UDP/IP packet. +void udpSend(uint32_t dstIp, uint16_t dstPort, uint16_t len, uint8_t* data); + +#endif +//@} + diff --git a/main.h b/main.h new file mode 100644 index 0000000..ffc4dd2 --- /dev/null +++ b/main.h @@ -0,0 +1,76 @@ +/** + * Arduino adaptation of AVRLib's netstack test and ENC28J60 driver. + * Cleft under a CC BY-SA-NC 2.5 (Matthieu Lalonde) + * Go to creativecommons.org for the full license. +**/ + +#ifndef Main_H +#define Main_H + +/** + * Libraries +**/ +// Program strings support +#include +#include "programStrings.h" + +// AVRLib defs & types compatibility +#include "avrlibdefs.h" +#include "avrlibtypes.h" + +/** + * Network support +**/ + +// PHY level +#include "nic.h" +#include "enc28j60.h" + +// Net Stack +#include "net.h" +#include "netstack.h" +#include "arp.h" +#include "ip.h" +#include "icmp.h" + +/** + * Definitions +**/ +#define pinLED 7 + +// Network options +#define IPADDRESS IPDOT(192l,168l,0l,126l) +#define NETMASK IPDOT(255l,255l,0l,0l) +#define GATEWAY IPDOT(192l,168l,0l,1l) + +#define ETHADDR0 0xCC /*'78'*/ +#define ETHADDR1 0x00 /*'53'*/ +#define ETHADDR2 0xFF /*'6D'*/ +#define ETHADDR3 0xFF /*'75'*/ +#define ETHADDR4 0xEE /*'72'*/ +#define ETHADDR5 0xEE /*'66'*/ + +#define LOOPBACK_PORT 7 // UDP packets sent to this port will be returned to sender +#define CONTROL_PORT 4950 // UDP packets sent to this port will be used for control +#define SERIAL_PORT 4951 // UDP packets sent to this port will be printed via serial + +/** + * Macros +**/ +#ifndef nop + #define nop() __asm__ __volatile__ ("nop") +#endif + +#ifndef __AVR_ATmega168__ + #define __AVR_ATmega168__ +#endif + +/** + * Function prototypes +**/ +void processCommand(u16 len, u08* data); +void serviceLocal(void); +void setup(void); +void loop(void); + +#endif diff --git a/main.pde b/main.pde new file mode 100644 index 0000000..b5a0340 --- /dev/null +++ b/main.pde @@ -0,0 +1,233 @@ +/** + * Arduino adaptation of AVRLib's netstack test and ENC28J60 driver. + * Cleft under a CC BY-SA-NC 2.5 (Matthieu Lalonde) + * Go to creativecommons.org for the full license. +**/ + +#include "main.h" + +struct netEthAddr myEthAddress; + +// prototypes +void netstackUDPIPProcess(unsigned int len, udpip_hdr* packet) +{ + SPrint_P(PSTR("-1")); + u16 payloadlen=0; + SPrint_P(PSTR("0")); + u08* payloaddata=0; + SPrint_P(PSTR("1")); + u16 i; + SPrint_P(PSTR("2")); + // get UDP payload length + payloadlen = htons(packet->udp.udplen); + payloadlen -= 8; // subtract header + // get UDP payload data + payloaddata = &((unsigned char*)packet)[IP_HEADER_LEN+UDP_HEADER_LEN]; + + SPrint_P(PSTR("UDP packet, len: ")); + Serial.println((unsigned int)len, DEC); +// debugPrintHexTable(len, (unsigned char*)packet); + + if(packet->udp.destport == HTONS(CONTROL_PORT)) + { + // command packet + processCommand(payloadlen, payloaddata); + } + else if(packet->udp.destport == HTONS(SERIAL_PORT)) + { + // serial output + for(i=0; iudp.destport == HTONS(LOOPBACK_PORT)) + { + // loopback - return packet to sender + udpSend(htonl(packet->ip.srcipaddr), LOOPBACK_PORT, payloadlen, payloaddata); + } +} + + +void netstackTCPIPProcess(unsigned int len, tcpip_hdr* packet) +{ + SPrint_P(PSTR("Rvd tcp Packet: len=")); + Serial.println((unsigned int)len, DEC); +} + + +void processCommand(u16 len, u08* data) +{ + SPrint_P(PSTR("Rvd UDP : CMD=")); + Serial.print(data[0], HEX); + SPrint_P(PSTR(" ARG0=")); + Serial.println(data[1], HEX); + + // do something based on command + switch(data[0]) + { + case 'C': // set PORTC + PORTC = data[1]; + break; // set DDRC + case 'c': + PORTC = data[1]; + break; + default: + SPrintln_P(PSTR("Unkown UDP command")); + break; + } +} + +void serviceLocal(void) +{ + byte c; + char* buffer[100]; + + if(Serial.available()) + { + c = Serial.read(); + Serial.println(c); + + // process command + switch(c) + { + /* + case 'a': // Assert CS + { + cbi(ENC28J60_CONTROL_PORT, ENC28J60_CONTROL_CS); + } break; + case 's': // reSet CS + { + sbi(ENC28J60_CONTROL_PORT, ENC28J60_CONTROL_CS); + } break; + */ + case 'a' : + { + arpPrintTable(); + } break; + + case 'd': + { + nicRegDump(); + } break; + + case 'i': + { + nicInit(); + } break; + + case 'n': // Netstack init + { + nicSoftReset(); + netstackInit(IPADDRESS, NETMASK, GATEWAY); + } break; + + case 'b': // reBoot + { + nicReboot(); + } break; + + case 'v': // reVision + { + SPrint_P(PSTR("RevID: ")); + Serial.println((byte)(enc28j60Read(EREVID)), BIN); + } break; + + case 'p': + { + SPrintln_P(PSTR("IP Configs")); + SPrint_P(PSTR("IP:")); + Serial.println(ipGetConfig()->ip); + SPrint_P(PSTR("Gateway:")); + Serial.println(ipGetConfig()->gateway); + SPrint_P(PSTR("Netmask:")); + Serial.println(ipGetConfig()->netmask); + } break; + /* + case 'x': + { + char b[2048] = "a"; + unsigned int i = 0; + Serial.println(); + for (i = 1; 1; i++) { + SPrint_P(PSTR(".")); delay(250); + b[i] = 255; + } + } + break; + */ + case '1': + enc28j60PhyWrite(PHLCON, 0x3992); // Off/On + break; + case '2': + enc28j60PhyWrite(PHLCON, 0x3882); // On/On + break; + case '3': + // A: Display link status and transmit/receive activity B: Display link status and receive activity + enc28j60PhyWrite(PHLCON, 0x3DC2); + break; + case '4': + enc28j60PhyWrite(PHLCON, 0x3DE2); // Default(?) + break; + + case 'l': // read LED + SPrint_P(PSTR("PHLCON: 00")); + Serial.println((long int)(enc28j60PhyRead(PHLCON)), BIN); + break; + + case 'u': + { + SPrintln_P(PSTR("Sending UDP packet")); + strcpy(buffer[ETH_HEADER_LEN+IP_HEADER_LEN+UDP_HEADER_LEN], "hello"); + SPrintln_P(PSTR("Buffered string")); + + udpSend(IPDOT(192l,168l,0l,12l), CONTROL_PORT, 6, (uint8_t*)buffer[ETH_HEADER_LEN+IP_HEADER_LEN+UDP_HEADER_LEN]); + SPrintln_P(PSTR("UDP Sent")); + } break; + + case '?': + { + SPrintln_P(PSTR("Usage:")); + SPrintln_P(PSTR("(a) Print ARP table")); + SPrintln_P(PSTR("(d) Dump nic registers")); + SPrintln_P(PSTR("(i) Nic initialization")); + SPrintln_P(PSTR("(n) Netstack initialization")); + SPrintln_P(PSTR("(b) Reboot NIC")); + SPrintln_P(PSTR("(v) Display NIC revision")); + SPrintln_P(PSTR("(p) Print IP configuration")); + SPrintln_P(PSTR("(l) Display lamps register")); + SPrintln_P(PSTR("(1-4) Set lamps register")); + SPrintln_P(PSTR("(u) Send UDP packet")); + SPrintln_P(PSTR("(?) Print this help")); + } break; + + case '\n': + default: + break; + } + + delay(10); + // print new prompt + SPrint_P(PSTR("cmd> ")); + } +} + +void setup(void) +{ + pinMode(pinLED, OUTPUT); + digitalWrite(pinLED, HIGH); + Serial.begin(115200); + delay(100); + + // init network stack + SPrintln_P(PSTR("Initializing Network Stack!")); + netstackInit(IPADDRESS, NETMASK, GATEWAY); + + SPrint_P(PSTR("Net Stack is up!")); + SPrint_P(PSTR("\r\ncmd> ")); +} + +void loop(void) +{ + // service local stuff + serviceLocal(); + netstackService(); +} diff --git a/net.cpp b/net.cpp new file mode 100644 index 0000000..18f8f1f --- /dev/null +++ b/net.cpp @@ -0,0 +1,138 @@ +/*! \file net.c \brief Network support library. */ +//***************************************************************************** +// +// File Name : 'net.c' +// Title : Network support library +// Author : Pascal Stang +// Created : 8/30/2004 +// Revised : 7/3/2005 +// Version : 0.1 +// Target MCU : Atmel AVR series +// Editor Tabs : 4 +// +//***************************************************************************** + +#include +#include "HardwareSerial.h" + +#include +#include "programStrings.h" + +#include "net.h" + +uint16_t htons(uint16_t val) +{ + return (val<<8) | (val>>8); +} + +uint32_t htonl(uint32_t val) +{ + return (htons(val>>16) | (uint32_t)htons(val&0x0000FFFF)<<16); +} + + +uint16_t netChecksum(netIpHeader *data, uint16_t len) +{ + register uint32_t sum = 0; + + for (;;) { + if (len < 2) + break; + //sum += *((uint16_t *)data)++; + sum += *((uint16_t *)data); + data+=2; + len -= 2; + } + if (len) + sum += *(uint8_t *) data; + + while ((len = (uint16_t) (sum >> 16)) != 0) + sum = (uint16_t) sum + len; + + Serial.print("Checksum: "); + Serial.println((uint16_t) sum ^ 0xFFFF, HEX); + + return (uint16_t) sum ^ 0xFFFF; +} + +void netPrintEthAddr(struct netEthAddr* ethaddr) +{ + Serial.print(ethaddr->addr[0], HEX); + Serial.print(':'); + Serial.print(ethaddr->addr[1], HEX); + Serial.print(':'); + Serial.print(ethaddr->addr[2], HEX); + Serial.print(':'); + Serial.print(ethaddr->addr[3], HEX); + Serial.print(':'); + Serial.print(ethaddr->addr[4], HEX); + Serial.print(':'); + Serial.print(ethaddr->addr[5], HEX); +} + +void netPrintIPAddr(uint32_t ipaddr) +{ + Serial.print(((unsigned char*)&ipaddr)[3], DEC); + SPrint_P(PSTR(".")); + Serial.print(((unsigned char*)&ipaddr)[2], DEC); + SPrint_P(PSTR(".")); + Serial.print(((unsigned char*)&ipaddr)[1], DEC); + SPrint_P(PSTR(".")); + Serial.print(((unsigned char*)&ipaddr)[0], DEC); +} + +/* +void netPrintEthHeader(struct netEthHeader* eth_hdr) +{ + SPrint_P(PSTR("Eth Packet Type: 0x")); + Serial.print(eth_hdr->type); + + SPrint_P(PSTR(" SRC:")); + netPrintEthAddr(ð_hdr->src); + SPrint_P(PSTR("->DST:")); + netPrintEthAddr(ð_hdr->dest); + Serial.println(); +} + +void netPrintIpHeader(struct netIpHeader* ipheader) +{ + SPrint_P(PSTR("IP Header\r\n")); + SPrint_P(PSTR("Ver : %d\r\n", (ipheader->vhl)>>4); + SPrint_P(PSTR("Length : %d\r\n", htons(ipheader->len)); + if(ipheader->proto == IP_PROTO_ICMP) + SPrint_P(PSTR("Protocol: ICMP\r\n")); + else if(ipheader->proto == IP_PROTO_TCP) + SPrint_P(PSTR("Protocol: TCP\r\n")); + else if(ipheader->proto == IP_PROTO_UDP) + SPrint_P(PSTR("Protocol: UDP\r\n")); + else + SPrint_P(PSTR("Protocol: %d\r\n", ipheader->proto); + + SPrint_P(PSTR("SourceIP: ")); netPrintIPAddr(htonl(ipheader->srcipaddr)); Serial.println(); + SPrint_P(PSTR("Dest IP: ")); netPrintIPAddr(htonl(ipheader->destipaddr)); Serial.println(); +} + +void netPrintTcpHeader(struct netTcpHeader* tcpheader) +{ + SPrint_P(PSTR("TCP Header\r\n")); + SPrint_P(PSTR("Src Port: %d\r\n", htons(tcpheader->srcport)); + SPrint_P(PSTR("Dst Port: %d\r\n", htons(tcpheader->destport)); + SPrint_P(PSTR("Seq Num : 0x")); Serial.printu32(htonl(tcpheader->seqno)); Serial.println(); + SPrint_P(PSTR("Ack Num : 0x")); Serial.printu32(htonl(tcpheader->ackno)); Serial.println(); + SPrint_P(PSTR("Flags : ")); + if(tcpheader->flags & TCP_FLAGS_FIN) + SPrint_P(PSTR("FIN ")); + if(tcpheader->flags & TCP_FLAGS_SYN) + SPrint_P(PSTR("SYN ")); + if(tcpheader->flags & TCP_FLAGS_RST) + SPrint_P(PSTR("RST ")); + if(tcpheader->flags & TCP_FLAGS_PSH) + SPrint_P(PSTR("PSH ")); + if(tcpheader->flags & TCP_FLAGS_ACK) + SPrint_P(PSTR("ACK ")); + if(tcpheader->flags & TCP_FLAGS_URG) + SPrint_P(PSTR("URG ")); + Serial.println(); +} +*/ + diff --git a/net.h b/net.h new file mode 100644 index 0000000..f7ba7c5 --- /dev/null +++ b/net.h @@ -0,0 +1,208 @@ +/*! \file net.h \brief Network support library. */ +//***************************************************************************** +// +// File Name : 'net.h' +// Title : Network support library +// Author : Pascal Stang +// Created : 8/30/2004 +// Revised : 7/3/2005 +// Version : 0.1 +// Target MCU : Atmel AVR series +// Editor Tabs : 4 +// +/// \ingroup network +/// \defgroup net Network support library (net.c) +/// \code #include "net/net.h" \endcode +/// \par Description +/// This is a general network support library including a multitude of +/// structure definitions for various types of network packets, functions +/// and macros for switching byte order, and an RFC-compliant function +/// for calculating checksums. +// +// This code is distributed under the GNU Public License +// which can be found at http://www.gnu.org/licenses/gpl.txt +//***************************************************************************** +//@{ + +#ifndef NET_H +#define NET_H +#define NET_DEBUG 7 + +#include "avrlibdefs.h" +#include "avrlibtypes.h" +#include + +// Representation of a 48-bit Ethernet address. +struct netEthAddr +{ + uint8_t addr[6]; +} GNUC_PACKED; + +// The Ethernet header +struct netEthHeader +{ + struct netEthAddr dest; + struct netEthAddr src; + uint16_t type; +} GNUC_PACKED; + +#define ETH_HEADER_LEN 14 + +#define ETHTYPE_ARP 0x0806 +#define ETHTYPE_IP 0x0800 +#define ETHTYPE_IP6 0x86dd + +// The ARP header +struct netArpHeader +{ + uint16_t hwtype; + uint16_t protocol; + uint8_t hwlen; + uint8_t protolen; + uint16_t opcode; + struct netEthAddr shwaddr; + uint32_t sipaddr; + struct netEthAddr dhwaddr; + uint32_t dipaddr; +} GNUC_PACKED; + +#define ARP_OPCODE_REQUEST 1 +#define ARP_OPCODE_REPLY 2 +#define ARP_HWTYPE_ETH 1 + +// The IP header +struct netIpHeader +{ + uint8_t vhl; + uint8_t tos; + uint16_t len; + uint16_t ipid; + uint16_t ipoffset; + uint8_t ttl; + uint8_t proto; + uint16_t ipchksum; + uint32_t srcipaddr; + uint32_t destipaddr; +} GNUC_PACKED; +#define IP_HEADER_LEN 20 + +#define IP_PROTO_ICMP 1 +#define IP_PROTO_TCP 6 +#define IP_PROTO_UDP 17 + +// The ICMP header +struct netIcmpHeader +{ + uint8_t type; + uint8_t icode; + uint16_t icmpchksum; + uint16_t id; + uint16_t seqno; +} GNUC_PACKED; +#define ICMP_HEADER_LEN 8 + +#define ICMP_TYPE_ECHOREPLY 0 +#define ICMP_TYPE_ECHOREQUEST 8 + +// The UDP header +struct netUdpHeader +{ + uint16_t srcport; + uint16_t destport; + uint16_t udplen; + uint16_t udpchksum; +} GNUC_PACKED; +#define UDP_HEADER_LEN 8 + +// The TCP header +struct netTcpHeader +{ + uint16_t srcport; + uint16_t destport; + uint32_t seqno; + uint32_t ackno; + uint8_t tcpoffset; + uint8_t flags; + uint16_t wnd; + uint16_t tcpchksum; + uint16_t urgp; +// uint8_t optdata[4]; +} GNUC_PACKED; +#define TCP_HEADER_LEN 20 + +#define TCP_FLAGS_FIN 0x01 +#define TCP_FLAGS_SYN 0x02 +#define TCP_FLAGS_RST 0x04 +#define TCP_FLAGS_PSH 0x08 +#define TCP_FLAGS_ACK 0x10 +#define TCP_FLAGS_URG 0x20 + +// Ethernet/ARP header +struct netEthArpHeader +{ + struct netEthHeader eth; + struct netArpHeader arp; +} GNUC_PACKED; + +// Ethernet/IP header +struct netEthIpHeader +{ + struct netEthHeader eth; + struct netIpHeader ip; +} GNUC_PACKED; + +// The IP header +typedef struct netIpHeader ip_hdr; + +// The IP/TCP headers +typedef struct +{ + struct netIpHeader ip; + struct netTcpHeader tcp; +} tcpip_hdr; + +// The IP/ICMP headers +typedef struct { + struct netIpHeader ip; + struct netIcmpHeader icmp; +} icmpip_hdr; + + +// The UDP and IP headers +typedef struct { + struct netIpHeader ip; + struct netUdpHeader udp; +} udpip_hdr; + + +//! Convert dot-notation IP address into 32-bit word. +/// Example: IPDOT(192l,168l,1l,1l) +#define IPDOT(a,b,c,d) ((a<<24)|(b<<16)|(c<<8)|(d)) + +//! Host-to-Network SHORT (16-bit) byte-order swap (macro). +#define HTONS(s) ((s<<8) | (s>>8)) +//! Host-to-Network LONG (32-bit) byte-order swap (macro). +#define HTONL(l) ((l<<24) | ((l&0x00FF0000l)>>8) | ((l&0x0000FF00l)<<8) | (l>>24)) + +//! Host-to-Network SHORT (16-bit) byte-order swap (function). +uint16_t htons(uint16_t val); +//! Host-to-Network LONG (32-bit) byte-order swap (function). +uint32_t htonl(uint32_t val); + +//! Calculate IP-style checksum from data. +uint16_t netChecksum(netIpHeader *data, uint16_t len); + +//! Print Ethernet address in XX:XX:XX:XX:XX:XX format. +void netPrintEthAddr(struct netEthAddr* ethaddr); +//! Print IP address in dot notation. +void netPrintIPAddr(uint32_t ipaddr); +//! Print Ethernet header information. +void netPrintEthHeader(struct netEthHeader* eth_hdr); +//! Print IP header information. +void netPrintIpHeader(struct netIpHeader* ipheader); +//! Print TCP header information. +void netPrintTcpHeader(struct netTcpHeader* tcpheader); + +#endif +//@} + diff --git a/netstack.cpp b/netstack.cpp new file mode 100644 index 0000000..303f71b --- /dev/null +++ b/netstack.cpp @@ -0,0 +1,161 @@ +/*! \file netstack.c \brief Network Stack. */ +//***************************************************************************** +// +// File Name : 'netstack.c' +// Title : Network Stack +// Author : Pascal Stang +// Created : 6/28/2005 +// Revised : 9/20/2005 +// Version : 0.3 +// Target MCU : Atmel AVR series +// Editor Tabs : 4 +// +//***************************************************************************** + +#include "debug.h" + +#include "netstack.h" + +#ifdef NETSTACK_DEBUG +#include +#include "programStrings.h" +#include "HardwareSerial.h" +#endif + +unsigned char NetBuffer[NETSTACK_BUFFERSIZE]; + +void netstackInit(uint32_t ipaddress, uint32_t netmask, uint32_t gatewayip) +{ + // init network device driver + #ifdef NETSTACK_DEBUG + SPrint_P(PSTR("Initializing Network Device\r\n")); + #endif + nicInit(); + // init ARP + #ifdef NETSTACK_DEBUG + SPrint_P(PSTR("Initializing ARP cache\r\n")); + #endif + arpInit(); + // init addressing + #ifdef NETSTACK_DEBUG + SPrint_P(PSTR("Initializing Addressing\r\n")); + #endif + ipSetConfig(ipaddress, netmask, gatewayip); +} + +u08* netstackGetBuffer(void) +{ + return NetBuffer; +} + +int netstackService(void) +{ + int len; + struct netEthHeader* ethPacket; + + // look for a packet + len = nicPoll(NETSTACK_BUFFERSIZE, NetBuffer); + + if(len) + { + ethPacket = (struct netEthHeader*)&NetBuffer[0]; + + #if NET_DEBUG >= 5 + SPrint_P(PSTR("Received packet len: ")); + Serial.print((unsigned int)len); + SPrint_P(PSTR(", type:")); + if(ethPacket->type == htons(ETHTYPE_IP)) + { + SPrint_P(PSTR("IP")); + } + else if(ethPacket->type == htons(ETHTYPE_ARP)) + { + SPrint_P(PSTR("ARP")); + } + SPrint_P(PSTR("Packet Contents\r\n")); + debugPrintHexTable(len, NetBuffer); + #endif + + if(ethPacket->type == htons(ETHTYPE_IP)) + { + // process an IP packet + #ifdef NETSTACK_DEBUG + SPrint_P(PSTR("NET Rx: IP packet\r\n")); + #endif + // add the source to the ARP cache + // also correctly set the ethernet packet length before processing? + arpIpIn((struct netEthIpHeader*)&NetBuffer[0]); + arpPrintTable(); + + netstackIPProcess( len-ETH_HEADER_LEN, (ip_hdr*)&NetBuffer[ETH_HEADER_LEN] ); + } + else if(ethPacket->type == htons(ETHTYPE_ARP)) + { + // process an ARP packet + #ifdef NETSTACK_DEBUG + SPrint_P(PSTR("NET Rx: ARP packet\r\n")); + #endif + arpPrintTable(); + arpArpIn(len, ((struct netEthArpHeader*)&NetBuffer[0]) ); + } + } + return len; +} + +void netstackIPProcess(unsigned int len, ip_hdr* packet) +{ + #ifdef NETSTACK_DEBUG + SPrint_P(PSTR("entering netstackIPProcess\r\n")); + //icmpPrintHeader((icmpip_hdr*)packet); + #endif + // check IP addressing, stop processing if not for me and not a broadcast + if( (htonl(packet->destipaddr) != ipGetConfig()->ip) && + (htonl(packet->destipaddr) != (ipGetConfig()->ip|ipGetConfig()->netmask)) && + (htonl(packet->destipaddr) != 0xFFFFFFFF) ) + return; + + // handle ICMP packet + if( packet->proto == IP_PROTO_ICMP ) + { + #ifdef NETSTACK_DEBUG + SPrint_P(PSTR("NET Rx: ICMP/IP packet\r\n")); + //icmpPrintHeader((icmpip_hdr*)packet); + #endif + icmpIpIn((icmpip_hdr*)packet); + } + else if( packet->proto == IP_PROTO_UDP ) + { + #ifdef NETSTACK_DEBUG + SPrint_P(PSTR("NET Rx: UDP/IP packet\r\n")); + //debugPrintHexTable(NetBufferLen-14, &NetBuffer[14]); + #endif + netstackUDPIPProcess(len, ((udpip_hdr*)packet) ); + } + else if( packet->proto == IP_PROTO_TCP ) + { + #ifdef NETSTACK_DEBUG + SPrint_P(PSTR("NET Rx: TCP/IP packet\r\n")); + #endif + netstackTCPIPProcess(len, ((tcpip_hdr*)packet) ); + } + else + { + #ifdef NETSTACK_DEBUG + SPrint_P(PSTR("NET Rx: IP packet\r\n")); + #endif + } +} + +void netstackUDPIPProcess(unsigned int len, udpip_hdr* packet) +{ + #ifdef NETSTACK_DEBUG + SPrint_P(PSTR("NetStack UDP/IP Rx Dummy Handler\r\n")); + #endif +} + +void netstackTCPIPProcess(unsigned int len, tcpip_hdr* packet) +{ + #ifdef NETSTACK_DEBUG + SPrint_P(PSTR("NetStack TCP/IP Rx Dummy Handler\r\n")); + #endif +} diff --git a/netstack.h b/netstack.h new file mode 100644 index 0000000..762f658 --- /dev/null +++ b/netstack.h @@ -0,0 +1,85 @@ +/*! \file netstack.h \brief Network Stack. */ +//***************************************************************************** +// +// File Name : 'netstack.h' +// Title : Network Stack +// Author : Pascal Stang +// Created : 6/28/2005 +// Revised : 9/20/2005 +// Version : 0.3 +// Target MCU : Atmel AVR series +// Editor Tabs : 4 +// +/// \ingroup network +/// \defgroup netstack Network Stack (netstack.c) +/// \code #include "net/netstack.h" \endcode +/// \par Description +/// This library co-ordinates the various pieces of a typical IP network +/// stack into one unit. Included are handling for ARP, ICMP, and IP +/// packets. UDP and TCP packets are processed and passed to the user. +/// +/// This is an example of how to use the various network libraries, and +/// is meant to be useful out-of-the-box for most users. However, some +/// users may find it restrictive and write their own handlers instead. +/// This stack implementation is by no means the only way to use the various +/// network libraries. +/// +/// \note This is NOT a full-blown TCP/IP stack. It merely handles lower +/// level stack functions so that UDP and TCP packets can be sent +/// and received easily. End-to-end TCP functionality may be added +/// in a future version. Until then, I can recommend using other embedded +/// TCP/IP stacks like Adam Dunkel's uIP. +// +// This code is distributed under the GNU Public License +// which can be found at http://www.gnu.org/licenses/gpl.txt +//***************************************************************************** +//@{ + +#ifndef NETSTACK_H +#define NETSTACK_H + +#include "net.h" +#include "arp.h" +#include "icmp.h" +#include "ip.h" +#include "nic.h" + +#define NETSTACK_DEBUG + +/// NET_BUFFERSIZE is the common receive/process/transmit buffer. +/// - You may override the default NET_BUFFERSIZE by defining an alternate value in global.h. +/// - Network packets larger than NET_BUFFERSIZE will not be accepted. +#ifndef NETSTACK_BUFFERSIZE +#define NETSTACK_BUFFERSIZE (576+ETH_HEADER_LEN) +#endif + +/// netstackInit prepares the network interface for use and should be called +/// once at the beginning of the user program. +/// \note Use ipSetAddress() to change network parameters in mid-run. +void netstackInit(uint32_t ipaddress, uint32_t netmask, uint32_t gatewayip); + +/// netstackGetBuffer returns a pointer to the common receive/process/transmit buffer. +u08* netstackGetBuffer(void); + +/// netstackService should be called in the main loop of the user program. +/// The function will process one received network packet per call. +/// The return value is the length of the packet processed, or zero if no +/// packet was processed. +int netstackService(void); + +/// netstackIPProcess handles distribution of IP received packets. +/// +void netstackIPProcess(unsigned int len, ip_hdr* packet); + +/// This weakly-defined function is the default handler for incoming UDP/IP packets. +/// Users should define this same function in user code (same name and arguments) to +/// override this default handler and get access to the received packets. +void netstackUDPIPProcess(unsigned int len, udpip_hdr* packet) __attribute__ ((weak)); + +/// This weakly-defined function is the default handler for incoming TCP/IP packets. +/// Users should define this same function in user code (same name and arguments) to +/// override this default handler and get access to the received packets. +void netstackTCPIPProcess(unsigned int len, tcpip_hdr* packet) __attribute__ ((weak)); + +#endif +//@} diff --git a/nic.h b/nic.h new file mode 100644 index 0000000..9992988 --- /dev/null +++ b/nic.h @@ -0,0 +1,80 @@ +/*! \file nic.h \brief Network Interface Card (NIC) software definition. */ +//***************************************************************************** +// +// File Name : 'nic.h' +// Title : Network Interface Card (NIC) software definition +// Author : Pascal Stang +// Created : 8/22/2004 +// Revised : 7/3/2005 +// Version : 0.1 +// Target MCU : Atmel AVR series +// Editor Tabs : 4 +// +/// \ingroup network +/// \defgroup nic Network Interface Card (NIC) software definition (nic.h) +/// \code #include "net/nic.h" \endcode +/// \par Description +/// This is the software interface standard for network interface hardware +/// as used by AVRlib. Drivers for network hardware must implement these +/// functions to allow upper network layers to initialize the interface, +/// and send and receive net traffic. +// +// This code is distributed under the GNU Public License +// which can be found at http://www.gnu.org/licenses/gpl.txt +//***************************************************************************** +//@{ + +#ifndef NIC_H +#define NIC_H + +#include + +//! Initialize network interface hardware. +/// Reset and bring up network interface hardware. This function should leave +/// the network interface ready to handle \c nicSend() and \c nicPoll() requests. +/// \note For some hardware, this command will take a non-negligible amount of +/// time (1-2 seconds). +void nicInit(void); + +//! Send packet on network interface. +/// Function accepts the length (in bytes) of the data to be sent, and a pointer +/// to the data. This send command may assume an ethernet-like 802.3 header is at the +/// beginning of the packet, and contains the packet addressing information. +/// See net.h documentation for ethernet header format. +void nicSend(unsigned int len, unsigned char* packet); + +//! Check network interface; return next received packet if avaialable. +/// Function accepts the maximum allowable packet length (in bytes), and a +/// pointer to the received packet buffer. Return value is the length +/// (in bytes) of the packet recevied, or zero if no packet is available. +/// Upper network layers may assume that an ethernet-like 802.3 header is at +/// the beginning of the packet, and contains the packet addressing information. +/// See net.h documentation for ethernet header format. +unsigned int nicPoll(unsigned int maxlen, unsigned char* packet); + +//! Return the 48-bit hardware node (MAC) address of this network interface. +/// This function can return a MAC address read from the NIC hardware, if available. +/// If the hardware does not provide a MAC address, a software-defined address may be +/// returned. It may be acceptable to return an address that is less than 48-bits. +void nicGetMacAddress(uint8_t* macaddr); + +//! Set the 48-bit hardware node (MAC) address of this network interface. +/// This function may not be supported on all hardware. +void nicSetMacAddress(uint8_t* macaddr); + +//! Print network interface hardware registers. +/// Prints a formatted list of names and values of NIC registers for debugging +/// purposes. +void nicRegDump(void); + +//! PHY Chip hard reset function +void nicHardReset(void); + +//! PHY Chip soft reset function +void nicSoftReset(void); + +//! PHY Chip reboot +void nicReboot(void); + +#endif +//@} diff --git a/programStrings.cpp b/programStrings.cpp new file mode 100644 index 0000000..0fff768 --- /dev/null +++ b/programStrings.cpp @@ -0,0 +1,40 @@ +#include +#include "programStrings.h" +#include "wiring.h" +#include "HardwareSerial.h" + +// call with printString(PSTR("My string")) +void SPrint_P(const char *data) +{ + char ch; + + for (;;) { + ch = pgm_read_byte( data++ ); + if ( !ch ) return; + Serial.print(ch); + } +} + +void SPrintln_P(const char *data) +{ + SPrint_P(data); + SPrint_P(PSTR("\r\n")); +} + +void SPrintHex_P(const char *data) +{ + char ch; + + for (;;) { + ch = pgm_read_byte( data++ ); + if ( !ch ) return; + Serial.print(ch, HEX); + } +} + +void SPrintlnHex_P(const char *data) +{ + printString(data); + + Serial.println(); +} diff --git a/programStrings.h b/programStrings.h new file mode 100644 index 0000000..df5a4e8 --- /dev/null +++ b/programStrings.h @@ -0,0 +1,13 @@ +#ifndef isDef_programStrings +#define isDef_programStrings + +#ifndef nop + #define nop() asm volatile ("nop") +#endif + +void SPrint_P(const char *data); +void SPrintln_P(const char *data); +void SPrintHex_P(const char *data); +void SPrintlnHex_P(const char *data); + +#endif