/*++ Copyright (c) 1990-1998 Microsoft Corporation, All Rights Reserved. Module Name: ioctl.c Abstract: This module contains the ioctl interface to this driver Author: Anil Francis Thomas (10/98) Environment: Kernel Revision History: DChen 092499 Bug fixes --*/ #include "precomp.h" #pragma hdrstop #define MODULE_ID MODULE_IOCTL NTSTATUS AtmSmDispatch( IN PDEVICE_OBJECT pDeviceObject, IN PIRP pIrp ) /*++ Routine Description: This is the common dispath routine for user Ioctls Arguments: Return Value: None --*/ { NTSTATUS Status; ULONG ulBytesWritten = 0; PIO_STACK_LOCATION pIrpSp; TraceIn(AtmSmDispatch); // // Get current Irp stack location // pIrpSp = IoGetCurrentIrpStackLocation(pIrp); pIrp->IoStatus.Information = 0; switch(pIrpSp->MajorFunction) { case IRP_MJ_CREATE: { DbgLoud(("IRP_MJ_CREATE\n")); InterlockedIncrement(&AtmSmGlobal.ulNumCreates); Status = STATUS_SUCCESS; break; } case IRP_MJ_CLOSE: { DbgLoud(("IRP_MJ_CLOSE\n")); Status = STATUS_SUCCESS; break; } case IRP_MJ_CLEANUP: { DbgLoud(("IRP_MJ_CLEANUP\n")); Status = STATUS_SUCCESS; InterlockedDecrement(&AtmSmGlobal.ulNumCreates); break; } case IRP_MJ_DEVICE_CONTROL: { ULONG ulControlCode; ULONG ulControlFunc; ulControlCode = pIrpSp->Parameters.DeviceIoControl.IoControlCode; ulControlFunc = IoGetFunctionCodeFromCtlCode(ulControlCode); // verify the IOCTL codes if(DEVICE_TYPE_FROM_CTL_CODE(ulControlCode) == FILE_DEVICE_ATMSM && ulControlFunc < ATMSM_NUM_IOCTLS && AtmSmIoctlTable[ulControlFunc] == ulControlCode) { // set the status to PENDING by default pIrp->IoStatus.Status = STATUS_PENDING; Status = (*AtmSmFuncProcessIoctl[ulControlFunc])(pIrp, pIrpSp); } else { DbgErr(("Unknown IRP_MJ_DEVICE_CONTROL code - %x\n", ulControlCode)); Status = STATUS_INVALID_PARAMETER; } break; } default: { DbgErr(("Unknown IRP_MJ_XX - %x\n",pIrpSp->MajorFunction)); Status = STATUS_INVALID_PARAMETER; break; } } if(STATUS_PENDING != Status) { pIrp->IoStatus.Status = Status; IoCompleteRequest(pIrp, IO_NETWORK_INCREMENT); } TraceOut(AtmSmDispatch); return Status; } NTSTATUS AtmSmIoctlEnumerateAdapters( PIRP pIrp, PIO_STACK_LOCATION pIrpSp ) /*++ Routine Description: This routine is called to enumerate the adapters that we are bound to. NOTE! This uses buffered I/O Arguments: Return Value: Status - doesn't pend --*/ { NTSTATUS Status = STATUS_SUCCESS; ULONG ulNum, ulOutputBufLen, ulNeededSize; PADAPTER_INFO pAdaptInfo = (PADAPTER_INFO) pIrp->AssociatedIrp.SystemBuffer; PATMSM_ADAPTER pAdapt; TraceIn(AtmSmIoctlEnumerateAdapters); ulOutputBufLen = pIrpSp->Parameters.DeviceIoControl.OutputBufferLength; ACQUIRE_GLOBAL_LOCK(); ulNeededSize = sizeof(ADAPTER_INFO) + (sizeof(UCHAR) * NSAP_ADDRESS_LEN * (AtmSmGlobal.ulAdapterCount - 1)); if(ulOutputBufLen < ulNeededSize) { DbgErr(("Output length is not sufficient\n")); RELEASE_GLOBAL_LOCK(); TraceOut(AtmSmIoctlEnumerateAdapters); return STATUS_BUFFER_TOO_SMALL; } pAdaptInfo->ulNumAdapters = 0; ulNum = 0; for(pAdapt = AtmSmGlobal.pAdapterList; pAdapt && ulNum < AtmSmGlobal.ulAdapterCount; pAdapt = pAdapt->pAdapterNext) { if(AtmSmReferenceAdapter(pAdapt)) { ACQUIRE_ADAPTER_GEN_LOCK(pAdapt); if(0 == (pAdapt->ulFlags & ADAPT_ADDRESS_INVALID)) { // this is a good adapter RtlCopyMemory(pAdaptInfo->ucLocalATMAddr[ulNum], pAdapt->ConfiguredAddress.Address, (sizeof(UCHAR) * ATM_ADDRESS_LENGTH)); pAdaptInfo->ulNumAdapters++; ulNum++; } RELEASE_ADAPTER_GEN_LOCK(pAdapt); AtmSmDereferenceAdapter(pAdapt); } } RELEASE_GLOBAL_LOCK(); pIrp->IoStatus.Information = ulOutputBufLen; TraceOut(AtmSmIoctlEnumerateAdapters); return Status; } NTSTATUS AtmSmIoctlOpenForRecv( PIRP pIrp, PIO_STACK_LOCATION pIrpSp ) /*++ Routine Description: This routine is used to open an adapter for receiving all the packets that come to our SAP. We allow only 1 user to open the adapter for recvs. NOTE! This uses buffered I/O Arguments: Return Value: Status - doesn't Pend --*/ { NTSTATUS Status = STATUS_SUCCESS; ULONG ulInputBufLen, ulOutputBufLen, ulCompareLength; POPEN_FOR_RECV_INFO pOpenInfo = (POPEN_FOR_RECV_INFO) pIrp->AssociatedIrp.SystemBuffer; PATMSM_ADAPTER pAdapt; #if DBG ATM_ADDRESS AtmAddr; #endif TraceIn(AtmSmIoctlOpenForRecv); ulInputBufLen = pIrpSp->Parameters.DeviceIoControl.InputBufferLength; ulOutputBufLen = pIrpSp->Parameters.DeviceIoControl.OutputBufferLength; if(ulInputBufLen < sizeof(OPEN_FOR_RECV_INFO)) { DbgErr(("Input length is invalid\n")); TraceOut(AtmSmIoctlOpenForRecv); return STATUS_INVALID_PARAMETER; } if(ulOutputBufLen < sizeof(HANDLE)) { DbgErr(("Output length is not sufficient\n")); TraceOut(AtmSmIoctlOpenForRecv); return STATUS_BUFFER_TOO_SMALL; } #if DBG AtmAddr.AddressType = ATM_NSAP; AtmAddr.NumberOfDigits = ATM_ADDRESS_LENGTH; RtlCopyMemory(AtmAddr.Address, pOpenInfo->ucLocalATMAddr, (sizeof(UCHAR) * ATM_ADDRESS_LENGTH)); DumpATMAddress(ATMSMD_INFO, "Recv Open - Local AtmAddress - ", &AtmAddr); #endif do { // break off loop // // grab the global lock and find out which adapter is being refered to. // ACQUIRE_GLOBAL_LOCK(); // we don't compare the selector byte ulCompareLength = sizeof(UCHAR) * (ATM_ADDRESS_LENGTH - 1); for(pAdapt = AtmSmGlobal.pAdapterList; pAdapt; pAdapt = pAdapt->pAdapterNext) { if(ulCompareLength == RtlCompareMemory( pOpenInfo->ucLocalATMAddr, pAdapt->ConfiguredAddress.Address, ulCompareLength)) break; } if(NULL == pAdapt) { RELEASE_GLOBAL_LOCK(); DbgErr(("Specified adapter address not found.\n")); Status = STATUS_OBJECT_NAME_INVALID; break; } // we have found the adapter put a reference on it if(!AtmSmReferenceAdapter(pAdapt)) { RELEASE_GLOBAL_LOCK(); DbgErr(("Couldn't put a reference on the adapter.\n")); Status = STATUS_UNSUCCESSFUL; break; } RELEASE_GLOBAL_LOCK(); // we have a reference on the adapter now // check if it is already opened for recv's . We allow only // one receiver. ACQUIRE_ADAPTER_GEN_LOCK(pAdapt); if(pAdapt->fAdapterOpenedForRecv) { // we already have an open for recv RELEASE_ADAPTER_GEN_LOCK(pAdapt); AtmSmDereferenceAdapter(pAdapt); DbgErr(("Already opened for recvs.\n")); Status = STATUS_UNSUCCESSFUL; break; } pAdapt->fAdapterOpenedForRecv = TRUE; RELEASE_ADAPTER_GEN_LOCK(pAdapt); // now set the opencontext *(PHANDLE)(pIrp->AssociatedIrp.SystemBuffer) = (HANDLE)pAdapt; pIrp->IoStatus.Information = sizeof(HANDLE); DbgInfo(("Success! Recv Open Context - 0x%x\n", pAdapt)); // remove the reference added when opening for recvs AtmSmDereferenceAdapter(pAdapt); } while(FALSE); TraceOut(AtmSmIoctlOpenForRecv); return Status; } NTSTATUS AtmSmIoctlRecvData( PIRP pIrp, PIO_STACK_LOCATION pIrpSp ) /*++ Routine Description: This routine is used to transfer data from network packets into the user buffers. If a packet is queued up, then we will immediately complete this reuqest. NOTE! This uses Direct I/O for buffer to recv data Arguments: Return Value: Status - Success, Pending or error --*/ { NTSTATUS Status = STATUS_SUCCESS; ULONG ulInputBufLen, ulOutputBufLen; PATMSM_ADAPTER pAdapt; ULONG ulControlCode; TraceIn(AtmSmIoctlRecvData); ulControlCode = pIrpSp->Parameters.DeviceIoControl.IoControlCode; ASSERT(METHOD_OUT_DIRECT == (ulControlCode & 0x3)); ulInputBufLen = pIrpSp->Parameters.DeviceIoControl.InputBufferLength; if(ulInputBufLen < sizeof(HANDLE)) { DbgErr(("Input length is invalid\n")); TraceOut(AtmSmIoctlRecvData); return STATUS_INVALID_PARAMETER; } ulOutputBufLen = pIrpSp->Parameters.DeviceIoControl.OutputBufferLength; if(0 == ulOutputBufLen) { DbgErr(("Output buffer length is 0!\n")); TraceOut(AtmSmIoctlRecvData); return STATUS_INVALID_PARAMETER; } DbgLoud(("Recv - Output buffer length = %u\n", ulOutputBufLen)); pAdapt = (PATMSM_ADAPTER)(*(PHANDLE)(pIrp->AssociatedIrp.SystemBuffer)); DbgLoud(("Recv Context is 0x%x\n", pAdapt)); // Note - VerifyRecvOpenContext adds a reference to the adapter // if successful, which we remove when we are done if(STATUS_SUCCESS != (Status = VerifyRecvOpenContext(pAdapt))) { TraceOut(AtmSmIoctlRecvData); return Status; } // we have a valid RecvContext - check if a recv is already queued do { // break off loop ACQUIRE_ADAPTER_GEN_LOCK(pAdapt); if(pAdapt->pRecvIrp) { // there is already an Irp pending RELEASE_ADAPTER_GEN_LOCK(pAdapt); Status = STATUS_UNSUCCESSFUL; DbgErr(("There is already a recv pending\n")); break; } // No irps pending, check if a queued packets is there, if so copy // else queue ourselves if(pAdapt->pRecvPktNext) { PPROTO_RSVD pPRsvd; PNDIS_PACKET pPkt; pPkt = pAdapt->pRecvPktNext; pPRsvd = GET_PROTO_RSVD(pPkt); pAdapt->pRecvPktNext = pPRsvd->pPktNext; if(pAdapt->pRecvLastPkt == pPkt) pAdapt->pRecvLastPkt = NULL; pAdapt->ulRecvPktsCount--; // release the recv queue lock RELEASE_ADAPTER_GEN_LOCK(pAdapt); // Copy the packet to the Irp buffer // Note this may be partial if the Irp buffer is not large enough pIrp->IoStatus.Information = CopyPacketToIrp(pIrp, pPkt); // return the packet to the miniport NdisReturnPackets(&pPkt, 1); // Status success } else { // no packets available, queue this Irp pAdapt->pRecvIrp = pIrp; // release the recv queue lock RELEASE_ADAPTER_GEN_LOCK(pAdapt); IoMarkIrpPending(pIrp); Status = STATUS_PENDING; } }while(FALSE); // remove the reference added while verifying AtmSmDereferenceAdapter(pAdapt); TraceOut(AtmSmIoctlRecvData); return Status; } NTSTATUS AtmSmIoctlCloseRecvHandle( PIRP pIrp, PIO_STACK_LOCATION pIrpSp ) /*++ Routine Description: This routine is used to close a handle that was obtained when the adapter was opened for recvs. NOTE! This uses buffered I/O Arguments: Return Value: Status - doesn't pend --*/ { NTSTATUS Status = STATUS_SUCCESS; ULONG ulInputBufLen; PATMSM_ADAPTER pAdapt; TraceIn(AtmSmIoctlCloseRecvHandle); ulInputBufLen = pIrpSp->Parameters.DeviceIoControl.InputBufferLength; if(ulInputBufLen < sizeof(HANDLE)) { DbgErr(("Input length is invalid\n")); TraceOut(AtmSmIoctlCloseRecvHandle); return STATUS_INVALID_PARAMETER; } pAdapt = (PATMSM_ADAPTER)(*(PHANDLE)(pIrp->AssociatedIrp.SystemBuffer)); DbgLoud(("Recv Context is 0x%x\n", pAdapt)); // Note - VerifyRecvOpenContext adds a reference to the adapter // if successful, which we remove when we are done if(STATUS_SUCCESS != (Status = VerifyRecvOpenContext(pAdapt))) { DbgInfo(("Couldn't put a reference on the adapter - pAdapt - 0x%x\n", pAdapt)); TraceOut(AtmSmIoctlCloseRecvHandle); return Status; } // we have a valid RecvContext ACQUIRE_ADAPTER_GEN_LOCK(pAdapt); if(pAdapt->pRecvIrp) { PIRP pRecvIrp = pAdapt->pRecvIrp; pAdapt->pRecvIrp = NULL; // there is an Irp pending, complete it pRecvIrp->IoStatus.Status = STATUS_CANCELLED; pRecvIrp->Cancel = TRUE; pRecvIrp->IoStatus.Information = 0; IoCompleteRequest(pRecvIrp, IO_NETWORK_INCREMENT); } pAdapt->fAdapterOpenedForRecv = FALSE; RELEASE_ADAPTER_GEN_LOCK(pAdapt); // remove the reference added while verifying AtmSmDereferenceAdapter(pAdapt); pIrp->IoStatus.Information = 0; TraceOut(AtmSmIoctlCloseRecvHandle); return Status; } NTSTATUS AtmSmIoctlConnectToDsts( PIRP pIrp, PIO_STACK_LOCATION pIrpSp ) /*++ Routine Description: This routine is used to initiate a connection to 1 (P-P) or more (PMP) destinations. NOTE! This uses buffered I/O Arguments: Return Value: Status - Pending or error --*/ { NTSTATUS Status = STATUS_SUCCESS; PCONNECT_INFO pConnectInfo = (PCONNECT_INFO) pIrp->AssociatedIrp.SystemBuffer; ULONG ul, ulInputBufLen, ulOutputBufLen, ulCompareLength; PATMSM_ADAPTER pAdapt; PATMSM_VC pVc; #if DBG ATM_ADDRESS AtmAddr; #endif TraceIn(AtmSmIoctlConnectToDsts); ulInputBufLen = pIrpSp->Parameters.DeviceIoControl.InputBufferLength; ulOutputBufLen = pIrpSp->Parameters.DeviceIoControl.OutputBufferLength; if(ulInputBufLen < sizeof(CONNECT_INFO)) { DbgErr(("Input length < sizeof(CONNECT_INFO)\n")); TraceOut(AtmSmIoctlConnectToDsts); return STATUS_INVALID_PARAMETER; } if(pConnectInfo->ulNumDsts == 0) { DbgErr(("Number of destinations is zero\n")); TraceOut(AtmSmIoctlConnectToDsts); return STATUS_INVALID_PARAMETER; } if((ulInputBufLen - (sizeof(CONNECT_INFO))/(sizeof(UCHAR)*ATM_ADDRESS_LENGTH)) < pConnectInfo->ulNumDsts -1) { DbgErr(("Input length is invalid\n")); TraceOut(AtmSmIoctlConnectToDsts); return STATUS_INVALID_PARAMETER; } if(ulOutputBufLen < sizeof(HANDLE)) { DbgErr(("Output length is not sufficient\n")); TraceOut(AtmSmIoctlConnectToDsts); return STATUS_BUFFER_TOO_SMALL; } #if DBG AtmAddr.AddressType = ATM_NSAP; AtmAddr.NumberOfDigits = ATM_ADDRESS_LENGTH; RtlCopyMemory(AtmAddr.Address, pConnectInfo->ucLocalATMAddr, (sizeof(UCHAR) * ATM_ADDRESS_LENGTH)); DumpATMAddress(ATMSMD_INFO, "Connect to Dsts - Local AtmAddress - ", &AtmAddr); DbgInfo(("No of destinations - %u\n", pConnectInfo->ulNumDsts)); for(ul = 0; ul < pConnectInfo->ulNumDsts; ul++) { RtlCopyMemory(AtmAddr.Address, pConnectInfo->ucDstATMAddrs[ul], (sizeof(UCHAR) * ATM_ADDRESS_LENGTH)); DumpATMAddress(ATMSMD_INFO, " Destination AtmAddress - ", &AtmAddr); } #endif do { // break off loop // initialize Status = STATUS_OBJECT_NAME_INVALID; // // grab the global lock and find out which adapter is being refered to. // ACQUIRE_GLOBAL_LOCK(); // we don't compare the selector byte ulCompareLength = sizeof(UCHAR) * (ATM_ADDRESS_LENGTH - 1); for(pAdapt = AtmSmGlobal.pAdapterList; pAdapt; pAdapt = pAdapt->pAdapterNext) { if(ulCompareLength == RtlCompareMemory( pConnectInfo->ucLocalATMAddr, pAdapt->ConfiguredAddress.Address, ulCompareLength)) { // create a VC structure Status = AtmSmAllocVc(pAdapt, &pVc, (pConnectInfo->bPMP? VC_TYPE_PMP_OUTGOING : VC_TYPE_PP_OUTGOING), NULL); if(NDIS_STATUS_SUCCESS != Status) { DbgErr(("Failed to create ougoing VC. Status - 0x%X.\n", Status)); Status = STATUS_NO_MEMORY; } break; } } RELEASE_GLOBAL_LOCK(); if(Status != NDIS_STATUS_SUCCESS) { break; } // no need to add reference here since the allocation of VC // will add a reference to the Adapter ACQUIRE_ADAPTER_GEN_LOCK(pAdapt); if(!pConnectInfo->bPMP) { // this is P-P // copy the destination address PATM_ADDRESS pAtmAddr = &pVc->HwAddr.Address; pAtmAddr->AddressType = ATM_NSAP; pAtmAddr->NumberOfDigits = ATM_ADDRESS_LENGTH; RtlCopyMemory(pAtmAddr->Address, pConnectInfo->ucDstATMAddrs[0], (sizeof(UCHAR) * ATM_ADDRESS_LENGTH)); // Note: we don't get the correct selector byte from user mode // we assume the selector byte used by the destination is // the same. pAtmAddr->Address[ATM_ADDRESS_LENGTH-1] = pAdapt->SelByte; } else { for(ul = 0; ul < pConnectInfo->ulNumDsts; ul++) { PATMSM_PMP_MEMBER pMember; PATM_ADDRESS pAtmAddr; AtmSmAllocMem(&pMember, PATMSM_PMP_MEMBER, sizeof(ATMSM_PMP_MEMBER)); if(NULL == pMember) { DbgErr(("Failed to allocate member. No resources\n")); // cleanup the members and VC while(NULL != (pMember = pVc->pPMPMembers)) { AtmSmFreeMem(pMember); pVc->ulRefCount--; } RELEASE_ADAPTER_GEN_LOCK(pAdapt); AtmSmDereferenceVc(pVc); Status = STATUS_NO_MEMORY; break; } NdisZeroMemory(pMember, sizeof(ATMSM_PMP_MEMBER)); pMember->ulSignature = atmsm_member_signature; pMember->pVc = pVc; ATMSM_SET_MEMBER_STATE(pMember, ATMSM_MEMBER_IDLE); pAtmAddr = &pMember->HwAddr.Address; pAtmAddr->AddressType = ATM_NSAP; pAtmAddr->NumberOfDigits = ATM_ADDRESS_LENGTH; RtlCopyMemory(pAtmAddr->Address, pConnectInfo->ucDstATMAddrs[ul], (sizeof(UCHAR) * ATM_ADDRESS_LENGTH)); // Note: we don't get the correct selector byte from user mode // we assume the selector byte used by the destination is // the same. pAtmAddr->Address[ATM_ADDRESS_LENGTH-1] = pAdapt->SelByte; pMember->pNext = pVc->pPMPMembers; pVc->pPMPMembers = pMember; pVc->ulNumTotalMembers++; // Also add a reference for each members pVc->ulRefCount++; } } pVc->pConnectIrp = pIrp; IoMarkIrpPending(pIrp); // we will return pending Status = STATUS_PENDING; RELEASE_ADAPTER_GEN_LOCK(pAdapt); DbgInfo(("Initiated a VC connection - Adapter 0x%x VC - 0x%x\n", pAdapt, pVc)); if(!pConnectInfo->bPMP) { AtmSmConnectPPVC(pVc); } else { AtmSmConnectToPMPDestinations(pVc); } } while(FALSE); TraceOut(AtmSmIoctlConnectToDsts); return Status; } NTSTATUS AtmSmIoctlSendToDsts( PIRP pIrp, PIO_STACK_LOCATION pIrpSp ) /*++ Routine Description: This routine is used to send a packet to destination(s) for which we already have a connection. NOTE! This uses Direct I/O for buffer to send data Arguments: Return Value: Status - Pending or error --*/ { NTSTATUS Status = STATUS_SUCCESS; ULONG ulInputBufLen, ulOutputBufLen; PATMSM_VC pVc; ULONG ulControlCode; TraceIn(AtmSmIoctlSendToDsts); ulControlCode = pIrpSp->Parameters.DeviceIoControl.IoControlCode; ASSERT(METHOD_IN_DIRECT == (ulControlCode & 0x3)); ulInputBufLen = pIrpSp->Parameters.DeviceIoControl.InputBufferLength; if(sizeof(HANDLE) != ulInputBufLen) { DbgErr(("Input buffer length is invalid!\n")); ASSERT(FALSE); TraceOut(AtmSmIoctlSendToDsts); return STATUS_INVALID_PARAMETER; } ulOutputBufLen = pIrpSp->Parameters.DeviceIoControl.OutputBufferLength; if(0 == ulOutputBufLen) { DbgErr(("Output buffer length is 0!\n")); ASSERT(FALSE); TraceOut(AtmSmIoctlRecvData); return STATUS_INVALID_PARAMETER; } DbgLoud(("Send - Output buffer length = %u\n", ulOutputBufLen)); pVc = (PATMSM_VC)(*(PHANDLE)(pIrp->AssociatedIrp.SystemBuffer)); DbgLoud(("Connect Context is 0x%x\n", pVc)); // Note - VerifyConnectContext adds a reference to the VC // if successful, which we remove when we are done if(STATUS_SUCCESS != (Status = VerifyConnectContext(pVc))) { TraceOut(AtmSmIoctlSendToDsts); return Status; } // we have a valid ConnectContext do { // break off loop PNDIS_PACKET pPacket; PATMSM_ADAPTER pAdapt = pVc->pAdapt; // // Try to get a packet // NdisAllocatePacket( &Status, &pPacket, pAdapt->PacketPoolHandle ); if(NDIS_STATUS_SUCCESS != Status) { // // No free packets // Status = STATUS_UNSUCCESSFUL; break; } (GET_PROTO_RSVD(pPacket))->pSendIrp=pIrp; #ifdef BUG_IN_NEW_DMA { PNDIS_BUFFER pBuffer; PVOID pSrcVA = MmGetSystemAddressForMdlSafe(pIrp->MdlAddress, NormalPagePriority); UINT uiBufSize = MmGetMdlByteCount(pIrp->MdlAddress); if (pSrcVA == NULL) { Status = NDIS_STATUS_RESOURCES; break; } // allocate the Buffer Descriptor NdisAllocateBuffer(&Status, &pBuffer, pAdapt->BufferPoolHandle, pSrcVA, uiBufSize); if(NDIS_STATUS_SUCCESS != Status) { NdisFreePacket(pPacket); break; } // add the buffer to the packet NdisChainBufferAtFront(pPacket, pBuffer); } #else // BUG_IN_NEW_DMA // // Attach the send buffer to the packet // NdisChainBufferAtFront(pPacket, pIrp->MdlAddress); #endif // BUG_IN_NEW_DMA IoMarkIrpPending(pIrp); Status = STATUS_PENDING; // send the packet on the VC AtmSmSendPacketOnVc(pVc, pPacket); }while(FALSE); // remove the reference added to the VC while verifying AtmSmDereferenceVc(pVc); TraceOut(AtmSmIoctlSendToDsts); return Status; } NTSTATUS AtmSmIoctlCloseSendHandle( PIRP pIrp, PIO_STACK_LOCATION pIrpSp ) /*++ Routine Description: This routine is used to close a handle that was obtained when we established a connection to destinations. NOTE! This uses buffered I/O Arguments: Return Value: Status - doesn't pend --*/ { NTSTATUS Status = STATUS_SUCCESS; ULONG ulInputBufLen; PATMSM_VC pVc; TraceIn(AtmSmIoctlCloseSendHandle); ulInputBufLen = pIrpSp->Parameters.DeviceIoControl.InputBufferLength; if(ulInputBufLen < sizeof(HANDLE)) { DbgErr(("Input length is invalid\n")); TraceOut(AtmSmIoctlCloseSendHandle); return STATUS_INVALID_PARAMETER; } pVc = (PATMSM_VC)(*(PHANDLE)(pIrp->AssociatedIrp.SystemBuffer)); DbgLoud(("Connect Context is 0x%x\n", pVc)); // Note - VerifyConnectContext adds a reference to the VC // if successful, which we remove when we are done if(STATUS_SUCCESS != (Status = VerifyConnectContext(pVc))) { TraceOut(AtmSmIoctlCloseSendHandle); return Status; } // we have a valid Connect Context - disconnect it AtmSmDisconnectVc(pVc); // remove the reference added to the VC while verifying AtmSmDereferenceVc(pVc); TraceOut(AtmSmIoctlCloseSendHandle); return Status; }