/*++ Copyright (c) 1992 Microsoft Corporation Module Name: atkact.c Abstract: This module contains the TDI action support code. Author: Jameel Hyder (jameelh@microsoft.com) Nikhil Kamkolkar (nikhilk@microsoft.com) Revision History: 19 Jun 1992 Initial Version Notes: Tab stop: 4 --*/ #include #pragma hdrstop #define FILENUM ATKACT #ifdef ALLOC_PRAGMA #pragma alloc_text(PAGE_NZ, AtalkNbpTdiAction) #pragma alloc_text(PAGE_NZ, AtalkZipTdiAction) #pragma alloc_text(PAGE, AtalkAspTdiAction) #pragma alloc_text(PAGE, AtalkAdspTdiAction) #pragma alloc_text(PAGE_PAP, AtalkPapTdiAction) #pragma alloc_text(PAGEASPC, AtalkAspCTdiAction) #endif ATALK_ERROR AtalkStatTdiAction( IN PVOID pObject, // Address or Connection object IN struct _ActionReq * pActReq // Pointer to action request ) /*++ Routine Description: This is the entry for Statistics TdiAction call. There are no input parameters. The statistics structure is returned. Arguments: Return Value: --*/ { ATALK_ERROR Error = ATALK_NO_ERROR; PPORT_DESCRIPTOR pPortDesc; KIRQL OldIrql; ULONG BytesCopied; LONG Offset; if (pActReq->ar_MdlSize < (SHORT)(sizeof(ATALK_STATS) + sizeof(ATALK_PORT_STATS) * AtalkNumberOfPorts)) Error = ATALK_BUFFER_TOO_SMALL; else { #ifdef PROFILING // This is the only place where this is changed. And it always increases. // Also the stats are changed using ExInterlocked calls. Acquiring a lock // does little in terms of protection anyways. AtalkStatistics.stat_ElapsedTime = AtalkTimerCurrentTick/ATALK_TIMER_FACTOR; #endif TdiCopyBufferToMdl(&AtalkStatistics, 0, sizeof(ATALK_STATS), pActReq->ar_pAMdl, 0, &BytesCopied); ASSERT(BytesCopied == sizeof(ATALK_STATS)); ACQUIRE_SPIN_LOCK(&AtalkPortLock, &OldIrql); for (pPortDesc = AtalkPortList, Offset = sizeof(ATALK_STATS); pPortDesc != NULL; pPortDesc = pPortDesc->pd_Next) { TdiCopyBufferToMdl(&pPortDesc->pd_PortStats, 0, sizeof(ATALK_PORT_STATS), pActReq->ar_pAMdl, Offset, &BytesCopied); Offset += sizeof(ATALK_PORT_STATS); ASSERT(BytesCopied == sizeof(ATALK_PORT_STATS)); } RELEASE_SPIN_LOCK(&AtalkPortLock, OldIrql); } (*pActReq->ar_Completion)(Error, pActReq); return ATALK_PENDING; } ATALK_ERROR AtalkNbpTdiAction( IN PVOID pObject, // Address or Connection object IN PACTREQ pActReq // Pointer to action request ) /*++ Routine Description: This is the entry for NBP TdiAction calls. The parameters are validated and the calls are dispacthed to the appropriate NBP routines. Arguments: Return Value: --*/ { ATALK_ERROR error = ATALK_NO_ERROR; PDDP_ADDROBJ pDdpAddr; PNBPTUPLE pNbpTuple; PAGED_CODE (); // Lock the Nbp stuff, if this is the first nbp action AtalkLockNbpIfNecessary(); ASSERT (VALID_ACTREQ(pActReq)); // First get the Ddp address out of the pObject for the device switch (pActReq->ar_DevType) { case ATALK_DEV_DDP: pDdpAddr = (PDDP_ADDROBJ)pObject; break; case ATALK_DEV_ASPC: pDdpAddr = AtalkAspCGetDdpAddress((PASPC_ADDROBJ)pObject); break; case ATALK_DEV_ASP: pDdpAddr = AtalkAspGetDdpAddress((PASP_ADDROBJ)pObject); break; case ATALK_DEV_PAP: pDdpAddr = AtalkPapGetDdpAddress((PPAP_ADDROBJ)pObject); break; case ATALK_DEV_ADSP: pDdpAddr = AtalkAdspGetDdpAddress((PADSP_ADDROBJ)pObject); break; default: DBGPRINT(DBG_COMP_ACTION, DBG_LEVEL_FATAL, ("AtalkNbpTdiAction: Invalid device type !!\n")); error = ATALK_INVALID_REQUEST; break; } // reference the Ddp address. if ((pActReq->ar_ActionCode == COMMON_ACTION_NBPREGISTER_BY_ADDR) || (pActReq->ar_ActionCode == COMMON_ACTION_NBPREMOVE_BY_ADDR)) { // In this case, we don't want to access the object related to // the filehandle in the IO request, we want to access the object // related to a specific user socket address. pNbpTuple = (PNBPTUPLE)(&((PNBP_REGDEREG_PARAMS)(pActReq->ar_pParms))->RegisterTuple); AtalkDdpReferenceByAddr(AtalkDefaultPort, &(pNbpTuple->tpl_Address), &pDdpAddr, &error); } else { AtalkDdpReferenceByPtr(pDdpAddr, &error); } if (!ATALK_SUCCESS(error)) { AtalkUnlockNbpIfNecessary(); return error; } // Call Nbp to do the right stuff switch (pActReq->ar_ActionCode) { case COMMON_ACTION_NBPLOOKUP: pNbpTuple = (PNBPTUPLE)(&((PNBP_LOOKUP_PARAMS)(pActReq->ar_pParms))->LookupTuple); error = AtalkNbpAction(pDdpAddr, FOR_LOOKUP, pNbpTuple, pActReq->ar_pAMdl, (USHORT)(pActReq->ar_MdlSize/sizeof(NBPTUPLE)), pActReq); break; case COMMON_ACTION_NBPCONFIRM: pNbpTuple = (PNBPTUPLE)(&((PNBP_CONFIRM_PARAMS)(pActReq->ar_pParms))->ConfirmTuple); error = AtalkNbpAction(pDdpAddr, FOR_CONFIRM, pNbpTuple, NULL, 0, pActReq); break; case COMMON_ACTION_NBPREGISTER: pNbpTuple = (PNBPTUPLE)(&((PNBP_REGDEREG_PARAMS)(pActReq->ar_pParms))->RegisterTuple); error = AtalkNbpAction(pDdpAddr, FOR_REGISTER, pNbpTuple, NULL, 0, pActReq); break; case COMMON_ACTION_NBPREMOVE: pNbpTuple = (PNBPTUPLE)(&((PNBP_REGDEREG_PARAMS)(pActReq->ar_pParms))->RegisteredTuple); error = AtalkNbpRemove(pDdpAddr, pNbpTuple, pActReq); break; case COMMON_ACTION_NBPREGISTER_BY_ADDR: pNbpTuple = (PNBPTUPLE)(&((PNBP_REGDEREG_PARAMS)(pActReq->ar_pParms))->RegisterTuple); error = AtalkNbpAction(pDdpAddr, FOR_REGISTER, pNbpTuple, NULL, 0, pActReq); break; case COMMON_ACTION_NBPREMOVE_BY_ADDR: pNbpTuple = (PNBPTUPLE)(&((PNBP_REGDEREG_PARAMS)(pActReq->ar_pParms))->RegisteredTuple); error = AtalkNbpRemove(pDdpAddr, pNbpTuple, pActReq); break; default: DBGPRINT(DBG_COMP_ACTION, DBG_LEVEL_FATAL, ("AtalkNbpTdiAction: Invalid Nbp Action !!\n")); error = ATALK_INVALID_REQUEST; break; } AtalkDdpDereference(pDdpAddr); if (error != ATALK_PENDING) { AtalkUnlockNbpIfNecessary(); } return error; } ATALK_ERROR AtalkZipTdiAction( IN PVOID pObject, // Address or Connection object IN PACTREQ pActReq // Pointer to action request ) /*++ Routine Description: This is the entry for ZIP TdiAction calls. The parameters are validated and the calls are dispacthed to the appropriate ZIP routines. Arguments: Return Value: --*/ { ATALK_ERROR error = ATALK_INVALID_PARAMETER; PPORT_DESCRIPTOR pPortDesc = AtalkDefaultPort; PWCHAR PortName = NULL; USHORT PortNameLen; UNICODE_STRING AdapterName, UpcaseAdapterName; WCHAR UpcaseBuffer[MAX_INTERNAL_PORTNAME_LEN]; KIRQL OldIrql; int i; PAGED_CODE (); // Lock the Zip stuff, if this is the first zip action AtalkLockZipIfNecessary(); ASSERT (VALID_ACTREQ(pActReq)); if ((pActReq->ar_ActionCode == COMMON_ACTION_ZIPGETLZONESONADAPTER) || (pActReq->ar_ActionCode == COMMON_ACTION_ZIPGETADAPTERDEFAULTS)) { // Map the port name to the port descriptor if ((pActReq->ar_pAMdl != NULL) && (pActReq->ar_MdlSize > 0)) { PortName = (PWCHAR)AtalkGetAddressFromMdlSafe( pActReq->ar_pAMdl, NormalPagePriority); } if (PortName == NULL) { AtalkUnlockZipIfNecessary(); return ATALK_INVALID_PARAMETER; } PortNameLen = pActReq->ar_MdlSize/sizeof(WCHAR); // make sure there is a NULL char in the buffer for (i=0; i= MAX_INTERNAL_PORTNAME_LEN) { DBGPRINT(DBG_COMP_ACTION, DBG_LEVEL_FATAL, ("AtalkZipTdiAction: port name too big (%d) for %lx\n",PortNameLen,PortName)); ASSERT(0); return ATALK_INVALID_PARAMETER; } PortNameLen = (USHORT)i; AdapterName.Buffer = PortName; AdapterName.Length = (PortNameLen)*sizeof(WCHAR); AdapterName.MaximumLength = (PortNameLen+1)*sizeof(WCHAR); UpcaseAdapterName.Buffer = UpcaseBuffer; UpcaseAdapterName.Length = UpcaseAdapterName.MaximumLength = sizeof(UpcaseBuffer); RtlUpcaseUnicodeString(&UpcaseAdapterName, &AdapterName, FALSE); ACQUIRE_SPIN_LOCK(&AtalkPortLock, &OldIrql); // Find the port corres. to the port descriptor for (pPortDesc = AtalkPortList; pPortDesc != NULL; pPortDesc = pPortDesc->pd_Next) { if ((UpcaseAdapterName.Length == pPortDesc->pd_AdapterName.Length) && RtlEqualMemory(UpcaseAdapterName.Buffer, pPortDesc->pd_AdapterName.Buffer, UpcaseAdapterName.Length)) { break; } } RELEASE_SPIN_LOCK(&AtalkPortLock, OldIrql); if (pPortDesc == NULL) { AtalkUnlockZipIfNecessary(); return ATALK_INVALID_PARAMETER; } } else if (pActReq->ar_ActionCode == COMMON_ACTION_ZIPGETZONELIST) { PPORT_DESCRIPTOR pTempPortDesc = NULL; // This is to take care of cases when zone list is requested // but the default adapter has gone away during PnP, and // AtalkDefaultPort points to NULL if (pPortDesc == NULL) { DBGPRINT(DBG_COMP_ACTION, DBG_LEVEL_ERR, ("COMMON_ACTION_ZIPGETZONELIST: PortDesc points to NULL\n")); AtalkUnlockZipIfNecessary(); return ATALK_PORT_INVALID; } // Check if the AtalkDefaultPort is still in the list // It is possible that AtalkDefaultPort holds a non-NULL value, but // the adapter has gone away during a PnP ACQUIRE_SPIN_LOCK(&AtalkPortLock, &OldIrql); // Find the port corres. to the port descriptor for (pTempPortDesc = AtalkPortList; pTempPortDesc != NULL; pTempPortDesc = pTempPortDesc->pd_Next) { if (pTempPortDesc == pPortDesc) { break; } } RELEASE_SPIN_LOCK(&AtalkPortLock, OldIrql); if (pTempPortDesc == NULL) { DBGPRINT(DBG_COMP_ACTION, DBG_LEVEL_ERR, ("COMMON_ACTION_ZIPGETZONELIST: PortDesc structure has gone away during PnP\n")); AtalkUnlockZipIfNecessary(); return ATALK_PORT_INVALID; } } switch (pActReq->ar_ActionCode) { case COMMON_ACTION_ZIPGETMYZONE: error = AtalkZipGetMyZone( pPortDesc, TRUE, pActReq->ar_pAMdl, pActReq->ar_MdlSize, pActReq); break; case COMMON_ACTION_ZIPGETZONELIST: error = AtalkZipGetZoneList(pPortDesc, FALSE, pActReq->ar_pAMdl, pActReq->ar_MdlSize, pActReq); break; case COMMON_ACTION_ZIPGETADAPTERDEFAULTS: // Copy the network range from the port and fall through ((PZIP_GETPORTDEF_PARAMS)(pActReq->ar_pParms))->NwRangeLowEnd = pPortDesc->pd_NetworkRange.anr_FirstNetwork; ((PZIP_GETPORTDEF_PARAMS)(pActReq->ar_pParms))->NwRangeHighEnd = pPortDesc->pd_NetworkRange.anr_LastNetwork; error = AtalkZipGetMyZone(pPortDesc, FALSE, pActReq->ar_pAMdl, pActReq->ar_MdlSize, pActReq); break; case COMMON_ACTION_ZIPGETLZONESONADAPTER: case COMMON_ACTION_ZIPGETLZONES: error = AtalkZipGetZoneList(pPortDesc, TRUE, pActReq->ar_pAMdl, pActReq->ar_MdlSize, pActReq); break; default: DBGPRINT(DBG_COMP_ACTION, DBG_LEVEL_FATAL, ("AtalkZipTdiAction: Invalid Zip Action !!\n")); error = ATALK_INVALID_REQUEST; break; } if (error != ATALK_PENDING) { AtalkUnlockZipIfNecessary(); } return error; } ATALK_ERROR AtalkAspTdiAction( IN PVOID pObject, // Address or Connection object IN PACTREQ pActReq // Pointer to action request ) /*++ Routine Description: This is the entry for ASP TdiAction calls. The parameters are validated and the calls are dispacthed to the appropriate ASP routines. The only ASP Action is: ASP_XCHG_ENTRIES Arguments: Return Value: --*/ { ATALK_ERROR error = ATALK_INVALID_REQUEST; PAGED_CODE (); ASSERT(VALID_ACTREQ(pActReq)); if (pActReq->ar_ActionCode == ACTION_ASP_BIND) { if (AtalkAspReferenceAddr((PASP_ADDROBJ)pObject) != NULL) { error = AtalkAspBind((PASP_ADDROBJ)pObject, (PASP_BIND_PARAMS)(pActReq->ar_pParms), pActReq); AtalkAspDereferenceAddr((PASP_ADDROBJ)pObject); } } return error; } ATALK_ERROR AtalkAdspTdiAction( IN PVOID pObject, // Address or Connection object IN PACTREQ pActReq // Pointer to action request ) /*++ Routine Description: This is the entry for ADSP TdiAction calls. The parameters are validated and the calls are dispacthed to the appropriate ADSP routines. Arguments: Return Value: --*/ { ATALK_ERROR error = ATALK_NO_ERROR; PAGED_CODE (); ASSERT (VALID_ACTREQ(pActReq)); return error; } ATALK_ERROR AtalkAspCTdiAction( IN PVOID pObject, // Address or Connection object IN PACTREQ pActReq // Pointer to action request ) /*++ Routine Description: This is the entry for ASP Client TdiAction calls. The parameters are validated and the calls are dispatched to the appropriate ASP routines. Arguments: Return Value: --*/ { ATALK_ERROR error = ATALK_NO_ERROR; PAMDL pReplyMdl; ATALK_ADDR atalkAddr; BOOLEAN fWrite; PAGED_CODE (); ASSERT (VALID_ACTREQ(pActReq)); switch (pActReq->ar_ActionCode) { case ACTION_ASPCGETSTATUS: AtalkAspCAddrReference((PASPC_ADDROBJ)pObject, &error); if (ATALK_SUCCESS(error)) { TDI_TO_ATALKADDR(&atalkAddr, &(((PASPC_GETSTATUS_PARAMS)pActReq->ar_pParms)->ServerAddr)); error = AtalkAspCGetStatus((PASPC_ADDROBJ)pObject, &atalkAddr, pActReq->ar_pAMdl, pActReq->ar_MdlSize, pActReq); AtalkAspCAddrDereference((PASPC_ADDROBJ)pObject); } break; case ACTION_ASPCCOMMAND: case ACTION_ASPCWRITE: // Split the mdl into command and reply/write mdls. The already constructed mdl // serves as the command mdl // First validate that the sizes are valid if (pActReq->ar_MdlSize < (((PASPC_COMMAND_OR_WRITE_PARAMS)pActReq->ar_pParms)->CmdSize + ((PASPC_COMMAND_OR_WRITE_PARAMS)pActReq->ar_pParms)->WriteAndReplySize)) { error = ATALK_BUFFER_TOO_SMALL; break; } pReplyMdl = AtalkSubsetAmdl(pActReq->ar_pAMdl, ((PASPC_COMMAND_OR_WRITE_PARAMS)pActReq->ar_pParms)->CmdSize, ((PASPC_COMMAND_OR_WRITE_PARAMS)pActReq->ar_pParms)->WriteAndReplySize); if (pReplyMdl == NULL) { error = ATALK_RESR_MEM; break; } AtalkAspCConnReference((PASPC_CONNOBJ)pObject, &error); if (ATALK_SUCCESS(error)) { fWrite = (pActReq->ar_ActionCode == ACTION_ASPCWRITE) ? TRUE : FALSE; error = AtalkAspCCmdOrWrite((PASPC_CONNOBJ)pObject, pActReq->ar_pAMdl, ((PASPC_COMMAND_OR_WRITE_PARAMS)pActReq->ar_pParms)->CmdSize, pReplyMdl, ((PASPC_COMMAND_OR_WRITE_PARAMS)pActReq->ar_pParms)->WriteAndReplySize, fWrite, pActReq); AtalkAspCConnDereference((PASPC_CONNOBJ)pObject); } break; default: DBGPRINT(DBG_COMP_ACTION, DBG_LEVEL_FATAL, ("AtalkAspCTdiAction: Invalid Asp Client Action !!\n")); error = ATALK_INVALID_REQUEST; break; } return error; } ATALK_ERROR AtalkPapTdiAction( IN PVOID pObject, // Address or Connection object IN PACTREQ pActReq // Pointer to action request ) /*++ Routine Description: This is the entry for PAP TdiAction calls. The parameters are validated and the calls are dispacthed to the appropriate PAP routines. Arguments: Return Value: --*/ { ATALK_ERROR error; ATALK_ADDR atalkAddr; PAGED_CODE (); ASSERT (VALID_ACTREQ(pActReq)); switch (pActReq->ar_ActionCode) { case ACTION_PAPGETSTATUSSRV: AtalkPapAddrReference((PPAP_ADDROBJ)pObject, &error); if (ATALK_SUCCESS(error)) { TDI_TO_ATALKADDR( &atalkAddr, &(((PPAP_GETSTATUSSRV_PARAMS)pActReq->ar_pParms)->ServerAddr)); error = AtalkPapGetStatus((PPAP_ADDROBJ)pObject, &atalkAddr, pActReq->ar_pAMdl, pActReq->ar_MdlSize, pActReq); AtalkPapAddrDereference((PPAP_ADDROBJ)pObject); } break; case ACTION_PAPSETSTATUS: AtalkPapAddrReference((PPAP_ADDROBJ)pObject, &error); if (ATALK_SUCCESS(error)) { error = AtalkPapSetStatus((PPAP_ADDROBJ)pObject, pActReq->ar_pAMdl, pActReq); AtalkPapAddrDereference((PPAP_ADDROBJ)pObject); } break; case ACTION_PAPPRIMEREAD: AtalkPapConnReferenceByPtr((PPAP_CONNOBJ)pObject, &error); if (ATALK_SUCCESS(error)) { error = AtalkPapPrimeRead((PPAP_CONNOBJ)pObject, pActReq); AtalkPapConnDereference((PPAP_CONNOBJ)pObject); } break; default: DBGPRINT(DBG_COMP_ACTION, DBG_LEVEL_FATAL, ("AtalkPapTdiAction: Invalid Pap Action !!\n")); error = ATALK_INVALID_REQUEST; break; } return error; }