Как писать драйвера (часть 3) - Направления и технологии - Shelek
Структура драйвера.
Когда мы программируем под Windows API, мы ставим на обработку сообщений от Windows функцию WindowProc, которая регистрируется в момент создания класса окна. Примерно так же при создании экземпляра драйвера, в системе происходит регистрация всех необходимых функций.

Для регистрации и инициализации всего необходимого используется входная функция, которая так и называется: DriverEntry.

Для тех, кому тяжело качать Win2000 DDK размером 41Мб мы выложили базисный код драйвера в архиве. Скачайте архив для более полного ознакомления с текстом и работы с нашим текстом.

Сама DriverEntry запускается один раз, но важность правильной регистрации, думаю, понятна всем. Рассмотрим ее текст полностью.

Код:
NTSTATUS DriverEntry(
IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath
)
/*++

Routine Description:

Arguments:

Return Value:

--*/
{
NDIS_STATUS Status;
NDIS_PROTOCOL_CHARACTERISTICS PChars;
NDIS_MINIPORT_CHARACTERISTICS MChars;
PNDIS_CONFIGURATION_PARAMETER Param;

NDIS_STRING Name;
NDIS_HANDLE WrapperHandle;

//
// Register the miniport with NDIS. Note that it is the miniport
// which was started as a driver and not the protocol. Also the miniport
// must be registered prior to the protocol since the protocol's BindAdapter
// handler can be initiated anytime and when it is, it must be ready to
// start driver instances.
//

NdisMInitializeWrapper(&WrapperHandle, DriverObject, RegistryPath, NULL);

NdisZeroMemory(&MChars, sizeof(NDIS_MINIPORT_CHARACTERISTICS));

MChars.MajorNdisVersion = 4;
MChars.MinorNdisVersion = 0;
MChars.InitializeHandler = MPInitialize;
MChars.QueryInformationHandler = MPQueryInformation;
MChars.SetInformationHandler = MPSetInformation;
MChars.ResetHandler = MPReset;
MChars.TransferDataHandler = MPTransferData;
MChars.HaltHandler = MPHalt;

//
// We will disable the check for hang timeout so we do not
// need a check for hang handler!
//

MChars.CheckForHangHandler = NULL;
MChars.SendHandler = MPSend;
MChars.ReturnPacketHandler = MPReturnPacket;

//
// Either the Send or the SendPackets handler should be specified.
// If SendPackets handler is specified, SendHandler is ignored
//
// MChars.SendPacketsHandler = MPSendPackets;

Status = NdisIMRegisterLayeredMiniport(WrapperHandle, &MChars,
sizeof(MChars), &DriverHandle);
ASSERT(Status == NDIS_STATUS_SUCCESS);

NdisMRegisterUnloadHandler(WrapperHandle, PtUnload);

//
// Now register the protocol.
//

NdisZeroMemory(&PChars, sizeof(NDIS_PROTOCOL_CHARACTERISTICS));
PChars.MajorNdisVersion = 4;
PChars.MinorNdisVersion = 0;

//
// Make sure the protocol-name matches the service-name under which this protocol is installed.
// This is needed to ensure that NDIS can correctly determine the binding and call us to bind
// to miniports below.
//

NdisInitUnicodeString(&Name, L"SFilter"); // Protocol name
PChars.Name = Name;
PChars.OpenAdapterCompleteHandler = PtOpenAdapterComplete;
PChars.CloseAdapterCompleteHandler = PtCloseAdapterComplete;
PChars.SendCompleteHandler = PtSendComplete;
PChars.TransferDataCompleteHandler = PtTransferDataComplete;
PChars.ResetCompleteHandler = PtResetComplete;
PChars.RequestCompleteHandler = PtRequestComplete;
PChars.ReceiveHandler = PtReceive;
PChars.ReceiveCompleteHandler = PtReceiveComplete;
PChars.StatusHandler = PtStatus;
PChars.StatusCompleteHandler = PtStatusComplete;
PChars.BindAdapterHandler = PtBindAdapter;
PChars.UnbindAdapterHandler = PtUnbindAdapter;
PChars.UnloadHandler = NULL;
PChars.ReceivePacketHandler = PtReceivePacket;
PChars.PnPEventHandler= PtPNPHandler;

NdisRegisterProtocol(&Status, &ProtHandle,
&PChars, sizeof(NDIS_PROTOCOL_CHARACTERISTICS));

ASSERT(Status == NDIS_STATUS_SUCCESS);

NdisIMAssociateMiniport(DriverHandle, ProtHandle);

return(Status);
}

Майкрософт утверждает что нам нужны 4 функции минимум, присутствующие в DriverEntry:

* NdisMInitializeWrapper
* NdisIMRegisterLayeredMiniport
* NdisRegisterProtocol
* NdisIMAssociateMiniport


NdisMInitializeWrapper - функция указывает системе NDIS, что пришло время инициировать miniport service в ее системе. Возвращаемое значение необходимо сохранить на будущее. Обязательно надо обратить внимание, что если происходит ошибка при инициализации любого объекта, то при уже нормально отработавшей функции NdisMInitializeWrapper нужно вызвать NdisTerminateWrapper для высвобождения ресурса.

