Интернет-магазин

Просмотр корзины
В корзине:

товаров - 0 шт.



§ 18. Пишем первый драйвер. Часть 1.

Дмитрий Иванов, 18 Ноября 2008 года
Статья доработана и обновлена 14 мая 2014

Файлы к статье скачать
Имя: PS018.zip (ZIP архив)
Размер: 19 КБ


Ну вот мы и добрались до самого интересного - сейчас будем писать драйвер. Итак, приступим. Для удобства выполнения, всю последовательность действий по написанию драйвера я распишу по пунктам.

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;
}

Представленный код - есть код простейшего драйвера, который может только записать данные в порт и прочесть их от туда. Выглядит страшновато? Ни чего, в следующей статье будем разбираться с тем что в нем понаписано.



© Дмитрий Иванов
18 ноября 2008 года
http://www.kernelchip.ru



© KERNELCHIP 2006 - 2023