unit CoPktDrv;

  {==========================================================}
  {                                                          }
  {  unit CoPktDrv                   Ver 12/09/2001          }
  {                                                          }
  {  Knihovna pro komunikaci v siti ethernet                 }
  {  pomoci rozhrani Packet Driver                           }
  {                                                          }
  {  (C)2001, SofCon, Stresovicka 49, 160 00 Praha 6         }
  {           Petr Cernin                                    }
  {                                                          }
  {==========================================================}


interface

{$G+}

{ DEFINE PKT_DEBUG}
{ DEFINE PKT_HEAP_DEBUG}

uses
  Objects,
  CoBase,
  CoEthDef;

{const

{  ETH_IOCTL_GETMACADDRESS  = 256; }

const

  { Chyby specificke pro zarizeni TCoPktDrv }

  CST_PKTDRV_ERR_CANTSEND   = 192;
  CST_PKTDRV_ERR_INTNUMBER  = 193;
  CST_PKTDRV_ERR_ACCESSTYPE = 194;
  CST_PKTDRV_ERR_INSTANCE   = 195;

const

  COPT_PKTDRV_INTNUMBER     = 256;  { Cislo sw preruseni sluzby packet driver }
  COPT_PKTDRV_RECVBUFF      = 257;  { Velikost bufferu pro prijate pakety     }
  COPT_PKTDRV_RECVQUEUE     = 258;  { Max. delka fronty s prijatymi pakety    }

type

  {-------------------------------}
  { TPktDriverInfo
  {---}
  {
  { Parametry funkce PktDriverInfo
  }

  TPktDriverInfo = record
    version   : Word;
    if_class  : Byte;
    if_Type   : Word;
    if_Number : Byte;
    name      : PChar;
    funct     : Byte;            { Functionality }
  end;

  {-------------------------------}
  { TCoPktHeap
  {---}
  {
  { Maly heap alokovany na globalnimu heapu. Oproti globalnimu heapu ma
  { dve vyhody:
  {   1) omezenou velikost
  {   2) omezenou fragmentaci
  {
  { Heap je synchronizovany
  }

  PCoPktHeap = ^TCoPktHeap;
  TCoPktHeap = object( TCoHeap )
  public
    HeapData : Pointer;   { Ukazatel na data heapu (na globalnim heapu) }
    HeapSize : Word;      { Velikost heapu                              }

    { Alokovani dat objektu, pocet referenci = 1 }
    constructor Init( AHeapSize: Word );

    { Alokovani bloku na heapu }
    function  AllocPtr( ASize: Word ): Pointer; virtual;
    { Uvolneni bloku z heapu }
    procedure FreePtr( AData: Pointer; ASize: Word ); virtual;

  private
    { Zruseni objektu, uvolneni alokovane pameti }
    destructor  Done; virtual;
  end;


  {--------------------------------------------------------------------}
  { TCoPktDrv                                                          }
  {--------------------------------------------------------------------}

  PCoPktDrv = ^TCoPktDrv;
  TCoPktDrv = object( TCoDevice )
  public{private}
  { Nastaveni zarizeni }

    Pkt_IntNumber       : Byte;  { Cislo interruptu sluzby PktDriver }
    Pkt_Handler         : Pointer;
    Pkt_Handle          : Word;  { Cislo handle                      }

  { Aktualni stav zarizeni }

    Pkt_Bound         : Boolean;
    Pkt_Connected     : Boolean;
    Pkt_LocalAddress  : TEthAddress;
    Pkt_RemoteAddress : TEthAddress;

{    Pkt_WaitingReq    : PCoRequest;}

    Pkt_RecvQueue     : TCoPacketQueue;  { Fronta prijatych paketu }

    { Zjistuje, zda na danem preruseni lezi Packet Driver }
    function  PktIntNumberValid( AIntNumber: Byte ): Boolean;
    { Inicializace podle parametru Settigs, provede reset a inicializaci,
      pokud vse probehne, pak vraci True, vysilani a prijem je zablokovany }
    function  PktInitialize: Boolean;
    { Vraci informace o packet driveru }
    function  PktDriverInfo( var DriverInfo: TPktDriverInfo): Byte;
    { Registrace callback funkce pro prijem v packet driveru }
    function  PktAccessType( IfClass: Byte; IfType: Word; IfNumber: Byte;
                TypeSpec: Pointer; TypeLen: Word; Receiver: Pointer;
                var Handle: Word ): Byte;
    { Odregistrovani callback funkce pro prijem }
    function  PktReleaseType( Handle: Word ): Byte;
    { Odeslani paketu pomoci packet driveru }
    function  PktSend( Buffer: Pointer; Length: Word ): Byte;
    { Ukonceni packet driveru }
    function  PktTerminate( Handle: Word ): Byte;
    { Cteni aktualni MAC adresy sitoveho adapteru }
    function  PktGetAddress( Handle: Word; Buffer: Pointer; Length: Word ): Byte;
    { Reset sitoveho adapteru }
    function  PktResetInterface( Handle: Word ): Byte;
    { Funkce prida pred data ramce hlavicku odpovidajici
      ARemoteAddress }
    function  PktAddEthHeader( const ARemoteAddress: TEthAddress;
                APacket: PCoPacket ): Boolean;
    { Callback funkce volana v okamziku, kdy packet driver ma
      paket pripraveny ke zkopirovani. Funkce musi naalokovat buffer
      o velikost ALen a vratit na nej ukazatel }
    function  PktAlloc( ALen: Word ): Pointer;
    { Callback funkce volana v okamziku, kdy packet driver prekopiroval
      data paketu do bufferu pripraveneho funkci PktAlloc. Funkce zaradi
      zaradit paket do fronty Pkt_RecvQueue }
    procedure PktEnqueue( ABuffer: Pointer; ALen: Word );
    { Funkce volana v MiniportTick, vrati ukazatel na prijaty paket
      nebo nil, pokud nebyl prijat zadny paket }
    function  PktFrameReceive: PCoPacket;

    function  PktSetOptString( AParams: PString ): TCoStatus;

    function  PktGetOptString( ABuffer: PString; ABuffSize: Word ): TCoStatus;

    { Nastaveni cisla preruseni, na kterem je povesen packet driver }
    function  PktSetIntNumber( AIntNumber: Byte ): TCoStatus;
    { Nastaveni velikost prijimaciho bufferu }
    function  PktSetRecvBuff( ASize: Word ): TCoStatus;
    { Nasteveni max. delky prijimaci fronty }
    function  PktSetRecvQueue( ACount: Integer ): TCoStatus;

  public

    { Implementace funkci TCoDevice }

    function  MiniportInit( var ADevParams: TCoDeviceParameters ):
                Boolean; virtual;

    procedure MiniportCancel( ACancelAll: Boolean ); virtual;

    function  MiniportSendPacket( APacket: PCoPacket; AFlags: Word;
                AAddress: PCoAddress; AHandle: PCoRequest ):
                TCoStatus; virtual;

    function  MiniportIoctl( AClassId: Word; AFlags: Word; ACode: Word;
                ABuffer: Pointer; ABuffSize: Word; AHandle: PCoRequest ):
                TCoStatus; virtual;

    function  MiniportSendBuffer( ABuffer: Pointer; ABuffSize: Word;
                AFlags: Word; AAddress: PCoAddress;
                AHandle: PCoRequest ): TCoStatus; virtual;

    procedure MiniportTick; virtual;

    function  MiniportConnect( AAddress: PCoAddress; AFlags: Word;
                AHandle: PCoRequest ): TCoStatus; virtual;

    function  MiniportDisconnect( AFlags: Word; AHandle: PCoRequest ):
                TCoStatus; virtual;

    function  MiniportBind( AAddress: PCoAddress; AFlags: Word;
                AHandle: PCoRequest ): TCoStatus; virtual;

    function  MiniportUnbind( AFlags: Word; AHandle: PCoRequest ):
                TCoStatus; virtual;

    function  MiniportSetOption( AClassId: Word; AOption: Word;
                ABuffer: Pointer; ABuffSize: Word; AFlags: Word;
                AHandle: PCoRequest ): TCoStatus; virtual;

    function  MiniportGetOption( AClassId: Word; AOption: Word;
                ABuffer: Pointer; ABuffSize: Word; AFlags: Word;
                AHandle: PCoRequest ): TCoStatus; virtual;

  end;


implementation

uses
  Dos,          { kvuli GetIntVec }
  NumToStr;

const

  PktDrvName  : String[6] = 'PKTDRV';

  PktDrvClass : TCoDeviceClass =
   ( ClassName : @PktDrvName;
     ClassId   : DEV_CLASS_NIC;
     VmtLink   : Ofs(TypeOf(TCoPktDrv)^);
     Init      : @TCoPktDrv.Init
   );

const

  CANCELED_REQUEST = PCoRequest(1);


{----------------------------------------------------------}
{ Struktura heapu                                          }
{----------------------------------------------------------}
{
       UP                UP                UP
|-----|-----------------|-----------------|------------|
| free|11   used        |00    free       |11  used    |
|-----|-----------------|-----------------|------------|

  U ... HEAP_FLAG_USED
  P ... HEAP_FLAG_PREV_FREE

  pokud je nastaven HEAP_FLAG_PREV_FREE je predchozi polozka
  volna a posledni 4 bajty predchozi polozky ukazuji na jeji
  zacatek. Takto je mozne jednoduse spojovat volne bloky
  pri volani funkce .Free()

}

const { THeapLink.Flags }

  HEAP_FLAG_USED      = 1;  { Polozka heapu je pouzita     }
  HEAP_FLAG_PREV_FREE = 2;  { Predchozi polozka je pouzita }

type

  PHeapLink = ^THeapLink;
  THeapLink = record
    Flags : Word;        { Priznaky polozky      }
    Next  : PHeapLink;   { Dalsi polozka         }
    Size  : Word;        { Velikost (pouze data) }
  end;


const

  { Chybove kody Packet Driveru }

  E_NOERROR         = 0;
  E_BAD_HANDLE	    = 1;
  E_NO_CLASS        = 2;
  E_NO_TYPE         = 3;
  E_NO_NUMBER       = 4;
  E_BAD_TYPE        = 5;
  E_NO_MULTICAST    = 6;
  E_CANT_TERMINATE  = 7;
  E_BAD_MODE        = 8;
  E_NO_SPACE        = 9;
  E_TYPE_INUSE      = 10;
  E_BAD_COMMAND     = 11;
  E_CANT_SEND       = 12;
  E_CANT_SET        = 13;
  E_BAD_ADDRESS     = 14;
  E_CANT_RESET      = 15;

  { Cisla sluzeb }

  S_DRIVER_INFO	    = 1;
  S_ACCESS_TYPE     = 2;
  S_RELEASE_TYPE    = 3;
  S_SEND_PKT        = 4;
  S_TERMINATE       = 5;
  S_GET_ADDRESS     = 6;
  S_RESET_IFACE     = 7;



var

  g_PktDrv           : PCoPktDrv;
  g_PktRecvStack     : Pointer;
  g_PktRecvStackSize : Word;
  g_PktRecvStackTop  : Pointer;
  g_PktSavedStack    : Pointer;


{----------------------------------------------------------}
{ TCoPktDrv.RegisterPktDrv
{---
{ !@#
{---
}

function RegisterPktDrv( APktDrv: PCoPktDrv ): Boolean;
begin
  PUSHFCLI;
  if g_PktDrv = nil then
  begin
    g_PktDrv := APktDrv;
    POPF;
    APktDrv^.AddRef;
    RegisterPktDrv := True;
    g_PktRecvStackSize := 4096;
    GetMem( g_PktRecvStack, g_PktRecvStackSize );
    g_PktRecvStackTop := g_PktRecvStack;
    Inc( PtrRec(g_PktRecvStackTop).Ofs, g_PktRecvStackSize - 1 );
  end
  else
    RegisterPktDrv := False;
  POPF;
end;


{----------------------------------------------------------}
{ TCoPktDrv.UnregisterPktDrv
{---
{ !@#
{---
}

{!@# Unregister se nikde nevola }
procedure UnregisterPktDrv( APktDrv: PCoPktDrv );
var
  P : PCoPktDrv;
begin
  PUSHFCLI;
  if g_PktDrv = APktDrv then
  begin
    g_PktDrv := nil;
    POPF;
    FreeMem( g_PktRecvStack, g_PktRecvStackSize );
    APktDrv^.Release;
  end
  else
    POPF;
end;


{----------------------------------------------------------}
{ TCoPktDrv.PktAlloc
{---
{ !@#
{---
}

function PktAlloc( AHandle: Word; ALen: Word ): Pointer;
var
  P : PCoPktDrv;
begin
  PUSHFCLI;
  P := g_PktDrv;
  POPF;
  if (P <> nil) and (P^.Pkt_Handle = AHandle) then
  begin
    PktAlloc := P^.PktAlloc( ALen );
  end
  else
    PktAlloc := nil;
end;

{----------------------------------------------------------}
{ TCoPktDrv.PktEnqueue
{---
{ !@#
{---
}

procedure PktEnqueue( AHandle: Word; Buffer: Pointer; Len: Word );
var
  P : PCoPktDrv;
begin
  PUSHFCLI;
  P := g_PktDrv;
  POPF;
  if (P <> nil) and (P^.Pkt_Handle = AHandle) then
  begin
    P^.PktEnqueue( Buffer, Len );
  end;
end;


{----------------------------------------------------------}
{ __PktReceiver
{---
{ !@#
{---
}

procedure __PktReceiver; far; assembler;
asm
        PUSH  DS

        MOV   DX, SEG @DATA
        MOV   ES, DX

        MOV   WORD PTR ES:[g_PktSavedStack], SP
        MOV   DX, SS
        MOV   WORD PTR ES:[g_PktSavedStack + 2], DX

        MOV   DX, WORD PTR ES:[g_PktRecvStackTop + 2]
        MOV   SS, DX
        MOV   SP, WORD PTR ES:[g_PktRecvStackTop]


        TEST  AX, AX  { AX = FLAGS }
        JNZ   @@1

        { Alokace bufferu }


        PUSH  BX      { Handle }
        PUSH  CX      { Len    }

        MOV   AX, SEG @DATA
        MOV   DS, AX

        CALL  PktAlloc

        MOV   ES, DX
        MOV   DI, AX

        JMP   @@2
@@1:

        PUSH  BX      { Handle }
        PUSH  DS      { Data   }
        PUSH  SI
        PUSH  CX      { Len    }

        MOV   AX, SEG @DATA
        MOV   DS, AX

        CALL  PktEnqueue

@@2:

        MOV   DX, WORD PTR [g_PktSavedStack + 2]
        MOV   SS, DX
        MOV   SP, WORD PTR [g_PktSavedStack]

        POP   DS
end;


{=======================================================================}
{ TCoPktHeap                                                            }

{----------------------------------------------------------}
{ TCoPktHeap.Init
{---
{ Inicializace objektu heapu
{---
}

constructor TCoPktHeap.Init( AHeapSize: Word );
begin
  inherited Init;

  HeapSize := AHeapSize;
  GetMem( HeapData, HeapSize );
  if HeapData = nil then Fail;

  with PHeapLink(HeapData)^ do
  begin
    Flags := 0;
    Next  := nil;
    Size  := HeapSize - SizeOf(THeapLink);
  end;
end;

{----------------------------------------------------------}
{ TCoPktHeap.Done
{---
{ Uvolneni heapu, funkce je typu private
{ volat release
{---
}

destructor TCoPktHeap.Done;
begin
  if HeapData <> nil then FreeMem( HeapData, HeapSize );
end;

{----------------------------------------------------------}
{ TCoPktHeap.AllocPtr
{---
{ Alokovani bloku o velikosti ASize na heapu, pokud
{ blok neni mozne naalokovat funkce vraci nil
{---
}

function TCoPktHeap.AllocPtr( ASize: Word ): Pointer;
var
  P       : PHeapLink;
  NewItem : PHeapLink;
begin
  PUSHFCLI;
  P := PHeapLink(HeapData);

  {$ifdef PKT_HEAP_DEBUG}
  WriteLn( 'HeapAlloc: ',  ASize );
  {$endif}

  while P <> nil do
  begin
    if (P^.Flags and HEAP_FLAG_USED = 0) and (P^.Size >= ASize) then Break;
    P := P^.Next;
  end;

  if P <> nil then
  begin
    AddRef;

    if ASize < P^.Size then
    begin
      NewItem := P;
      Inc( PtrRec( NewItem ).Ofs, ASize + SizeOf( THeapLink ));
      NewItem^.Flags := 0;
      NewItem^.Next  := P^.Next;
      NewItem^.Size  := P^.Size - ASize - SizeOf( THeapLink );
      P^.Next  := NewItem;
      P^.Size  := ASize;
    end;

    P^.Flags := HEAP_FLAG_USED;
    POPF;

    Inc( PtrRec( P ).Ofs, SizeOf( THeapLink ));

    {$ifdef PKT_HEAP_DEBUG}
    WriteLn( 'at ', PtrToStr( P ) );
    {$endif}

    AllocPtr := P;

  end
  else begin
    AllocPtr := nil;
  end;

end;


{----------------------------------------------------------}
{ TCoPktHeap.FreePtr
{---
{ Uvolneni bloku z heapu. Pokud blok byl jiz uvolnen, nebo je
{ neplatny funkce vyvola runtime error 255
{---
}

procedure TCoPktHeap.FreePtr( AData: Pointer; ASize: Word );
type
  PPointer = ^Pointer;
var
  P    : PHeapLink;
  Next : PHeapLink;
begin
  PUSHFCLI;

  {$ifdef PKT_HEAP_DEBUG}
  WriteLn( 'HeapFree: ', PtrToStr( AData ) + ' ', ASize );
  {$endif}

  P := AData;
  Dec( PtrRec( P ).Ofs, SizeOf(THeapLink) );

  if (P <> nil) and (P^.Size = ASize) and
     (P^.Flags and HEAP_FLAG_USED <> 0) then
  begin

    Next := P^.Next;

    if P^.Flags and HEAP_FLAG_PREV_FREE <> 0 then
    begin
      P := PPointer(PChar( P ) - SizeOf( Pointer ))^;
      Inc( ASize, P^.Size + SizeOf( THeapLink ) );
    end;

    if (Next <> nil) and (Next^.Flags and HEAP_FLAG_USED = 0) then
    begin
      Inc( ASize, Next^.Size + SizeOf( THeapLink ) );
      Next := Next^.Next;
    end;

    if Next <> nil then
    begin
      Next^.Flags := Next^.Flags or HEAP_FLAG_PREV_FREE;
    end;

    P^.Flags := 0;
    P^.Next := Next;
    P^.Size := ASize;
    POPF;

    Dec( PtrRec(Next).Ofs, SizeOf( Pointer ) );
    PPointer(Next)^ := P;

    Release;
  end
  else begin
    { Nemelo by nikdy nastavat, poruseny heap }
    POPF;
    RunError( 255 );
  end;

end;


{=======================================================================}
{ TCoPktDrv                                                             }

{-------------------------------
{ TCoPktDrv.PktInitialize
{---
{ !@#
{---
}

{ Inicializace podle parametru Settigs, provede reset a inicializaci,
  pokud vse probehne, pak vraci True, vysilani a prijem je zablokovany }
function  TCoPktDrv.PktInitialize: Boolean;
begin

  if PktIntNumberValid( Pkt_IntNumber ) then
  begin
    { Reset pkt driveru }

    GetIntVec( Pkt_IntNumber, Pkt_Handler );

    PktInitialize := True;
  end
  else
    PktInitialize := False;

end;


{-------------------------------
{ TCoPktDrv.PktAddEthHeader
{---
{ !@#
{---
}

function TCoPktDrv.PktAddEthHeader( const ARemoteAddress: TEthAddress;
  APacket: PCoPacket ): Boolean;
var
  DataSize : Word;

  H1 : PEthIIHeader;
  H2 : PEthLLCHeader;
  H3 : PEthSnapHeader;

begin
  PktAddEthHeader := False;

  case ARemoteAddress.Format of

    ETH_FORMAT_ETHII:
    begin
      H1 := APacket^.AddHeader( SizeOf( TEthIIHeader ) );

      if H1 = nil then Exit;

      with H1^ do
      begin
        DestAddr := ARemoteAddress.MAC;
        SrcAddr  := Pkt_LocalAddress.MAC;
        Protocol := Swap( ARemoteAddress.Protocol );
      end;
    end;

    ETH_FORMAT_LLC:
    begin
      DataSize := APacket^.DataSize;

      H2 := APacket^.AddHeader( SizeOf( TEthLLCHeader ) );

      if H2 = nil then Exit;

      with H2^ do
      begin
        DestAddr := ARemoteAddress.MAC;
        SrcAddr  := Pkt_LocalAddress.MAC;
        Length   := Swap( DataSize );
        DSAP     := ARemoteAddress.DSAP;
        SSAP     := ARemoteAddress.SSAP;
        Ctrl     := $03;
      end;
    end;

    ETH_FORMAT_SNAP:
    begin
      DataSize := APacket^.DataSize;

      H3 := APacket^.AddHeader( SizeOf( TEthSnapHeader ) );

      if H3 = nil then Exit;

      with H3^ do
      begin
        DestAddr := ARemoteAddress.MAC;
        SrcAddr  := Pkt_LocalAddress.MAC;
        Length   := Swap( DataSize );
        DSAP     := $AA;
        SSAP     := $AA;
        Ctrl     := $03;
        Oui[0]   := 0;
        Oui[1]   := 0;
        Oui[2]   := 0;
        Protocol := Swap( ARemoteAddress.Protocol );
      end;
    end;

  end;

  PktAddEthHeader := True;

end;

{-------------------------------
{ TCoPktDrv.PktAlloc
{---
{ !@#
{---
}

function TCoPktDrv.PktAlloc( ALen: Word ): Pointer;
var
  Pkt: PCoPacket;
begin

  Pkt := CoAllocPacket( 0, ALen, 0, hptRemote );

  if Pkt <> nil then
  begin
    PktAlloc := Pkt^.DataPtr;
  end
  else
    PktAlloc := nil;

{  Pkt := Pkt_Heap^.Alloc( CoPacketSize( 0, ALen, 0 ) );
  if Pkt <> nil then
  begin
    CoInitPacket( Pkt, 0, ALen, 0, __PktHeapFree, Pkt_Heap );
    PktAlloc := Pkt^.DataPtr;
  end
  else
    PktAlloc := nil;}
end;

{-------------------------------
{ TCoPktDrv.PktEnqueue
{---
{ !@#
{---
}

procedure TCoPktDrv.PktEnqueue( ABuffer: Pointer; ALen: Word );
var
  Pkt : PCoPacket;
begin
  Pkt := ABuffer;
  Dec( PtrRec(Pkt).Ofs, SizeOf( TCoPacket ) );

  if Pkt^.DataSize = ALen then
  begin
    Pkt_RecvQueue.InsertTail( Pkt );
    CoReleasePacket( Pkt );
  end
  else
    RunError( 255 );
end;

{-------------------------------
{ TCoPktDrv.PktFrameReceive
{---
{ Funkce volana v MiniportTick, vraci
{ ukazatel na prijaty paket nebo nil
{---
}

function TCoPktDrv.PktFrameReceive: PCoPacket;
var
  Packet  : PCoPacket;
  Eth2Hdr : PEthIIHeader;
  IeeeHdr : PEthSnapHeader;
begin

  Packet := Pkt_RecvQueue.RemoveHead;

  if Packet <> nil then
  begin
    {$IFDEF PKT_DEBUG}
    WriteLn( 'PktFrameReceive() frame received' );
    {$ENDIF}

    { Paket se podarilo naalokovat }

    Eth2Hdr := PEthIIHeader( Packet^.DataPtr );

    if Swap(Eth2Hdr^.Protocol) >= $800 then
    begin
      {$IFDEF PKT_DEBUG}
      WriteLn( 'EthII header' );
      {$ENDIF}


      { Zahlavi je typu Ethernet2 }
      with PEthAddress(@Packet^.Address)^ do
      begin
        Size      := SizeOf( TEthAddress );
        Mac       := Eth2Hdr^.SrcAddr;
        Format    := ETH_FORMAT_ETHII;
        Protocol  := Swap(Eth2Hdr^.Protocol);
      end;

      Packet^.Header := Packet^.RemoveHeader( SizeOf(TEthIIHeader) );
    end
    else begin
      { Zahlavi je pravdepodobne typu IEEE 802.3 }
      IeeeHdr := PEthSnapHeader( Eth2Hdr );

      if (IeeeHdr^.DSAP = IeeeHdr^.SSAP) and (IeeeHdr^.Ctrl = $03) then
      begin
        {$IFDEF PKT_DEBUG}
        WriteLn( 'SNAP header' );
        {$ENDIF}

        { Je to IEEE }

        if IeeeHdr^.DSAP = $AA then { SNAP }
        begin
          with PEthAddress(@Packet^.Address)^ do
          begin
            Size      := SizeOf( TEthAddress );
            Mac       := IeeeHdr^.SrcAddr;
            Format    := ETH_FORMAT_SNAP;
            Protocol  := Swap( IeeeHdr^.Protocol );
          end;

          Packet^.Header :=
            Packet^.RemoveHeader( SizeOf(TEthSnapHeader) );
        end
        else begin
          {$IFDEF PKT_DEBUG}
          WriteLn( 'LLC header' );
          {$ENDIF}

          { IEEE 802.2 bez SNAP }

          with PEthAddress(@Packet^.Address)^ do
          begin
            Size      := SizeOf( TEthAddress );
            Mac       := IeeeHdr^.SrcAddr;
            Format    := ETH_FORMAT_LLC;
            DSAP      := IeeeHdr^.DSAP;
            SSAP      := IeeeHdr^.SSAP;
          end;

          Packet^.Header :=
            Packet^.RemoveHeader( SizeOf(TEthLLCHeader) );
        end;

      end
      else begin
        { Neznamy typ paketu }

        CoReleasePacket( Packet );
        Packet := nil;
      end;
    end;

  end;

  if Pkt_Connected then
  begin
    with PEthAddress(@Packet^.Address)^ do
    begin
      {!@# porovnat lokalni adresu s adresou prichoziho ramce,
       pokud nesouhlasi, pak zahodit paket }
    end;
  end;

  PktFrameReceive := Packet;

end;

{-------------------------------
{ TCoPktDrv.PktIntNumberValid
{---
{ Funkce zjistuje zda na danem preruseni je povesen packet driver
{---
}

function TCoPktDrv.PktIntNumberValid( AIntNumber: Byte ): Boolean;
type

  PPacketDriverEntry = ^TPacketDriverEntry;
  TPacketDriverEntry = packed record
    Jump : array[0..2] of Byte;
    Sign : array[0..7] of Char;
  end;

var
  Entry: PPacketDriverEntry;

begin
  if (AIntNumber >= $60) and (AIntNumber <= $80) then
  begin
    GetIntVec( AIntNumber, Pointer(Entry) );
    PktIntNumberValid := Entry^.Sign = 'PKT DRVR';
  end
  else
    PktIntNumberValid := False;
end;

{-------------------------------
{ TCoPktDrv.PktDriverInfo
{---
{ Vraci informace o packet driveru
{---
}

function TCoPktDrv.PktDriverInfo(
  var DriverInfo: TPktDriverInfo): Byte; assembler;

var
  PktHandler : Pointer;
asm
         LES   DI, Self
         MOV   AX, WORD PTR ES:[DI + 0].Pkt_Handler
         MOV   WORD PTR [PktHandler + 0], AX
         MOV   AX, WORD PTR ES:[DI + 2].Pkt_Handler
         MOV   WORD PTR [PktHandler + 2], AX

         PUSH  DS
         MOV   AX, 01FFh

         PUSHF
         CALL  DWORD PTR [PktHandler]

         LES   DI, DriverInfo
         MOV   ES:[DI].TPktDriverInfo.version, BX
         MOV   ES:[DI].TPktDriverInfo.if_class, CH
         MOV   ES:[DI].TPktDriverInfo.if_type, DX
         MOV   ES:[DI].TPktDriverInfo.if_number, CL
         MOV   WORD PTR ES:[DI].TPktDriverInfo.name, SI
         MOV   SI, DS
         MOV   WORD PTR ES:[DI + 2].TPktDriverInfo.name, SI
         MOV   ES:[DI].TPktDriverInfo.funct, AL
         MOV   AL, 0
         JNC   @@1
         MOV   AL, DH
  @@1:
         POP   DS
end;

{-------------------------------
{ TCoPktDrv.PktAccessType
{---
{ Nastaveni callbacku v packet driveru
{---
}


function TCoPktDrv.PktAccessType( IfClass: Byte; IfType: Word;
  IfNumber: Byte; TypeSpec: Pointer; TypeLen: Word; Receiver: Pointer;
  var Handle: Word ): Byte; assembler;
var
  PktHandler : Pointer;
asm
         LES   DI, Self
         MOV   AX, WORD PTR ES:[DI + 0].Pkt_Handler
         MOV   WORD PTR [PktHandler + 0], AX
         MOV   AX, WORD PTR ES:[DI + 2].Pkt_Handler
         MOV   WORD PTR [PktHandler + 2], AX

         PUSH  DS
         MOV   AH, S_ACCESS_TYPE
         MOV   AL, IfClass
         MOV   BX, IfType
         MOV   DL, IfNumber
         LDS   SI, TypeSpec
         MOV   CX, TypeLen
         LES   DI, Receiver

         PUSHF
         CALL  DWORD PTR [PktHandler]

         LES   DI, Handle
         MOV   ES:[DI], AX
         MOV   AL, 0
         JNC   @@1
         MOV   AL, DH
  @@1:
         POP   DS
end;

{-------------------------------
{ TCoPktDrv.PktReleaseType
{---
{ Uvolneni callbacku v packet driveru
{---
}


function TCoPktDrv.PktReleaseType( Handle: Word ): Byte; assembler;
var
  PktHandler : Pointer;
asm
         LES   DI, Self
         MOV   AX, WORD PTR ES:[DI + 0].Pkt_Handler
         MOV   WORD PTR [PktHandler + 0], AX
         MOV   AX, WORD PTR ES:[DI + 2].Pkt_Handler
         MOV   WORD PTR [PktHandler + 2], AX

         MOV   AH, S_RELEASE_TYPE
         MOV   BX, Handle

         PUSHF
         CALL  DWORD PTR [PktHandler]

         MOV   AL, 0
         JNC   @@1
         MOV   AL, DH
  @@1:
end;

{-------------------------------
{ TCoPktDrv.PktSend
{---
{ Odeslani buffeu dat
{---
}

function TCoPktDrv.PktSend( Buffer: Pointer; Length: Word ): Byte; assembler;
var
  PktHandler : Pointer;
asm
         LES   DI, Self
         MOV   AX, WORD PTR ES:[DI + 0].Pkt_Handler
         MOV   WORD PTR [PktHandler + 0], AX
         MOV   AX, WORD PTR ES:[DI + 2].Pkt_Handler
         MOV   WORD PTR [PktHandler + 2], AX

         PUSH  DS
         LDS   SI, Buffer
         MOV   CX, Length
         MOV   AH, S_SEND_PKT;

         PUSHF
         CALL  DWORD PTR [PktHandler]

         MOV   AL, 0
         JNC   @@1
         MOV   AL, DH
@@1:
         POP   DS
end;


{-------------------------------
{ TCoPktDrv.PktTerminate
{---
{ Uvoleni ovladace z pameti
{---
}


function TCoPktDrv.PktTerminate( Handle: Word ): Byte; assembler;
var
  PktHandler : Pointer;
asm
         LES   DI, Self
         MOV   AX, WORD PTR ES:[DI + 0].Pkt_Handler
         MOV   WORD PTR [PktHandler + 0], AX
         MOV   AX, WORD PTR ES:[DI + 2].Pkt_Handler
         MOV   WORD PTR [PktHandler + 2], AX

         MOV   AH, 5
         MOV   BX, Handle

         PUSHF
         CALL  DWORD PTR [PktHandler]

         MOV   AL, 0
         JNC   @@1
         MOV   AL, DH
@@1:
end;

{-------------------------------
{ TCoPktDrv.PktGetAddress
{---
{ Vraci MAC adresu sitove karty
{---
}


function TCoPktDrv.PktGetAddress( Handle: Word; Buffer: Pointer; Length: Word ): Byte; assembler;
var
  PktHandler : Pointer;
asm
         LES   DI, Self
         MOV   AX, WORD PTR ES:[DI + 0].Pkt_Handler
         MOV   WORD PTR [PktHandler + 0], AX
         MOV   AX, WORD PTR ES:[DI + 2].Pkt_Handler
         MOV   WORD PTR [PktHandler + 2], AX

         MOV   AH, S_GET_ADDRESS
         MOV   BX, Handle
         LES   DI, Buffer
         MOV   CX, Length

         PUSHF
         CALL  DWORD PTR [PktHandler]

         MOV   AL, 0
         JNC   @@1
         MOV   AL, DH
@@1:
end;

{-------------------------------
{ TCoPktDrv.PktResetInterface
{---
{ Reset sitove karty, nastaveni implicitni adresy apod.
{---
}


function TCoPktDrv.PktResetInterface( Handle: Word ): Byte; assembler;
var
  PktHandler : Pointer;
asm
         LES   DI, Self
         MOV   AX, WORD PTR ES:[DI + 0].Pkt_Handler
         MOV   WORD PTR [PktHandler + 0], AX
         MOV   AX, WORD PTR ES:[DI + 2].Pkt_Handler
         MOV   WORD PTR [PktHandler + 2], AX

         MOV   AH, S_RESET_IFACE
         MOV   BX, Handle

         PUSHF
         CALL  DWORD PTR [PktHandler]

         MOV   AL, 0
         JNC   @@1
         MOV   AL, DH
@@1:
end;


{function TCoPktDrv.PktGetStatistics( Handle: Word;
  var Statistics: TPktStatistics ): Byte; assembler;
var
  PktHandler : Pointer;
asm
         LES   DI, Self
         MOV   AX, WORD PTR ES:[DI + 0].Pkt_Handler
         MOV   WORD PTR [PktHandler + 0], AX
         MOV   AX, WORD PTR ES:[DI + 2].Pkt_Handler
         MOV   WORD PTR [PktHandler + 2], AX

         PUSH  DS
         MOV   AH, ID_GET_STATISTICS
         MOV   BX, Handle

         PUSHF
         CALL  DWORD PTR [PktHandler]

         MOV   AL, DH
         JC    @@1
         MOV   CX, TYPE TPktStatistics
         LES   DI, Statistics
         REP   MOVSB
         XOR   AL, AL
@@1:
         POP   DS
end;
}

{-------------------------------
{ TCoPktDrv.Pkt
{---
{ !@#
{---
}



{-------------------------------
{ TCoPktDrv.PktIntNumberValid
{---
{ !@#
{---
}



{-------------------------------
{ TCoPktDrv.PktSetintNumber
{---
{ !@#
{---
}

function TCoPktDrv.PktSetIntNumber( AIntNumber: Byte ): TCoStatus;
begin
  if not Pkt_Bound then
  begin
    if AIntNumber in [$60..$80] then
    begin
      PktSetIntNumber := CST_SUCCESS;
      Pkt_IntNumber := AIntNumber;
    end
    else
      PktSetIntNumber := CST_ERR_INVRANGE;
  end
  else
    PktSetIntNumber := CST_ERR_ISBOUND;
end;

{-------------------------------
{ TCoPktDrv.PktSetintNumber
{---
{ Nastaveni velikost prijimaciho bufferu
{---
}

function TCoPktDrv.PktSetRecvBuff( ASize: Word ): TCoStatus;
begin
  if not Pkt_Bound then
  begin
    if (ASize >= $200) and (ASize <= $FFF8) then
    begin
      {!@#}
    end
    else
      PktSetRecvBuff := CST_ERR_INVRANGE;
  end
  else
    PktSetRecvBuff := CST_ERR_ISBOUND;
end;


{-------------------------------
{ TCoPktDrv.PktSetintNumber
{---
{ Nastaveni max. delky prijimaci fronty }
{---
}

function TCoPktDrv.PktSetRecvQueue( ACount: Integer ): TCoStatus;
begin
  if not Pkt_Bound then
  begin
    if (ACount >= 1) and (ACount <= 255) then
    begin
      {!@#}
    end
    else
      PktSetRecvQueue := CST_ERR_INVRANGE
  end
  else
    PktSetRecvQueue := CST_ERR_ISBOUND;
end;

{-------------------------------
{ TCoPktDrv.PktSetOptString
{---
{ !@#
{---
}

function TCoPktDrv.PktSetOptString( AParams: PString ): TCoStatus;
var
  Tok   : TCoToken;
  Idx   : Byte;

const
  Table : array[1..3] of TCoIdentItem =
    ( (ParamName: 'INT'       ; ParamId : COPT_PKTDRV_INTNUMBER),
      (ParamName: 'BUFF'      ; ParamId : COPT_PKTDRV_RECVBUFF),
      (ParamName: 'QUEUE'     ; ParamId : COPT_PKTDRV_RECVQUEUE)
    );
var

  Option : Word;
  Value  : TCoToken;
  Status : TCoStatus;

begin

  Status := CST_SUCCESS;

  Idx := 1;

  repeat

    Status := CoParseParamString( AParams^, Idx, Table, Option, Value );

    if Status = CST_SUCCESS then
    begin
      case Option of

        COPT_PKTDRV_INTNUMBER:
        begin
          if Value.TokType = tokNumber then
          begin
            if (Value.VNumber >= 0) and
               (Value.VNumber <= 255) then
            begin
              Status := PktSetIntNumber( Value.VNumber );
            end
            else
              Status := CST_ERR_INVRANGE;
          end
          else Status := CST_ERR_INVALID;
        end;

        COPT_PKTDRV_RECVBUFF:
        begin
          if Value.TokType = tokNumber then
          begin
            if (Value.VNumber >= 0) and
               (Value.VNumber <= 65535) then
            begin
              Status := PktSetRecvBuff( Value.VNumber );
            end
            else
              Status := CST_ERR_INVRANGE;
          end
          else Status := CST_ERR_INVALID;
        end;

        COPT_PKTDRV_RECVQUEUE:
        begin
          if Value.TokType = tokNumber then
          begin
            if (Value.VNumber >= 0) and
               (Value.VNumber <= 32776) then
            begin
              Status := PktSetRecvQueue( Value.VNumber );
            end
            else
              Status := CST_ERR_INVRANGE;
          end
          else Status := CST_ERR_INVALID;
        end;

      else
        Break;
      end;

    end;

  until Status <> CST_SUCCESS;

  PktSetOptString := Status;

end;

{-------------------------------
{ TCoPktDrv.PktGetOptString
{---
{ !@#
{---
}

function TCoPktDrv.PktGetOptString( ABuffer: PString;
  ABuffSize: Word ): TCoStatus;
var
  S : String;
begin

  if ABuffer <> nil then
  begin

    S :=      'INT='       + ByteStrDec( Pkt_IntNumber, 0 ) +
             ' BUFF='      + WordStrDec( PCoPktHeap( CO_Heap[ hptRemote ])^.HeapSize, 0 ) +
             ' QUEUE='     + IntegerStrDec( Pkt_RecvQueue.GetCount, 0 );

    if (Length( S ) <= ABuffSize) then
    begin
      ABuffSize := Length( S );
      PktGetOptString := CST_SUCCESS;
    end
    else
      PktGetOptString := CST_ERR_BUFFSIZE;

    ABuffer^ := Copy( S, 1, ABuffSize );

  end
  else
    PktGetOptString := CST_ERR_INVALID;

end;



{-------------------------------
{ TCoPktDrv.MiniportInit
{---
{ !@#
{---
}

function TCoPktDrv.MiniportInit( var ADevParams: TCoDeviceParameters ): Boolean;
begin
  { Vsechny polozky jsou implicitne nulove }

  Pkt_IntNumber       := $60;     { Cislo sluzby         }

  with ADevParams do
  begin
    { Protoze neni nizsi vrstva, muzeme "zkrouhnout" velikosti
      nekterych front }
    RecvQueueLength    := 0;

    Properties := Properties or CPR_LOWER_NONE;
  end;

  CO_ClassId := DEV_CLASS_NIC;

  CO_RecvDeviceList := New( PCoEthDeviceList, Init( 7 ) );

  if CO_RecvDeviceList <> nil then
  begin

    CO_Heap[ hptRemote ]^.Release;
    CO_Heap[ hptRemote ] := New(PCoPktHeap, Init( 8 * 1024 ));

    Pkt_RecvQueue.Initialize( 16 );

    MiniportInit := True;
  end
  else
    MiniportInit := False;

end;

{-------------------------------
{ TCoPktDrv.MiniportTick
{---
{ !@#
{---
}

procedure TCoPktDrv.MiniportTick;
var
  Packet : PCoPacket;
begin

  if Pkt_Bound then
  begin

    { Prijem paketu }

    Packet := PktFrameReceive;
    if Packet <> nil then
    begin
      UpperReceivePacket( Packet );
      CoReleasePacket( Packet );
    end;

  end;
end;


{-------------------------------
{ TCoPktDrv.MiniportIoctl
{---
{ !@#
{---
}

function TCoPktDrv.MiniportIoctl( AClassId: Word; AFlags: Word; ACode: Word;
  ABuffer: Pointer; ABuffSize: Word; AHandle: PCoRequest ): TCoStatus;
begin

  if AClassId = DEV_CLASS_NIC then
  begin
    case ACode of

      IOCTL_NIC_GETMACADDRESS:
      begin
        if ABuffSize >= SizeOf( TEthMacAddress ) then
        begin
          MemCopy( ABuffer, @Pkt_LocalAddress.Mac, SizeOf( TEthMacAddress ) );
          MiniportIoctl := CST_SUCCESS;
        end
        else
          MiniportIoctl := CST_ERR_BUFFSIZE;
      end;

    else
      MiniportIoctl := CST_ERR_INVOPT;
    end;
  end
  else
    MiniportIoctl := CST_ERR_INVDEVICE;

end;


{-------------------------------
{ TCoPktDrv.MiniportSendPacket
{---
{ !@#
{---
}

function TCoPktDrv.MiniportSendPacket( APacket: PCoPacket; AFlags: Word;
  AAddress: PCoAddress; AHandle: PCoRequest ):
  TCoStatus;
var
  Res   : Byte;
  Addr  : PEthAddress;
begin

  { Kontrola maximalni delky paketu }
  if APacket^.DataSize > MAX_ETH_FRAME_SIZE then
  begin
    MiniportSendPacket := CST_ERR_BUFFSIZE;
    Exit;
  end;

  { Zarizeni musi mit prirazenu lokalni adresu }
  if not Pkt_Bound then
  begin
    MiniportSendPacket := CST_ERR_NOTBOUND;
    Exit;
  end;

  { Pokud neni "navazano spojeni", musime uvest adresu }
  if (not Pkt_Connected) and (not CoIsAddrValid(AAddress)) then
  begin
    MiniportSendPacket := CST_ERR_INVADDR;
    Exit;
  end;

  if Pkt_Connected and (not CoIsAddrValid(AAddress)) then
  begin
    Addr := @Pkt_RemoteAddress;
  end
  else
    Addr := PEthAddress(AAddress);

  PktAddEthHeader( Addr^, APacket );

  Res := PktSend( APacket^.DataPtr, APacket^.DataSize );

  case Res of
    E_NOERROR : MiniportSendPacket := CST_SUCCESS;
  else
    MiniportSendPacket := CST_PKTDRV_ERR_CANTSEND;
  end;

end;


{----------------------------------------------------------}{-------------------------------
{ TCoPktDrv.MiniportSendBuffer
{---
{ !@#
{---
}

function  TCoPktDrv.MiniportSendBuffer( ABuffer: Pointer; ABuffSize: Word;
  AFlags: Word; AAddress: PCoAddress; AHandle: PCoRequest ): TCoStatus;

begin

  MiniportSendBuffer := CST_ERR_NOTSUPP;
(*
  { Kontrola maximalni delky paketu }
  if ABuffSize > MAX_ETH_FRAME_SIZE then
  begin
    { Velikost posilanych dat neni v poradku }
    MiniportSendBuffer := CST_ERR_PKTSIZE;
    Exit;
  end;

  { Zarizeni musi mit prirazenu lokalni adresu }
  if not Eth_Bound then
  begin
    MiniportSendBuffer := CST_ERR_NOTBOUND;
    Exit;
  end;

  { Pokud neni "navazano spojeni", musime uvest adresu }
  if (not Eth_Connected) and (not CoIsAddrValid(AAddress)) then
  begin
    MiniportSendBuffer := CST_ERR_INVADDR;
    Exit;
  end;

  if Eth_Connected and (not CoIsAddrValid(AAddress)) then
  begin
    Addr := @Eth_RemoteAddress;
  end
  else
    Addr := PEthAddress(AAddress);

  { Alokujeme buffer pro paket v radici }

  if EthAllocateFrame( Addr^, ABuffSize, PNR ) then
  begin
    { Podarilo se naalokovat }

    { Zkopirujeme data do radice }

    EthFrameCopy( PNR, ABuffer, ABuffSize, Addr^ );

    EthFrameEnqueue( PNR );

    { Request ulozime do seznamu, spolecne s id paketu }
    Eth_SentList[ PNR ] := AHandle;

    { A uz muzeme zpracovat dalsi vysilaci pozadavek }
    UpperNextRequest( AHandle );

    MiniportSendBuffer := CST_PENDING;
  end
  else begin
    { Nepodarilo se naalokovat, zbytek vyresime v tiku }
    Eth_WaitingReq := AHandle;
    MiniportSendBuffer := CST_PENDING;
  end;
*)
end;

{-------------------------------
{ TCoPktDrv.MiniportCancel
{---
{ !@#
{---
}


procedure TCoPktDrv.MiniportCancel( ACancelAll: Boolean );
{var
  Req : PCoRequest;}
begin

(*  if Eth_WaitingReq <> nil then
  begin
    { Zruseni pozadavku cekajiciho na nalokovani
      pameti v radici }
    if ACancelAll or CoShouldCancel(Eth_WaitingReq) then
    begin
      UpperComplete( Eth_WaitingReq, CST_ERR_ABORTED );
      Eth_WaitingReq := CANCELED_REQUEST;
    end;
  end;
*)
end;


{-------------------------------
{ TCoPktDrv.MiniportConnect
{---
{ !@#
{---
}


function TCoPktDrv.MiniportConnect( AAddress: PCoAddress; AFlags: Word;
  AHandle: PCoRequest ): TCoStatus;
begin
{  if not Eth_Bound then
  begin
    Status := MiniportBind( nil, 0, nil );
    if Status <> CST_SUCCESS then
    begin
      MiniportConnect := Status;
      Exit;
    end;
  end;
}

  if Pkt_Bound then
  begin
    if not Pkt_Connected then
    begin

      if CoIsAddrValid( AAddress ) then
      begin
        Pkt_RemoteAddress := PEthAddress(AAddress)^;
        Pkt_Connected     := True;
        MiniportConnect   := CST_SUCCESS;
      end
      else { Adresa neni specifikovana -> neni chyba, ale neprovedeme connect }
        MiniportConnect := CST_SUCCESS;

    end
    else
      MiniportConnect := CST_ERR_ISCONN;
  end
  else
    MiniportConnect := CST_ERR_NOTBOUND;

end;

{-------------------------------
{ TCoEth01.MiniportDisconnect
{---
{ !@#
{---
}

function TCoPktDrv.MiniportDisconnect( AFlags: Word; AHandle: PCoRequest ):
  TCoStatus;
begin
  if Pkt_Bound {and Eth_Connected} then
  begin
    Pkt_Connected := False;
  end
  else
    MiniportDisconnect := CST_ERR_NOTCONN;
end;


{-------------------------------
{ TCoEth01.MiniportBind
{---
{ !@#
{---
}

function TCoPktDrv.MiniportBind( AAddress: PCoAddress; AFlags: Word;
  AHandle: PCoRequest ): TCoStatus;
var
  Res : Byte;
begin

  if not Pkt_Bound then
  begin
    { Inicializace radice podle parametru Pkt_xxx}
    if PktInitialize then
    begin

      if RegisterPktDrv( @Self ) then
      begin

        { Zaregistrovani prijimaci funkce v Packet Driveru }

        Res := PktAccessType( 1, $FFFF, 0, nil, 0, @__PktReceiver, Pkt_Handle );

        if Res = 0 then
        begin

  {        PktResetInterface...;}

          { Nastaveni adresy .. }

          if not CoIsAddrValid( AAddress ) then
          begin
            { Default adresa v eeprom }

            Pkt_LocalAddress.Size := SizeOf( TEthAddress );
            Pkt_LocalAddress.Protocol := 0;

            PktGetAddress( Pkt_Handle, @Pkt_LocalAddress.Mac,
                           SizeOf( Pkt_LocalAddress.Mac ) );
          end
          else begin
            { Specifikovana adresa }
            MiniportBind := CST_ERR_INVADDR;

            PktReleaseType( Pkt_Handle );

            Exit;

           {
            Pkt_LocalAddress := PEthAddress(AAddress)^;
            PktSetMacAddress( Eth_LocalAddress.Mac );
           }
          end;


          Pkt_Connected := False;
          Pkt_Bound     := True;

          MiniportBind := CST_SUCCESS;

        end
        else
          MiniportBind := CST_PKTDRV_ERR_ACCESSTYPE;

      end
      else
        MiniportBind := CST_PKTDRV_ERR_INSTANCE;

    end
    else
      MiniportBind := CST_PKTDRV_ERR_INTNUMBER;

  end
  else
    MiniportBind := CST_ERR_ISBOUND;

end;

{-------------------------------
{ TCoPktDrv.MiniportUnbind
{---
{ !@#
{---
}

function TCoPktDrv.MiniportUnbind( AFlags: Word; AHandle: PCoRequest ):
  TCoStatus;
begin

  if Pkt_Bound then
  begin
    Pkt_Connected := False;
    Pkt_Bound     := False;

    { Uvolneni callbacku alokovaneho v Packet Driveru }
    PktReleaseType( Pkt_Handle );

    { Uvolneni heapu prijatych paketu }
{    PktReleaseHeap( .. );}

    MiniportUnbind := CST_SUCCESS;
  end
  else
    MiniportUnbind := CST_ERR_NOTBOUND;

end;

{-------------------------------
{ TCoPktDrv.MiniportSetOption
{---
{ !@#
{---
}


function TCoPktDrv.MiniportSetOption( AClassId: Word; AOption: Word; ABuffer: Pointer;
  ABuffSize: Word; AFlags: Word; AHandle: PCoRequest ): TCoStatus;
var
  Status : TCoStatus;
begin

  if AClassId = CO_ClassId then
  begin
    case AOption of

      COPT_STRING      : Status := PktSetOptString( PString(ABuffer) );

      COPT_PKTDRV_INTNUMBER : if ABuffSize >= SizeOf( Byte ) then
                                Status := PktSetIntNumber( PByte( ABuffer )^ )
                              else
                                Status := CST_ERR_BUFFSIZE;

      COPT_PKTDRV_RECVBUFF:   if ABuffSize >= SizeOf( Word ) then
                                Status := PktSetRecvBuff( PWord( ABuffer )^ )
                              else
                                Status := CST_ERR_BUFFSIZE;

      COPT_PKTDRV_RECVQUEUE:  if ABuffSize >= SizeOf( Integer ) then
                                Status := PktSetRecvQueue( PInteger( ABuffer )^ )
                              else
                                Status := CST_ERR_BUFFSIZE;

    else
      Status := CST_ERR_INVOPT;
    end;
  end
  else
    Status :=  CST_ERR_INVCLASS;

  MiniportSetOption := Status;
end;


{-------------------------------
{ TCoPktDrv.MiniportGetOption
{---
{ !@#
{---
}

function TCoPktDrv.MiniportGetOption( AClassId: Word; AOption: Word;
  ABuffer: Pointer; ABuffSize: Word; AFlags: Word;
  AHandle: PCoRequest ): TCoStatus;
var
  Status : TCoStatus;
begin

  if AClassId = CO_ClassId then
  begin

    Status := CST_SUCCESS;

    case AOption of

      COPT_STRING      : Status := PktGetOptString(
                           PString(ABuffer), ABuffSize );

      COPT_PKTDRV_INTNUMBER : if ABuffSize >= SizeOf( Byte ) then
                                PByte(ABuffer)^ := Pkt_IntNumber
                              else
                                Status := CST_ERR_BUFFSIZE;

      COPT_PKTDRV_RECVBUFF:   if ABuffSize >= SizeOf( Word ) then
                                PWord(ABuffer)^ := PCoPktHeap( CO_Heap[ hptRemote ] )^.HeapSize
                              else
                                Status := CST_ERR_BUFFSIZE;

      COPT_PKTDRV_RECVQUEUE:  if ABuffSize >= SizeOf( Word ) then
                                PWord(ABuffer)^ := Pkt_RecvQueue.GetCount
                              else
                                Status := CST_ERR_BUFFSIZE;


    else
      Status := CST_ERR_INVOPT;
    end;

  end
  else
    Status :=  CST_ERR_INVCLASS;

  MiniportGetOption := Status;
end;


begin
  CoRegisterDeviceClass( @PktDrvClass );
end.