windows-nt/Source/XPSP1/NT/net/rndis/usb8023/rndis.c
2020-09-26 16:20:57 +08:00

686 lines
22 KiB
C

/*++
Copyright (c) 1999 Microsoft Corporation
Module Name:
rndis.c
Author:
ervinp
Environment:
Kernel mode
Revision History:
--*/
#include <ndis.h>
#include <ntddndis.h> // 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