NdisIMRegisterLayeredMiniport - Функция, регистрирующая все функции уровня miniport

NdisRegisterProtocol - Функция, регистрирующая все функции протокола

NdisIMAssociateMiniport - Функция, информирующая NDIS о том, что есть, существует два уровня, минипорт и протокол, и говорящая об экспорте функций, если таковой присутствует.

Теперь, для полной расшифровки этой шарады, посмотрим, как работает система драйвера, уже на уровне кода.

После того, как драйвер зарегистрирует функции группы miniport, он должен зарегистрировать полученный HANDLE, для правильной un-регистрации этих функций в момент окончания работы драйвера. Это делает функция NdisMRegisterUnloadHandler. Затем мы регистрируем протокольную группу и связываем эти две группы, сообщая NDIS об их существовании - функцией NdisIMAssociateMiniport.

На этом в нашем примере функция DriverEntry закачивается. Стоит немного пояснить еще несколько моментов работы инициализации. При работе на уровне kernel не стоит использовать функции: malloc, realloc, memset и т. д. Для этого существуют NdisZeroMemory, NdisAllocateMemory и др. Они более корректно работают, и предназначены именно для использования в драйверах связанных с NDIS.

Конкретно сами функции, которые принадлежат группам, мы с вами будем рассматривать в процессе написания кода, да и то не все. Полное их описание вы уже посмотрите в DDK help. А мы с вами посмотрим на неявную третью группу.

Третья группа функций используется для коммуникации с аппликацией. Иногда и для непосредственного создания пакетов, и их отправки, как в случае с драйвером СОМ порта. В нашем случае драйвер пока никак не взаимодействует с уровнем аппликаций, поэтому их нет. Но, любой драйвер нуждается в управлении, наш пример, особенно, ибо он является тестовым и нам необходимо уметь давать нужные команды. Поэтому мы создадим и проинициализируем следующие функции:

extern NTSTATUS FilterOpen( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);

extern NTSTATUS FilterClose( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);

extern NTSTATUS FilterRead( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);

extern NTSTATUS FilterWrite( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);

extern NTSTATUS FilterIoControl( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);


Внесите их в файл passthru.h.

Теперь внесем изменения в нашу функцию DriverEntry.
Объявим массив:
Код:
PDRIVER_DISPATCH MajorFunctions [IRP_MJ_MAXIMUM_FUNCTION + 1];
Этот массив будет содержать все указатели функций для регистрации.
И добавим еще несколько переменных:
Код:
NDIS_STRING ntDeviceName; //имя для вызова виртуального device-а для NT
NDIS_STRING win32DeviceName; // тоже для win32
PDEVICE_OBJECT deviceObject; // и объект.

Затем начнем инициализацию. Перед завершающей регистрацией протокольных функций и связкой их с группой miniport и NDIS, функцией NdisIMAssociateMiniport, добавим следующий код:
Код:
// Name of control deviceObject.

// DeviceName that names the device object.
NdisInitUnicodeString(&ntDeviceName, L"\Device\passthru" );

// SymbolicName that is the Win32-visible name of the device
NdisInitUnicodeString(&win32DeviceName, L"\DosDevices\passthru" );

//Создаем строку имени

NdisZeroMemory(MajorFunctions, sizeof(MajorFunctions));
// Регистрируем ее
//Связываем имена функций с массивом
MajorFunctions[IRP_MJ_CREATE] = FilterOpen;
MajorFunctions[IRP_MJ_CLOSE] = FilterClose;
MajorFunctions[IRP_MJ_READ] = FilterRead;
MajorFunctions[IRP_MJ_WRITE] = FilterWrite;
MajorFunctions[IRP_MJ_DEVICE_CONTROL] = FilterIoControl;
//Регистрируем их
Status = NdisMRegisterDevice( WrapperHandle, &ntDeviceName,
&win32DeviceName, MajorFunctions,
&deviceObject, &GlobalData.NdisDeviceHandle);
// проверяем статус
if( Status != NDIS_STATUS_SUCCESS )
{
if(GlobalData.ProtHandle)
NdisDeregisterProtocol( &Status, GlobalData.ProtHandle );

if( GlobalData.NdisDeviceHandle )
NdisMDeregisterDevice( GlobalData.NdisDeviceHandle );

if( WrapperHandle )
NdisTerminateWrapper( WrapperHandle, NULL );
return (Status);
}

// set access method into deviceObject ( received from NdisMRegisterDevice() )
// объявление буферизации для связывающих операций
deviceObject->Flags |= DO_BUFFERED_IO;

// все.

Тела функций, типа Filter, объявите сразу после тела функции DriverEntry и оставьте пока пустыми, добавив, только возвращение значения return NDIS_STATUS_SUCCESS;
Information
  • Posted on 01.02.2010 00:57
  • Просмотры: 4100