/*+++

Copyright (c) 1992  Microsoft Corporation

Module Name:

	ddp.h

Abstract:

	This module contains the DDP address object and ddp related definitions

Author:

	Jameel Hyder (jameelh@microsoft.com)
	Nikhil Kamkolkar (nikhilk@microsoft.com)

Revision History:
	19 Jun 1992		Initial Version

Notes:	Tab stop: 4
--*/

#ifndef	_DDP_
#define	_DDP_

// Network number information.
#define FIRST_VALID_NETWORK			0x0001
#define LAST_VALID_NETWORK			0xFFFE
#define FIRST_STARTUP_NETWORK		0xFF00
#define LAST_STARTUP_NETWORK		0xFFFE
#define NULL_NETWORK				0x0000
#define UNKNOWN_NETWORK				NULL_NETWORK
#define CABLEWIDE_BROADCAST_NETWORK	NULL_NETWORK

//	Appletalk Sockets Definitions
#define UNKNOWN_SOCKET				0
#define	DYNAMIC_SOCKET				UNKNOWN_SOCKET
#define LAST_VALID_SOCKET			254
#define FIRST_DYNAMIC_SOCKET		128
#define LAST_DYNAMIC_SOCKET			LAST_VALID_SOCKET
#define FIRST_STATIC_SOCKET			1
#define FIRST_VALID_SOCKET			FIRST_STATIC_SOCKET
#define LAST_STATIC_SOCKET			127

// "Well known" sockets:
#define RTMP_SOCKET					1		// RTMP
#define NAMESINFORMATION_SOCKET		2		// NBP
#define ECHOER_SOCKET				4		// EP
#define ZONESINFORMATION_SOCKET		6		// ZIP

#define LAST_APPLE_RESD_SOCKET		0x3F	// Apple reserves 1 thru 0x3F

//	DDP Datagram Definitions
#define MAX_DGRAM_SIZE				586
#define MAX_LDDP_PKT_SIZE			600		// Really 599, but even is nicer
#define MAX_SDDP_PKT_SIZE			592		// Again, really 591

//	Define temporary buffer sizes, these must be big enough to hold both all
//	of the packet data plus any link/hardware headers...
#define MAX_PKT_SIZE				(MAX_HDR_LEN + MAX_LDDP_PKT_SIZE)

#define DDP_LEN_MASK1				0x03	// High order 3 bits of length
#define DDP_LEN_MASK2				0xFF	// Next byte of length

// DDP packet offsets (skipping Link/Hardware headers):
#define SDDP_HDR_LEN				5

#define SDDP_LEN_OFFSET				0
#define SDDP_DEST_SOCKET_OFFSET		2
#define SDDP_SRC_SOCKET_OFFSET		3
#define SDDP_PROTO_TYPE_OFFSET		4
#define SDDP_DGRAM_OFFSET			5

#define LDDP_HDR_LEN				13

#define LDDP_LEN_OFFSET				0
#define LDDP_CHECKSUM_OFFSET		2
#define LDDP_DEST_NETWORK_OFFSET	4
#define LDDP_SRC_NETWORK_OFFSET		6
#define LDDP_DEST_NODE_OFFSET		8
#define LDDP_SRC_NODE_OFFSET		9
#define LDDP_DEST_SOCKET_OFFSET		10
#define LDDP_SRC_SOCKET_OFFSET		11
#define LDDP_PROTO_TYPE_OFFSET		12
#define LDDP_DGRAM_OFFSET			13

#define LEADING_UNCHECKSUMED_BYTES	4
#define LDDP_HOPCOUNT_MASK			0x3C

#define DECIMAL_BASE    			10

// DDP protocol types:
#define	DDPPROTO_ANY				0	// Used to allow any protocol packet

#define DDPPROTO_DDP    			0
#define DDPPROTO_RTMPRESPONSEORDATA 1
#define DDPPROTO_NBP				2
#define DDPPROTO_ATP				3
#define DDPPROTO_EP					4
#define DDPPROTO_RTMPREQUEST		5
#define DDPPROTO_ZIP				6
#define DDPPROTO_ADSP				7
#define DDPPROTO_MAX        		255

