/* * epacket.c * * Ethernet Packet Interface to NDIS drivers. * * Copyright 1998 Equivalence Pty. Ltd. * * Original code by William Ingle (address unknown) * * $Log: epacket.c,v $ * Revision 1.2 1998/10/06 10:24:42 robertj * Fixed hang when using reset command, removed the command! * * Revision 1.1 1998/09/28 08:08:31 robertj * Initial revision * */ #include #include #include #include #include #include // From PWLib #include "lock.h" #pragma intrinsic(memset,memcpy,strlen,strcat,strcpy) /////////////////////////////////////////////////////////////////////////////// #define MAJOR_VERSION 1 #define MINOR_VERSION 2 #define MAX_OPEN 4 #define MAX_REQUESTS 4 #define TRANSMIT_PACKETS 64 //was 16 #define ETHERNET_HEADER_LENGTH 14 #define ETHERNET_DATA_LENGTH 1500 #define ETHERNET_PACKET_LENGTH (ETHERNET_HEADER_LENGTH+ETHERNET_DATA_LENGTH) typedef struct _PACKET_RESERVED { LIST_ENTRY ListElement; char* lpBuffer; DWORD cbBuffer; DWORD* lpcbBytesReturned; OVERLAPPED* lpoOverlapped; DWORD hDevice; DWORD tagProcess; } PACKET_RESERVED, *PPACKET_RESERVED; typedef struct _INTERNAL_REQUEST { PACKET_RESERVED Reserved; NDIS_REQUEST Request; } INTERNAL_REQUEST, *PINTERNAL_REQUEST; typedef struct _OPEN_INSTANCE { LIST_ENTRY ListElement; DWORD hDevice; NDIS_STATUS Status; NDIS_HANDLE AdapterHandle; NDIS_HANDLE BindAdapterContext; NDIS_HANDLE PacketPool; NDIS_HANDLE BufferPool; NDIS_SPIN_LOCK RcvQSpinLock; LIST_ENTRY RcvList; NDIS_SPIN_LOCK RequestSpinLock; LIST_ENTRY RequestList; NDIS_SPIN_LOCK ResetSpinLock; LIST_ENTRY ResetIrpList; INTERNAL_REQUEST Requests[MAX_REQUESTS]; } OPEN_INSTANCE, *POPEN_INSTANCE; typedef struct _DEVICE_EXTENSION { PDRIVER_OBJECT DriverObject; NDIS_HANDLE NdisProtocolHandle; LIST_ENTRY OpenList; } DEVICE_EXTENSION, *PDEVICE_EXTENSION; #define RESERVED(_p) ((PPACKET_RESERVED)((_p)->ProtocolReserved)) // // define wrapper for VWIN32_DIOCCompletionRoutine // void VXDINLINE VWIN32_DIOCCompletionRoutine( DWORD hEvent ) { _asm mov ebx, hEvent VxDCall( VWIN32_DIOCCompletionRoutine ); } #pragma VxD_LOCKED_CODE_SEG #pragma VxD_LOCKED_DATA_SEG PDEVICE_EXTENSION GlobalDeviceExtension = NULL; /////////////////////////////////////////////////////////////////////////////// VOID NDIS_API PacketTransferDataComplete(IN NDIS_HANDLE ProtocolBindingContext, IN PNDIS_PACKET pPacket, IN NDIS_STATUS Status, IN UINT BytesTransfered) { // upcall when no more data available POPEN_INSTANCE Open = (POPEN_INSTANCE)ProtocolBindingContext; PPACKET_RESERVED pReserved = (PPACKET_RESERVED)(pPacket->ProtocolReserved); OVERLAPPED * pOverlap = (OVERLAPPED *)(pReserved->lpoOverlapped); PNDIS_BUFFER pNdisBuffer; // free buffer descriptor NdisUnchainBufferAtFront(pPacket, &pNdisBuffer); if (pNdisBuffer) NdisFreeBuffer(pNdisBuffer); // set total bytes returned BytesTransfered += ETHERNET_HEADER_LENGTH; *pReserved->lpcbBytesReturned += BytesTransfered; pOverlap->O_InternalHigh = *(pReserved->lpcbBytesReturned); // The internal member of overlapped structure contains // a pointer to the event structure that will be signalled, // resuming the execution of the waitng GetOverlappedResult // call. VWIN32_DIOCCompletionRoutine(pOverlap->O_Internal); // Unlock buffers PacketPageUnlock(pReserved->lpBuffer, pReserved->cbBuffer); PacketPageUnlock(pReserved->lpcbBytesReturned, sizeof(DWORD)); PacketPageUnlock(pReserved->lpoOverlapped, sizeof(OVERLAPPED)); // recycle the packet NdisReinitializePacket(pPacket); // Put the packet on the free queue NdisFreePacket(pPacket); } /////////////////////////////////////////////////////////////////////////////// VOID NDIS_API PacketSendComplete(IN NDIS_HANDLE ProtocolBindingContext, IN PNDIS_PACKET pPacket, IN NDIS_STATUS Status) { // upcall on completion of send PNDIS_BUFFER pNdisBuffer; PPACKET_RESERVED Reserved = (PPACKET_RESERVED)pPacket->ProtocolReserved; // free buffer descriptor NdisUnchainBufferAtFront(pPacket, &pNdisBuffer); if (pNdisBuffer) NdisFreeBuffer(pNdisBuffer); // return status Reserved->lpoOverlapped->O_InternalHigh = Status; // The internal member of overlapped structure contains // a pointer to the event structure that will be signalled, // resuming the execution of the waiting GetOverlappedResult // call. VWIN32_DIOCCompletionRoutine(Reserved->lpoOverlapped->O_Internal); // Unlock buffers PacketPageUnlock(Reserved->lpBuffer, Reserved->cbBuffer); PacketPageUnlock(Reserved->lpcbBytesReturned, sizeof(DWORD)); PacketPageUnlock(Reserved->lpoOverlapped, sizeof(OVERLAPPED)); // recycle the packet NdisReinitializePacket(pPacket); // Put the packet back on the free list NdisFreePacket(pPacket); } /////////////////////////////////////////////////////////////////////////////// VOID NDIS_API PacketResetComplete(IN NDIS_HANDLE ProtocolBindingContext, IN NDIS_STATUS Status) { // upcall on reset completion POPEN_INSTANCE Open = (POPEN_INSTANCE)ProtocolBindingContext; PLIST_ENTRY ResetListEntry; // remove the reset request from the list NdisAcquireSpinLock(&Open->ResetSpinLock); if (IsListEmpty(&Open->ResetIrpList)) { NdisReleaseSpinLock(&Open->ResetSpinLock); return; } ResetListEntry = RemoveHeadList(&Open->ResetIrpList); NdisReleaseSpinLock(&Open->ResetSpinLock); // Acquire request element from list NdisAcquireSpinLock(&Open->RequestSpinLock); InsertTailList(&Open->RequestList, ResetListEntry); NdisReleaseSpinLock(&Open->RequestSpinLock); } /////////////////////////////////////////////////////////////////////////////// NDIS_STATUS NDIS_API PacketReset(POPEN_INSTANCE pOpen) { // reset the protocol PLIST_ENTRY ResetListEntry; NDIS_STATUS Status; // Acquire request element from list NdisAllocateSpinLock(&pOpen->RequestSpinLock); if (IsListEmpty(&pOpen->RequestList)) { NdisReleaseSpinLock(&pOpen->RequestSpinLock); return NDIS_STATUS_RESOURCES; } ResetListEntry = RemoveHeadList(&pOpen->RequestList); NdisReleaseSpinLock(&pOpen->RequestSpinLock); // Insert Reset IRP into Request Queue NdisAcquireSpinLock(&pOpen->ResetSpinLock); InsertTailList(&pOpen->ResetIrpList, ResetListEntry); NdisReleaseSpinLock(&pOpen->ResetSpinLock); // Reset the adapter NdisReset(&Status, pOpen->AdapterHandle); if (Status != NDIS_STATUS_PENDING) PacketResetComplete(pOpen, Status); return Status; } /////////////////////////////////////////////////////////////////////////////// VOID NDIS_API PacketRequestComplete(IN NDIS_HANDLE ProtocolBindingContext, IN PNDIS_REQUEST NdisRequest, IN NDIS_STATUS Status) { // perform a packet request complete POPEN_INSTANCE Open = (POPEN_INSTANCE)ProtocolBindingContext; PINTERNAL_REQUEST pRequest = CONTAINING_RECORD(NdisRequest, INTERNAL_REQUEST, Request); PPACKET_RESERVED pReserved = &pRequest->Reserved; OVERLAPPED * pOverlap = (OVERLAPPED *)pReserved->lpoOverlapped; EPACKET_OID * oidData = (EPACKET_OID*)pReserved->lpBuffer; if (Status == NDIS_STATUS_SUCCESS) { // set total bytes returned *pReserved->lpcbBytesReturned = oidData->Length + sizeof(EPACKET_OID) - sizeof(oidData->Data); pOverlap->O_InternalHigh = *(pReserved->lpcbBytesReturned); } else { *pReserved->lpcbBytesReturned = 0; // set total bytes returned pOverlap->O_InternalHigh = *pReserved->lpcbBytesReturned; oidData->Length = Status; // return status in oidData if there is an error } // The internal member of overlapped structure contains // a pointer to the event structure that will be signalled, // resuming the execution of the waitng GetOverlappedResult // call. VWIN32_DIOCCompletionRoutine(pOverlap->O_Internal); // Unlock buffers PacketPageUnlock(pReserved->lpBuffer, pReserved->cbBuffer); PacketPageUnlock(pReserved->lpcbBytesReturned, sizeof(DWORD)); PacketPageUnlock(pReserved->lpoOverlapped, sizeof(OVERLAPPED)); // Return request element to list NdisAcquireSpinLock(&Open->RequestSpinLock); InsertTailList(&Open->RequestList, &pReserved->ListElement); NdisReleaseSpinLock(&Open->RequestSpinLock); } /////////////////////////////////////////////////////////////////////////////// DWORD NDIS_API PacketRequest(POPEN_INSTANCE Open, DWORD FunctionCode, DWORD dwDDB, DWORD hDevice, PDIOCPARAMETERS pDiocParms) { // perform a packet request PLIST_ENTRY RequestListEntry; PINTERNAL_REQUEST pRequest; PPACKET_RESERVED pReserved; EPACKET_OID * OidData; NDIS_STATUS Status; // Acquire request element from list NdisAcquireSpinLock(&Open->RequestSpinLock); if (IsListEmpty(&Open->RequestList)) { *(DWORD *)(pDiocParms->lpcbBytesReturned) = 0; NdisReleaseSpinLock(&Open->RequestSpinLock); return NDIS_STATUS_SUCCESS; } RequestListEntry = RemoveHeadList(&Open->RequestList); NdisReleaseSpinLock(&Open->RequestSpinLock); pReserved = CONTAINING_RECORD(RequestListEntry, PACKET_RESERVED, ListElement); pRequest = CONTAINING_RECORD(pReserved, INTERNAL_REQUEST, Reserved); OidData = (EPACKET_OID*)(pDiocParms->lpvInBuffer); if ((pDiocParms->cbInBuffer != pDiocParms->cbOutBuffer) || (pDiocParms->cbInBuffer < sizeof(*OidData) - sizeof(OidData->Data) + OidData->Length)) { *(DWORD *)pDiocParms->lpcbBytesReturned = 1; return NDIS_STATUS_BUFFER_TOO_SHORT; } // The buffer is valid pReserved->lpBuffer = (PVOID)PacketPageLock(pDiocParms->lpvInBuffer, pDiocParms->cbInBuffer); pReserved->lpcbBytesReturned = (PVOID)PacketPageLock(pDiocParms->lpcbBytesReturned, sizeof(DWORD)); pReserved->lpoOverlapped = (PVOID)PacketPageLock(pDiocParms->lpoOverlapped, sizeof(OVERLAPPED)); pReserved->cbBuffer = pDiocParms->cbInBuffer; pReserved->hDevice = pDiocParms->hDevice; pReserved->tagProcess = pDiocParms->tagProcess; if (FunctionCode == IOCTL_EPACKET_SET_OID) { pRequest->Request.RequestType = NdisRequestSetInformation; pRequest->Request.DATA.SET_INFORMATION.Oid = OidData->Oid; pRequest->Request.DATA.SET_INFORMATION.InformationBufferLength = OidData->Length; pRequest->Request.DATA.SET_INFORMATION.InformationBuffer = OidData->Data; } else { if (OidData->Oid >= 0x01000000) pRequest->Request.RequestType = NdisRequestQueryInformation; else pRequest->Request.RequestType = NdisRequestGeneric1; pRequest->Request.DATA.QUERY_INFORMATION.Oid = OidData->Oid; pRequest->Request.DATA.QUERY_INFORMATION.InformationBufferLength = OidData->Length; pRequest->Request.DATA.QUERY_INFORMATION.InformationBuffer = OidData->Data; } // submit the request NdisRequest(&Status, Open->AdapterHandle, &pRequest->Request); if (Status == NDIS_STATUS_PENDING) return(-1); // This will make DeviceIOControl return ERROR_IO_PENDING PacketRequestComplete(Open, &pRequest->Request, Status); return Status; } /////////////////////////////////////////////////////////////////////////////// NDIS_STATUS NDIS_API PacketReceiveIndicate(IN NDIS_HANDLE ProtocolBindingContext, IN NDIS_HANDLE MacReceiveContext, IN PVOID HeaderBuffer, IN UINT HeaderBufferSize, IN PVOID LookaheadBuffer, IN UINT LookaheadBufferSize, IN UINT PacketSize) { // upcall on packet arrival POPEN_INSTANCE Open; PLIST_ENTRY PacketListEntry; PNDIS_PACKET pPacket; NDIS_STATUS Status; UINT BytesTransfered = 0; PPACKET_RESERVED pReserved; if (HeaderBufferSize != ETHERNET_HEADER_LENGTH) return NDIS_STATUS_NOT_ACCEPTED; Open = (POPEN_INSTANCE) ProtocolBindingContext; // See if there are any pending reads that we can satisfy NdisAcquireSpinLock(&Open->RcvQSpinLock); // fixed 5.11.97 if (IsListEmpty(&Open->RcvList)) { NdisReleaseSpinLock(&Open->RcvQSpinLock); return NDIS_STATUS_NOT_ACCEPTED; } PacketListEntry = RemoveHeadList(&Open->RcvList); NdisReleaseSpinLock(&Open->RcvQSpinLock); pReserved = CONTAINING_RECORD(PacketListEntry, PACKET_RESERVED, ListElement); pPacket = CONTAINING_RECORD(pReserved, NDIS_PACKET, ProtocolReserved); // Copy the MAC header NdisMoveMemory(RESERVED(pPacket)->lpBuffer, HeaderBuffer, HeaderBufferSize); // Call the Mac to transfer the data portion of the packet NdisTransferData(&Status, Open->AdapterHandle, MacReceiveContext, 0, PacketSize, pPacket, &BytesTransfered); if (Status == NDIS_STATUS_PENDING) return NDIS_STATUS_PENDING; if (Status == NDIS_STATUS_SUCCESS) { PacketTransferDataComplete(Open, pPacket, Status, BytesTransfered); return NDIS_STATUS_SUCCESS; } PacketTransferDataComplete(Open, pPacket, Status, 0); return NDIS_STATUS_SUCCESS; } /////////////////////////////////////////////////////////////////////////////// VOID NDIS_API PacketReceiveComplete(IN NDIS_HANDLE ProtocolBindingContext) { // upcall when receive complete } /////////////////////////////////////////////////////////////////////////////// VOID NDIS_API PacketStatus(IN NDIS_HANDLE ProtocolBindingContext, IN NDIS_STATUS Status, IN PVOID StatusBuffer, IN UINT StatusBufferSize) { // get packet status } /////////////////////////////////////////////////////////////////////////////// VOID NDIS_API PacketStatusComplete(IN NDIS_HANDLE ProtocolBindingContext) { // completion handler } /////////////////////////////////////////////////////////////////////////////// VOID NDIS_API PacketBindAdapterComplete(IN NDIS_HANDLE ProtocolBindingContext, IN NDIS_STATUS Status, IN NDIS_STATUS OpenErrorStatus) { // upcall on Bind completion POPEN_INSTANCE Open = (POPEN_INSTANCE)ProtocolBindingContext; // If the binding is unsuccessful then we deallocate data structures in // preparation for unloading if (Status != NDIS_STATUS_SUCCESS) { NdisFreeSpinLock(&Open->RequestSpinLock); NdisFreeSpinLock(&Open->RcvQSpinLock); NdisFreeBufferPool(Open->BufferPool); NdisFreePacketPool(Open->PacketPool); NdisFreeMemory(Open, sizeof(OPEN_INSTANCE), 0); } else { // Insert New Adapter into list InsertTailList(&GlobalDeviceExtension->OpenList, &Open->ListElement); } } /////////////////////////////////////////////////////////////////////////////// VOID NDIS_API PacketBindAdapter(OUT PNDIS_STATUS pStatus, IN NDIS_HANDLE BindAdapterContext, IN PNDIS_STRING pAdapterName, IN PVOID SystemSpecific1, IN PVOID SystemSpecific2) { // bind this driver to a NIC POPEN_INSTANCE oiNew; NDIS_STATUS ErrorStatus, AllocStatus; UINT Medium; NDIS_MEDIUM MediumArray = NdisMedium802_3; UINT i; // allocate some memory for the open structure NdisAllocateMemory((PVOID *)&oiNew, sizeof(OPEN_INSTANCE), 0, -1); if (oiNew == NULL) { // not enough memory *pStatus = NDIS_STATUS_RESOURCES; return; } NdisZeroMemory((PVOID)oiNew, sizeof(OPEN_INSTANCE)); // Save Binding Context oiNew->BindAdapterContext = BindAdapterContext; // Save the device handle oiNew->hDevice = (DWORD)SystemSpecific1; // Allocate a packet pool for our xmit and receive packets NdisAllocatePacketPool(&AllocStatus, &(oiNew->PacketPool), TRANSMIT_PACKETS, sizeof(PACKET_RESERVED)); if (AllocStatus != NDIS_STATUS_SUCCESS) { // not enough memory NdisFreeMemory(oiNew, sizeof(OPEN_INSTANCE), 0); *pStatus = NDIS_STATUS_RESOURCES; return; } // Allocate a buffer pool for our xmit and receive buffers NdisAllocateBufferPool(&AllocStatus, &(oiNew->BufferPool), TRANSMIT_PACKETS); if (AllocStatus != NDIS_STATUS_SUCCESS) { // not enough memory NdisFreeMemory(oiNew, sizeof(OPEN_INSTANCE), 0); *pStatus = NDIS_STATUS_RESOURCES; return; } // list to hold irp's that want to reset the adapter NdisAllocateSpinLock(&oiNew->ResetSpinLock); InitializeListHead(&oiNew->ResetIrpList); // Initialize list for holding pending read requests NdisAllocateSpinLock(&oiNew->RcvQSpinLock); InitializeListHead(&oiNew->RcvList); // Initialize the request list NdisAllocateSpinLock(&oiNew->RequestSpinLock); InitializeListHead(&oiNew->RequestList); // link up the request stored in our open block for (i = 0; i < MAX_REQUESTS; i++) { // Braces are required as InsertTailList macro has multiple statements in it InsertTailList(&oiNew->RequestList, &oiNew->Requests[i].Reserved.ListElement); } // Try to open the MAC NdisOpenAdapter(pStatus, &ErrorStatus, &oiNew->AdapterHandle, &Medium, &MediumArray, 1, GlobalDeviceExtension->NdisProtocolHandle, oiNew, pAdapterName, 0, NULL); // Save the status returned by NdisOpenAdapter for completion routine oiNew->Status = *pStatus; switch (*pStatus) { case NDIS_STATUS_PENDING: break; case NDIS_STATUS_SUCCESS: ErrorStatus = NDIS_STATUS_SUCCESS; // fall through to completion routine with oiNew->Status // set to !NDIS_STATUS_PENDING default: PacketBindAdapterComplete(oiNew, *pStatus, ErrorStatus); break; } } /////////////////////////////////////////////////////////////////////////////// VOID NDIS_API PacketUnbindAdapterComplete(IN POPEN_INSTANCE Open, IN NDIS_STATUS Status) { // upcall on NdisCloseAdapter completion // If Open->Status == NDIS_STATUS_PENDING then we must complete the pended unbinding if (Open->Status == NDIS_STATUS_PENDING) { NdisCompleteUnbindAdapter(Open->BindAdapterContext, Status); Open->Status = NDIS_STATUS_SUCCESS; } if (Status != NDIS_STATUS_SUCCESS) return; // Remove Adapter from global list RemoveEntryList(&Open->ListElement); // Free Memory NdisFreeSpinLock(&Open->RequestSpinLock); NdisFreeSpinLock(&Open->RcvQSpinLock); NdisFreeSpinLock(&Open->ResetSpinLock); NdisFreeBufferPool(Open->BufferPool); NdisFreePacketPool(Open->PacketPool); NdisFreeMemory(Open, sizeof(OPEN_INSTANCE), 0); } /////////////////////////////////////////////////////////////////////////////// VOID NDIS_API PacketUnbindAdapter(OUT PNDIS_STATUS Status, IN POPEN_INSTANCE Open, IN POPEN_INSTANCE junk) { // detach protocol from the NIC clean up any pending I/O requests PLIST_ENTRY PacketListEntry; PNDIS_PACKET pPacket; // The open instance of the device is about to close // We need to complete all pending I/O requests // First we complete any pending read requests NdisAcquireSpinLock(&Open->RcvQSpinLock); while (!IsListEmpty(&Open->RcvList)) { PacketListEntry = RemoveHeadList(&Open->RcvList); pPacket = CONTAINING_RECORD(PacketListEntry, NDIS_PACKET, ProtocolReserved); // complete normally PacketTransferDataComplete(Open, pPacket, NDIS_STATUS_SUCCESS, 0); } NdisReleaseSpinLock(&Open->RcvQSpinLock); // close the adapter NdisCloseAdapter(Status, Open->AdapterHandle); // Save status returned from NdisCloseAdapter for completion routine Open->Status = *Status; if (*Status != NDIS_STATUS_PENDING) PacketUnbindAdapterComplete(Open, *Status); } /////////////////////////////////////////////////////////////////////////////// VOID NDIS_API PacketUnload() { // deregister the protocol, free remaining memory // - called by NdisCloseAdapter when last adapter closed NDIS_STATUS Status; if (GlobalDeviceExtension != NULL) { NdisDeregisterProtocol(&Status, GlobalDeviceExtension->NdisProtocolHandle); if (Status == NDIS_STATUS_SUCCESS) NdisFreeMemory(GlobalDeviceExtension, sizeof(DEVICE_EXTENSION), 0); GlobalDeviceExtension = NULL; } } /////////////////////////////////////////////////////////////////////////////// NTSTATUS NDIS_API DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath) { // initialiae the driver NDIS_PROTOCOL_CHARACTERISTICS ProtocolChar; NDIS_STRING ProtoName = NDIS_STRING_CONST("EPACKET"); NDIS_STATUS Status; // Because the driver can be loaded once for each Netcard on the system, // and because DriverEntry is called each time, we must ensure that // initialization is performed only once. if (GlobalDeviceExtension != NULL) return NDIS_STATUS_SUCCESS; NdisAllocateMemory((PVOID *)&GlobalDeviceExtension, sizeof(DEVICE_EXTENSION), 0, -1 ); if (GlobalDeviceExtension == NULL) return NDIS_STATUS_RESOURCES; NdisZeroMemory((UCHAR*)GlobalDeviceExtension, sizeof(DEVICE_EXTENSION)); NdisZeroMemory((UCHAR*)&ProtocolChar, sizeof(NDIS_PROTOCOL_CHARACTERISTICS)); ProtocolChar.MajorNdisVersion = 0x03; ProtocolChar.MinorNdisVersion = 0x0a; ProtocolChar.Reserved = 0; ProtocolChar.OpenAdapterCompleteHandler = PacketBindAdapterComplete; ProtocolChar.CloseAdapterCompleteHandler = PacketUnbindAdapterComplete; ProtocolChar.SendCompleteHandler = PacketSendComplete; ProtocolChar.TransferDataCompleteHandler = PacketTransferDataComplete; ProtocolChar.ResetCompleteHandler = PacketResetComplete; ProtocolChar.RequestCompleteHandler = PacketRequestComplete; ProtocolChar.ReceiveHandler = PacketReceiveIndicate; ProtocolChar.ReceiveCompleteHandler = PacketReceiveComplete; ProtocolChar.StatusHandler = PacketStatus; ProtocolChar.StatusCompleteHandler = PacketStatusComplete; ProtocolChar.BindAdapterHandler = PacketBindAdapter; ProtocolChar.UnbindAdapterHandler = PacketUnbindAdapter; ProtocolChar.UnloadProtocolHandler = PacketUnload; ProtocolChar.Name = ProtoName; NdisRegisterProtocol(&Status, &GlobalDeviceExtension->NdisProtocolHandle, &ProtocolChar, sizeof(NDIS_PROTOCOL_CHARACTERISTICS)); if (Status != NDIS_STATUS_SUCCESS) { NdisFreeMemory(GlobalDeviceExtension, sizeof(DEVICE_EXTENSION), 0); return Status; } // initialize open list InitializeListHead(&GlobalDeviceExtension->OpenList); // initialize global device extension GlobalDeviceExtension->DriverObject = DriverObject; return Status; } /////////////////////////////////////////////////////////////////////////////// POPEN_INSTANCE GetOpen(DWORD handle) { // return a specified Open Instance PLIST_ENTRY pHead = &GlobalDeviceExtension->OpenList; PLIST_ENTRY pTemp; POPEN_INSTANCE Open; if (GlobalDeviceExtension == NULL) return NULL; // search the list for the Open Instance containing the specified handle for (pTemp = pHead->Flink; pTemp != pHead; pTemp = pTemp->Flink) { Open = CONTAINING_RECORD(pTemp, OPEN_INSTANCE, ListElement); if (Open && Open->hDevice == handle) return Open; } return NULL; // just in case } /////////////////////////////////////////////////////////////////////////////// VOID PacketAllocatePacketBuffer(PNDIS_STATUS pStatus, POPEN_INSTANCE pOpen, PNDIS_PACKET *ppPacket, PDIOCPARAMETERS pDiocParms, DWORD FunctionCode ) { // allocate a buffer for reading/writing PNDIS_BUFFER pNdisBuffer; PNDIS_PACKET pPacket; // Try to get a packet from our list of free ones NdisAllocatePacket(pStatus, ppPacket, pOpen->PacketPool); if (*pStatus != NDIS_STATUS_SUCCESS) { *(DWORD *)(pDiocParms->lpcbBytesReturned) = 0; return; } pPacket = *ppPacket; // Buffers used asynchronously must be page locked switch (FunctionCode) { case IOCTL_EPACKET_READ: RESERVED(pPacket)->lpBuffer = (PVOID)PacketPageLock(pDiocParms->lpvOutBuffer, pDiocParms->cbOutBuffer); RESERVED(pPacket)->cbBuffer = pDiocParms->cbOutBuffer; break; case IOCTL_EPACKET_WRITE: RESERVED(pPacket)->lpBuffer = (PVOID)PacketPageLock(pDiocParms->lpvInBuffer, pDiocParms->cbInBuffer); RESERVED(pPacket)->cbBuffer = pDiocParms->cbInBuffer; break; default: // recycle the packet NdisReinitializePacket(pPacket); // Put the packet on the free queue NdisFreePacket(pPacket); *(DWORD *)(pDiocParms->lpcbBytesReturned) = 0; *pStatus = NDIS_STATUS_NOT_ACCEPTED; return; } RESERVED(pPacket)->lpcbBytesReturned = (PVOID)PacketPageLock(pDiocParms->lpcbBytesReturned, sizeof(DWORD)); RESERVED(pPacket)->lpoOverlapped = (PVOID)PacketPageLock(pDiocParms->lpoOverlapped, sizeof(OVERLAPPED)); RESERVED(pPacket)->hDevice = pDiocParms->hDevice; RESERVED(pPacket)->tagProcess = pDiocParms->tagProcess; switch (FunctionCode) { case IOCTL_EPACKET_READ: NdisAllocateBuffer(pStatus, &pNdisBuffer, pOpen->BufferPool, (PVOID)(RESERVED(pPacket)->lpBuffer + ETHERNET_HEADER_LENGTH), pDiocParms->cbOutBuffer); break; case IOCTL_EPACKET_WRITE: NdisAllocateBuffer(pStatus, &pNdisBuffer, pOpen->BufferPool, (PVOID)RESERVED(pPacket)->lpBuffer, pDiocParms->cbInBuffer); break; } if (*pStatus == NDIS_STATUS_SUCCESS) NdisChainBufferAtFront(pPacket, pNdisBuffer); // Attach buffer to Packet else { NdisReinitializePacket(pPacket); // recycle the packet NdisFreePacket(pPacket); // Put the packet on the free queue *(DWORD *)(pDiocParms->lpcbBytesReturned) = 0; } } /////////////////////////////////////////////////////////////////////////////// DWORD PacketRead(POPEN_INSTANCE Open, DWORD dwDDB, DWORD hDevice, PDIOCPARAMETERS pDiocParms) { // read a packet NDIS_STATUS Status; PNDIS_PACKET pPacket; // Check that the buffer can hold a max length Ethernet packet if (pDiocParms->cbOutBuffer < ETHERNET_PACKET_LENGTH) { *(DWORD *)(pDiocParms->lpcbBytesReturned) = 0; // Need bigger buffer return NDIS_STATUS_SUCCESS; } PacketAllocatePacketBuffer(&Status, Open, &pPacket, pDiocParms, IOCTL_EPACKET_READ); if (Status == NDIS_STATUS_SUCCESS) { // Put this packet in a list of pending reads. // The receive indication handler will attempt to remove packets // from this list for use in transfer data calls NdisAcquireSpinLock(&Open->RcvQSpinLock); // fixed 6.11.97 InsertTailList(&Open->RcvList, &RESERVED(pPacket)->ListElement); NdisReleaseSpinLock(&Open->RcvQSpinLock); } return -1; // This will make DeviceIOControl return ERROR_IO_PENDING } /////////////////////////////////////////////////////////////////////////////// DWORD PacketWrite(POPEN_INSTANCE Open, DWORD dwDDB, DWORD hDevice, PDIOCPARAMETERS pDiocParms) { // write a packet PNDIS_PACKET pPacket; NDIS_STATUS Status; PacketAllocatePacketBuffer(&Status, Open, &pPacket, pDiocParms, IOCTL_EPACKET_WRITE); if (Status != NDIS_STATUS_SUCCESS) return 0; // This will return immediately with no data written // Call the MAC NdisSend(&Status, Open->AdapterHandle, pPacket); if (Status != NDIS_STATUS_PENDING) { // The send didn't pend so call the completion handler now PacketSendComplete(Open, pPacket, Status); } return(-1); // This will make DeviceIOControl return ERROR_IO_PENDING } /////////////////////////////////////////////////////////////////////////////// DWORD _stdcall PacketIOControl(DWORD dwService, DWORD dwDDB, DWORD hDevice, PDIOCPARAMETERS pDiocParms) { // called from applications POPEN_INSTANCE Open; NDIS_STATUS Status; UCHAR AdapterBuffer[5]; NDIS_STRING AdapterName = NDIS_STRING_CONST(AdapterBuffer); switch (dwService) { case DIOC_OPEN: return NDIS_STATUS_SUCCESS; case DIOC_CLOSEHANDLE: if ((Open = GetOpen(hDevice)) != NULL) PacketUnbindAdapter(&Status, Open, NULL); return NDIS_STATUS_SUCCESS; case IOCTL_EPACKET_VERSION: if (pDiocParms->cbOutBuffer < 2) *(DWORD *)(pDiocParms->lpcbBytesReturned) = 0; else { ((BYTE *)pDiocParms->lpvOutBuffer)[0] = MAJOR_VERSION; ((BYTE *)pDiocParms->lpvOutBuffer)[1] = MINOR_VERSION; *(DWORD *)pDiocParms->lpcbBytesReturned = 2; } return NDIS_STATUS_SUCCESS; case IOCTL_EPACKET_BIND: memcpy(AdapterName.Buffer, (BYTE *)pDiocParms->lpvInBuffer, min(strlen((char *)pDiocParms->lpvInBuffer), sizeof(AdapterBuffer)-1)); AdapterName.Buffer[sizeof(AdapterBuffer)-1] = '\0'; PacketBindAdapter(&Status, GlobalDeviceExtension->NdisProtocolHandle, &AdapterName, (PVOID)hDevice, /* special */ NULL); // Note: If the above usage of the 4'th arg to PacketBindAdapter // causes problems, use a global variable instead. if (Status == NDIS_STATUS_SUCCESS || Status == NDIS_STATUS_PENDING) { *(DWORD *)pDiocParms->lpcbBytesReturned = 1; return NDIS_STATUS_SUCCESS; } break; case IOCTL_EPACKET_SET_OID: case IOCTL_EPACKET_QUERY_OID: if ((Open = GetOpen(hDevice)) != NULL) return PacketRequest(Open, dwService, dwDDB, hDevice, pDiocParms); break; case IOCTL_EPACKET_READ: if ((Open = GetOpen(hDevice)) != NULL) return PacketRead(Open, dwDDB, hDevice, pDiocParms); break; case IOCTL_EPACKET_WRITE: if ((Open = GetOpen(hDevice)) != NULL) return PacketWrite(Open, dwDDB, hDevice, pDiocParms); break; } *(DWORD *)pDiocParms->lpcbBytesReturned = 0; return NDIS_STATUS_SUCCESS; } // End of File ////////////////////////////////////////////////////////////////