unit CoIPX;

  {==========================================================}
  {                                                          }
  {  unit CoIpx                      Ver 12/09/2001          }
  {                                                          }
  {  Implementace IPX protokolu                              }
  {                                                          }
  {                                                          }
  {  (C)2001, SofCon, Stresovicka 49, 160 00 Praha 6         }
  {           Petr Cernin                                    }
  {                                                          }
  {==========================================================}


interface

uses
  CoEth01,
  CoEthDef,
  CoBase;

const

  IOCTL_IPX_ADDROUTE  = 256;
  IOCTL_IPX_DELROUTE  = 257;

type

  {-------------------------------
  { TIpxNetworkAddress
  {---
  { Adresa site v sitich NOVELL
  {
  }

  TIpxNetworkAddress = packed record
    case Integer of
      0: ( Bytes: array[0..3] of Byte );
      1: ( Words: array[0..1] of Word );
      2: ( Long : Longint );
  end;

  {-------------------------------
  { TIpxNodeAddress
  {---
  { Adresa uzlu v sitich NOVELL
  { Je shodna s fyzickou adresou podle IEEE802.3
  }


  TIpxNodeAddress = TEthMacAddress;

  {-------------------------------
  { TIpxAddress
  {---
  { Uplna adresa procesu v sitich NOVELL
  {
  }

{  PIpxAddress = ^TIpxAddress;}
  TIpxSockAddress = packed record
    Net  : TIpxNetworkAddress;      { Identifikace site                    }
    Node : TIpxNodeAddress;         { Identifikace uzlu                    }
    Sock : Word;                    { Cislo soketu                         }
  end;

  {-------------------------------
  { TIpxAddress
  {---
  { Adresa
  { Kontrolni soucet se nepouziva, spoleha se na crc ethernetu
  }

  PIpxAddress = ^TIpxAddress;
  TIpxAddress = record
    Size : Byte;                    { Velikost struktury                   }
    case Integer of
      0: ( Data : TIpxSockAddress );
      1: ( Net  : TIpxNetworkAddress;
           Node : TIpxNodeAddress;
           Sock : Word );
  end;

  {-------------------------------
  { TIpxHeader
  {---
  { Zahlavi IPX/SPX protokolu
  { Kontrolni soucet se nepouziva, spoleha se na crc ethernetu
  }


  PIpxHeader = ^TIpxHeader;
  TIpxHeader = packed record
    IpxChecksum : Word;             { IPX_NO_CHECKSUM                      }
    IpxPktSize  : Word;             { Velikost paketu vcetne hlavicky      }
    IpxCtrl     : Byte;             { asi 0                                }
    IpxType     : Byte;             { IPX_TYPE_UNKNOWN,  nastavitelne !@#  }
    IpxDest     : TIpxSockAddress;  { Cilova adresa                        }
    IpxSrc      : TIpxSockAddress;  { Zdrojova adresa                      }
  end;


  {-------------------------------
  { TIpxRouteItem
  {---
  { Informace o sitich a smerovacich udrzovana v objektu TIpxMux
  }

  PIpxRouteItem = ^TIpxRouteItem;
  TIpxRouteItem = record
    Net     : TIpxNetworkAddress;   { Identifikace site                    }
    Router  : TEthMacAddress;       { MAC adresa smerovace                 }
    Next    : PIpxRouteItem;        { dalsi polozka ve spojovem seznamu    }
  end;

  {-------------------------------
  { TIpxRouteItem
  {---
  { Smerovaci tabulka
  }

  TIpxRouteTable = object
  private
    List    : PIpxRouteItem;        { Zacatek spojoveho seznamu            }

  public

    procedure Initialize;
    procedure Finalize;

    { Pridani smerovace do seznamu }
    function  AddRoute( const ANet: TIpxNetworkAddress;
                ARouter: TEthMacAddress ): TCoStatus;

    { Odstraneni smerovace ze seznamu }
    function  DeleteRoute( const ANet: TIpxNetworkAddress ): TCoStatus;

    { Vyhledani smerovace, ktery smeruje do site ANet,
      pokud do dane site neexistuje smerovac, pak funkce vrati nil }
    function  FindRoute( const ANet: TIpxNetworkAddress ): PIpxRouteItem;
  end;


  {=======================================================================}
  { TCoIpxProtoDeviceList                                                 }
  {=======================================================================}


  PCoIpxSockDeviceList = ^TCoIpxSockDeviceList;
  TCoIpxSockDeviceList = object( TCoProtoDeviceList )
    function  DispatchPacket( APacket: PCoPacket ): TCoStatus; virtual;
  end;


  {=======================================================================}
  { TCoIpxMux                                                             }
  {=======================================================================}

  { Multiplexer IPX/SPX protokolu,
    Tato vrstva zajistuje:
        1) Vetveni na zaklade polozky IpxDest.Sock hlavicky IPX protokolu
        2) Doplnovani cilovych adres, pokud odchozi paket smeruje
           do jine site
  }

  PCoIpxMux = ^TCoIpxMux;
  TCoIpxMux = object( TCoDevice )

    IPXMUX_TempHandle  : PCoRequest;   { Pomocny handle pozadavku          }
    IPXMUX_Bound       : Boolean;      { Priznak asociovane lokalni adresy }

    IPXMUX_RouteTable  : TIpxRouteTable;

    function  IpxMuxBind( AAddress: PCoAddress; AFlags: Word ): TCoStatus;
    function  IpxMuxUnbind( AFlags: Word ): TCoStatus;

  {------}

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

    procedure MiniportDone; virtual;

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

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

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


    procedure MiniportCancel( ACancelAll: Boolean ); virtual;

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

    { async funkce }
    function  ProtocolReceiveFilter( APacket: PCoPacket{;
                UserVal: Word} ): Boolean; virtual;

    procedure ProtocolReceive( APacket: PCoPacket ); virtual;


    function  ProtocolSendComplete( AHandle: PCoRequest;
                Status: TCoStatus ): Boolean; virtual;

    function  ProtocolBindComplete( AHandle: PCoRequest;
                Status: TCoStatus ): Boolean; virtual;

    function  ProtocolUnbindComplete( AHandle: PCoRequest;
                Status: TCoStatus ): Boolean; virtual;

  end;

  {=======================================================================}
  { TCoIpx                                                                }
  {=======================================================================}

  PCoIpx = ^TCoIpx;
  TCoIpx = object( TCoDevice )

    IPX_LocalAddress   : TIpxSockAddress;  { Lokalni adresa uzlu               }
    IPX_RemoteAddress  : TIpxSockAddress;  { Adresa vzdaleneho uzlu            }
    IPX_Bound          : Boolean;          { Priznak asociovani lokalni adresy }
    IPX_Connected      : Boolean;          { Priznak asociovani vzdalene adresy}
    IPX_TempHandle     : PCoRequest;       { Pomocny handle pozadavku          }


    function  IpxBind( AAddress: PCoAddress; AFlags: Word ): TCoStatus;
    function  IpxConnect( AAddress: PCoAddress; AFlags: Word ): TCoStatus;
    function  IpxUnbind( AFlags: Word ): TCoStatus;
    function  IpxDisconnect( AFlags: Word ): TCoStatus;


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

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

    function  MiniportSendBuffer( ABuffer: Pointer; ABuffSize: Word;
                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  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;

    procedure MiniportCancel( ACancelAll: Boolean ); virtual;

    { async funkce }
    function  ProtocolReceiveFilter( APacket: PCoPacket{;
                UserVal: Word} ): Boolean; virtual;

    procedure ProtocolReceive( APacket: PCoPacket ); virtual;

    function  ProtocolBindComplete( AHandle: PCoRequest;
                Status: TCoStatus ): Boolean; virtual;

    function  ProtocolUnbindComplete( AHandle: PCoRequest;
                Status: TCoStatus ): Boolean; virtual;

    function  ProtocolConnectComplete( AHandle: PCoRequest;
                Status: TCoStatus ): Boolean; virtual;

    function  ProtocolDisconnectComplete( AHandle: PCoRequest;
                Status: TCoStatus ): Boolean; virtual;

    function  ProtocolIoctlComplete( AHandle: PCoRequest;
                Status: TCoStatus ): Boolean; virtual;

    function  ProtocolSendComplete( AHandle: PCoRequest;
                Status: TCoStatus ): Boolean; virtual;
  end;



implementation

const

  IpxMuxName  : String[6] = 'IPXMUX';

  IpxMuxClass : TCoDeviceClass =
   ( ClassName : @IpxMuxName;
     ClassId   : DEV_CLASS_IPXMUX;
     VmtLink   : Ofs(TypeOf(TCoIpxMux)^);
     Init      : @TCoIpxMux.Init
   );


  IpxName     : String[3] = 'IPX';

  IpxClass : TCoDeviceClass =
   ( ClassName : @IpxName;
     ClassId   : DEV_CLASS_IPX;
     VmtLink   : Ofs(TypeOf(TCoIpx)^);
     Init      : @TCoIpx.Init
   );


const { TIpxHeader.IpxCtrl }

  IPX_TYPE_UNKNOWN = $00;
  IPX_TYPE_RIP     = $01;
  IPX_TYPE_SAP     = $04;
  IPX_TYPE_SPX     = $05;
  IPX_TYPE_NCP     = $11;
  IPX_TYPE_PPROP   = $14;

const { TIpxHeader.IpxCheckSum }

  IPX_NO_CHECKSUM  = $FFFF;

const

  MAX_IPX_FRAME_SIZE = 512; {!@# --- to je blbe }


  function TCoIpxSockDeviceList.DispatchPacket(
             APacket: PCoPacket ): TCoStatus;
  var
    P      : PCoProtoDeviceListItem;
    Sock   : Word;
    Device : PCoDevice;
  begin
    Sock := PIpxHeader( APacket^.DataPtr )^.IpxDest.Sock;

    PUSHFCLI;

    P := Table^[ Sock mod TableSize ];

    while (P <> nil) and (P^.Proto <> Sock) do P := P^.Next;

    if P <> nil then
    begin
      Device := P^.Device;

      if Device^.ProtocolReceiveFilter( APacket ) then
      begin

        if Device <> nil then
        begin
          if Device^.CO_ReceiveQ.InsertTail( APacket ) then
          begin
            DispatchPacket := CST_SUCCESS
          end
          else
            DispatchPacket := CST_ERR_QUEUEFULL;
        end;
      end
      else
        DispatchPacket := CST_ERR_DROPPED;

    end
    else
      DispatchPacket := CST_ERR_DROPPED;

    POPF;
  end;

  {-------------------------------
  { TIpxRouteTable.Initialize
  {---
  { !@#
  {---
  {
  }

  procedure TIpxRouteTable.Initialize;
  begin
    List := nil;
  end;


  {-------------------------------
  { TIpxRouteTable.Finalize
  {---
  { !@#
  {---
  {
  }

  procedure TIpxRouteTable.Finalize;
  var
    P : PIpxRouteItem;
  begin
    while List <> nil do
    begin
      P := List^.Next;
      Dispose( P );
      List := P;
    end;
  end;


  {-------------------------------
  { TIpxRouteTable.AddRoute
  {---
  { !@#
  {---
  {
  }

  function TIpxRouteTable.AddRoute( const ANet: TIpxNetworkAddress;
    ARouter: TEthMacAddress ): TCoStatus;
  var
    P : PIpxRouteItem;
  begin
    New( P );
    if P <> nil then
    begin
      P^.Net := ANet;
      P^.Router := ARouter;
      PUSHFCLI;
      P^.Next := List;
      List := P;
      POPF;
    end
    else
      AddRoute := CST_ERR_MEMOVF;
  end;

  {-------------------------------
  { TIpxRouteTable.DeleteRoute
  {---
  { !@#
  {---
  {
  }

  function TIpxRouteTable.DeleteRoute(
    const ANet: TIpxNetworkAddress ): TCoStatus;
  var
    P    : PIpxRouteItem;
    Prev : PIpxRouteItem;
  begin
    PUSHFCLI;
    P := List;
    Prev := nil;
    while (P <> nil) and (P^.Net.Long <> ANet.Long) do
    begin
      Prev := P;
      P := P^.Next;
    end;

    if P <> nil then
    begin
      if Prev = nil then
      begin
        List := P^.Next;
      end
      else
        Prev^.Next := P^.Next;

      Dispose( P );
    end;
    POPF;
  end;

  {-------------------------------
  { TIpxRouteTable.FindRoute
  {---
  { !@#
  {---
  {
  }

  function TIpxRouteTable.FindRoute(
    const ANet: TIpxNetworkAddress ): PIpxRouteItem;
  var
    P   : PIpxRouteItem;
  begin
    PUSHFCLI;
    P    := List;
    while (P <> nil) and (P^.Net.Long <> ANet.Long) do
    begin
      P := P^.Next;
    end;
    POPF;
    FindRoute := P;
  end;


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

  function TCoIpxMux.MiniportInit( var ADevParams: TCoDeviceParameters ):
             Boolean;
  begin
    IPXMUX_RouteTable.Initialize;

    with ADevParams do
    begin
      AppRecvQueueLength       := 0;
    { nejlepe zrusit pristup z aplikace,
      tj. zkrouhnout aplikacni casovace a aplikacni frontu na
      nulovou velikost }

    end;

    CO_ClassId := DEV_CLASS_IPXMUX;

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

    MiniportInit := (CO_RecvDeviceList <> nil);

  end;

  {-------------------------------
  { TCoIpxMux.MiniportDone
  {---
  { !@#
  {---
  {
  }

  procedure TCoIpxMux.MiniportDone;
  begin
    IPXMUX_RouteTable.Finalize;
  end;

  {-------------------------------
  { TCoIpxMux.MiniportSendPacket
  {---
  { Odeslani paketu
  {---
  {
  }

  function TCoIpxMux.MiniportSendPacket( APacket: PCoPacket; AFlags: Word;
    AAddress: PCoAddress; AHandle: PCoRequest ): TCoStatus;
  var
    Hdr        : PIpxHeader;
    LowerAddr  : TEthAddress;
    Status     : TCoStatus;
    Route      : PIpxRouteItem;
  begin
    Hdr := PIpxHeader(APacket^.DataPtr);

    with LowerAddr do
    begin
      Size      := SizeOf( TEthAddress );
      Mac       := Hdr^.IpxDest.Node;

      Format    := ETH_FORMAT_LLC;
      DSAP      := $E0;
      SSAP      := $E0;
    end;

    if Hdr^.IpxDest.Net.Long <> 0 then
    begin
      Route := IPXMUX_RouteTable.FindRoute( Hdr^.IpxDest.Net );
      if Route = nil then
      begin
        MiniportSendPacket := CST_ERR_NETUNREACH;
        Exit;
      end
      else
        LowerAddr.Mac := Route^.Router;
    end
    else begin
      LowerAddr.Mac := Hdr^.IpxDest.Node;
      { !@# mozna by se tady mela doplnit cislo site v ipx hlavicce }
    end;

    Status := LowerSendPacketTo( APacket, AFlags, @LowerAddr, AHandle );

    if Status = CST_PENDING then IPXMUX_TempHandle := AHandle;

    MiniportSendPacket := Status;
  end;


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

  function  TCoIpxMux.MiniportIoctl( AClassId: Word; AFlags: Word; ACode: Word;
    ABuffer: Pointer; ABuffSize: Word; AHandle: PCoRequest ): TCoStatus;
  var
    Status: TCoStatus;
  begin
    if AClassId = CO_ClassId then
    begin
      case ACode of

      {---------------------}
        IOCTL_IPX_ADDROUTE:
        begin
          {IPXMUX_RouteTable.AddRoute(}
          Status := CST_ERR_NOTSUPP;
        end;

      {---------------------}
        IOCTL_IPX_DELROUTE:
        begin
          {IPXMUX_RouteTable.DelRoute(}
          Status := CST_ERR_NOTSUPP;
        end;

      else
        Status := CST_ERR_INVOPT;
        {!@# doplnit IPX_IOCTL_FINDROUTE}
        {!@# doplnit IPX_IOCTL_..route.. }
      end;

      MiniportIoctl := Status;
    end
    else { inherited metoda vola miniportioctl nizsich vrstev }
      MiniportIoctl :=
        inherited MiniportIoctl( AClassId, AFlags, ACode,
                                 ABuffer, ABuffSize, AHandle );
  end;



  {-------------------------------
  { TCoIpxMux.IpxMuxBind
  {---
  { !@#
  {---
  {
  }

  function TCoIpxMux.IpxMuxBind( AAddress: PCoAddress;
    AFlags: Word ): TCoStatus;
  var
    Status : Word;
    Proto  : TCoEthProtocol;
  begin

    if IPXMUX_Bound then
    begin

      if AFlags and COF_QUIET <> 0 then
      begin
        Status := CST_SUCCESS
      end
      else
        Status := CST_ERR_ISBOUND;

    end
    else begin

      if CoIsAddrValid( AAddress ) then
      begin
        { Tato vrstva nepracuje s adresou }
        Status := CST_ERR_INVADDR
      end
      else begin
        { Zaregistrujeme protokol u nizsi vrstvy }

        Proto.Format  := ETH_FORMAT_LLC;
        Proto.DSAP    := $E0;
        Proto.SSAP    := $E0;

        Status := LowerAddToReceiveList( 0, @Proto, SizeOf( Proto ) );

        if Status = CST_SUCCESS then
        begin

          Proto.Format  := ETH_FORMAT_ETHII;
          Proto.Proto   := ETH_TYPE_IPX;

          Status := LowerAddToReceiveList( 1, @Proto, SizeOf( Proto ) );

          if Status = CST_SUCCESS then
          begin
            Proto.Format  := ETH_FORMAT_SNAP;
            Proto.Proto   := ETH_TYPE_IPX;

            Status := LowerAddToReceiveList( 2, @Proto, SizeOf( Proto ) );

            if Status = CST_SUCCESS then
            begin
              IPXMUX_Bound := True;
            end
            else begin
              LowerRemoveFromReceiveList( 0 );
              LowerRemoveFromReceiveList( 1 );
            end;

          end
          else begin
            LowerRemoveFromReceiveList( 0 );
          end;

        end;

      end;

    end;
    IpxMuxBind := Status;
  end;


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


  function TCoIpxMux.MiniportBind( AAddress: PCoAddress; AFlags: Word;
    AHandle: PCoRequest ): TCoStatus;
  var
    Status : TCoStatus;
  begin

    if AFlags and COF_PROPAGATE <> 0 then
    begin
      { Automaticky bind nizsi vrstvy }
      Status := LowerBind( nil, COF_PROPAGATE or COF_QUIET, AHandle );

      case Status of
        CST_SUCCESS: Status := IpxMuxBind( AAddress, AFlags );
        CST_PENDING: IPXMUX_TempHandle := AHandle;
      end;

    end
    else begin
      Status := IpxMuxBind( AAddress, AFlags );
    end;

    MiniportBind := Status;
  end;

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

  function TCoIpxMux.IpxMuxUnbind( AFlags: Word ): TCoStatus;
  var
    Status: TCoStatus;
  begin
    if not IPXMUX_Bound then
    begin

      if AFlags and COF_QUIET <> 0 then
      begin
        Status := CST_SUCCESS
      end
      else
        Status := CST_ERR_NOTBOUND;

    end
    else begin
      LowerRemoveFromReceiveList( 0 );
      LowerRemoveFromReceiveList( 1 );
      LowerRemoveFromReceiveList( 2 );

      IPXMUX_Bound := False;
      Status := CST_SUCCESS;
    end;

    IpxMuxUnbind := Status;

  end;

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

  function TCoIpxMux.MiniportUnbind( AFlags: Word; AHandle: PCoRequest ):
    TCoStatus;
  var
    Status : TCoStatus;
  begin
    Status := IpxMuxUnbind( AFlags );

    if (AFlags and COF_PROPAGATE <> 0) and (Status = CST_SUCCESS) then
    begin
      Status := LowerUnbind(
                  COF_PROPAGATE or COF_QUIET or COF_IFNOTUSED, AHandle );
      if Status = CST_PENDING then IPXMUX_TempHandle := AHandle;
    end;

    MiniportUnbind := Status;
  end;


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


  procedure TCoIpxMux.MiniportCancel( ACancelAll: Boolean );
  begin
    if (IPXMUX_TempHandle <> nil) and (ACancelAll or
         CoShouldCancel( IPXMUX_TempHandle )) then
    begin
      LowerCancelIO( IPXMUX_TempHandle, 0 );
    end;
  end;

  {-------------------------------
  { TCoIpxMux.ProtocolSendComplete
  {---
  { !@#
  {---
  {
  }

  function  TCoIpxMux.ProtocolSendComplete( AHandle: PCoRequest;
    Status: TCoStatus ): Boolean;
  begin

    if AHandle = IPXMUX_TempHandle then
    begin
      UpperComplete( AHandle, Status );
      ProtocolSendComplete := True;
    end
    else
      ProtocolSendComplete := False;

  end;


  {-------------------------------
  { TCoIpxMux.ProtocolReceiveFilter
  {---
  { !@#
  {---
  { async funkce
  }

  function TCoIpxMux.ProtocolReceiveFilter( APacket: PCoPacket ): Boolean;
{  var
    Hdr  : PIpxHeader;}
  begin
    ProtocolReceiveFilter := False;

    with PEthAddress(@APacket^.Address)^ do
    begin

      case Format of
        ETH_FORMAT_ETHII,
        ETH_FORMAT_SNAP:
          if Protocol <> ETH_TYPE_IPX then Exit;

        ETH_FORMAT_LLC:
          if (DSAP <> $E0) or (SSAP <> $E0) then Exit;
      else
        Exit;
      end;

    end;


{    Hdr := PIpxHeader( APacket^.DataPtr );}
    { Provest nejake kontroly }
    { !@# netnumber, delka paketu.. }

    ProtocolReceiveFilter := True;
  end;

  {-------------------------------
  { TCoIpxMux.ProtocolReceive
  {---
  { !@#
  {---
  {
  }

  procedure TCoIpxMux.ProtocolReceive( APacket: PCoPacket );
  begin
    UpperReceivePacket( APacket );
  end;


  {-------------------------------
  { TCoIpx.ProtocolBindComplete
  {---
  { !@#
  {---
  {
  }


  function TCoIpxMux.ProtocolBindComplete( AHandle: PCoRequest;
    Status: TCoStatus ): Boolean;
  var
    Slot : PCoRequestSlot;
  begin
    if IPXMUX_TempHandle = AHandle then
    begin
      IPXMUX_TempHandle := nil;
      if Status = CST_SUCCESS then
      begin
        Slot := CoGetDeviceSlot( AHandle );
        with TCoBindParams(Slot^.Params) do
          UpperComplete( AHandle,  IpxMuxBind( @Address, Flags ) );
      end
      else
        UpperComplete( AHandle, Status );

      ProtocolBindComplete := True;
    end
    else
      ProtocolBindComplete := False;
  end;


  {-------------------------------
  { TCoIpxMux.ProtocolUnbindComplete
  {---
  { !@#
  {---
  {
  }


  function TCoIpxMux.ProtocolUnbindComplete( AHandle: PCoRequest;
    Status: TCoStatus ): Boolean;
  begin
    if IPXMUX_TempHandle = AHandle then
    begin
      IPXMUX_TempHandle := nil;
      UpperComplete( AHandle, Status );
      ProtocolUnbindComplete := True;
    end
    else
      ProtocolUnbindComplete := False;
  end;



  {=======================================================================}
  { TCoIpx                                                                }


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

  function TCoIpx.MiniportInit( var ADevParams: TCoDeviceParameters ):
             Boolean;
  begin
    with ADevParams do
    begin
      HeaderSize := SizeOf( TIpxHeader );
    end;

    CO_ClassId := DEV_CLASS_IPX;

    MiniportInit := True;
  end;

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

  function  TCoIpx.MiniportSendPacket( APacket: PCoPacket; AFlags: Word;
    AAddress: PCoAddress; AHandle: PCoRequest ): TCoStatus;
  var
    Hdr        : PIpxHeader;
    LowerAddr  : TEthAddress;
  begin

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

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

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

    { Doplnime paket o IPX hlavicku }
    Hdr := APacket^.AddHeader( SizeOf( TIpxHeader ) );

    with Hdr^ do
    begin
      IpxChecksum := IPX_NO_CHECKSUM;
      IpxPktSize  := Swap( APacket^.DataSize );
      IpxCtrl     := 0;
      IpxType     := IPX_TYPE_UNKNOWN;

      if CoIsAddrValid(AAddress) then
      begin
        IpxDest   := PIpxAddress(AAddress)^.Data;
      end
      else
        IpxDest   := IPX_RemoteAddress;

      IpxSrc      := IPX_LocalAddress;
    end;
   {--}

    MiniportSendPacket := LowerSendPacket( APacket, AFlags, AHandle );

    UpperNextRequest( AHandle, [RQ_TYPE_SENDBUFFER, RQ_TYPE_SENDPACKET] );
  end;


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


  function TCoIpx.MiniportSendBuffer( ABuffer: Pointer; ABuffSize: Word;
    AFlags: Word; AAddress: PCoAddress; AHandle: PCoRequest ): TCoStatus;
  var
    Hdr        : PIpxHeader;
    LowerAddr  : TEthAddress;
    Packet     : PCoPacket;
    Status     : TCoStatus;
  begin

    { Kontrola maximalni delky paketu }
    if ABuffSize > MAX_IPX_FRAME_SIZE then
    begin
      MiniportSendBuffer := CST_ERR_BUFFSIZE;
      Exit;
    end;

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

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

    { Alokujeme paket }

    Packet := CoAllocPacket( SizeOf( TIpxHeader ), ABuffSize, 0, hptUser );
    {!@# kontrolovat navratovou hodnotu ... }

    MemCopy( Packet^.DataPtr, ABuffer, ABuffSize );

    { Doplnime paket o IPX hlavicku }

    Hdr := Packet^.AddHeader( SizeOf( TIpxHeader ) );
    {!@# kontrolovat navratovou hodnotu ... }

    with Hdr^ do
    begin
      IpxChecksum := IPX_NO_CHECKSUM;
      IpxPktSize  := Swap( Packet^.DataSize );
      IpxCtrl     := 0;
      IpxType     := IPX_TYPE_UNKNOWN;

    {!@# Nejak poresit sit }
      if CoIsAddrValid(AAddress) then
      begin
        IpxDest   := PIpxAddress(AAddress)^.Data;
      end
      else
        IpxDest   := IPX_RemoteAddress;

      IpxSrc      := IPX_LocalAddress;
    end;
   {--}

    Status := LowerSendPacket( Packet, AFlags, AHandle );

    if Status = CST_PENDING then
    begin
      UpperNextRequest( AHandle, [RQ_TYPE_SENDBUFFER, RQ_TYPE_SENDPACKET] );
    end;

    CoReleasePacket( Packet );

    MiniportSendBuffer := Status;
  end;


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

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

  end;}

  {-------------------------------
  { TCoIpx.IpxConnect
  {---
  { !@#
  {---
  {
  }

  function TCoIpx.IpxConnect( AAddress: PCoAddress; AFlags: Word ): TCoStatus;
  begin
    if IPX_Connected then
    begin

      if AFlags and COF_QUIET <> 0 then
      begin
        IpxConnect := CST_SUCCESS
      end
      else
        IpxConnect := CST_ERR_ISBOUND;

    end
    else begin
      if CoIsAddrValid( AAddress ) then
      begin
        IPX_RemoteAddress := PIpxAddress(AAddress)^.Data;
        IPX_Connected := True;
        IpxConnect := CST_SUCCESS;
      end
      else { zatim nemame implicitni adresu }
        IpxConnect := CST_ERR_INVADDR;
    end;
  end;


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


  function TCoIpx.MiniportConnect( AAddress: PCoAddress; AFlags: Word;
              AHandle: PCoRequest ): TCoStatus;
  var
    Status : TCoStatus;
  begin

    if AFlags and COF_PROPAGATE <> 0 then
    begin
      { Automaticky bind nizsi vrstvy }
      Status := LowerConnect( nil, COF_PROPAGATE or COF_QUIET, AHandle );

      case Status of
        CST_SUCCESS: Status := IpxConnect( AAddress, AFlags );
        CST_PENDING: IPX_TempHandle := AHandle;
      end;

    end
    else begin
      Status := IpxConnect( AAddress, AFlags );
    end;

    MiniportConnect := Status;
  end;


  {-------------------------------
  { TCoIpx.IpxDisconnect
  {---
  { !@#
  {---
  {
  }

  function TCoIpx.IpxDisconnect( AFlags: Word ): TCoStatus;
  begin
    if not IPX_Connected then
    begin

      if AFlags and COF_QUIET <> 0 then
      begin
        IpxDisconnect := CST_SUCCESS
      end
      else
        IpxDisconnect := CST_ERR_NOTCONN;

    end
    else begin
   {  IPX_RemotelAddress.Size := 0;;}
      IPX_Connected := False;
      IpxDisconnect := CST_SUCCESS;
    end;

  end;


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


  function TCoIpx.MiniportDisconnect( AFlags: Word; AHandle: PCoRequest ):
    TCoStatus;
  var
    Status : TCoStatus;
  begin
    Status := IpxDisconnect( AFlags );

    if (AFlags and COF_PROPAGATE <> 0) and (Status = CST_SUCCESS) then
    begin
      Status := LowerDisconnect(
                  COF_PROPAGATE or COF_QUIET or COF_IFNOTUSED,  AHandle );
      if Status = CST_PENDING then IPX_TempHandle := AHandle;
    end;

    MiniportDisconnect := Status;
  end;


  {-------------------------------
  { TCoIpx.IpxBind
  {---
  { !@#
  {---
  {
  }

  function TCoIpx.IpxBind( AAddress: PCoAddress; AFlags: Word ): TCoStatus;
  var
    Status : TCoStatus;
    Sock   : Word;
  begin

    if IPX_Bound then
    begin

      if AFlags and COF_QUIET <> 0 then
      begin
        Status := CST_SUCCESS
      end
      else
        Status := CST_ERR_ISBOUND;

    end
    else begin
      if CoIsAddrValid( AAddress ) then
      begin
        { Zaregistrujeme cislo soketu u Ipx multiplexeru }
        Sock := PIpxAddress(AAddress)^.Sock;

        Status := LowerAddToReceiveList( 0, @Sock, SizeOf( Word ) );
        if Status = CST_SUCCESS then
        begin
          IPX_LocalAddress := PIpxAddress(AAddress)^.Data;
          IPX_Bound := True;
        end;
      end
      else { zatim nemame implicitni adresu }
        Status := CST_ERR_INVADDR;
    end;

    IpxBind := Status;

  end;


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


  function TCoIpx.MiniportBind( AAddress: PCoAddress; AFlags: Word;
    AHandle: PCoRequest ): TCoStatus;
  var
    Status : TCoStatus;
  begin

    if AFlags and COF_PROPAGATE <> 0 then
    begin
      { Automaticky bind nizsi vrstvy }
      Status := LowerBind( nil, COF_PROPAGATE or COF_QUIET, AHandle );

      case Status of
        CST_SUCCESS: Status := IpxBind( AAddress, AFlags );
        CST_PENDING: IPX_TempHandle := AHandle;
      end;

    end
    else begin
      Status := IpxBind( AAddress, AFlags );
    end;

    MiniportBind := Status;
  end;

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

  function TCoIpx.IpxUnbind( AFlags: Word ): TCoStatus;
  var
    Status : TCoStatus;
  begin
    if not IPX_Bound then
    begin

      if AFlags and COF_QUIET <> 0 then
      begin
        Status := CST_SUCCESS
      end
      else
        Status := CST_ERR_NOTBOUND;

    end
    else begin
      Status := LowerRemoveFromReceiveList( 0 );

      if Status = CST_SUCCESS then
      begin
        IPX_Bound := False;
      end;
    end;

    IpxUnbind := Status;
  end;


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


  procedure TCoIpx.MiniportCancel( ACancelAll: Boolean );
  begin
    if (IPX_TempHandle <> nil) and (ACancelAll or
         CoShouldCancel( IPX_TempHandle )) then
    begin
      LowerCancelIO( IPX_TempHandle, 0 );
    end;
  end;


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


  function TCoIpx.MiniportUnbind( AFlags: Word; AHandle: PCoRequest ):
    TCoStatus;
  var
    Status : TCoStatus;
  begin
    Status := IpxUnbind( AFlags );

    if (AFlags and COF_PROPAGATE <> 0) and (Status = CST_SUCCESS) then
    begin
      Status := LowerUnbind(
                  COF_PROPAGATE or COF_QUIET or COF_IFNOTUSED, AHandle );
      if Status = CST_PENDING then IPX_TempHandle := AHandle;
    end;

    MiniportUnbind := Status;
  end;


  {-------------------------------
  { TCoIpx.ProtocolReceiveFilter
  {---
  { !@#
  {---
  {
  }

  { async funkce }
  function TCoIpx.ProtocolReceiveFilter( APacket: PCoPacket ): Boolean;
  var
    Hdr : PIpxHeader;
  begin
    ProtocolReceiveFilter := False;

    if IPX_Bound then
    begin
      { Pote co ipx protokol, nastavi promennou IPX_Bound nesmi menit
        IPX_LocalAddress }

      with PEthAddress(@APacket^.Address)^ do
      begin
        case Format of
          ETH_FORMAT_SNAP,
          ETH_FORMAT_ETHII:
             if Protocol <> ETH_TYPE_IPX then Exit;

          ETH_FORMAT_LLC:
             if DSAP <> $E0 then Exit;
        else
          Exit;
        end;
      end;

      Hdr := PIpxHeader( APacket^.DataPtr );

      { Minimalni delka paketu = delka hlavicky + 1 }
      if Swap( APacket^.DataSize ) <= SizeOf( TIpxHeader ) then Exit;

      with Hdr^ do
      begin
        { Typ paketu }
        if not (IpxType in [IPX_TYPE_UNKNOWN, IPX_TYPE_SAP]) then Exit;

        { Adresa uzlu }
        if not MemCmp( @IpxDest.Node, @IPX_LocalAddress.Node,
            SizeOf( TIpxNodeAddress ) ) then Exit;

        { Cislo soketu }
        if IpxDest.Sock <> IPX_LocalAddress.Sock then Exit;
        { !@# zkontrolovat Net number }
        { !@# mozna delku paketu }
        { !@# mozna crc }
      end;

      ProtocolReceiveFilter := True;
    end;
  end;


  {-------------------------------
  { TCoIpx.ProtocolReceive
  {---
  { !@#
  {---
  {
  }

  procedure TCoIpx.ProtocolReceive( APacket: PCoPacket );
  begin
    if IPX_Bound then
    begin
      APacket^.RemoveHeader( SizeOf( TIpxHeader ) );
      UpperReceivePacket( APacket );
    end;
  end;


  {-------------------------------
  { TCoIpx.ProtocolSendComplete
  {---
  { !@#
  {---
  {
  }

  function  TCoIpx.ProtocolSendComplete( AHandle: PCoRequest;
    Status: TCoStatus ): Boolean;
  begin
    { Tato vrstva negeneruje zadne pakety,
      takze vse co dostane od nizsi vrstvy, lifruje okamzite
      vrstve vyssi }
    UpperComplete( AHandle, Status );
    ProtocolSendComplete := True;
  end;


  {-------------------------------
  { TCoIpx.ProtocolBindComplete
  {---
  { !@#
  {---
  {
  }


  function TCoIpx.ProtocolBindComplete( AHandle: PCoRequest;
    Status: TCoStatus ): Boolean;
  var
    Slot : PCoRequestSlot;
  begin
    if IPX_TempHandle = AHandle then
    begin
      IPX_TempHandle := nil;
      if Status = CST_SUCCESS then
      begin
        Slot := CoGetDeviceSlot( AHandle );
        with TCoBindParams(Slot^.Params) do
          UpperComplete( AHandle,  IpxBind( @Address, Flags ) );
      end
      else
        UpperComplete( AHandle, Status );
    end
    else
      ProtocolBindComplete := False;
  end;

  {-------------------------------
  { TCoIpx.ProtocolUnbindComplete
  {---
  { !@#
  {---
  {
  }


  function TCoIpx.ProtocolUnbindComplete( AHandle: PCoRequest;
    Status: TCoStatus ): Boolean;
  begin
    if IPX_TempHandle = AHandle then
    begin
      IPX_TempHandle := nil;
      UpperComplete( AHandle, Status );
      ProtocolUnbindComplete := True;
    end
    else
      ProtocolUnbindComplete := False;
  end;


  {-------------------------------
  { TCoIpx.ProtocolConnectComplete
  {---
  { !@#
  {---
  {
  }


  function TCoIpx.ProtocolConnectComplete( AHandle: PCoRequest;
    Status: TCoStatus ): Boolean;
  var
    Slot : PCoRequestSlot;
  begin
    if IPX_TempHandle = AHandle then
    begin
      IPX_TempHandle := nil;
      if Status = CST_SUCCESS then
      begin
        Slot := CoGetDeviceSlot( AHandle );
        with TCoConnectParams(Slot^.Params) do
          UpperComplete( AHandle,  IpxConnect( @Address, Flags ) );
      end
      else
        UpperComplete( AHandle, Status );
    end
    else
     ProtocolConnectComplete := False;
  end;

  {-------------------------------
  { TCoIpx.ProtocolDisconnectComplete
  {---
  { !@#
  {---
  {
  }

  function TCoIpx.ProtocolDisconnectComplete( AHandle: PCoRequest;
    Status: TCoStatus ): Boolean;
  begin
    if IPX_TempHandle = AHandle then
    begin
      IPX_TempHandle := nil;
      UpperComplete( AHandle, Status );
      ProtocolDisconnectComplete := True;
    end
    else
      ProtocolDisconnectComplete := False;
  end;

  {-------------------------------
  { TCoIpx.ProtocolIoctlComplete
  {---
  { !@#
  {---
  {
  }

  function TCoIpx.ProtocolIoctlComplete( AHandle: PCoRequest;
    Status: TCoStatus ): Boolean;
  begin
  end;

begin
  CoRegisterDeviceClass( @IpxMuxClass );
  CoRegisterDeviceClass( @IpxClass );
end.