typedef	struct _DDPEVENT_INFO
{
	//	Event handler routines: DDP Only has RecvDatagram/Error handlers

	//	The following function pointer always points to a TDI_IND_RECEIVE_DATAGRAM
	//	event handler for the address.
	PTDI_IND_RECEIVE_DATAGRAM	ev_RcvDgramHandler;
	PVOID						ev_RcvDgramCtx;

	// The following function pointer always points to a TDI_IND_ERROR
	// handler for the address.
	PTDI_IND_ERROR				ev_ErrHandler;
	PVOID						ev_ErrCtx;
	PVOID						ev_ErrOwner;

	//	Winsock assumes a buffering transport. So we buffer the last datagram
	//	indicated that was not accepted.
	BYTE						ev_IndDgram[MAX_DGRAM_SIZE];
	int							ev_IndDgramLen;
	int							ev_IndProto;

	//	Source address of buffered datagram
	ATALK_ADDR					ev_IndSrc;

} DDPEVENT_INFO, *PDDPEVENT_INFO;



//	Handler type for the DDP address object
typedef	VOID	(*DDPAO_HANDLER)(
					IN	PPORT_DESCRIPTOR	pPortDesc,
					IN	struct _DDP_ADDROBJ *pAddr,
					IN	PBYTE				pPkt,
					IN	USHORT				pPktLen,
					IN	PATALK_ADDR			pSrcAddr,
					IN	PATALK_ADDR			pActDest,
					IN	ATALK_ERROR			ErrorCode,
					IN	BYTE				pDdpType,
					IN	PVOID				pHandlerCtx,
					IN	BOOLEAN				OptimizedPath,
					IN	PVOID				OptimizeCtx);

//	DDP Address Object
//	This is the basic address object in the stack. All other address objects
//	eventually resolve to this one. It also holds the AppletalkSocket opened
//	as its actual address. One address object corresponds to one address.

//	DDP ADDRESS OBJECT	STATES
#define	DDPAO_DGRAM_EVENT		0x00000001
#define	DDPAO_DGRAM_ACTIVE		0x00000002
#define	DDPAO_DGRAM_PENDING		0x00000004
#define DDPAO_SOCK_INTERNAL     0x00000008
#define DDPAO_SOCK_PNPZOMBIE    0x00000010
#define	DDPAO_CLOSING			0x80000000


#define	DDPAO_SIGNATURE			(*(PULONG)"DDPA")
#define	VALID_DDP_ADDROBJ(pDdpAddr)	(((pDdpAddr) != NULL) &&	\
			(((struct _DDP_ADDROBJ *)(pDdpAddr))->ddpao_Signature == DDPAO_SIGNATURE))
typedef struct _DDP_ADDROBJ
{
	ULONG					ddpao_Signature;

	//	This will be a hash overflow list. Hash on the internet address.
	//	List of address objects on the node linkage
	struct _DDP_ADDROBJ	*	ddpao_Next;

	ULONG					ddpao_RefCount;

	//	State of the address object
	ULONG					ddpao_Flags;

	//	Backpointer to the node on which this socket exists
	struct _ATALK_NODE	 *	ddpao_Node;

	//	The Appletalk address number for this object
	ATALK_ADDR				ddpao_Addr;

	//	List of NBP names registered on this socket
	struct _REGD_NAME	*	ddpao_RegNames;

	//	List of NBP names being looked up, registered or confirmed on
	//	this socket.
	struct _PEND_NAME	*	ddpao_PendNames;

	//	Linked list of pending ddp reads
	LIST_ENTRY				ddpao_ReadLinkage;

	//	The protocol type to use for datagrams sent on this socket and
	//	which can be received on this socket. 0 => no restrictions.
	BYTE					ddpao_Protocol;

	ATALK_SPIN_LOCK			ddpao_Lock;
	PATALK_DEV_CTX			ddpao_DevCtx;

	//	The handler below is an listener for the upper layers. Note that
	//	this will take precedence over a incoming datagram event handler
	//	which would be set in ddpao_EventInfo.
	DDPAO_HANDLER			ddpao_Handler;
	PVOID					ddpao_HandlerCtx;

	//	This structure is allocated when setting an event handler
	//	on this socket. All the event handler addresses are part of this
	//	structure.
	PDDPEVENT_INFO			ddpao_EventInfo;

	//	Completion routine to be called when socket is closed
	GENERIC_COMPLETION		ddpao_CloseComp;
	PVOID					ddpao_CloseCtx;
} DDP_ADDROBJ, *PDDP_ADDROBJ;

