A tiny UDP IP network library for small embedded controllers

This package was written by me  as a way of expanding the capability of a line of microcontrollers manufactured by TERN. I created a prototype board using the Crystal (now Cirrus ) CS8900 ethernet controller chip. Tern then  incorporated this library into their toolkit.

This code may be useful as an example of a lightweight, stand-alone implementation of a CS8900 driver in 8 bit mode, as well as a reasonably complete implementation of the IP portion of the TCP/IP stack.

You may download the source code of the final version HERE.

Table of contents
  • TFTP Server Library
  • OVERVIEW

    CS89UDP is a source code library which
    provides UDP-based services for CS8900-based ethernet controller
    as implemented for TERN microcontrollers. It supports the CS8900
    Ethernet controller as supplied on the MM-A adaptor board
    interfaced to any AM188-based Tern controller which supports the
    MM-A.

    The software tool chain used consists of
    the the Borland Turbo C++ V3.1 Compiler, Turbo
    Link 5.1
    and Tern LOC 3.1 EV Version 6.00.43
    as distributed with Tern controller products.

    INSTALLATION
    PREREQUISITES

    The distribution is a self-extracting zip
    archive: CSUDP09x.EXE

    Its contents should be extracted into the \TERN\186
    directory which was created during the installation of the
    TERN EV SDK
    . It does not overwrite any existing files.
    One change is required to the pre-existing MAKEFILE
    (see below) in order for the CS89UDP samples to run.

    The following steps should be taken at a
    DOS command prompt; The distribution file may be contained on
    floppy or in a temp directory. The following assumes it is on a
    floppy mounted in the A drive:

    • Change Directory to \TERN\186 and
      unpack the archive:

    C:\>CD \TERN\186

    C:\tern\186> A:CSUDP09x -d

     

    x‘ will vary depending on
    the particular release.

    The following files will be extracted:

    This docment

    CSREADME.HTM

    Batch scripts to compile and run sample
    applications:

    METH.BAT

    TETH.BAT

    Source code to library:

    CS89DEFS.H CS89DEV.H CS89UDP.H
    IRUP188.H

    CSETH_A.ASM CS89UDP.C

    Sample applications:

    “PING” demonstrations.
    Polled and Interrupt driven versions:

    PNGIRUP.C

    PNGPOLL.C

    Trivial File Transfer Protocol – Server
    application demo:

    TERNETD.C TFTP.C TFTP.H

    Optional PC-side test routines

    CSTEST.PL CS_TFTP.PM – Perl-language

    TERNET/ -
    Microsoft Visual C project

    PREPARING
    HARDWARE

    Confirm that the MM-A board has been
    firmly seated into the Am188 CPU card.

    CS8900 LEDs

    There are two LEDS on the CS8900 board,
    the LINK LED should come on and stay once when power is
    applied and a valid connection has been made on the 10BASET
    interface. The other LED will flicker on and off to indicate
    any traffic activity on the 10BASET interface.

    BEFORE
    COMPILING SAMPLE PROGRAMS

    One change is required to the stock /TERN/186/MAKEFILE as distributed by Tern; Edit it and make the
    following change:

    Before:

    USER_OBJS=

    After:

    #USER_OBJS=

    This change allows the TETH.BAT
    and METH.BAT scripts to pass in their own
    settings for this parameter and should not disturb your
    ability to compile and run any other sample Tern programs.

    COMPILING AND RUNNING SAMPLE PROGRAMS

    Any one of the test applications can be
    compiled with the supplied script – METH.BAT,
    or compiled and run with METH.BAT; These
    batch scripts act the same as T.BAT and M.BAT
    as supplied with the Tern EV kit with the
    exception that they pass in the necessary list of .OBJ files
    to link.

    SAMPLE PROGRAMS

    Setting Network Parameters

    Prior to running any of the sample
    programs, you must alter the network address definitions in the main()
    function of the respective program. To do this, you will need to
    know the following about your LAN:

    • AN UNUSED IP NETWORK
      ADDRESS
    • NETWORK MASK IN USE
    • OPTIONALLY, THE ADDRESS OF
      A DEFAULT GATEWAY ROUTER

    For example, if your local net address was
    192.168.0.0, with a netmask of 255.255.255.0, and there was an
    unused host address of 192.168.0.1 available for the controller,
    then in main() of the sample program, you would set the following
    lines:

    CS_SET_IP_VAL(192,168,0,1,
    csp->ipoct4);

    CS_SET_IP_VAL(255,255,255,0,
    csp->netmask_ipoct4);

    Note the commas which are different from
    tranditional IP address notation.

    If you are communicating with the
    controller through a router, then you will need to set the
    optional default gateway address; Continuing with the above
    example, if the gateway router’s address on the controller’s LAN
    as 192.168.0.254, the you would set the following address:

    CS_SET_IP_VAL(192,168,0,254,
    csp->default_router_ipoct4);

    PINGPOLL

    Basic polled mode response to ping.

    This function demonstrates basic
    library functionality by answering ICMP Echo, aka ‘ping’
    packets sent from another host.

    With a Tern controller / Ethernet
    connected serially to your PC, Compile and download with the
    command:

    TETH PNGPOLL

    Run the program. Now, from a PC on the
    same ethernet, At a DOS prompt type:

    PING 192.168.0.1

    Replace 192.168.0.1 with the address
    you set in pngpoll.c You should see reply
    packets. You may single-step PINGPOLL and
    analyze program flow.

    PNGIRUP

    This application demo behaves
    identically to PINGPOLL with the exception that it employs
    interrupts. Consequently, it will respond faster to requests.

    TERNETD

    This application implements a TFTP
    protocol server which can be connected to via any pc software
    which utilizes the TFTP protocol. Source code to pc programs
    have been provided with TERNET.EXE being the
    most flexible.

    Compile and run TFTP by entering:

    TETH TERNETD

    Once running, TERNETD will listen for
    TFTP connects. It accepts TFTP GET commands
    with specifications of the command to perform as the source
    file parameter. The following commands are accepted by
    entering them as the source file parameter in the TFTP
    client:

    File Name
    Parameter
    MEANING
    ether status returns statistics on
    ethernet
    ether clear resets all statistics
    counters to 0
    sourcedata NNNN controller sends NNNNN
    bytes of test data
    ledmorse message
    ..
    blinks message in morse
    code on controller led
    bid break into debugger

    In addition, TERNETD
    accepts commands from a selection of ae_lib
    functions; All paremters are numeric and can be entered
    decimal or hex (preceded with 0x). See the
    tern software documentation for more information.

    COMMAND MEANING
    ae_init perform ae_init() call
    led state perform led() call. state
    = 0: off state = 1: on
    pio_init
    bit mode
    perform pio_init() call.
    pio_wr bit
    dat
    perform pio_wr() call.
    pio_rd port perform pio_rd() call.
    outport port_id
    value
    perform outport() call.
    outportb port_id
    value
    perform outportb() call.
    inport port_id perform inport() call.
    inportb port_id perform inportb() call.
    ae_ad12 channel perform ae_ad12() call.
    ae_reset perform ae_reset() call.
    Does not return.

    Be sure to set the TFTP client to
    transfer in BINARY mode.

    PC CLIENT
    SOFTWARE – TERNET

    TERNET

    TERNET is a a
    win32/winsock-based utility for use with TERNETD on the Tern
    controller. TERNET polls the controller,
    sending arbitrary directives on the command line.

    TERNET [ -t timeout-secs -n
    repeat-count -d delay-ms -s(tamp time) ]
    controller-ip-address command

    A TERNET.EXE executable is located in
    /tern/186/ternet/debug. The director /tern/186/ternet
    contains the source and the other files necessary to compile
    it as a Microsoft VC5 project.

    PC CLIENT SOFTWARE – CSTEST

    A perl script which acts as a
    customized TFTP client is included. It requires that you
    install Active State Perl for WIN32, available from
    www.activestate.com/ActivePerl.

    After installing ActiveState PERL, you
    can then invoke cstest.pl by double-clicking it from an
    Windows Explorer window or entering PERL CSTEST.PL at a DOS
    command prompt. It has a number of options including running
    any of the supported TFTP commands with one keystroke, plus
    an option for running them repetitively for testing..

    The TFTPOLL script combined with the
    CSTEST script provides a complete example of how to create an
    application which requires two-way communication over an
    ethernet LAN.

    Other PC Client Software

    Since the Tern controller demo program
    TERNETD implements a tftp protocol server, Any third-party
    tftp client software. An example of one such client is TFTP
    Client 95
    from Weird Solutions – (
    www.weird-solutions.com ).

     

    TROUBLESHOOTING

    If the Am188 cannot reset the the
    CS8900, the LED on the Am188 mainboard will blink a pattern
    in morse code:

    PATTERN MEANING
    .   .-. ER Could not
    reset CS8900
    .
    -…
    EI Could not
    initialize CS8900

    Both of these patterns could most
    likely indicate a problem with the attachment of the CS8900
    to the Am188 board.

    DEBUGGING
    TERN-RESIDENT SOFTWARE

    If you call cs_init() with
    the CS_DEBUG option set, debugging will be
    sent to the SER2 serial interface at 9600/8N1. Debugging of
    Interrupt-driven software in the Turbo Debugger is not
    reliable, so it is recommended that code be tested and
    development be done in polled mode. As long as the controller
    is operated in non-interrupt mode, all functionality of the
    Turbo Debugger is available.


    Function Descriptions

    int cs_init(u_char opts, struct cs_info
    *csp )

    Initialize (or reinitialize) CS8900 Chip and driver code
      must be called prior to any other cs_ library calls.
      can be called with any flag
    
        opts - or-combinable bits: 
    
    	CS_PROMISC - causes chip to receive all packets on network
         	CS_IENABL  - causes received packets to generate interrupts
         	CS_DEBUG   - turns on debugging information
         	CS_RESET   - resets the ethernet chip be filled in.
    
        csp - pointer to ethernet controller info structure.  The following
              members must be filled in prior to calling:
    
    		csp->baseaddr
    		csp->vec
    		csp->ipoct[1-4]
    		csp->netmask_ipoct[1-4]
    		csp->default_router_ipoct[1-4]
    		csp->numbufs
    		csp->poll_interval

    
    

     

    void cs_errcode(char *inpat)

    displays a message on the serial port and on the controller led
     using morse code
    
         inpat - char * to issue. Note, pattern should consist of no
                 more than a few characters. Pattern will blink code
                 on leds forever.

    
    

     

    void cs_ledmorse(char *inpat,int forever)

    blinks pattern on controller led. 
    
        forever = 0 : blink it once and return
                = 1 : blink it repeatedly forever

    
    

     

    u_int cs_bcmp( char *d, char *s, int c)

    Compare memory blocks, return 0 if equal.
    
            d : pointer to destination
            s : pointer to source
            c : how many bytes to compare

    
    

     

    int cs_bcopy(from, to, cnt)

    Copy 'cnt' bytes from 'from' to 'to'. careful of overlay.

    
    

     

    void cs_bzero(u_char *str, u_int cnt)

    zero out a buffer
    
        	str : pointer to area
        	cnt : how many bytes to zero

    
    

     

    u_int cs_rdreset(struct cs_info *csp) {

    read reset status of chip generate error if there's a problem.
        otherwise return value read from Self Control Regsiter.
    
            csp : pointer to cs_info ether controller information

    
    

     

    int cs_reset(struct cs_info *csp) {

    reset the cs8900 controller.

    
    

     

    u_int cs_inpagew(struct cs_info *csp, u_int loc) {

    get a single word from a specified packet page addr
    
        	csp : pointer to cs_info ether controller information
        	loc : cs8900 register location from which to read

    
    

     

    void cs_outpagew(struct cs_info *csp, u_int loc, u_int val)

    write a single word from a specified packet page addr
    
        	csp : pointer to cs_info ether controller information
        	loc : cs8900 register location from which to read

    
    

     

    void cs_per_second_chores(struct cs_info *csp)

    Handle periodic timeout chores.
        Should be called 1x per second to clear out old ARP table entries, etc.
    
        	csp : pointer to cs_info ether controller information

    
    

     

    cs_poll(struct cs_info *csp)

    csp : pointer to cs_info ether controller information
    
        Take care of any pending business.
        In an polled environment, it should be called
        Every .1/sec . There is no harm in calling it
        as infrequently as 1/sec, however response to incoming
        packets will be delayed by up to that amount.
    
        In an interrupt driven environment cs_poll()
        should still be called at a minimum of at least once per second
        to check for missed interrupts, and handle timeout chores 
    
        Prior to cs_init(), set csp->poll_interval to the number
        of times you intend to call cs_poll() per second.  This
        is to calibrate how often the per_second function gets
        called by cs_poll()

    
    

     

    void interrupt far cs_interrupt(void)

    Ethernet interrupt handler. 
    
        Sorts out type of interrupt and calls cs_rx() if need be.
        Can be called directly as an ISR, or manually, usually via  cs_poll().

    
    

     

    void cs_rx(void) {

    called from cs_interrupt()
        Read packets in from ethernet controller.
        determine disposition and call appropriate handler functions.

    
    

     

    void cs_arp_tx_reply( struct cs_info *csp, struct ether_arp *ea)

    Reply to an arp 'whohas' packet w/our IP address.
    
        	csp : pointer to cs_info ether controller information
        	ea  : sender's arp information

    
    

     

    void cs_ip_input( struct cs_info *csp, struct ip *ip)

    handle packets inbound IP packets..
    
        	csp : pointer to cs_info ether controller information
        	ip  : ip portion of received packet

    
    

     

    int cs_in_cksum(void *p, int len)

    Checksum routine for Internet Protocol family headers (Portable Version).
        This routine is very heavily used in the network
        code and should be modified for each CPU to be as fast as possible.
    
        Obtained from BSD Stack.
    
        	p : start of buffer to checksum
            len : how many bytes to checksum
    
        	returns the checksum value

    
    

     

    struct arptabentry * cs_arp_resolve(struct cs_info *csp, struct ip *ip)

    Send an arp whohas packet 
    
        	csp : pointer to cs_info ether controller information
    
        	ip  : ip packet containing the ipaddr to resolve
    
        	returns :  pointer to valid arp entry or 0.

    
    

     

    void cs_icmp_input( struct cs_info *csp, struct ip *ip)

    Handle inbound ICMP packets. This includes 'ping' packets
    
        	csp : pointer to cs_info ether controller information
        	ip  : pointer to ip portion of rx'd packet

    
    

     

    void cs_arp_rx_reply( struct cs_info *csp, struct ether_arp *ea)

    handle an answer to one of our who-has requests
    
        	csp : pointer to cs_info ether controller information
        	ea : pointer to received arp packet

    
    

     

    void cs_ip_output( struct cs_info *csp, u_char bufno, struct ip *ip)

    send an IP packet that is already in the
        buffer and filled in except for perhaps the ARP address. 
    
        	csp : pointer to cs_info ether controller information
        	bufno : index of buffer in csp->packetbuffers array
        	ip  : pointer to ip section of packet to be transmitted
    
        cs_ip_output() will perfrom the necessary routing and arp lookup
        to send the packet. In the case that the ARP entry for the destination
        is not on hand, it will keep the packet in keep the packet for
        later transmission.

    void cs_txdrainq(struct cs_info
    *csp)

    Transmit any untransmitted packets in the queue.
        This routine is typically called by poll
        routines and by the rx routine when the corresponding destination
        has replied with the ARP packet.
    
        	csp : pointer to cs_info ether controller information

    void cs_dbg_putser(char *buf)

    send a message to the serial port (for debugging)
    
        buf : pointer to null-terminated string to send

    void cs_dbg_putsern(char *buf) {

    same as putser(), but add \r\n on the end

    void cs_serinit(u_char speed) {

    initialize serial port
    
        	speed : baud rate code for serial port.
                 passed to s1_init() of ae_lib

    void cs_dbg_dump_bytes(char
    *mesg, u_char *AbsBaseAddr, int Length)

    dump out bytes in hex and ascii
    
        mesg : ascii string to print prior to dump
        AbsBaseAddr: pointer to start of area to dump
        Length : how many bytes to dump

    void cs_linkled(struct cs_info *csp,
    int what)

    control link led attached to cs8900 chip 
    
        	csp    : pointer to cs_info ether controller information
        	what   : 0 : turn link led off
                 1 : turn link led on

    void cs_udp_bind( struct cs_info
    *csp, u_short port, void far (*handler)(struct cs_info *csp, void
    *whatever) )

        csp : pointer to cs_info ether controller information
        port : udp port on which to listen
        void far (*handler)(struct cs_info *csp, void *whatever))  : pointer to user-supplied handler function
    
        handler params:
    
        csp : pointer to cs_info ether controller information
        whatever : untyped pointer to inbound packet.
    
        Instructs stack to accept packets inbound on udp 'port',
        sending them to user-supplied 'handler' function.
        Multiple calls can be made to
        define multiple handlers for different services can be defined.

    cs_clearstats(struct cs_info
    *csp) {

    clears network statistics
    
        csp : pointer to cs_info ether controller information

    
    

     

    cs_intsetup(struct cs_info *csp) {

    setup interrupts 
    
        csp : pointer to cs_info ether controller information.
    
        	  csp->vec needs be set to hardware interrupt vector
                prior to calling

    
    

     

    void cs_udp_input( struct cs_info *csp, struct ip *ip) {

    process incoming application udp packets 
    
        If a handler function has been setup via cs_udp_bind(),
        it will be called from here. Otherwise the packet is dropped.
    
        		csp : pointer to cs_info ether controller information
        		ip  : pointer to ip section of packet to be transmitted

    int cs_route(struct cs_info *csp,
    struct ip *oip, struct ip *nip) {

    Route an ip packet. 
    
        Determines if destination address
        is not on local LAN. Returns doctored-up packet with
        destination IP address pointing to gateway if need be. 
    
        returns : 0 : if routing is necessary
                  1 : if not

    void cs_pbo(struct cs_info *csp) {

    print buffer ownerships to debug
    
        	csp : pointer to cs_info ether controller information

    u_char * cs_get_next_buffer(
    struct cs_info *csp, u_char *theirpointer, u_char theirstatcode)

    Get next tx/rx buffer. Returns address of next free buffer.  
    
        	csp : pointer to cs_info ether controller information.
        	theirpointer : user pointer to be filled in.
        	theirstatcode : buffer ownership code to fill in.
    
        CS8900 receive and transmit buffers are shared. They are managed by tagging them
        with 'ownerships' as defined in cs89udp.h: 
    
        	CS_BUF_AVAIL 	 	 : owned by no one. this function will pass one of these two you.
        	CS_BUF_RX        	 : owned by receive code
        	CS_BUF_TX_ARP_WAIT   : queued but waiting dest for on an arp
        	CS_BUF_TXQ       	 : queued for transmission.
        	CS_BUF_TX        	 : transmitted. not acked yet.
        	CS_BUF_USER 	     : owned by user
        	CS_BUF_NOFREE 	     : stack shouldn't free once xmitted. User will do it.
        	CS_BUF_ROUTED 	     : packet is being sent through gway
        	CS_BUF_NO_RE_TX 	 : don't re-tx this packet if there's a timeout
    
        returns : pointer to buffer
        side-effect: sets cs_info->curbuf to the index of the buffer allocated.

    
    

     

    void cs_free_bufp(struct cs_info *csp, u_char *theirpointer)

    release the packet buffer pointed to by 'theirpointer'.
        changes ownership to CS_BUF_AVAIL
    
        	csp : pointer to cs_info ether controller information
        	theirpointer : address of buffer to release

    
    

     

    void cs_free_bufi(u_char theirindex)

    release the packet buffer pointed to by 'theirindex'
    
        	changes ownership of buffer with index 'theirindex' to CS_BUF_AVAIL

     

    TFTP Server Library


    
    

     

    void tftpd_init( void far *handler)

    Initialize tftpd functions
    
        	handler : pointer to function which will be called when
         inbound tftp packets are received

    
    

     

    tftp_rx_handler(struct cs_info *csp, void *whatever)

    This callback gets called when a udp packet
     on the previously bound-to udp port is received

    
    

     

    void tftpd_timeout_check(struct cs_info *csp)

    Called at .1 sec intervals to take care
        of timeout conditions during tftp activity
    
            csp : pointer to cs8900 ethernet controller information

    
    

     

    void tftpd_finish_reply(struct cs_info *csp, u_char *pktbuf, struct udp *rx_udp, struct tftp *tx_tftp, short len)

    Finishes filling in an packet and sends it off..
    
        	csp : pointer to ethernet controller information
        	pktbuf : buffer to be filled in
        	rx_udp : pointer to udp section of received packet
        	tx_tftp : pointer to tftp section of packet to be transmitted
        	len : length of tftp data