
                    PKTDRVR Interface for Turbo Pascal 7.0
                                  Version 1.0
                        Written 1993 by Oliver Rehmann
                          (released to public domain)


What is PKTDRVR ?
=================

PKTDRVR is a unit for Turbo Pascal 7.0. It provides an object oriented
interface for crynrware packet drivers.

The Object
==========

       TPKTDRVR = OBJECT(TOBJECT)

private           { member variables }
                  pktInt         : Integer;
                  pktHandle      : Integer;
                  pktRecvHandler : Pointer;
                  pktStatus      : TPKTSTATUS;
                  pktError       : Byte;
                  pktRegs        : Registers;

                  pktAccessInfo  : TACCESSTYPE;

                  PROCEDURE   TestForPktDriver;

public            { member functions }

                  CONSTRUCTOR Init(IntNo : Integer);
                  DESTRUCTOR  Done; VIRTUAL;

                  PROCEDURE   ScanForPktDriver;
                  FUNCTION    GetStatus                          : TPKTSTATUS;
                  FUNCTION    GetError                           : Byte;
                  FUNCTION    GetHandle                          : Word;

                  PROCEDURE   GetAccessType   (VAR pktAccessType : TACCESSTYPE);
                  PROCEDURE   DriverInfo      (VAR pktInfo       : TDRVRINFO  );

                  PROCEDURE   AccessType      (VAR pktAccessType : TACCESSTYPE);
                  PROCEDURE   ReleaseType;
                  PROCEDURE   TerminateDriver;

                  PROCEDURE   GetAddress      (Buffer : Pointer;BufLen : Word; VAR BufCopied : Word);
                  PROCEDURE   ResetInterface;
                  PROCEDURE   GetParameters   (VAR pktParams : TPKTPARAMS);

                  PROCEDURE   SendPkt         (Buffer : Pointer;BufLen : Word );
                  PROCEDURE   As_SendPkt      (Buffer : Pointer;BufLen : Word;Upcall : Pointer     );

                  PROCEDURE   SetRCVmode      (Mode   : Word);
                  FUNCTION    GetRCVmode              : Word;

                  PROCEDURE   SetMulticastList(VAR mcList : Pointer; VAR mcLen : Word);
                  PROCEDURE   GetMulticastList(VAR mcList : Pointer; VAR mcLen : Word);

                  PROCEDURE   GetStatistics   (VAR pktStatistics : TSTATISTICS       );
                  PROCEDURE   SetAddress      (Address : Pointer; VAR AddrLen  : Word);

                  END;

The object provides all functions found in the packet driver specification
1.09 from FTP. The functions will not be explained here.
If more information is needed, consult the file PACKET_D.109 delivered with 
Crynrware packet drivers.

The most important advantage is that you don't have to pass the handle and
the packet driver interrupt to the different functions. These variable
are member of the object TPKTDRVR.

Sending and receiving packets
=============================

IEEE 802.3 (Ethernet) Frame

Ŀ
 7 Bytes   1 Byte  6 Bytes  6 Bytes  2 Bytes  46...1500 Bytes  4 Bytes 
 Preamble    SFD   Dest.    Source   Length   of Data            FCS   
                   Address  Address  (Type)   (at least 46 B)          


                                                                   
                    
                     That is what you have to send.
                     That is what you receive.

If you send a packet (SendPkt-Function,As_SendPkt-Function) you have to
pass a buffer to the function.

BUFFER
------
0                     6                      12              14
Ĵ...Ŀ
 6 Bytes dest. addr.  6 Bytes source addr.  Length (Type)  Da...ta     
Ĵ...

The Length of an Ethernet packet
--------------------------------

The maximum length of an ethernet packet is 1518 bytes

  6 bytes (dest) + 6 bytes (src) + 2 bytes (length) + 46-1500 bytes (data) +
  4 bytes (FCS) = 1518 bytes

  This is because every station must be able to detect a collision on the
  network. (Defined for a network with 2500 meters; 5 segments of 500 meters
  connected to each other with four repeaters)

  A packet must be at least 64 bytes which means 46 or more bytes of data.
  If your data packet is smaller than 46 bytes you have to fill it up with
  dummy bytes.

  The following record definition is my favourite data structure to store
  packets received from packet driver.

  CONST EthernetPacketLen = 1520;

  TYPE
        TPacketBuffer     = RECORD
                              DataLen : Word; { Contents of register CX }
                              Data    : Array[00..EthernetPacketLen-1] of Byte;
                            END;

The receiver
============

When receiving frames using packet drivers you have to write a receiver 
function.

For each arriving frame this function is called twice. First, the packet
driver calls the function to get a pointer to a free memory block. The
second call signals completion; that means the frame has been copied to your
buffer.

  1. Packet driver calls your function with AX = 0.

     AX = 0
     CX = Size of frame including dest. address, source address and type
          field. That means size of data + 18 Bytes.

       - You have to pass back a pointer in ES:DI to the packet driver
         if you want to grab the packet otherwise ES:DI must be set to
         0000:0000.

         ATTENTION ! Because you are in an interrupt (IRQ of LAN Adapter)
                     you can't call any DOS-Functions (Int 21h) !!!!
                     So allocate memory when your program starts. An
                     array as an example.

  2. Packet driver calls your function with AX = 1

     AX = 1

       - This signals completion. Packet driver has copied the frame
         including the header (6+6+2 Bytes) to your buffer.

Make sure your receiver doesn't perform any timeconsuming work !

Example of a receiver
---------------------