//	Receive datagram completion: This will return the mdl we pass in along
//	with the received length written into the mdl. Also, the protocol type
//	and the RemoteAddress are passed back. The receive context will be the
//	irp for the request. As will be the send context.
typedef	VOID	(*RECEIVE_COMPLETION)(
						IN	ATALK_ERROR			ErrorCode,
						IN	PAMDL				OpaqueBuffer,
						IN	USHORT          	LengthReceived,
						IN	PATALK_ADDR			RemoteAddress,
						IN	PVOID				ReceiveContext);

typedef	VOID	(FASTCALL *WRITE_COMPLETION)(
						IN	NDIS_STATUS			Status,
						IN	PVOID				Ctx);

typedef	VOID	(FASTCALL *TRANSMIT_COMPLETION)(
						IN	NDIS_STATUS			Status,
						IN	struct _SEND_COMPL_INFO	*	pInfo);

//	If the above routine was set in the AtalkDdpSend(), then
//	then context values would be:
//	Ctx1 = pddp address object
//	Ctx2 = pbuffer descriptor
//	Ctx3 = Only for DdpWrite calls, this will be a pointer to the
//			write structure enqueued in the ddp address object.
//
//	If the above routine was set in the AtalkDdpTransmit(), then
//	the context values would be (as specified by the client of
//	course):
//	Ctx1 = pport descriptor
//	Ctx2 = pbuffer descriptor
//	Ctx3 = not used.
//
//	These are only suggested ideas, but probably is what the internal
//	stack routines will use.

//	This is used to store a pending read on a particular socket.
typedef struct _DDP_READ
{
	//	Linkage chain for reads on a socket.
	LIST_ENTRY			dr_Linkage;

	PAMDL				dr_OpBuf;
	USHORT				dr_OpBufLen;

	RECEIVE_COMPLETION	dr_RcvCmp;
	PVOID				dr_RcvCtx;

} DDP_READ, *PDDP_READ;


//	This is used to store a pending write on a particular socket
//	DDP will create a buffer descriptor for the header
//	and will chain it in front of the buffer descriptor passed in.
//	A pointer to this structure will then be passed as a completion
//	context to DdpSend.
typedef struct _DDP_WRITE
{
	//	Linkage chain for writes on a socket.
	LIST_ENTRY		dw_Linkage;

	//	The buffer descriptor chain, including the ddp buffer
	//	descriptor containing the ddp/optional/link headers.
	PBUFFER_DESC	dw_BufferDesc;

	//	Write completion
	//	This will be called with the context (which will be a pointer
	//	to the write irp) after the write completes.
	WRITE_COMPLETION	dw_WriteRoutine;
	PVOID				dw_WriteCtx;

} DDP_WRITE, *PDDP_WRITE;

//
//	CANCEL IRP Functionality for NT:
//
//	We have decided that if we receive a cancel irp for a particular request,
//	we will shutdown the file object associated with that request, whether it
//	be a connection object or an address object. This implies that the socket/
//	connection/listener will be closed, thus cancelling *all* pending requests.
//

ATALK_ERROR
AtalkDdpOpenAddress(
	IN		PPORT_DESCRIPTOR		pPortDesc,
	IN		BYTE					Socket,
	IN	OUT	PATALK_NODEADDR			pDesiredNode	OPTIONAL,
	IN		DDPAO_HANDLER			pSktHandler		OPTIONAL,
	IN		PVOID					pSktCtx			OPTIONAL,
	IN		BYTE					ProtoType		OPTIONAL,
	IN		PATALK_DEV_CTX			pDevCtx,
	OUT		PDDP_ADDROBJ	*		pAddr);

