• Группа минипорт.
  • Группа протокол.
  • Как писать драйвера (часть 4)

    В прошлый раз мы заготовили списки необходимых функций, зарегистрировали их, а сегодня рассмотрим их поподробнее.

    Группа минипорт.

    Функции этой группы занимаются обработкой потока данных и событий, происходящих в верхнем уровне драйвера, и вызываемых обращением к NDIS TCP/IP стека.

    Если посмотреть на схемы из второй части, то видно, что в нижней части находятся функции протокола, а в верхней минипорта. Почему? Каждый драйвер выступает в двух ипостасях. Общаясь с верхним уровнем драйверов он становиться для него драйвером минипорта, а для нижнего уровня, драйвером протокола.

    Список функций минипорт:

    MPInitialize – инициализация группы.

    MPSend

    MPSendPackets

    MPTransferData

    MPReturnPacket

    Функции отвечающие за пересылку пакетов данных.

    MPQueryInformation

    MPSetInformation

    MPQueryPNPCapbilities

    MPIsSendOID

    MPProcessSetPowerOid

    Функции работы с питанием состоянием системы и системой PlagNPlay. Сказать особенно нечего. Стандартное отслеживание внутренних событий системы прописанное 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 SECONDARY\n");

      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 сама по контексту определяет к какому адаптеру предназначен пакет.

    Оставшиеся функции аналогичны по назначению с группой функций минипорта.







     

    Главная | В избранное | Наш E-MAIL | Добавить материал | Нашёл ошибку | Наверх