В прошлый раз мы заготовили списки необходимых функций, зарегистрировали их, а сегодня рассмотрим их поподробнее.
Группа минипорт. Функции этой группы занимаются обработкой потока данных и событий, происходящих в верхнем уровне драйвера, и вызываемых обращением к NDIS TCP/IP стека.
Если посмотреть на схемы из второй части, то видно, что в нижней части находятся функции протокола, а в верхней минипорта. Почему? Каждый драйвер выступает в двух ипостасях. Общаясь с верхним уровнем драйверов он становиться для него драйвером минипорта, а для нижнего уровня, драйвером протокола.
Список функций минипорт: MPInitialize - инициализация группы.
MPSend
MPSendPackets
MPTransferData
MPReturnPacketФункции отвечающие за пересылку пакетов данных.
MPQueryInformation
MPSetInformation
MPQueryPNPCapbilities
MPIsSendOID
MPProcessSetPowerOidФункции работы с питанием состоянием системы и системой PlugNPlay. Сказать особенно нечего. Стандартное отслеживание внутренних событий системы прописанное Microsoft.
MPHalt - отработка выгрузки и де регистрации драйвера при аварийном.
MPReset - как написано у Microsoft - мы не должны ничего делать :)
Работа с системой - необходимость отрабатывать события важные для сервиса корректно.
MPSetMiniportSecondary
MPPromoteSecondary
MPBundleSearchAndSetSecondary В системе может быть не один адаптер и соответственно не один драйвер к которому приходится обращаться. В случае такого используются эти функции.
В нашем случае основными функциями из этой группы - являются функции пересылки данных. Все остальные мы можем не рассматривать, их назначение - обслуживать правильно системные связи, вся основная часть которых написана Microsoft.
MPSend Основная функция вызываемая всегда, при прохождении данных. По правилам работы с данными в NDIS необходимо написать (что в примере и сделано) re-wrap пакету.
Для этого сначала пакет надо захватить, перекопировать содержимое пакета в свою память и переслать его далее, после чего освободить пакет. Вот как будет это выглядеть в коде:
PADAPT pAdapt = (PADAPT)MiniportAdapterContext;
//Контекст адаптера приходящий в качестве параметра. Присвоим его своему типизированному указателю.
NDIS_STATUS Status;
//Возвращаемый статус.
PNDIS_PACKET MyPacket;
//Наш пакет - пока только указатель.
PRSVD Rsvd;
//Резервный указатель.
PVOID MediaSpecificInfo = NULL;
//Тип адаптера с которым будем работать.
ULONG MediaSpecificInfoSize = 0;
//Размер типа адаптера.
ASSERT (pAdapt->pSecondaryAdapt);
pAdapt = pAdapt->pSecondaryAdapt;
//Проверка наличия второго сетевого адаптера. Вверху я говорил, что его наличие необходимо предусматривать.
if (IsIMDeviceStateOn (pAdapt) == FALSE)
{
return NDIS_STATUS_FAILURE;
}
//Проверка наличия и состояния.
NdisAllocatePacket(&Status, &MyPacket, pAdapt->SendPacketPoolHandle);
//Выделение места под размер полученного пакета (Pool) данных.
if (Status == NDIS_STATUS_SUCCESS)
{
PNDIS_PACKET_EXTENSION Old, New;
Rsvd = (PRSVD)(MyPacket->ProtocolReserved);
Rsvd->OriginalPkt = Packet;
MyPacket->Private.Flags = Flags;
MyPacket->Private.Head = Packet->Private.Head;
MyPacket->Private.Tail = Packet->Private.Tail;
//Собственно копирование всей служебной информации
NdisSetPacketFlags(MyPacket, NDIS_FLAGS_DONT_LOOPBACK);
//Установка ее в наш внутренний буфер.
NdisMoveMemory(NDIS_OOB_DATA_FROM_PACKET(MyPacket), NDIS_OOB_DATA_FROM_PACKET(Packet),
sizeof(NDIS_PACKET_OOB_DATA));
//Перенос данных в сам пакет.
NdisIMCopySendPerPacketInfo(MyPacket, Packet);
//Копирование служебных данных по пересылке пакета.
//Копирование данных о типе адаптера, куда пересылать данные.
NDIS_GET_PACKET_MEDIA_SPECIFIC_INFO(Packet, &MediaSpecificInfo, &MediaSpecificInfoSize);
if (MediaSpecificInfo || MediaSpecificInfoSize)
{
NDIS_SET_PACKET_MEDIA_SPECIFIC_INFO(MyPacket, MediaSpecificInfo, MediaSpecificInfoSize);
}
//Собственно пересылка имеющихся данных в NDIS, что вызовет нормальное
//прохождение пакета далее по цепочке драйверов.
NdisSend(&Status, pAdapt->BindingHandle, MyPacket);
if (Status != NDIS_STATUS_PENDING)
{
NdisIMCopySendCompletePerPacketInfo (Packet, MyPacket);
NdisFreePacket(MyPacket);
}
//Если нет задержки на отсылке освободить пакет.
}
else
{
//Это говорит об отсутствии пакета в системе - ничего не надо делать.
}
return(Status);
Возврат значения SUCCESS или код ошибки.
Стоит остановиться еще на одном моменте. Когда система ответила, что посылка данных закончена кодом задержки пакета - NDIS_STATUS_PENDING.
В этом случае мы пакет не освободим, блокировав таким образом всю систему NDIS по пересылке данных. Такое случается при посылке по медленной сети большого числа пакетов.
Как нам освободить пакет? Система предусматривает такое и при освобождении ресурса после освобождению пакета вызовет функцию из группы протокола PtSendComplete. Смена на протокольную группу вызвана тем, что система получит сообщение от низлежащего драйвера, что вызывает обращение именно к этой группе.
В данной функции, код которой идет следом мы увидим параметры в которых нам передадут контекст операции Send и адаптера, в результате чего мы получим возможность вызвать функцию NdisMSendComplete после NdisDprFreePacket и освободить NDIS для передачи нам следующих пакетов.
{
PADAPT pAdapt =(PADAPT)ProtocolBindingContext;
PNDIS_PACKET Pkt;
PRSVD Rsvd;
pAdapt = pAdapt->pPrimaryAdapt;
Rsvd =(PRSVD)(Packet->ProtocolReserved);
Pkt = Rsvd->OriginalPkt;
NdisIMCopySendCompletePerPacketInfo (Pkt, Packet);
NdisDprFreePacket(Packet);
NdisMSendComplete(pAdapt->MiniportHandle, Pkt, Status);
Группа протокол.Протокольные функции несколько отличаются от функций минипорта, но имеют схожую функциональную нагрузку, обратную функциям минипорта по направлению передачи пакетов.
PtOpenAdapterComplete
PtCloseAdapterComplete
PtBindAdapter
PtUnbindAdapterФункции работы с адаптером - в нашем случае адаптером является драйвер модема или сетевой карты. Адаптер соответственно при запуске надо открыть и при окончании работы - закрыть. При обращении к нему привязать (bind) адаптер, захватить ресурс. В конце работы - освободить.
PtResetCompleteАбсолютно пустая функция - она должна быть но мы сюда не приходим.
PtRequestCompleteФункция вызываемая из PtPnPNetEventSetPower.
PtStatusФункция отвечающая за проверку статуса нижестоящего адаптера, вернее статуса взаимодействия с ним нашего уровня.
PtStatusCompleteЗавершение в случае невозможности быстрого ответа, примерно как и в случае MPSend.
PtSendCompleteОписана в секции минипорт
PtTransferDataComplete
PtReceive
PtReceiveComplete
PtReceivePacketФункции работы с данными - аналогичны функциям минипорта. Одну из функций мы уже разбирали. Функции остальных - чуть позже.
PtUnloadФункция заведующая выгрузкой драйвера при выходе.
PtPNPHandler
PtPnPNetEventReconfigure
PtPnPNetEventSetPowerРабота с PnP.
Вновь обратим внимание на симметричную функцию PtReceive.
PADAPT pAdapt =(PADAPT)ProtocolBindingContext;
//Контекст адаптера
PNDIS_PACKET MyPacket, Packet; //Пакеты.
NDIS_STATUS Status = NDIS_STATUS_SUCCESS;
//Статус
if(!pAdapt->MiniportHandle)
{
Status = NDIS_STATUS_FAILURE;
}
else
do
{
//Эта часть работает при наличии второго адаптера :)
if(pAdapt->isSecondary)
{
DBGPRINT("PASSTHRU GETTING RECIEVES ON SECONDARYn");
ASSERT(0);
}
//Забираем указатель на пакет в NDIS.
Packet = NdisGetReceivedPacket(pAdapt->BindingHandle, MacReceiveContext);
//Если пакета нет то мы выходим иначе продолжаем как и в случае с отправкой.
if(Packet != NULL)
{
//Резервируем пакет для себя.
NdisDprAllocatePacket(&Status, &MyPacket, pAdapt->RecvPacketPoolHandle);
if(Status == NDIS_STATUS_SUCCESS)
{
//Копируем данные, как служебные, так и сами данные передаваемые наверх.
MyPacket->Private.Head = Packet->Private.Head;
MyPacket->Private.Tail = Packet->Private.Tail;
NDIS_SET_ORIGINAL_PACKET(MyPacket, NDIS_GET_ORIGINAL_PACKET(Packet));
NDIS_SET_PACKET_HEADER_SIZE(MyPacket, HeaderBufferSize);
NdisGetPacketFlags(MyPacket) = NdisGetPacketFlags(Packet);
NDIS_SET_PACKET_STATUS(MyPacket, NDIS_STATUS_RESOURCES);
//В этом случае мы не посылаем пакет как при отправке, а просто указываем
//NDIS что MyPacket готов к передаче наверх.
NdisMIndicateReceivePacket(pAdapt->MiniportHandle, &MyPacket, 1);
ASSERT(NDIS_GET_PACKET_STATUS(MyPacket) == NDIS_STATUS_RESOURCES);
//Освобождение пакета при нормальной передаче.
NdisDprFreePacket(MyPacket);
break;
}
}
}
Выбор типа адаптера внизу дает возможность применить для индикации готовности к передаче в случае ошибки стандартного сообщения NDIS. Это происходит в случае специфичных сетей и обрабатывается функциями связанными с этими типами.
pAdapt->IndicateRcvComplete = TRUE;
switch(pAdapt->Medium)
{
case NdisMedium802_3:
NdisMEthIndicateReceive(pAdapt->MiniportHandle, MacReceiveContext, HeaderBuffer,
HeaderBufferSize, LookAheadBuffer, LookAheadBufferSize, PacketSize);
break;
case NdisMedium802_5:
NdisMTrIndicateReceive(pAdapt->MiniportHandle, MacReceiveContext, HeaderBuffer,
HeaderBufferSize, LookAheadBuffer, LookAheadBufferSize, PacketSize);
break;
case NdisMediumFddi:
NdisMFddiIndicateReceive(pAdapt->MiniportHandle, MacReceiveContext, HeaderBuffer,
HeaderBufferSize, LookAheadBuffer, LookAheadBufferSize, PacketSize);
break;
default:
//Это в случае если тип сети неизвестен.
ASSERT(0);
break;
}
} while(FALSE);
return Status;
Выход с статусом завершения.
Некоторое пояснение.Когда тип адаптера и сети специфичен и отличается от стандарта LAN, нам нужно сообщить о пакете и его отправке соответствующей части сервиса NDIS. Именно в связи с этим и появляется выбор типа адаптера. При получении возможно наличие одновременного запроса на прием пакета с разных адаптеров.
В случае с операцией Send этого не происходит, так как NDIS сама по контексту определяет к какому адаптеру предназначен пакет.
Оставшиеся функции аналогичны по назначению с группой функций минипорта.