ATALK_ERROR
AtalkDdpCloseAddress(
	IN	PDDP_ADDROBJ				pAddr,
	IN	GENERIC_COMPLETION			pCloseCmp	OPTIONAL,	
	IN	PVOID						pCloseCtx	OPTIONAL);

ATALK_ERROR
AtalkDdpPnPSuspendAddress(
	IN	PDDP_ADDROBJ			pDdpAddr);

ATALK_ERROR
AtalkDdpCleanupAddress(
	IN	PDDP_ADDROBJ				pAddr);

ATALK_ERROR
AtalkDdpInitCloseAddress(
	IN	PPORT_DESCRIPTOR			pPortDesc,
	IN	PATALK_ADDR					pAtalkAddr);

ATALK_ERROR
AtalkInitDdpOpenStaticSockets(
	IN		PPORT_DESCRIPTOR		pPortDesc,
	IN	OUT PATALK_NODE				pNode);

ATALK_ERROR
AtalkDdpReceive(
	IN		PDDP_ADDROBJ			pAddr,
	IN		PAMDL					pAmdl,
	IN		USHORT					AmdlLen,
	IN		ULONG					RecvFlags,
	IN		RECEIVE_COMPLETION		pRcvCmp,
	IN		PVOID					pRcvCtx);

ATALK_ERROR
AtalkDdpSend(
	IN	PDDP_ADDROBJ				pDdpAddr,
	IN	PATALK_ADDR					DestAddr,
	IN	BYTE						ProtoType,
	IN	BOOLEAN						DefinitelyRemoteAddr,
	IN	PBUFFER_DESC				pBufDesc,
	IN	PBYTE						pOptHdr			OPTIONAL,
	IN	USHORT						OptHdrLen		OPTIONAL,
	IN	PBYTE						pZoneMcastAddr	OPTIONAL,
	IN	struct _SEND_COMPL_INFO	*	pInfo			OPTIONAL);

ATALK_ERROR
AtalkDdpTransmit(
	IN	PPORT_DESCRIPTOR			pPortDesc,
	IN	PATALK_ADDR					SrcAddr,
	IN	PATALK_ADDR					DestAddr,
	IN	BYTE						ProtoType,
	IN	PBUFFER_DESC				pBufDesc,
	IN	PBYTE						pOptHdr			OPTIONAL,
	IN	USHORT						OptHdrLen		OPTIONAL,
	IN	USHORT						HopCnt,
	IN	PBYTE						pMcastAddr		OPTIONAL,	
	IN	PATALK_NODEADDR				pXmitDestNode	OPTIONAL,
	IN	struct _SEND_COMPL_INFO	*	pInfo			OPTIONAL);

VOID
AtalkDdpSendComplete(
	IN	NDIS_STATUS					Status,
	IN	PBUFFER_DESC				pBufDesc,
	IN	struct _SEND_COMPL_INFO	*	pInfo			OPTIONAL);

VOID
AtalkDdpPacketIn(
	IN	PPORT_DESCRIPTOR			pPortDesc,
	IN	PBYTE						pLinkHdr,
	IN	PBYTE						pPkt,
	IN	USHORT						PktLen,
    IN  BOOLEAN                     fWanPkt);

VOID
AtalkDdpQuery(
	IN	PDDP_ADDROBJ				pDdpAddr,
	IN	PAMDL						pAmdl,
	OUT	PULONG						BytesWritten);

VOID
AtalkDdpRefByAddr(
	IN		PPORT_DESCRIPTOR		pPortDesc,
	IN		PATALK_ADDR				pAtalkAddr,
	OUT		PDDP_ADDROBJ	*		ppDdpAddr,
	OUT		PATALK_ERROR			pErr);

VOID
AtalkDdpRefByAddrNode(
	IN		PPORT_DESCRIPTOR		pPortDesc,
	IN		PATALK_ADDR				pAtalkAddr,
	IN		PATALK_NODE				pAtalkNode,
	OUT		PDDP_ADDROBJ	*		ppDdpAddr,
	OUT		PATALK_ERROR			pErr);

