/*++ Copyright (c) 1999 Microsoft Corporation Module Name: rndis.c Author: ervinp Environment: Kernel mode Revision History: --*/ #include #include // defines OID's #include "..\inc\rndis.h" #include "..\inc\rndisapi.h" #include "usb8023.h" #include "debug.h" NDIS_STATUS RndisInitializeHandler( OUT PNDIS_HANDLE pMiniportAdapterContext, OUT PULONG pMaxReceiveSize, IN NDIS_HANDLE RndisMiniportHandle, IN NDIS_HANDLE NdisMiniportHandle, IN NDIS_HANDLE WrapperConfigurationContext, IN PDEVICE_OBJECT Pdo) { NDIS_STATUS rndisStat; ADAPTEREXT *adapter; DBGVERBOSE(("RndisInitializeHandler")); /* * Allocate a new device object to represent this connection. */ adapter = NewAdapter(Pdo); if (adapter){ adapter->ndisAdapterHandle = (PVOID)NdisMiniportHandle; adapter->rndisAdapterHandle = (PVOID)RndisMiniportHandle; if (InitUSB(adapter)){ /* * Figure out the buffer size required for each packet. * * For native RNDIS, the buffer must include the rndis message and RNDIS_PACKET. * For KLSI, we have to prepend a two-byte size field to each packet. * For other prototypes, we have to append zeroes to round the length * up to the next multiple of the endpoint packet size. * * We must also need one extra byte for the one-byte short packet that * must follow a full-sized frame. */ ASSERT(adapter->writePipeLength); ASSERT(adapter->readPipeLength); /* * Allocate common resources before miniport-specific resources * because we need to allocate the packet pool first. */ if (AllocateCommonResources(adapter)){ EnqueueAdapter(adapter); /* * Give RNDIS our adapter context, which it will use to call us. */ *pMiniportAdapterContext = (NDIS_HANDLE)adapter; *pMaxReceiveSize = PACKET_BUFFER_SIZE; rndisStat = NDIS_STATUS_SUCCESS; } else { rndisStat = NDIS_STATUS_NOT_ACCEPTED; } } else { rndisStat = NDIS_STATUS_NOT_ACCEPTED; } if (rndisStat != NDIS_STATUS_SUCCESS){ FreeAdapter(adapter); } } else { rndisStat = NDIS_STATUS_NOT_ACCEPTED; } return rndisStat; } NDIS_STATUS RndisInitCompleteNotify(IN NDIS_HANDLE MicroportAdapterContext, IN ULONG DeviceFlags, IN OUT PULONG pMaxTransferSize) { ADAPTEREXT *adapter = (ADAPTEREXT *)MicroportAdapterContext; if (*pMaxTransferSize > PACKET_BUFFER_SIZE) { DBGWARN(("Reducing adapter MaxTransferSize from %xh to %xh.", *pMaxTransferSize, PACKET_BUFFER_SIZE)); *pMaxTransferSize = PACKET_BUFFER_SIZE; } StartUSBReadLoop(adapter); return NDIS_STATUS_SUCCESS; } VOID RndisHalt(IN NDIS_HANDLE MicroportAdapterContext) { BOOLEAN workItemOrTimerPending; KIRQL oldIrql; ADAPTEREXT *adapter = (ADAPTEREXT *)MicroportAdapterContext; ASSERT(KeGetCurrentIrql() <= APC_LEVEL); DBGOUT(("> RndisHalt(%ph)", adapter)); ASSERT(adapter->sig == DRIVER_SIG); HaltAdapter(adapter); KeAcquireSpinLock(&adapter->adapterSpinLock, &oldIrql); workItemOrTimerPending = adapter->workItemOrTimerPending; KeReleaseSpinLock(&adapter->adapterSpinLock, oldIrql); if (workItemOrTimerPending){ /* * Wait until workItem fires back to us before freeing the adapter context. */ KeWaitForSingleObject(&adapter->workItemOrTimerEvent, Executive, KernelMode, FALSE, NULL); } DequeueAdapter(adapter); FreeAdapter(adapter); #if DBG_WRAP_MEMORY if (dbgTotalMemCount != 0){ DBGERR(("RndisHalt: unloading with %xh bytes still allocated !!", dbgTotalMemCount)); } #endif DBGOUT(("< RndisHalt")); } VOID RndisShutdown(IN NDIS_HANDLE MicroportAdapterContext) { ADAPTEREXT *adapter = (ADAPTEREXT *)MicroportAdapterContext; DBGOUT(("RndisShutdown(%ph)", adapter)); #if DBG_WRAP_MEMORY if (dbgTotalMemCount != 0){ DBGERR(("RndisShutdown: unloading with %xh bytes still allocated !!", dbgTotalMemCount)); } #endif } VOID RndisSendMessageHandler( IN NDIS_HANDLE MicroportAdapterContext, IN PMDL pMessageMdl, IN NDIS_HANDLE RndisMessageHandle, IN RM_CHANNEL_TYPE ChannelType) { ADAPTEREXT *adapter = (ADAPTEREXT *)MicroportAdapterContext; ASSERT(adapter->sig == DRIVER_SIG); if (!adapter->resetting){ /* * The message header is guaranteed to be contained in the first buffer of the MDL. */ PRNDIS_MESSAGE pMsg = GetSystemAddressForMdlSafe(pMessageMdl); if (pMsg){ ASSERT(!adapter->halting); if (adapter->numActiveWritePackets <= USB_PACKET_POOL_SIZE*3/4){ USBPACKET *packet = DequeueFreePacket(adapter); if (packet){ packet->rndisMessageHandle = (PVOID)RndisMessageHandle; /* * Move our packet to the usbPendingWritePackets queue * and send it down the USB pipe. * Native RNDIS packet messages go intact to the write pipe. * All other encapsulated commands go to the control pipe. */ EnqueuePendingWritePacket(packet); if (ChannelType == RMC_DATA) { ASSERT(!packet->ndisSendPktMdl); #ifdef RAW_TEST if (adapter->rawTest) { pMessageMdl = AddDataHeader(pMessageMdl); if (pMessageMdl == NULL) { DequeuePendingWritePacket(packet); RndisMSendComplete( (NDIS_HANDLE)adapter->rndisAdapterHandle, RndisMessageHandle, NDIS_STATUS_RESOURCES); return; } packet->dataPacket = TRUE; } #endif // RAW_TEST packet->ndisSendPktMdl = pMessageMdl; packet->dataBufferCurrentLength = CopyMdlToBuffer(packet->dataBuffer, pMessageMdl, packet->dataBufferMaxLength); SubmitUSBWritePacket(packet); } else { NTSTATUS status; ULONG msgType = pMsg->NdisMessageType; BOOLEAN synchronizeUSBcall = FALSE; ULONG oid; RNDIS_REQUEST_ID reqId; switch (msgType){ case REMOTE_NDIS_INITIALIZE_MSG: { ULONG maxXferSize = pMsg->Message.InitializeRequest.MaxTransferSize; DBGOUT(("---- REMOTE_NDIS_INITIALIZE_MSG (MaxTransferSize = %xh) ----", maxXferSize)); ASSERT(maxXferSize <= PACKET_BUFFER_SIZE); adapter->rndismpMajorVersion = pMsg->Message.InitializeRequest.MajorVersion; adapter->rndismpMinorVersion = pMsg->Message.InitializeRequest.MinorVersion; adapter->rndismpMaxTransferSize = maxXferSize; synchronizeUSBcall = TRUE; } break; case REMOTE_NDIS_SET_MSG: case REMOTE_NDIS_QUERY_MSG: oid = pMsg->Message.SetRequest.Oid; reqId = pMsg->Message.SetRequest.RequestId; DBGVERBOSE(("> %s (req#%d)", DbgGetOidName(oid), reqId)); if (oid == OID_GEN_CURRENT_PACKET_FILTER){ ULONG pktFilter = *(PULONG)((PUCHAR)&pMsg->Message.SetRequest+pMsg->Message.SetRequest.InformationBufferOffset); adapter->currentPacketFilter = pktFilter; adapter->gotPacketFilterIndication = TRUE; DBGOUT(("---- Got OID_GEN_CURRENT_PACKET_FILTER (%xh) ----", pktFilter)); } else if (oid == OID_802_3_CURRENT_ADDRESS){ /* * This oid can be a query or a set. * If it's a set, save the assigned * MAC address in case we need to simulate * it later on a reset. */ if (msgType == REMOTE_NDIS_SET_MSG){ ASSERT(pMsg->Message.SetRequest.InformationBufferLength == ETHERNET_ADDRESS_LENGTH); DBGVERBOSE(("COVERAGE - OID_802_3_CURRENT_ADDRESS (SET), msg=%xh.", pMsg)); RtlMoveMemory( adapter->MAC_Address, ((PUCHAR)&pMsg->Message.SetRequest+pMsg->Message.SetRequest.InformationBufferOffset), ETHERNET_ADDRESS_LENGTH); } } adapter->dbgCurrentOid = oid; break; case REMOTE_NDIS_RESET_MSG: DBGWARN(("---- REMOTE_NDIS_RESET_MSG ----")); adapter->numSoftResets++; break; case REMOTE_NDIS_HALT_MSG: DBGWARN(("---- REMOTE_NDIS_HALT_MSG ----")); break; } packet->dataBufferCurrentLength = CopyMdlToBuffer( packet->dataBuffer, pMessageMdl, packet->dataBufferMaxLength); #ifdef RAW_TEST packet->dataPacket = FALSE; #endif status = SubmitPacketToControlPipe(packet, synchronizeUSBcall, FALSE); /* * If this is an init message, then start reading the notify pipe. */ switch (msgType){ case REMOTE_NDIS_INITIALIZE_MSG: if (NT_SUCCESS(status)){ adapter->initialized = TRUE; SubmitNotificationRead(adapter, FALSE); } else { DBGERR(("Device failed REMOTE_NDIS_INITIALIZE_MSG with %xh.", status)); } break; } } } else { RndisMSendComplete( (NDIS_HANDLE)adapter->rndisAdapterHandle, RndisMessageHandle, NDIS_STATUS_RESOURCES); } } else { DBGWARN(("RndisSendMessageHandler: throttling sends because only %d packets available for rcv ", USB_PACKET_POOL_SIZE-adapter->numActiveWritePackets)); RndisMSendComplete( (NDIS_HANDLE)adapter->rndisAdapterHandle, RndisMessageHandle, NDIS_STATUS_RESOURCES); } } else { DBGERR(("GetSystemAddressForMdlSafe failed")); RndisMSendComplete( (NDIS_HANDLE)adapter->rndisAdapterHandle, RndisMessageHandle, NDIS_STATUS_INVALID_PACKET); } } else { DBGWARN(("RndisSendMessageHandler - failing send because adapter is resetting")); RndisMSendComplete( (NDIS_HANDLE)adapter->rndisAdapterHandle, RndisMessageHandle, NDIS_STATUS_MEDIA_BUSY); } } /* * RndisReturnMessageHandler * * This is the completion of a received packet indication call. */ VOID RndisReturnMessageHandler( IN NDIS_HANDLE MicroportAdapterContext, IN PMDL pMessageMdl, IN NDIS_HANDLE MicroportMessageContext) { USBPACKET *packet; DBGVERBOSE(("RndisReturnMessageHandler: msgMdl=%ph, msg context = %ph.", pMessageMdl, MicroportMessageContext)); ASSERT(MicroportMessageContext); packet = (USBPACKET *)MicroportMessageContext; ASSERT(packet->sig == DRIVER_SIG); #ifdef RAW_TEST { ADAPTEREXT * adapter = (ADAPTEREXT *)MicroportAdapterContext; if (adapter->rawTest) { if (packet->dataPacket) { UnskipRcvRndisPacketHeader(packet); } } } #endif // RAW_TEST /* * The receive indication is done. * Put our packet back in the free list. */ DequeueCompletedReadPacket(packet); EnqueueFreePacket(packet); } BOOLEAN RegisterRNDISMicroport(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath) { RNDIS_MICROPORT_CHARACTERISTICS rndisAttribs; NDIS_HANDLE ndisWrapperHandle; DBGVERBOSE(("RegisterRNDISMicroport")); RtlZeroMemory(&rndisAttribs, sizeof(rndisAttribs)); rndisAttribs.RndisVersion = RNDIS_VERSION; rndisAttribs.Reserved = 0; rndisAttribs.RmInitializeHandler = RndisInitializeHandler; rndisAttribs.RmInitCompleteNotifyHandler = RndisInitCompleteNotify; rndisAttribs.RmHaltHandler = RndisHalt; rndisAttribs.RmShutdownHandler = RndisShutdown; rndisAttribs.RmSendMessageHandler = RndisSendMessageHandler; rndisAttribs.RmReturnMessageHandler = RndisReturnMessageHandler; RndisMInitializeWrapper( &ndisWrapperHandle, NULL, DriverObject, RegistryPath, &rndisAttribs); return TRUE; } VOID IndicateSendStatusToRNdis(USBPACKET *packet, NTSTATUS status) { #ifdef RAW_TEST ADAPTEREXT *adapter = packet->adapter; if (adapter->rawTest && packet->dataPacket) { FreeDataHeader(packet); } #endif /? RAW_TEST packet->ndisSendPktMdl = NULL; ASSERT(packet->rndisMessageHandle); RndisMSendComplete( (NDIS_HANDLE)packet->adapter->rndisAdapterHandle, (NDIS_HANDLE)packet->rndisMessageHandle, (NDIS_STATUS)status); } VOID RNDISProcessNotification(ADAPTEREXT *adapter) { UCHAR notification = *(PUCHAR)adapter->notifyBuffer; UCHAR notificationCode = *((PUCHAR)adapter->notifyBuffer + 1); if ((notification == NATIVE_RNDIS_RESPONSE_AVAILABLE) || ((notification == CDC_RNDIS_NOTIFICATION) && (notificationCode == CDC_RNDIS_RESPONSE_AVAILABLE))) { /* * Try to read a native RNDIS encapsulated command from the control pipe. */ DBGVERBOSE(("NativeRNDISProcessNotification: NATIVE_RNDIS_RESPONSE_AVAILABLE")); { USBPACKET *packet = DequeueFreePacket(adapter); if (packet){ EnqueuePendingReadPacket(packet); ReadPacketFromControlPipe(packet, FALSE); } else { DBGWARN(("couldn't get free packet in NativeRNDISProcessNotification")); } } } else { DBGERR(("NativeRNDISProcessNotification: unknown notification %xh.", notification)); } } NTSTATUS IndicateRndisMessage( IN USBPACKET *packet, IN BOOLEAN bIsData) { ADAPTEREXT *adapter = packet->adapter; PRNDIS_MESSAGE rndisMsg = (PRNDIS_MESSAGE)packet->dataBuffer; NDIS_STATUS rcvStat; ASSERT(packet->dataBufferCurrentLength <= packet->dataBufferMaxLength); /* * Indicate the packet to RNDIS, and pass a pointer to our usb packet * as the MicroportMessageContext. * The packet/message will be returned to us via RndisReturnMessageHandler. */ MyInitializeMdl(packet->dataBufferMdl, packet->dataBuffer, packet->dataBufferCurrentLength); if (adapter->numFreePackets < USB_PACKET_POOL_SIZE/8){ rcvStat = NDIS_STATUS_RESOURCES; } else { rcvStat = NDIS_STATUS_SUCCESS; } #ifdef RAW_TEST if (adapter->rawTest) { packet->dataPacket = bIsData; if (bIsData) { SkipRcvRndisPacketHeader(packet); } } #endif // RAW_TEST RndisMIndicateReceive( (NDIS_HANDLE)packet->adapter->rndisAdapterHandle, packet->dataBufferMdl, (NDIS_HANDLE)packet, (bIsData? RMC_DATA: RMC_CONTROL), rcvStat); return STATUS_PENDING; } #ifdef RAW_TEST // // Add an RNDIS_PACKET header to a sent "raw" encapsulated Ethernet frame. // PMDL AddDataHeader(IN PMDL pMessageMdl) { PMDL pHeaderMdl, pTmpMdl; PRNDIS_MESSAGE pRndisMessage; PRNDIS_PACKET pRndisPacket; ULONG TotalLength; // // Compute the total length. // TotalLength = 0; for (pTmpMdl = pMessageMdl; pTmpMdl != NULL; pTmpMdl = pTmpMdl->Next) { TotalLength += MmGetMdlByteCount(pTmpMdl); } // // Allocate an RNDIS packet header: // pRndisMessage = AllocPool(RNDIS_MESSAGE_SIZE(RNDIS_PACKET)); if (pRndisMessage != NULL) { pHeaderMdl = IoAllocateMdl(pRndisMessage, RNDIS_MESSAGE_SIZE(RNDIS_PACKET), FALSE, FALSE, NULL); if (pHeaderMdl != NULL) { MmBuildMdlForNonPagedPool(pHeaderMdl); // // Fill in the RNDIS message generic header: // pRndisMessage->NdisMessageType = REMOTE_NDIS_PACKET_MSG; pRndisMessage->MessageLength = RNDIS_MESSAGE_SIZE(RNDIS_PACKET) + TotalLength; // // Fill in the RNDIS_PACKET structure: // pRndisPacket = (PRNDIS_PACKET)&pRndisMessage->Message; pRndisPacket->DataOffset = sizeof(RNDIS_PACKET); pRndisPacket->DataLength = TotalLength; pRndisPacket->OOBDataOffset = 0; pRndisPacket->OOBDataLength = 0; pRndisPacket->NumOOBDataElements = 0; pRndisPacket->PerPacketInfoOffset = 0; pRndisPacket->PerPacketInfoLength = 0; pRndisPacket->VcHandle = 0; pRndisPacket->Reserved = 0; // // Link it to the raw data frame: // pHeaderMdl->Next = pMessageMdl; } else { FreePool(pRndisMessage); pHeaderMdl = NULL; } } else { pHeaderMdl = NULL; } return (pHeaderMdl); } // // Remove an RNDIS_PACKET header that we had added to a raw encapsulated // Ethernet frame. // VOID FreeDataHeader(IN USBPACKET * packet) { PMDL pHeaderMdl; PRNDIS_MESSAGE pRndisMessage; ASSERT(packet->dataPacket == TRUE); // // Take out the MDL we had pre-pended // pHeaderMdl = packet->ndisSendPktMdl; packet->ndisSendPktMdl = pHeaderMdl->Next; // // Free the RNDIS_PACKET header: // pRndisMessage = MmGetMdlVirtualAddress(pHeaderMdl); FreePool(pRndisMessage); // // ... and the MDL itself. // IoFreeMdl(pHeaderMdl); } // // Modify a received message to skip the RNDIS_PACKET header // before indicating this up to RNDISMP, to test raw encapsulation. // VOID SkipRcvRndisPacketHeader(IN USBPACKET * packet) { PMDL pHeaderMdl; RNDIS_MESSAGE UNALIGNED * pRndisMessage; RNDIS_PACKET UNALIGNED * pRndisPacket; ULONG DataLength; ULONG DataOffset; // // Get some info from the received RNDIS_PACKET message. // Note that this may contain multiple data packets, in which // case we only pass up the first one. // pHeaderMdl = packet->dataBufferMdl; pRndisMessage = MmGetMdlVirtualAddress(pHeaderMdl); pRndisPacket = (RNDIS_PACKET UNALIGNED *)&pRndisMessage->Message; DataLength = pRndisPacket->DataLength; DataOffset = FIELD_OFFSET(RNDIS_MESSAGE, Message) + pRndisPacket->DataOffset; // // Save away some existing values to restore later. // packet->rcvDataOffset = DataOffset; packet->rcvByteCount = pHeaderMdl->ByteCount; // // This is ONLY for test purposes. Simply modify the MDL to reflect // a single "raw" encapsulated frame. // pHeaderMdl->ByteOffset += DataOffset; (ULONG_PTR)pHeaderMdl->MappedSystemVa += DataOffset; pHeaderMdl->ByteCount = DataLength; } // // Undo for the above function. // VOID UnskipRcvRndisPacketHeader(IN USBPACKET * packet) { PMDL pHeaderMdl; ASSERT(packet->dataPacket == TRUE); // // Undo everything we did in the SkipRcv... function. // pHeaderMdl = packet->dataBufferMdl; pHeaderMdl->ByteOffset -= packet->rcvDataOffset; (ULONG_PTR)pHeaderMdl->MappedSystemVa -= packet->rcvDataOffset; pHeaderMdl->ByteCount = packet->rcvByteCount; } #endif // RAW_TEST