VAR LastFrame   : Array[00..1520] of Byte; { Buffer for arriving frames           }
    FrameCount  : Word;                    { Flag to signal if buffer is occupied }
    FrameLength : Word;                    { Length of the last frame arrived     }

{$S-}PROCEDURE pktReceiver; ASSEMBLER;
ASM
  PUSH AX                      { Push registers onto stack }
  PUSH BX
  PUSH CX
  PUSH DX


  CMP  AX,0001                 { AX=1 means frame copied }
  JZ   @@FrameCopied
  CMP  AX,0000                 { AX=0 means allocate memory please }
  JZ   @@AllocMemory
  JMP  @@EXIT                  { Invalid register contents for AX so exit}

@@AllocMemory:

  MOV  DX,0                    { ES:DI = 0000:0000, we don't want the packet }
  MOV  ES,DX
  MOV  DI,0                    { We don't grab the packet }

  MOV  DX,SEG FrameCount       { Set correct data segment }
  MOV  DS,DX
  MOV  DX,FrameCount           { Check if buffer is free (FrameCount = 0)    }
  CMP  DX,0

  JNZ  @@Exit                  { buffer is not free ! So exit with ES:DI = 0000:0000 }

  MOV  DX,SEG LastFrame        { Return a valid address for storage to the           }
  MOV  ES,DX                   { packet driver. ES:DI = Seg(LasFrame):Ofs(LastFrame) }
  MOV  DI,OFFSET LastFrame

  MOV  DX,SEG FrameLength
  MOV  DS,DX
  MOV  SI,OFFSET FrameLength
  MOV  WORD PTR DS:[SI],CX     { Store length of frame in FrameLength }

  JMP  @@Exit

@@FrameCopied:

  MOV  DX,SEG FrameCount       { Set correct data segment }
  MOV  DS,DX
  MOV  FrameCount,1            { Set Flag to 1 }

  { Now our buffer is occupied (FrameCount = 1). Our receiver will
    not accept any packets unless FrameCount = 0.
  }

@@Exit:

  POP  DX                      { Pop registers from stack }
  POP  CX
  POP  BX
  POP  AX
END;
{$S+}

I know it is possible to shorten the receiver by not setting the DS register
every time when accessing a variable, but I have written receivers which 
really are much more complex than this one. I could have saved much time and 
many REBOOTS if I had done this from the beginning. (:-)

PROCEDURE ProcessFrame;
BEGIN
  { Do something with the frame }
  .
  .
  .
  FrameCount := 0; { Reset Flag. Now the receiver kann grab the next frame }
END;


Initializing the packet driver
==============================

There are some important steps you have to do before the packet driver
is ready to send and receive packets.

VAR pktDriver      : TPKTDRVR;    { Declare an instance of the packet driver object.             }
    pktDriverInfo  : TDRVRINFO;   { Declare a record for retrieving information from the driver. }
    pktDriverAccess: TACCESSTYPE; { Declare a record for accessing the driver.                   }

PROCEDURE InitPktDriver;
BEGIN
  WriteLn('Initializing packet driver....');
  IF (pktDriver.GetStatus <> INITIALIZED) THEN
  BEGIN
    WriteLn('Could not initialze packet driver...');
    WriteLn('Aborting...');
    Halt($FF);
  END
  ELSE
  BEGIN
    { Packet driver found. Show some information }
    WriteLn('Packet driver found:');
    pktDriver.DriverInfo(pktDriverInfo);

    WriteLn('Name    = ',StrPas(pktDriverInfo.PName));
    WriteLn('Version = ',pktDriverInfo.Version);
    WriteLn('IF-Type = ',pktDriverInfo.Type_);
    Write  ('Func    = ');

    CASE pktDriverInfo.Functionality OF
      01 : WriteLn('Basic functions present.');
      02 : WriteLn('Basic & extended functions present.');
      05 : WriteLn('Basic & high-performance functions present.');
      06 : WriteLn('Basic, high-performance & extended functions present.');
    END;
  END;

  { Fill in information used for accessing packet driver }
  WITH pktDriverAccess DO
  BEGIN
    if_class  := pktDriverInfo.Class;
    if_type   := ANYTYPE;
    if_number := 0;
    type_     := @TypeField;
    typelen   := 0;
    receiver  := @pktReceiver; { receiver procedure }
  END;

  { Access packet driver }
  pktDriver.AccessType(pktDriverAccess);
  WriteLn('Handle  = ',pktDriver.GetHandle);
  WriteLn;

  { Setting packet driver to promiscuous mode
    Do the following step only if you want to grab all the packets
    on your network. Attention ! There can be heavy traffic on
    an Ethernet ! Be prepared for a hughr amount of frames arriving.
  }
  pktDriver.SetRCVmode(6);
END;

PROCEDURE TerminatePktDriver;
BEGIN
  pktDriver.ReleaseType;
END;


{ Main program entry }
BEGIN
  InitPktDriver;
  REPEAT
    IF (FrameCount = 1) THEN
      ProcessFrame;
  UNTIL KeyPressed;
  TerminatePktDriver;
END.


For more programming details see the examples TRAFGEN and TRAFMON enclosed
in this package.

For more details on MAC (Medium Access Control) layer problems see the
file MAC.TXT enclosed in this packet.


Conditions
==========

This packet is released to public domain. You can give copies to your friends or
place it on BBSes as long as it remains in its original state.

If you write comercial software based on this interface you are requested
to duplicate the headers and the Copyright indications.

The author can not be held responsible for any damages resulting from the use
of this software !

The author can be reached through the following e-mail adresses:


Internet  : 100016.732@compuserve.com
            CZ8OR@zcvx00.decnet.ascom.ch

CompuServe: ID: 100016,732