VOID
AtalkDdpRefNextNc(
	IN	PDDP_ADDROBJ				pDdpAddr,
	IN	PDDP_ADDROBJ	*			ppDdpAddr,
	OUT	PATALK_ERROR				pErr);

VOID FASTCALL
AtalkDdpDeref(
	IN	OUT		PDDP_ADDROBJ		pDdpAddr,
	IN			BOOLEAN				AtDpc);

VOID
AtalkDdpOutBufToNodesOnPort(
	IN	PPORT_DESCRIPTOR			pPortDesc,
	IN	PATALK_ADDR					pSrc,
	IN	PATALK_ADDR					pDest,
	IN	BYTE						ProtoType,
	IN	PBUFFER_DESC				pBufDesc,
	IN	PBYTE						pOptHdr,
	IN	USHORT						OptHdrLen,
	OUT	PBOOLEAN					Delivered);

VOID
AtalkDdpInPktToNodesOnPort(
	IN	PPORT_DESCRIPTOR			pPortDesc,
	IN	PATALK_ADDR					pDest,
	IN	PATALK_ADDR					pSrc,
	IN	BYTE						ProtoType,
	IN	PBYTE						pPkt,
	IN	USHORT						PktLen,
	OUT	PBOOLEAN					Routed);

VOID
AtalkDdpInvokeHandlerBufDesc(
	IN		PPORT_DESCRIPTOR		pPortDesc,
	IN		PDDP_ADDROBJ			pDdpAddr,
	IN		PATALK_ADDR				pSrcAddr,
	IN		PATALK_ADDR				pActDest,
	IN		BYTE					ProtoType,
	IN		PBUFFER_DESC			pBufDesc,
	IN		PBYTE					pOptHdr,
	IN		USHORT					OptHdrLen);

VOID
AtalkDdpInvokeHandler(
	IN		PPORT_DESCRIPTOR		pPortDesc,
	IN		PDDP_ADDROBJ			pDdpAddr,
	IN		PATALK_ADDR				pSrcAddr,
	IN		PATALK_ADDR				pActDest,
	IN		BYTE					ProtoType,
	IN		PBYTE					pPkt,
	IN		USHORT					PktLen);

USHORT
AtalkDdpCheckSumBuffer(
	IN	PBYTE						Buffer,
	IN	USHORT						BufLen,
	IN	USHORT						CurrentCheckSum);

USHORT
AtalkDdpCheckSumBufferDesc(
	IN	PBUFFER_DESC				pBuffDesc,
	IN	USHORT						Offset);

USHORT
AtalkDdpCheckSumPacket(
	IN	PBYTE						pHdr,
	IN	USHORT						HdrLen,
	IN	PBYTE						pPkt,
	IN	USHORT						PktLen);

VOID
AtalkDdpNewHandlerForSocket(
	IN	PDDP_ADDROBJ				pDdpAddr,
	IN	DDPAO_HANDLER				pSktHandler,
	IN	PVOID						pSktHandlerCtx);

//	MACROS
#define	DDP_MSB_LEN(L)			(((L) >> 8) & 0x03)
#define	DDP_GET_LEN(P)			((((*P) & 0x03) << 8) + *(P+1))
#define	DDP_GET_HOP_COUNT(P)	(((*P) >> 2) & 0x0F)
#define	DDP_HOP_COUNT(H)		(((H) & 0x0F) << 2)

#if DBG

#define	AtalkDdpReferenceByPtr(pDdpAddr, pErr)					\
	{															\
		KIRQL	OldIrql;										\
																\
		ACQUIRE_SPIN_LOCK(&(pDdpAddr)->ddpao_Lock, &OldIrql);	\
		AtalkDdpRefByPtrNonInterlock(pDdpAddr, pErr);			\
		RELEASE_SPIN_LOCK(&(pDdpAddr)->ddpao_Lock, OldIrql);	\
		DBGPRINT(DBG_COMP_DDP, DBG_LEVEL_REFDDP,				\
				("AtalkDdpReferenceByPtr: %s %d PostCount %d\n",\
				__FILE__, __LINE__,pDdpAddr->ddpao_RefCount));	\
	}

