Пишем первый драйвер. Часть 1. |
|
Ну вот мы и добрались до самого интересного - сейчас будем писать драйвер. Итак, приступим. Для удобства выполнения, всю последовательность действий по написанию драйвера я распишу по пунктам.
1. Создание директории проекта
Установив DDK, на Вашем компьютере в директории C:\WINDDK\2600.1106\ должны появиться файлы DDK. В этой директории создадим папку, в которой будут храниться наши проекты. Назовем ее, например, MyDrivers.
В папке MyDrivers создадим папку FirstDriver - тут будет находится наш первый проект драйвера.
2. Подготовка файлов проекта
В папке FirstDriver создайте пустой текстовый файл и переименуйте его под именем FirstDriver.c
При попытке переименовки со сменой расширения файла, появляется следующее предупреждение:
Не обращаем внимания на это предупреждение, и нажимаем Да. При этом наш файл примет вид:
Если же никакого предупреждения не было и переименованный файл так и остался текстовым с именем FirstDriver.c и расширением .txt, то в настройках своийств папки, которые можно найти в Пуск-> Настройка-> Панель управления-> Свойства паки уберите галочку напротив пункта "Скрывать расширения для зарегестрированных типов файлов". Попробуйте еще раз и все должно быть в порядке.
Теперь нам надо добавить еще два очень важных файла в наш проект, без которых драйвер нам не сделать. Они называются makefile и sources (обратите внимание, у них нет расширения). Их можно создать самим, но мы сделаем проще: скопируем готовые из какого либо примера проекта драйвера из DDK. Например, возьмем их из C:\WINDDK\2600.1106\src\general\cancel\sys\. Итак, копируем из указанной директории эти два файла и вставляем их в нашу папку проекта FirstDriver. Эти файлы управляют процессрм компиляции драйвера. Файл makefile оставляем без изменений, а вот sources надо подредактировать.
Открываем этот файл с помощью блокнота, и удаляем из него все что там есть и вставляем туда текст, представленный ниже. Сохранияем файл.
TARGETNAME=Port TARGETPATH=c:\WINDDK\2600.1106\MyDrivers\FirstDriver TARGETTYPE=DRIVER SOURCES=FirstDriver.c
Первым параметром идет TARGETNAME, которому мы присвоили Port. Это значит, что когда DDK откомпилирует наш код и создаст драйвер, имя этого файла будет Port.sys Следующем параметром идет TARGETPATH, которому мы указали путь к папке нашего проекта. Если Вы устанавливали DDK в другое место, или создали пупку проекта в другой директории, здесь Вам надо это поправить на тот путь, который у Вас. Параметр TARGETTYPE пока оставлю без комментариев. В параметре SOURCES указываем, из каких файлов будет компилироваться драйвер. У нас это файл FirstDriver.c, вот мы его и указали.
3. Код драйвера
Всю подготовительную работу мы сделали. Можно приступать к самой содержательной части - коду драйвера. Писать мы будем его на Си.
Еще раз напомню решаемую нами задачу: надо написать драйвер под Windows 2000, XP с помощью которого можно будет работать с портами компьютера (читать и писать данные) .
Рассмотрим код, представленный ниже. Серым цветом обозначены те участки кода, которые являются как бы шаблонными для большинства драйверов, а зеленым - код, который относится именно к нашему текущему драйверу.
#include "ntddk.h"
#define NT_DEVICE_NAME L"\\Device\\NTName" #define WIN32_DEVICE_NAME L"\\DosDevices\\MYDRIVER" #define IOCTL_READ CTL_CODE (FILE_DEVICE_UNKNOWN, 0x800, METHOD_BUFFERED, FILE_ANY_ACCESS) #define IOCTL_WRITE CTL_CODE (FILE_DEVICE_UNKNOWN, 0x801, METHOD_BUFFERED, FILE_ANY_ACCESS)
NTSTATUS CtlCreate(IN PDEVICE_OBJECT, IN PIRP); NTSTATUS CtlClose(IN PDEVICE_OBJECT, IN PIRP); NTSTATUS CtlDispatch(IN PDEVICE_OBJECT,IN PIRP); VOID UnloadOperation(IN PDRIVER_OBJECT pDriverObject); NTSTATUS DriverEntry(IN PDRIVER_OBJECT pDriverObject, IN PUNICODE_STRING RegistryPath) { PDEVICE_OBJECT pDeviceObject; UNICODE_STRING uniNtName; UNICODE_STRING uniWin32Name; RtlInitUnicodeString(&uniNtName, NT_DEVICE_NAME); RtlInitUnicodeString(&uniWin32Name, WIN32_DEVICE_NAME); IoCreateSymbolicLink(&uniWin32Name, &uniNtName); IoCreateDevice(pDriverObject,0,&uniNtName,FILE_DEVICE_UNKNOWN,0,FALSE,&pDeviceObject); pDriverObject->MajorFunction[IRP_MJ_CREATE]=CtlCreate; pDriverObject->MajorFunction[IRP_MJ_CLOSE]=CtlClose; pDriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL]=CtlDispatch; pDriverObject->DriverUnload = UnloadOperation; return STATUS_SUCCESS; } NTSTATUS CtlCreate(IN PDEVICE_OBJECT pDeviceObject,IN PIRP Irp) { Irp->IoStatus.Status=STATUS_SUCCESS; Irp->IoStatus.Information=0; IoCompleteRequest(Irp,IO_NO_INCREMENT); return STATUS_SUCCESS; } NTSTATUS CtlClose(IN PDEVICE_OBJECT pDeviceObject,IN PIRP Irp) { Irp->IoStatus.Status=STATUS_SUCCESS; Irp->IoStatus.Information=0; IoCompleteRequest(Irp,IO_NO_INCREMENT); return STATUS_SUCCESS; } VOID UnloadOperation(IN PDRIVER_OBJECT pDriverObject) { PDEVICE_OBJECT deviceObject = pDriverObject->DeviceObject; UNICODE_STRING uniWin32NameString; RtlInitUnicodeString( &uniWin32NameString, WIN32_DEVICE_NAME ); IoDeleteSymbolicLink( &uniWin32NameString ); IoDeleteDevice( deviceObject ); return; } NTSTATUS CtlDispatch(IN PDEVICE_OBJECT pDeviceObject,IN PIRP Irp) { PIO_STACK_LOCATION pIrpStack; PUSHORT pIOBuffer;
USHORT Port; USHORT ValueToPort;
pIrpStack=IoGetCurrentIrpStackLocation(Irp); pIOBuffer=Irp->AssociatedIrp.SystemBuffer; switch (pIrpStack->Parameters.DeviceIoControl.IoControlCode) {
case IOCTL_READ: Port=pIOBuffer[0]; pIOBuffer[0]=READ_PORT_UCHAR((PUCHAR)Port); Irp->IoStatus.Information=2; break; case IOCTL_WRITE: Port=pIOBuffer[0]; ValueToPort=pIOBuffer[1]; WRITE_PORT_USHORT((PUSHORT)Port,(USHORT)ValueToPort); Irp->IoStatus.Information=0; break;
} Irp->IoStatus.Status=STATUS_SUCCESS; IoCompleteRequest (Irp,IO_NO_INCREMENT); return STATUS_SUCCESS; }
Представленный код - есть код простейшего драйвера, который может только записать данные в порт и прочесть их от туда. Выглядит страшновато? Ни чего, в следующей статье будем разбираться с тем что в нем понаписано.