A network library for small embedded controllers
This package was written by me in 1999 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 package is built for an older version of the Borland-based Tern software development kit which is has been replaced with a newer kit based on the Paradigm C tool chain. If you are using this newer SDK, a version of this kit is included in the directory Tern\186\SAMPLES.
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.
TERN CS8900 Driver Library Version 0.9
17-AUG-99
(C) 1999 Cogwheel, Inc.
(C) 1999 Tern, Inc.
FUNCTION DESCRIPTIONS
TFTP LIBRARY FUNCTIONS
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.
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
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.
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.
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.
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);
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.
This application demo behaves
identically to PINGPOLL with the exception that it employs
interrupts. Consequently, it will respond faster to requests.
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.
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.
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 ).
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.
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
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
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
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
send a message to the serial port (for debugging)
buf : pointer to null-terminated string to send
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.
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
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
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
(C) 1999 Cogwheel, Inc. (C) 1999 Tern, Inc. All Rights Reserved
/
|