#define	AtalkDdpReferenceByPtrDpc(pDdpAddr, pErr)				\
	{															\
		ACQUIRE_SPIN_LOCK_DPC(&(pDdpAddr)->ddpao_Lock);			\
		AtalkDdpRefByPtrNonInterlock(pDdpAddr, pErr);			\
		RELEASE_SPIN_LOCK_DPC(&(pDdpAddr)->ddpao_Lock);			\
		DBGPRINT(DBG_COMP_DDP, DBG_LEVEL_REFDDP,				\
				("AtalkDdpReferenceByPtr: %s %d PostCount %d\n",\
				__FILE__, __LINE__,pDdpAddr->ddpao_RefCount));	\
	}

#define	AtalkDdpRefByPtrNonInterlock(pDdpAddr, pErr)			\
	{															\
		ASSERT (VALID_DDP_ADDROBJ(pDdpAddr));					\
																\
		*pErr = ATALK_DDP_CLOSING;								\
																\
		if ((pDdpAddr->ddpao_Flags & DDPAO_CLOSING) == 0)		\
		{														\
			pDdpAddr->ddpao_RefCount++;							\
			*pErr = ATALK_NO_ERROR;								\
		}														\
		DBGPRINT(DBG_COMP_DDP, DBG_LEVEL_REFDDP,				\
				("AtalkDdpReferenceByPtrNonInterlock: %s %d PostCount %d\n",\
				__FILE__, __LINE__,							\
				pDdpAddr->ddpao_RefCount));					\
	}

#define	AtalkDdpReferenceNextNc(pDdpAddr, ppDdpAddr, pErr)		\
	{															\
		AtalkDdpRefNextNc(pDdpAddr, ppDdpAddr, pErr);			\
		if (ATALK_SUCCESS(*pErr))								\
		{														\
			DBGPRINT(DBG_COMP_DDP, DBG_LEVEL_REFDDP,			\
					("DdpRefNextNc : %s %d PostCount %d\n",		\
					__FILE__, __LINE__,							\
					(*ppDdpAddr)->ddpao_RefCount));				\
		}														\
	}

#define	AtalkDdpReferenceByAddr(pPortDesc, pAddr, ppDdpAddr, pErr)	\
	{															\
		AtalkDdpRefByAddr(pPortDesc, pAddr, ppDdpAddr, pErr);	\
		if (ATALK_SUCCESS(*pErr))								\
		{														\
			DBGPRINT(DBG_COMP_DDP, DBG_LEVEL_REFDDP,			\
					("AtalkDdpReferenceByAddr: %s %d PostCount %d\n",\
					__FILE__, __LINE__,							\
					(*ppDdpAddr)->ddpao_RefCount));				\
		}														\
	}

#define	AtalkDdpDereference(pDdpAddr)							\
	{															\
		DBGPRINT(DBG_COMP_DDP, DBG_LEVEL_REFDDP,				\
				("AtalkDdpDereference: %s %d PreCount %d\n",	\
				__FILE__, __LINE__,pDdpAddr->ddpao_RefCount));	\
		AtalkDdpDeref(pDdpAddr, FALSE);							\
	}

#define	AtalkDdpDereferenceDpc(pDdpAddr)						\
	{															\
		DBGPRINT(DBG_COMP_DDP, DBG_LEVEL_REFDDP,				\
				("AtalkDdpDereferenceDpc: %s %d PreCount %d\n",	\
				__FILE__, __LINE__,pDdpAddr->ddpao_RefCount));	\
		AtalkDdpDeref(pDdpAddr, TRUE);							\
	}

#else
#define	AtalkDdpReferenceByPtr(pDdpAddr, pErr) 					\
	{															\
		KIRQL	OldIrql;										\
																\
		ACQUIRE_SPIN_LOCK(&(pDdpAddr)->ddpao_Lock, &OldIrql);	\
		AtalkDdpRefByPtrNonInterlock(pDdpAddr, pErr);			\
		RELEASE_SPIN_LOCK(&(pDdpAddr)->ddpao_Lock, OldIrql);	\
	}

#define	AtalkDdpReferenceByPtrDpc(pDdpAddr, pErr)				\
	{															\
		ACQUIRE_SPIN_LOCK_DPC(&(pDdpAddr)->ddpao_Lock);			\
		AtalkDdpRefByPtrNonInterlock(pDdpAddr, pErr);			\
		RELEASE_SPIN_LOCK_DPC(&(pDdpAddr)->ddpao_Lock);			\
	}

#define	AtalkDdpRefByPtrNonInterlock(pDdpAddr, pErr)			\
	{															\
		*pErr = ATALK_DDP_CLOSING;								\
																\
		if ((pDdpAddr->ddpao_Flags & DDPAO_CLOSING) == 0)		\
		{														\
			pDdpAddr->ddpao_RefCount++;							\
			*pErr = ATALK_NO_ERROR;								\
		}														\
	}

#define	AtalkDdpReferenceByAddr(pPortDesc, pAddr, ppDdpAddr, pErr) \
		AtalkDdpRefByAddr(pPortDesc, pAddr, ppDdpAddr, pErr)

#define	AtalkDdpReferenceNextNc(pDdpAddr, ppDdpAddr, pErr)		\
		AtalkDdpRefNextNc(pDdpAddr, ppDdpAddr, pErr)

#define	AtalkDdpDereference(pDdpAddr) 							\
		AtalkDdpDeref(pDdpAddr, FALSE)

#define	AtalkDdpDereferenceDpc(pDdpAddr) 						\
		AtalkDdpDeref(pDdpAddr, TRUE)
#endif

#define	NET_ON_NONEXTPORT(pPort)								\
			(pPort->pd_LtNetwork)

#define	NODE_ON_NONEXTPORT(pPort)								\
			(((pPort)->pd_Nodes != NULL) ?						\
				(pPort)->pd_Nodes->an_NodeAddr.atn_Node : 0)

ATALK_ERROR
atalkDdpAllocSocketOnNode(
	IN		PPORT_DESCRIPTOR		pPortDesc,
	IN		BYTE					Socket,
	IN		PATALK_NODE				pAtalkNode,
	IN		DDPAO_HANDLER			pSktHandler	OPTIONAL,
	IN		PVOID					pSktCtx			OPTIONAL,
	IN		BYTE					ProtoType		OPTIONAL,
	IN		PATALK_DEV_CTX			pDevCtx,
	OUT		PDDP_ADDROBJ			pDdpAddr);

VOID
atalkDdpInitCloseComplete(
	IN	ATALK_ERROR					Error,
	IN	PVOID						Ctx);

/*
PBRE
atalkDdpFindInBrc(
	IN	PPORT_DESCRIPTOR			pPortDesc,
	IN	PATALK_NODEADDR				pDestNodeAddr);
*/
#define	atalkDdpFindInBrc(_pPortDesc, _Network, _ppBre)		\
{															\
	USHORT		index;										\
	KIRQL		OldIrql;									\
	PBRE		pBre;										\
															\
	index = (_Network) & (PORT_BRC_HASH_SIZE - 1);			\
															\
	ACQUIRE_SPIN_LOCK(&(_pPortDesc)->pd_Lock, &OldIrql);	\
															\
	for (pBre = (_pPortDesc)->pd_Brc[index];				\
		 pBre != NULL;										\
		 pBre = pBre->bre_Next)								\
	{														\
		if ((_Network) == pBre->bre_Network)				\
		{													\
			break;											\
		}													\
	}														\
															\
	RELEASE_SPIN_LOCK(&(_pPortDesc)->pd_Lock, OldIrql);		\
															\
 	*(_ppBre) = pBre;										\
}


BOOLEAN
atalkDdpFindAddrOnList(
	IN	PATALK_NODE					pAtalkNode,
	IN	ULONG						Index,
	IN	BYTE						Socket,
	OUT	PDDP_ADDROBJ	*			ppDdpAddr);

#define	IS_VALID_SOCKET(Socket)								\
			((Socket == DYNAMIC_SOCKET)			||			\
			 (Socket == LAST_DYNAMIC_SOCKET)	||			\
			 ((Socket >= FIRST_STATIC_SOCKET) &&			\
				(Socket <= LAST_STATIC_SOCKET)))

#endif	// _DDP_