1. Загальні відомості про драйвери пристроїв в системі Windows.

Природно, кожна операційна система має власну архітектуру і свої особливості функціонування драйверів. Але практично у всіх сучасних ОС можна виділити наступні особливості, характерні для роботи підсистеми введення-виведення:


В ОС Windows, як і в більшості сучасних ОС, драйвера управляють буквально всім: роботою з апаратурою, підтримкою файлових систем різних типів, мережевих протоколів і т.п. Це дає певні переваги і робить систему більш гнучкою: наприклад, для того, щоб ОС стала “розуміти” інший мережевий протокол, потрібно всього лише встановити відповідний драйвер.

1.1 Система введення-виведення в Windows.


На даний момент найбільш поширені два сімейства ОС Windows: Windows NT, куди відносяться Windows NT, 2000, XP, і Windows 9x (Win 95, 98, ME). При цьому відзначається тенденція до відмирання гілки 9х, хоча такі системи будуть зустрічатися ще досить довго. Кожна гілка використовує свою архітектуру ядра і підсистеми вводу-виводу. Тому природно, написання драйверів для цих систем повинне відрізнятися.

У Windows 9x довгий час використовувалися. Vxd – драйвера. Ця модель драйверів починає свою історію ще з Windows 3.1. Для. Vxd – драйверів збереглася сумісність “знизу вгору”: тобто драйвер, написаний під Windows 3.1, буде нормально працювати і під Windows 95, а може бути, і 98. Функції драйверів. vxd використовуються як Win32, так і Win16 додатками.

У Windows NT 4.0 з’явилася своя архітектура драйверів. Вона ставила перед собою мети підвищення стійкості роботи драйвера, переносимості з одного платформи на іншу, підтримки багатопроцесорності т.п. Разом з тим архітектура драйверів Windows NT 4.0 була, що називається, “сирий” і недопрацьованою, хоча і дуже перспективною. З виходом систем Win98 і Win2000 з’явилася нова архітектура драйверів – WDM (Windows Driver Model). Вона розвинулася з архітектури драйверів Windows NT 4.0 з невеликими змінами. WDM – драйвера з рівним успіхом можуть бути використані як в Win 98, так і в Win 2000.

Система Win 98 складається як би з двох шарів: User Mode (режим користувача) і Kernel Mode (режим ядра). У режимі користувача функціонують користувальницькі додатка. Вони працюють в 3-му кільці захисту; кожна програма працює у своєму віртуальному адресному просторі. Для кожного DOS або Windows – додатки створюється своя віртуальна машина (Virtual Machine, VM), завданням якої є віртуалізація апаратури комп’ютера для даного застосування. Тобто кожне додаток вважає, що вся оперативна пам’ять і всі інші апаратні ресурси належать тільки йому і додаток може звернутися до них у будь-який момент. Ядро ОС місти диспетчер віртуальних машин (Virtual Machine Manager, VMM). Завдання VMM – коректно вирішувати конфлікти, що виникають при доступі до ресурсів системи з різних VM. Ядро, VMМ, віртуальні машини і драйвера віртуальних пристроїв (Virtual Device Drivers), природно, працюють в режимі ядра (Kernel Mode).


Підсистема вводу-виводу Win 98.
Рис. 1. Підсистема вводу-виводу Win 98.

У Windows 98 обробка запитів на введення-виведення від додатків DOS і від старих Win16 – додатків відрізняється від обробки запитів нових Win32 – додатків. Для DOS – додатків створюється своя віртуальна машина (DOS virtual machine), Win 16 і Win32 – додатки використовують віртуальну машину Windows (System Virtual Machine). Зазвичай, коли додаток запитує операцію введення-виведення (Наприклад, викликає функцію API ReadFile – читання з файлу), цей запит надходить в одну із системних DLL (у нашому випадку – kernel32.dll). Звідти запит на операцію з зовнішнім пристроєм передається відразу системним драйверам. Така організація запиту Додаток -> dll -> Драйвер отримала найбільше поширення.

Система Windows 2000 має іншу архітектуру, відмінну від Win98. Це зумовлено підвищеними вимогами до надійності, захисту і переносимості цієї системи (теоретично, Win2000 – переносима система, і існують реалізації Win2000 під системи Alpha, MIPS та ін.) В даний час саме завдяки цим особливостям Win2000 завойовує все більшу популярність, тому варто розглянути особливості її архітектури докладніше.


головні компоненти Windows2000
Рис. 2 – Головні компоненти Windows2000.

Оточення Win2000 включає компоненти, які працюють в режимі користувача (User mode) і в режимі ядра (Kernel mode). У режимі користувача працюють підсистема захисту, підсистема Win32-архітектури (забезпечує стандартні API – виклики Windows), підсистема POSIX (забезпечення платформ). У режимі ядра працюють всі основні компоненти системи: диспетчер вводу-виводу (I / O manager), диспетчер конфігурації (Configuration Manager), підсистема PnP, диспетчер управління енергоспоживанням (Power Manager), диспетчер пам’яті (Memory Manager) та інші життєво необхідні служби. Драйвера в Win2000 включені в підсистему введення-виведення. При цьому драйвера тісно взаємодіють практично з усіма компонентами ядра. Драйвера взаємодіють з апаратурою за допомогою Hardware Abstraction Level, HAL (рівень абстракції апаратури). HAL – Програмний компонент ядра Win2000, який забезпечує інтерфейс ядра (у тому числі і деяких драйверів) з апаратурою. Т.к. Win2000 – платформенно незалежна система (вже зараз є версії Win2000 для процесорів Alpha і RISC), то HAL позбавляє ядро ​​від безпосереднього спілкування з кешем, перериваннями, шинами введення-виведення і більшістю інших пристроїв, залишаючи цю роботу драйверам, спеціально написаним для даної системи. Таким чином, ядро ​​системи представляється набором окремих ізольованих модулів з чітко визначеними зовнішніми інтерфейсами.


Всі драйвера NT мають безліч стандартних методів драйвера, визначених системою, і, можливо, кілька специфічних методів, визначених розробником. Драйвера Windows 2000 використовують архітектуру WDM (Windows Driver Model). У Windows 2000 драйвера бувають наступних типів:


У свою чергу, існує три типи драйверів ядра, кожен тип має чітко певні структуру і функціональність.


Драйвера Windows 2000 повинні відповідати таким вимогам:


Система введення-виведення Windows 2000 має такі особливості:


2. Використання пакету NuMega Driver Studio для написання
WDM – драйверів пристроїв.


Розробка WDM – драйвера з використанням тільки DDK є складною і трудомістким завданням. При цьому доводиться виконувати багато однотипних операцій: створення скелета драйвера, написання inf – файлу для його установки, створення програми для тестування і т.п. При цьому багато хто з цих операцій однотипні і стандартні. Часто при написанні драйверів доводиться виконувати однотипні операції. Наприклад, якщо ми розробляє драйвер пристрою для шини PCI, то нам напевно доведеться зробити:


Всі перераховані операції – рутинна, однотипна робота, стандартна для більшості драйверів.

Для прискорення проектування та розробки драйверів пристроїв під Windows використовуються програмні пакети різних фірм. Найбільш відомим пакетом є програма DriverStudio фірми NuMega. Для роботи цієї програми обов’язковою є установка пакета DDK (бажано – DDK 2000 як найбільш універсального) і середовища Visual C + + версії не нижче 5.0. Особисто я використовував таку конфігурацію:
– DriverStudio 2.01 (далі в тексті – DS);
– DDK 2000;
– Visual C + + 6.0 (далі в тексті – VC + +).

У неї входять наступні програми:

DriverWorks – ця програма є основним компонентом DriverStudio. Саме за допомогою DriverWorks виконується розробка драйвера під Windows 98/ME/2K з використанням WDM. Установка цієї програми обов’язкове. При інсталяції DriverWorks інтегрується в середовище розробки Visual C + +.

VtoolsD – засіб для розробки. Vxd – драйверів. Дана утиліта не залежить від інших програм DS або VC + + і може як інсталюватися, так і немає. В принципі, якщо не передбачається розробка. vxd – драйверів, даний компонент не є необхідним.

SoftIce – kernel-mode відладчик. Ця програма може бути використана як для налагодження драйверів, так і в інших цілях. Фактично це дуже потужний відладчик, який може отримувати доступ до практично будь-якими елементами системи. Недоліками його є його висока складність і незручність в експлуатації. Робота з SoftIce буває небезпечна саме в силу його великих можливостей: будь невірне дію зазвичай фатально для системи. Хоча, для налагодження драйверів пристроїв важко знайти щось краще.

DriverNetworks – використовується для розробки драйверів мережевих пристроїв. Якщо не передбачається розробка такого драйвера, даний компонент не є необхідним.

Для інсталяції DS на комп’ютері необхідно проінсталювати пакет DDK і середу VC + +. Після цього можна починати інсталяцію DS. Сама інсталяція проста та не відрізняється від процесу інсталяції того ж VC + +, наприклад. За замовчуванням пакет ставиться в папку C: Program FilesNuMegaDriverStudio. Знання шляху до DS необхідно для подальшої роботи з програмою. Надалі ми будемо його називати <путь_к_DS>. При першому запуску DS необхідно скомпілювати бібліотеки, необхідні для роботи. Для цього слід запустити середу VC + + і відкрити проект <путь_к_DS> DriverVorkssourcevdwlibs.dsw. Вся суть в тому, що DS використовує власну бібліотеку класів для написання драйвера. Ця бібліотека поставляється в вихідних кодах, подібно бібліотеці MFC або бібліотекам під UNIX. Тому тепер необхідно відкомпілювати за допомогою VC + + даний проект.

Варто відразу перевірити опції проекту і встановити активну конфігурацію VdwLibs – Win32 WDM Checked (якщо планується налагоджувати скомпільовані драйвера) або Win32 WDM Free. Тепер запускаємо проект на компіляцію. В Внаслідок в папці <путь_к_DS> DriverVorkslibi386checked з’являється бібліотека vdw.lib (при використанні ОС win2K) або vdw_wdm.lib (win 9x). DS готовий до роботи.

2.1. Система класів DriverWorks.


Можливо, ідея писати драйвера об’єктно-орієнтованими і здається на перший погляд нелогічною. Але при більш близькому знайомстві з DriverStudio і з драйверами в загальному, виявляється, що це не так вже й страшно і досить зручно. Об’єктна модель DriverWorks відображає архітектуру WDM і являє собою систему класів, побудовану на системних викликах. Мета DriverWorks – з одного боку, залишатися на досить низькому рівні програмування, щоб ефективно писати драйвера, а з іншого – спростити і впорядкувати процес розробки драйверів режиму ядра.

У відповідності з ідеологією DriverWorks драйвер видається, як набір об’єктів. Ця ж ідея присутня і в “чистій” архітектурі WDM, але DriverWorks впорядковує ці об’єкти і представляє їх екземплярами класів. Класи DriverWorks також кілька спрощують код драйвера в порівнянні з DDK, роблять його більш компактним і доступним для розуміння. Часто повторювані, рутинні фрагменти коду драйвера заховані усередині методів класу. І те, що при використанні пакета DDK займало кілька рядків у програмі, тепер можна цілком замінити викликом одного єдиного методу.

Також в DriverWorks запропоновано декілька корисних класів: наприклад клас KFile – доступ до файлів або класи динамічних списків і масивів.

Загалом, сама ідея DriverWorks нагадує Visual C + + і бібліотеку MFC. MFC вдає із себе якусь прошарок, що відділяє програміста від моторошнуватих функцій API і дозволяє створювати об’єктно-орієнтовані проекти, при цьому залишаючись на досить низькому рівні програмування.

Втім, в системі класів DriverWorks є одна особливість: ієрархія класів практично відсутня. Це цілком природно: в системі класів DriverWorks присутні самі різні класи – класи, що представляють собою ресурси пристрою (лінії ПДП, переривань, областей пам’яті, портів введення-виведення), самі пристрої, класи для взаємодії з реєстром, файлами та т.п. Ще одним аргументом на користь відсутності спадкування є те, що розгалужена ієрархія класів може знизити швидкодію програми. Для драйвера, це, звичайно, неприйнятно.

В основі архітектури DriverWorks лежить кілька основних класів.


Об’єкт драйвера (Driver Object).


Об’єкт драйвера є екземпляром класу KDriver. Він являє драйвер в цілому як якусь абстракцію. Для об’єкта драйвера абсолютно все одно, яким обладнанням він управляє, об’єкт драйвера про це по-справжньому ніколи не замислюється. Його завдання – забезпечити інтерфейс драйвера з ОС: завантаження і ініціалізація драйвер, вивантаження і т.п. А управління апаратурою покладається на інші об’єкти драйвера, зокрема, на об’єкт пристрою.

Коли ОС завантажує драйвер, то вона створює для нього відповідний об’єкт драйвера. Компонент ядра операційної системи – диспетчер вводу-виводу (I / O Manager) – використовує об’єкти драйверів для управління пристроями. Кожен драйвер відповідає за управління одним або декількома об’єктами пристроїв. Запит на операцію вводу-виводу (I / O request), посланий додатком користувача, надходить до диспетчеру введення-виведення.

Диспетчер введення-виведення визначає, який саме об’єкт драйвера відповідає за відповідний об’єкт пристрою, і перенаправляє йому запит. Крім управління об’єктами пристроїв, об’єкт драйвера має додаткові методи, що відповідають за ініціалізацію і завершення роботи драйвера. Програміст створює свій підклас класу KDriver для взаємодії з системою. Він обов’язково повинен містити метод DriverEntry – функцію, що спричинюється при ініціалізації драйвера.

На відміну від звичайного не-WDM драйвера, процедура ініціалізації WDM-драйвера виконує дуже обмежену кількість функцій: в основному це завантаження деяких внутрішніх змінних на основі даних реєстру. WDM-драйвер не ініціалізує ресурси пристрою при старті у виклику EntryPoint. Для цього існує об’єкт пристрої.

WDM-драйвер експортує метод AddDevice, який викликається системою, якщо виявлено пристрій, підтримуване даними драйвером. Цей метод відповідає за створення об’єктів пристроїв, відповідних системним фізичним об’єктам пристроїв (Physical Device Object, PDO).

Об’єкт драйвера може містити додаткові методи для реініціалізаціі драйвера на більш пізніх стадіях завантаження системи. Такий підхід необхідний, якщо в системі присутній кілька драйверів, критичних до порядку їх завантаження. Втім, такі проблеми зустрічаються нечасто.

В принципі, добре спроектований драйвер повинен експортувати метод Unload, який викликається при вивантаженні драйвера. Але такі випадки зустрічаються досить рідко.


Клас KRegistryKey


Як було згадано вище, драйвер звертається до системного реєстру при ініціалізації. Системний реєстр (registry) – Системна база даних, організована у вигляді дерева, схожого на дерево каталогів. Кожну гілку цього дерева (реєстру) називають розділом (key), кожен аркуш – параметром (value). Дані, що зберігаються в реєстрі, можуть бути різних типів: ціле (integer), рядок, набір байтів.

Система дозволяє кожному драйверу зберігати дані в реєстрі. Ці дані використовуються драйверами при старті і ініціалізації. Зазвичай драйвер зберігає дані в розділі HKLMSYSTEMCurrentControlSetServices <ім'я драйвера> Parameters.

У DriverWorks є клас KRegistryKey, який полегшує доступ до параметрів реєстру. Він має методи для читання (QueryValue), записи (WriteValue), видалення (Delete) значень ключів реєстру. При виклику конструктора KRegistryKey відразу вказується ключ, з яким буде пов’язаний створюваний об’єкт. Далі можна змінити ключ за допомогою методу Reconstruct.


Об’єкт запиту на введення-виведення (I / O Request Object)


Об’єкти запиту на введення-виведення, більш відомі, як пакети запиту на ввід-висновок (I / O request packet, IRP – так ми і будемо їх називати надалі), призначені для управління драйверами режиму ядра.

Фізично IRP являє собою досить складну структуру даних, що містить безліч полів, таких як код статусу, покажчик на буфер користувача, покажчик на IRP драйвера більш високого рівня, різні прапори і т.п. Багато з цих полів не використовується драйверами режиму ядра, але необхідні для того, щоб IRP був функціонально повним інструментом управління драйверами. Тобто при допомоги IRP можна керувати будь-яким типом драйвера. При бажанні побачити структуру IRP у всій її пишності – див Win2000 DDK.

Обмін інформацією та управління драйверами за допомогою IRP виглядає приблизно таким чином: коли додаток користувача посилає дані або намагається отримати дані з пристрою, диспетчер вводу-виводу формує IRP і відправляє його драйверу, відповідальному за даний пристрій. Об’єкт драйвера отримує цей IRP і перенаправляє його одному зі своїх об’єктів пристроїв. Об’єкт пристрої, отримавши пакет, може або почати його обробку негайно, або поставити його в чергу, щоб обробити цей пакет пізніше. Що саме зробить об’єкт пристрою, залежить від того, який прийшов IRP, від стану об’єкта пристрою і від стану самого пристрою. Після того, як пакет буде оброблений, об’єкт пристрою пошле IRP з інформацією про результат операції назад диспетчеру введення-виведення.

Кожен IRP описує операцію В / В, яка може бути виконана пристроєм. Для того, щоб драйвер зміг отримати інформацію про те, яка саме операція повинна бути виконана, IRP містить цілий набір атрибутів: старший і молодший коди функції, код статусу і різні параметри: число байт, які мають бути прочитані, зсув і т.п. За час свого існування IRP може проходити кілька рівнів ієрархії драйверів пристроїв в системі. Тому в пакеті резервується місце для збереження даних і параметрів, необхідних для наступного драйвера в ієрархії – так званий “стек IRP”, “IRP stack location “. Коли об’єкт пристрою обробляє запит, то він має доступ тільки до тих ділянок стека, які призначені для використання ним або пристроєм більш низького рівня, якому буде перенаправлено IRP.


Інтерфейс із драйвером за допомогою IRP
Рис.3 – Інтерфейс із драйвером за допомогою IRP

IRP можуть створюватися як диспетчером В / В, так і самими драйверами. Частіше все це відбувається при виконанні функцій CreateFile, CloseFile, ReadFile, WriteFile і DeviceControl.

IRP може бути знищений, якщо необхідно відмінити операцію В / В, наприклад, при закритті додатка. Об’єкт IRP містить покажчик на функцію, що спричинюється при знищенні пакета.


Об’єкт пристрої (Device Object).


Об’єкти пристроїв є екземплярами класу KDevice або KPnpDevice. Ці класи є наріжними каменями архітектури DriverWorks: вони представляють собою як би програмний образ тих пристроїв, які присутні в системі. Саме об’єкти пристроїв забезпечують управління і обмін даними з зовнішніми пристроями, управління їх ресурсами – лініями переривань, каналами ПДП, діапазонами адрес пам’яті, портами В / В і т.п. Коли виконується системний виклик типу CreateFile, ReadFile, WriteFile, диспетчер В / В посилає IRP відповідному драйверу. Але сам драйвер, вірніше об’єкт драйвера, не виконує ніяких операцій по обробці цього пакету – він просто передає його об’єкту пристрої та забуває про саме існування цього IRP. Це природно, адже управління фізичним пристроєм – не його завдання, це справа відповідного об’єкта пристрою.

Клас KDevice є суперкласом для всіх класів пристроїв. Але на практиці він зараз майже не використовується. Частіше використовують його нащадка – клас KPnpDevice. Цей клас призначений для управління PnP-пристроями, тобто пристроями, які конфігурується системою. У даний момент практично всі пристрої є PnP-пристроями. Поява таких пристроїв здорово полегшило життя розробникам драйверів: використовувати KPnpDevice набагато простіше, а часто і безпечніше, ніж KDevice. Ще б пак, адже в даному випадку всі проблеми конфігурування і ініціалізації ресурсів устаткування лягають на широкі плечі системи.

Будь-який об’єкт пристрою містить стандартні методи обробки запитів на читання, запис і управління пристроєм (device control). Ці методи викликаються при виклику відповідних функцій API ReadFile (), WriteFile (), DeviceControl ().

Кожен драйвер містить мінімум один об’єкт пристрою. Як було згадано вище, об’єкт пристрою є екземпляром класу, породженого програмістом від класу KDevice або KPnpDevice. Для додання функціональності об’єкту пристрої розробник драйвера може перевизначати віртуальні методи суперкласу, включаючи ті методи, які обробляють різні типи IRP, а також додавати свої властивості і методи в клас. У процесі розробки можна використовувати як ієрархію класів DriverStudio, так і функції DDK. Втім, для більшості завдань без використання DDK цілком можна обійтися.

Природно, робити все це треба з обережністю. Виклик некотроих методів є обов’язковим. Перевизначення віртуальних методів також вимагає обережності: багато з них містять код, який обов’язково повинен бути виконаний. Якщо його видалити, то драйвер буде працювати неправильно (якщо буде працювати взагалі). В результаті, система швидше за все, зависне.

Все вищесказане, звичайно, може бути дуже цікавим, але залишається відкритим питання: так як же все-таки драйвер, вірніше об’єкт пристрої, управляє апаратурою? Офіційно проголошується, що Win2000 – переносима система. Тобто, якщо вона добре працює на архітектурі Intel, то вона також може бути перенесена і на інші системи, наприклад Alpha. Для того, щоб система з мінімальними виправленнями могла працювати на комп’ютерах з іншою архітектурою, і був введений HAL – рівень абстракції апаратури. Він дійсно абстрагує драйвера і більшу частину коду ядра ОС від того, як саме побудований комп’ютер. Тепер розробнику драйвера стає абсолютно все одно, як на даному комп’ютері реалізований контролер переривань або контролер прямого доступу до пам’яті – всі ресурси апаратури також представлені об’єктами. Це діапазони адрес пам’яті і портів В / В пристрої, лінії переривань і ПДП. Всі вони можуть бути реалізовані в різних архітектурах по-різному, але загальні принципи їх роботи залишаються одними і тими ж. Відповідно, і інтерфейс класів, реалізують управління цими ресурсами, залишається однаковим. Нам, як програмістам, тепер не потрібно знати тонкощі роботи апаратної частини на цьому комп’ютері – це завдання HAL і тих людей, які переносили систему.

На практиці це означає те, що якщо пристрій, наприклад, має діапазон адрес пам’яті і лінію запиту на переривання, то клас пристрою буде містити дві властивості (дані). Одне з них – екземпляр класу KMemoryRange, який буде реалізовувати управління пам’яттю пристрою, а інше – екземпляр класу KInterrupt, який управлет лінією запиту на переривання, і всім, що з нею пов’язано. Якщо пристрій буде мати кілька областей адрес пам’яті, то, відповідно, клас пристрою буде містити декілька екземплярів класу
KMemoryRange.

Іншим способом управління пристроями є наявність пристроїв нижнього рівня (Lower devices). Як вже було зазначено, особливістю архітектури WDM є наявність стека драйверів, коли драйвера можуть обмінюватися IRP-пакетами між собою. Дану ситуацію легше пояснити за допомогою рисунка:


стек пристроїв
Рис.4 – стек пристроїв

На рис.3 зображено стек пристроїв, що складається з трьох об’єктів пристроїв. Пристрій 1 – найперше (верхнє) в стеку, пристрій 3 – найостанніше (Нижнє) в стеку. Тоді по відношенню до пристрою 1 пристрій 2 буде пристроєм нижнього рівня. Пристрою верхнього рівня для пристрою 1 немає. Пристрій 2 має і пристрій верхнього рівня (пристрій 1) і пристрій нижнього рівня (пристрій 3). Для пристрою 3 є тільки пристрій верхнього рівня (пристрій 2), пристрої нижнього рівня в нього немає, пристрій 3 безпосередньо контролює обладнання.

Такий метод управління обладнанням, коли в системі присутня не один драйвер, а цілий ланцюжок драйверів, може мати свої переваги. Припустимо, наше фізичне пристрій – це клавіатура, підключена до USB – порту. Тоді об’єкт пристрою 3 – драйвер USB – порту. Пристрій 2 виконує дії, специфічні саме для даного типу клавіатур: читає дані з портів введення-виведення клавіатури, “висить” на перериванні, виконує додаткові функції (Наприклад, якщо у нас мультимедійна клавіатура). Він передає коди натиснутих клавіш пристрою 1. Пристрій 1 не залежить від того, який саме тип клавіатури підключений до комп’ютера. Воно реалізує чергу кодів натиснутих клавіш; реакцію на клавіші CapsLock, Shift і т.п.

Якщо в даному випадку у комп’ютера зміниться клавіатура, то необхідно встановити тільки новий драйвер 2. Якщо клавіатура перемкнеться на інший порт, то пристрій 2 буде спілкуватися не з пристроєм 3, а з якимось іншим пристроєм. У такому випадку система стає більш гнучкою, легкої в проектуванні, більш надійною і простий у використанні. І користувачеві, і додаткам стає абсолютно все одно, який тип клавіатури встановлений на комп’ютері.

У нашому прикладі виходить, що і пристрій 1, і пристрій 2 управляє обладнанням – клавіатурою. Але вони роблять це не напряму, а посилаючи IRP пристрою 3. Для того, щоб наш об’єкт пристрою міг передавати IRP-пакети іншим об’єктам пристроїв, введений клас пристроїв нижнього рівня (KLowerDevice, KPnpLowerDevice). Природно, для цього пристрій повинен знати, як керувати пристроєм нижнього рівня за допомогою IRP.

Втім, подбная ситуація має місце практично у всіх сучасних ОС. Тільки в інших системах це виражено менш яскраво і не декларується, як “Офіційна ідеологія”.

Порушуючи тему управління апаратурою, не можна не згадати ще про один способі управління пристроями. Іноді немає можливості використовувати класи DriverWorks або функції DDK. Наприклад, необхідно звернеться безпосередньо до портів вводу-виводу комп’ютера, зокрема, до портів управління принтером. Безпосередньо зробити це з додатка користувача, що працює під Win2000, неможливо. Всі користувальницькі програми працюють в непривілейованому кільці захисту 3 і не можуть виконувати асемблерні команди типу inp / outp. Але драйвер працює в кільці захисту 0 і, фактично, може робити все що завгодно. У цьому випадку слід перевизначити методи класу пристрою, наприклад ReadFile (), WriteFile (), DeviceControl () – додати туди асемблерні вставки або код на С, виконує те, що нам необхідно зробити (найчастіше це звернення до портів вводу-виводу). Втім, будь-яке звернення до портів вводу-виводу комп’ютера безпосередньо може виявитися небезпечним. Якщо програміст допустить помилку або неточність у маніпуляціях з паралельним портом, то це, швидше за все, пройде безслідно для системи. Але якщо він помилиться при зверненні до портів управління таймером, вінчестером або іншими життєво важливими пристроями комп’ютера, то в кращому випадку система зависне.


Об’єкти черг і буферизація запитів.


Скільки операцій може паралельно виконувати наше фізичне пристрій? Природно, це визначається самою природою цього пристрою. Багато видів обладнання можуть одночасно робити щось одне. Наприклад, паралельний порт не може передати два байти за один раз при всьому нашому бажанні, адже фізично це один канал передачі. Але ж IRP – пакети можуть приходити в будь-який час! Тому більшість об’єктів пристроїв повинні містити будь-який механізм для буферизації і упорядкування (serialization) запитів, тому найчастіше тільки один запит може бути оброблений в одиницю часу. Найпростішим і в той же час ефективним методом такої буферизації є черга.

Об’єкти, впроваджені в об’єкт пристрою, представлені в класі KDeviceQueue. Його методи не тільки реалізують маніпуляцію з чергою, але і вирішують більш інтелектуальні завдання. Наприклад, є метод, зміст якого може бути описаний таким чином: “Якщо пристрій зараз обробляє запит і зайнято, то помісти новий запит в чергу, інакше негайно почни його обробку “. Подібні методи сильно полегшують завдання буферизації запитів для об’єкта пристрою. Але можлива й інша ситуація: пристрій може одночасно обробляти запити різного виду. Приміром, наш пристрій – це дуплексний канал зв’язку. Воно одночасно може і приймати, і передавати інформацію. Якщо ми будемо використовувати для буферизації всіх одну чергу, то такий підхід є неефективним. Тому система дозволяє об’єктам пристроїв створювати додаткові об’єкти черг. Вони реалізовані в класі KDriverManagedQueue.


Буферизація запитів
Рис.5 – об’єкт черзі, впроваджений в об’єкт драйвера.

Ці класи мають методи, схожі з методами класу KDeviceQueue. Втім, ситуації, коли слід застосовувати більше однієї черги для буферизації запитів, зустрічаються не так вже й часто.

Обробка запитів на переривання за допомогою DriverWorks

У контексті даного керівництва будемо вважати, що переривання (Interrupt) – асинхронний апаратний сигнал, який зазвичай виникає, коли периферійному влаштуванню необхідні ресурси процесора. “Асинхронний” означає те, що переривання виникає в довільні моменти часу (якщо взагалі виникає). Переривання змушує процесор перервати виконання програми, зберегти своє стан, обробити запит, що надійшов (викликається процедура обробки переривання, Interrupt Service Routine, ISR) і відновити виконання перерваної програми. При цьому зупиняються всі інші процеси і потоки ОС поза Залежно від їх пріоритету.

Для того, щоб задовольняти різноманітним вимогам, що виникають при роботі різноманітних пристроїв та програм на різних типах комп’ютерів, ОС пропонує концепцію рівня запиту на переривання (Interrupt Request Level), IRQL. Всього існує 32 IRQL для даного процесора, пронумерованих від 0 до 31. При цьому 0 – найнижчий пріоритет, 31 – найвищий.

























31 Збій роботи шини
29 Збій в ланцюзі живлення
28 Запит від іншого процесора (в багатопроцесорної системі)
  Переривання, доступні пристроям В / В
2 Виконання DPC
1 Виняток захисту (page fault)
0 Passive level
Табл. 1 – рівні IRQL.

Для катастрофічних подій ОС резервує найбільш пріоритетні переривання (31 – 29). Для програмних переривань – переривання з найнижчим пріоритетом (2 – 1). PassiveLevel – звичайний режим роботи драйвера. IRQL, надані для роботи системних пристроїв, перебувають десь посередині нумерації рівнів. Про те, як ці переривання сполучаються з архітектурою комп’ютера, піклується HAL.

Природно, в будь-який момент процесор може обробляти тільки один запит на переривання. Обробка надійшов переривання перерветься тільки в тому випадку, якщо надійде переривання з більш високим пріоритетом.

При проектуванні процедури обробки переривання слід мінімізувати час, який буде витрачено на обробку переривання. Інакше процесор буде надто довго обробляти переривання і жоден процес не зможе відновити свою роботу. Коли викликається ISR перше, що вона повинна зробити повідомити устаткуванню, що запит на переривання отриманий і оброблений. Після цього можна завершувати обробку переривання. Але як тоді обробити дані, що надійшли від пристрою, якщо ми відразу ж завершимо обробку переривання? Для цього введено механізм виклику відкладених процедур (Deferred Procedure Call, DPC). Перед завершенням роботи ISR слід викликати відкладену процедуру (DPC). DPC почне виконаються, як тільки процесор звільниться від обробки переривань. DriverWorks надає клас KDeferredCall, в якому інкапсулюються дані і методи, необхідні для використання механізму DPC.

DriverWorks інкапсулює всі функції, необхідні для обробки переривань, в класі KInterrupt. Екземпляр класу KInterrupt повинен бути створений, як властивість в класі пристрою. Нехай у нашому випадку клас пристрою називається MyDevice, об’єкт класу KInterrupt – m_TheInterrupt. Далі в класі пристрої описується функція ISR:

BOOLEAN MyDevice::TheIsr(void);
Далі, в методі OnStartDevice слід додати код для прив’язки ISR до пристрою:
status = m_TheInterrupt.InitializeAndConnect(pAssignedResource,Isr,Context,0,FALSE);
       де Context – значення без типу (void), передане ISR.
              Isr – адресу ISR, процедури обробки переривань.

Тепер залишилося тільки додати в конструктор наступний код:

VOID MyDevice::MyDevice(void)
{
. . .
status = m_TheInterrupt.InitializeAndConnect(pAssignedResource,
LinkTo(Isr), this, 0, FALSE );
. . .
}
Для відключення ISR слід викликати метод Disconnect ().

Природно, даний опис не претендує бути повним описом такої важливої теми, як обробка переривань і пов’язані з нею проблеми. Але в прикладі драйвера, описуваному нижче, відсутня реакція на переривання, а не згадати про них не можна. Для більш детального огляду теми переривань і DPC слід звернутися до документації DriverWorks або DDK.


Об’єкти для керування устаткуванням

Як було згадано вище, об’єкт пристрої управляє роботою пристрою при допомогою спеціальних об’єктів, керуючих роботою устаткування – портами В / В, перериваннями, пам’яттю, контролерами ПДП. Драйвер створює ці об’єкти для представлення фізичних параметрів пристрою.

Більшість периферійних пристроїв знаходяться на шинах комп’ютера. В сучасному комп’ютері є кілька шин. Зазвичай процесор, зовнішня кеш-пам’ять, і оперативна пам’ять знаходяться на високошвидкісний шині, архітектура якої специфічна для даного типу процесора. Шина процесора з’єднана мостом зі стандартної швидкісний шиною, на якій знаходяться контролери дисплея, деякі швидкісні пристрої. Архітектура цієї шини може бути Процесори-незалежною. Приклад такої шини – PCI. Ця шина також може бути з’єднана мостом з вторинною локальною шиною, часто більш повільною. На ній можуть знаходитися контролери дискових накопичувачів, мережевих адаптерів і т.п.

Периферійні пристрої зазвичай мають “на борту” регістри і діапазони адрес пам’яті, за допомогою яких реалізується інтерфейс пристрою з системою. Але дістатися до них не так просто: процесор адже фізично використовує інші механізми для звернення до своїх “рідних” портів вводу-виводу і оперативної пам’яті. Для того, щоб звернеться до пам’яті і портів пристрою, що знаходиться на локальній шині, процесор повинен виконати відображення (mapping) адресного простору процесора і тієї шини, де знаходиться наш пристрій. В результаті цієї операції до ділянки пам’яті, фізично знаходиться в устройтсва, можна звертатися, як до ділянки оперативної пам’яті процесора. При такому зверненні процесор переадресує запит локальній шині. Але тут слід згадати про особливості архітектури Windows (та й практично будь-якої сучасної ОС): адже система підтримує механізм віртуальної пам’яті! Користувальницькі додатки тепер працюють у своєму адресному просторі, а система, в тому числі і драйвера, – у своєму. Куди ж буде відображена пам’ять пристрою?

Відповідь проста. Можна відобразити діапазон адрес пристрою як на адресне простір системи, так і на адресний простір користувацького процесу. Відповідно розрізнятися буде і спосіб доступу до пам’яті пристрою з додатки користувача: в першому випадку буфер з даними для запису або читання буде передаватися драйверу з додатка, а в драйвері ці дані будуть пересилатися пристрою. У другому випадку додаток буде писати і читати дані у виділений йому ділянку пам’яті, який знаходиться в адресному просторі процесу. Який механізм вибрати – справа розробника драйвера.

Об’єкти, що представляють адресний простір периферійних пристроїв, представлені класами KPeripherialAdress, KIoRange, KMemoryRange, KIoregister, KMemoryRegister. KPeripherialAdress є базовим класом для більшості решти класів управління діапазонами пам’яті і портів введення-виведення. Сам клас KperipherialAdress в основному, не використовується. Використовуються, в основному, наступні його підкласи:


Варто відзначити, що чимала частина пристроїв можуть спілкуються зі своєю пам’яттю тільки словами. Довжина слова залежить від пристрою, і може коливатися в широких межах. Зазвичай для PCI-пристроїв – 32 біт.

У документації настійно рекомендується використовувати тільки ці класи для управління устаткуванням. Це пов’язано з можливою переносимістю драйвера на інші платформи. При використанні цих класів, які, в свою чергу, використовують функції DDK для доступу до обладнання, процес портування пройде безболісно, ​​тому що для доступу до пристрою буде використаний HAL. Якщо ж програміст буде намагатися керувати пристроями самостійно, то драйвер доведеться переписувати при перенесенні на іншу платформу.

Є ще одна причина, по якій варто використовувати ці класи, адже з ними розробляти драйвер набагато простіше!


Об’єкти синхронізації

Як і всі Windows – програми, драйвера є частиною багатозадачного операційної системи, в якій виконується безліч процесів і потоків. Драйвер, як і програма, також може містити кілька потоків. При цьому, природно, виникає проблема синхронізації роботи цих потоків, спільного доступу до даних і т.п. Особливо актуальною ця проблема стає в багатопроцесорної системі. Windows 2000 призначається для роботи в багатопроцесорних системах, і якщо знехтувати синхронізацією при розробці драйвера, то це може спричинити за собою неприємні наслідки.

Для вирішення завдань синхронізації WDM (і, відповідно, DriverWorks) пропонує різні засоби. Найпростішим з об’єктів синхронізації є засувка (Spin Lock), представлена ​​класом KSpinLock. Принцип дії засувки дуже простий: щоб заборонити будь-якому іншому потоку в системі доступ до даними, потрібно викликати метод Lock засувки. Будь потік, який намагається отримати доступ до заблокованих даними, засне. Щоб зняти блокування, потрібно викликати метод Unlock.

Клас диспетчера KDispatcherObject є суперкласом для кількох важливих класів синхронізації. Ці класи керують планувальником Windows і дозволяють синхронізувати як роботу драйверів, так і роботу додатки користувача і драйвера. Всі класи, породжені від KDispatcherObject, мають дві важливі відмінності:

При роботі з об’єктами диспетчера та його підкласів слід мати на увазі, що не можна блокувати потік при обробці переривання. Наслідки будуть фатальними.

Підкласи класу KDispatcherObject:

KEvent – Використовується для синхронізації роботи потоків. Kevent майже не відрізняється від об’єкта диспетчера.

KSemaphore інкапсулює системний об’єкт семафора. Семафор відрізняється від об’єкта події тим, що має лічильник. Семафор сигналізує в тому випадку, якщо лічильник більше нуля. Семафори можуть бути корисні, наприклад, при управлінні декількома пото-ками.

KTimer – Таймер. При створенні таймера його прапорець перебуває в стані “Мовчить”. Часовий інтервал таймера задається функцією Set з точністю до 100 нс. На практиці таймер стійко працює з часом очікування> = 10 мс. Коли пройде вказаний проміжок часу, таймер перейде в стан “Сигналізує”. Підкласом Ktimer є клас KTimedCallBack. У ньому після закінчення проміжку часу виконується виклик відкладеної процедури (DPC).

KSystemThread дозволяє створити новий потік в драйвері. Потоки в драйвері використовуються в різних цілях. В основному це – Поллінг повільних пристроїв і робота на багатопроцесорних системах. Для запуску потоку слід створити функцію, яка стане функцією потоку і викликати метод Start. Для знищення потоку – метод Terminate. При роботі з потоками можна використовувати всі згадані вище класи синхронізації.


Додаткові класи.


DriverWorks надає додаткові класи для потреб програміста. Це класи черг, списків, стеков; класи файлів і Unicode-рядків; класи синхронізації.

Списки представлені класами KList, KInterlockedList, KInterruptSafeList. Вони представляють шаблони двонаправлених списків і стандартні методи для вставки, видалення і додавання елементів. Розрізняються ці класи методами синхронізації. KList не містить ніяких методів синхронізації і захисту даних. KInterLockedList використовує засувки (spin locks) для захисту внутрішніх зв’язків в списку. KInterruptSafeList використовує приєднаний об’єкт переривання для захисту зв’язків. За аналогічним принципом працюють шаблони класів FIFO (стек): KFifo, KLockableFifo, KInterruptSafeFifo. Клас KFile інкапсулює методи для роботи з файлами. Цей клас дозволяє читати і записувати дані в файл а також змінювати атрибути файлів. Для представлення Unicode – рядків використовується клас KUstring. Методи даного класу дозволяють виконувати порівняння, конкатенацію, доступ до символів рядка і різноманітні перетворення типу.


Зв’язок драйвера з додатком користувача


Також залишається неясним ще одне питання, пов’язане з драйверами: як саме з нашим об’єктом пристрої може зв’язатися додаток або інший драйвер? Більшість з пристроїв в системі іменувати, хоча теоретично допускається існування неіменованого (anonymous) пристроїв. Зв’язок з пристроєм можна встановити двома методами:


1. GUID (Globally Unique Identifier, глобально унікальний ідентифікатор) – 16-байтного унікальне число. GUID використовуються для ідентифікації в системі драйверів, СОМ-об’єктів і т.п. В ідеалі, у всьому світі не може бути двох однакових GUID, тому GUID може бути абсолютно унікальним ідентифікатором драйвера. GUID генерується на основі поточної дати, часу та номери мережевого адаптера, якщо такий присутній, і зазвичай вказується в заголовному файлі класу пристрою і програми, яка хоче зв’язатися з ним приблизно таким чином:

#define MyDevice_CLASS_GUID
{ 0xff779f4c, 0x8b57, 0x4a65, { 0x85, 0xc4, 0xc8, 0xad, 0x7a, 0x56, 0x64, 0xa6 } }

2. Символічна посилання (symbloic link) схожа на шлях до файлу і в тексті програми має вигляд:

char *sLinkName = "\.MyDevice";

Якщо відкинути зайві символи бекслеша, необхідні для дотримання синтаксису С + +, то символічне посилання опиняється рядком. MyDevice. Щоб зрозуміти принцип роботи символічного посилання, слід знати, що в ОС є системний каталог різних об’єктів, які присутні в системі: драйверів, пристроїв, об’єктів подій, семафорів і т.п. Символічна посилання – специфічний тип об’єкта, який забезпечує доступ до інших системним об’єктам. Спеціальний підкаталог системного каталогу зарезервований для символічних посилань на інші об’єкти ОС. Програма користувача може звернутися до цих символічним посиланнях за допомогою функцій API.

Як же слід проектувати інтерфейс з драйвером? Слід використовувати GUID або символічне посилання?

Ідентифікація драйвера за допомогою GUID вважається більш правильною. Як було згадано вище, спеціальні алгоритми гарантують те, що GUID буде дійсно унікальним. А хто заважає розробнику, що знаходиться на іншому кінці світу, також створити пристрій з тією ж посиланням на нього. MyDevice? Взагалі-то, ніхто. Але з іншого боку, з написаною на зрозумілій англійській мові посиланням набагато простіше звертатися, особливо на етапі розробки драйвера, ніж з довгим і незрозумілим GUID. Так що, ймовірно, на етапі розробки та налагодження драйвера для інтерфейсу драйвера з додатком краще використовувати символічну посилання, а для комерційної версії драйвера – GUID.

2. Розробка драйвера в середовищі DriverStudio.


2.1 Використання Driver Wizard


Процес розробки драйвера за допомогою DriverStudio багато в чому напоsмінает розробку додатка в середовищі Visual C + +. Створення проекту відбувається за допомогою майстри DriverWizard, схожого на майстер Visual C + +. Майстер викликається або з головного меню (Пуск – Програми – DriverStudio – DriverWorks – DriverWizard) або з середовища Visual C + + за допомогою пункту меню DriverStudio – DriverWizard. Програмі DriverWizard відповідає іконка


Іконка Driver Wizard

Далі при роботі майстра з’являється серія діалогових вікон, куди користувач повинен ввести дані, необхідні для формування скелета драйвера.


Перший крок DriverWizard
Рис 6. Перший крок DriverWizard

На першому кроці створення драйвера необхідно ввести ім’я проекту (в нашому випадку – XDSP) і директорію для проекту. Після цього – натиснути на кнопку Next, щоб перейти до наступного кроку.


Другий крок DriverWizard
Рис 7. Другий крок DriverWizard

На другому кроці слід вибрати архітектуру, по якій буде розроблятися драйвер: Windows NT 4.0 (яка зараз практично не використовується) або WDM, яку нам і слід вибрати.


Третій крок DriverWizard
Рис 8. Третій крок DriverWizard

На третьому кроці виберемо шину, на якій розташовується пристрій, який буде контролювати драйвер. Якщо цей пристрій буде підключатися до порту комп’ютера, наприклад до паралельного – набо вибрати None – driver does not control any hardware. Якщо ж пристрій буде розташовуватися на одній із шин комп’ютера, наприклад на PCI – треба задати додаткові параметри. У разі PCI пристрої треба вказати наступні параметри:

Ці коди вельми важливі: по ним система буде знаходити драйвер для пристрою. Ці ж коди апаратно прошиті в PCI-картці. І якщо коди, задані в драйвері (якщо бути точним, то вони задаються не в самому файлі драйвера, а в інсталяційному скрипті – inf-файлі), не співпадуть з кодами в PCI-пристрої, то драйвер не встановиться.


Четвертий крок DriverWizard
Рис 9. Четвертий крок DriverWizard

На четвертому кроці майстра необхідно задати імена, які DriverWizard присвоїть файлу С + +, який містить клас драйвера, і самому класу драйвера
(Driver Class).

П
Рис 13. П’ятий крок DriverWizard

На п’ятому кроці слід вказати, які функції повинен виконувати драйвер. Це може бути:


Шостий крок DriverWizard
Рис 14. Шостий крок DriverWizard

На шостому кроці DriverWizard задає питання про спосіб обробки запитів. Опція Select queuing method вибирає, яким чином будуть буферізірованний запити на введення-виведення:


Також треба вибрати, чи будуть буферізірованний запити на читання і запис. Як було сказано раніше, пристрій може одночасно виконувати якусь одну операцію, наприклад, тільки читання або тільки запис, або може виконувати кілька операцій відразу. Щоб гарантувати нормальну роботу пристрою в цьому випадку, слід буферізіровать (Serialize) вступники запити на читання і запис, поміщаючи їх в чергу. Установка прапорців Seralize all Read requests і Serialize all Write requests дозволяє буферізіровать всі запити на читання і запис, що надходять в об’єкт пристрою.


Сьомий крок DriverWizard
Рис. 15 – Сьомий крок DriverWizard.

На сьомому кроці пропонується задати параметри, які драйвер буде завантажувати з реєстру Windows при старті, коли система завантажується. При цьому задається параметр реєстру, ім’я змінної, куди зберігається його значення, тип даного параметра і його значення за замовчуванням. Якщо не міняти настройки, то під час завантаження драйвер читає з реєстру параметр BreakOnEntry типу boolean, зберігає його значення у змінній m_BreakOnEntry. Значення за замовчуванням для параметра – false. Зазвичай m_BreakOnEntry використовується в налагоджувальних челях.

Запис і зчитування параметрів з реєстру дозволяє драйверу задавати небудь конфігураційні параметри, зберігати дані, необхідні для його запуску або роботи.

За допомогою кнопок Add, Edit і Delete можна відповідно додавати, редагувати і видаляти параметри.


Восьмий крок DriverWizard
Рис. 16 – Восьмий крок DriverWizard.

Восьмий крок DriverWizard – один з найважливіших моментів у розробці драйвера PCI – пристрою за допомогою DriverWorks. Тому вікно майстра несе величезна кількість інформації і елементів управління.

На даному кроці пропонується змінити імена класів пристрої для даного драйвера. У списку у верхній частині вікна слід вибирати клас пристрою, який слід перейменувати, і, натиснувши на кнопку Rename, можна задати нове ім’я класу пристрою.

Вікно DriverWizard також містить кілька вкладок:


Вкладка Resource
Рис.17 – вкладка
Resource

Вкладка Resource. На ній визначаються основні апаратні ресурси, які є в пристрої і які буде контролювати цей драйвер. У їх числі адреси пам’яті, діапазони портів вводу-виводу, лінії запиту на переривання і лінії прямого доступу до пам’яті (DMA), які необхідні для роботи драйвера. Задати ресурси можна за допомогою кнопок в нижній частині вкладки.

Наприклад, задати діапазон пам’яті, яку несе “на борту” пристрій, можна, натиснувши на кнопку Add Memory Range. При цьому виводиться діалогове вікно, куди слід ввести відомості про новий діапазоні адрес пам’яті: ім’я об’єкта класу KMemoryRange, який буде контролювати цей діапазон адрес, адресу базового регістра в PCI – заголовку (PCI header) даного пристрою, який визначає цей діапазон адрес, а також параметри доступу для даної пам’яті: тільки читання (Read Only), тільки запис (Write Only) і повний доступ (Read / Write). Також можна ще задати опції поділу доступу (Share options). Ці опції дозволяють розділяти доступ до ресурсу: до нього можна звертатися тільки з класу даного пристрою (Exclusive to this device), з будь-якої частини драйвера (Shareable within this driver) або з будь-якого драйвера в системі (Shareable system wide). Втім, для розробки простих драйверів ці опції є даремними і змінювати їх не варто. У нашому випадку ми створюємо діапазон адрес пам’яті з ім’ям m_MainMemoryRange, обумовлений нульовим базовим регістром в PCI – Header “e, з повним доступом.


Завдання діапазону адрес пам
Рис. 18 – завдання діапазону адрес пам’яті.

За аналогічним принципом можна задати параметри портів вводу-виводу і ліній DMA. Параметри ліній запиту на переривання поскладніше: тут можна дати вказівку DriverWizard “у створити шаблони для класів ISR, DPC та їх функцій (Make ISR / DPC
class functions).

Якщо в процесі завдання ресурсів задана помилка або необходімол внести небудь зміни, то для цього треба клацнути по назві ресурсу у вікні правою клавішею миші. З’явиться контекстне меню, в якому треба вибрати пункт Delete, щоб видалити ресурс або Edit – редагувати його.


Вкладка Interface
Рис. 19 – вкладка
Interface.

На вкладці Interface задається спосіб, яким чином буде здійснюватись зв’язок програми або бібліотеки DLL з драйвером.

Надійним способом є зв’язок за допомогою GUID класу. GUID – унікальний номер, який однозначно ідентифікує небудь об’єкт системи. За допомогою GUID ідентифікуються не тільки драйвера, а й СОМ – інтерфейси і пр.

Іншим способом реалізації інтерфейсу є є символічне посилання. Це більш природний шлях, тому просто вказати ім’я класу пристрою – набагато простіше, ніж вказувати незрозумілого виду GUID.


Вкладка Buffers
Рис. 20 – вкладка
Buffers.

На вкладці Buffers визначається метод, яким чином буферизує запити до пристрою.

Буферізірованний (buffered) метод – придатний для пристроїв типу миші, клавіатури, які передають невеликі обсяги даних за короткий проміжок часу. Прямий (direct) метод – використовується при пересиланні великих обсягів інформації за короткий проміжок часу, наприклад, при зверненні до дисковода.


Вкладка Power
Рис. 21 – вкладка
Power.

При створенні WDM – драйвера необхідно задати спосіб управління енергоспоживанням. За допомогою прапорця Управляти енергоспоживанням цього пристрої (Manage power for this device) можна створити в драйвері методи управління енергоспоживанням нашого пристрою. У нашому простому випадку ми не будемо цього робити.


Дев
Рис. 22 – дев’ятого крок DriverWizard.

Природно, для більш-менш складного драйвера пристрою буде недостатньо двох запитів на читання і запис. На дев’ятому кроці можна задати коди управління драйвером пристрою. Код управління (Device IO control code, IOCTL) просто являє собою число, яке передається драйверу. Коди управління в драйвері обробляються спеціальною функцією. У відповідь на кожен код драйвер виконує якусь дію. Наприклад, у нашому випадку об’єкт пристрою буде повертати кількість пам’яті, яке має PCI-картка. Для цього задамо код управління XDSP_GetMemSize. Для цього натиснемо на кнопку Add, з’явиться діалогове вікно Edit IO Control Code (редагування коду управління).


завдання коду управління драйвером
Рис. 23 – завдання коду управління драйвером.

При завданні коду управління пристроєм потрібно вказати ім’я коду в зрозумілому програмісту вигляді, метод спілкування з пристроєм (прямий чи буферізірованний). Також задається порядковий номер коду (Ordinal) – число, що є його унікальним номером. Числа, менші 0x800 використовуються для стандартних кодів, таких, як читання, запис і т.п.

Запити IOCTL також можна буферізіровать, подібно запитам на читання і запис. Для цього треба встановити прапорець Queue (serialize) this request code.

Внизу вікна майстра вказано ім’я заголовного файлу, в якому будуть зберігатися

Схожі статті:


Сподобалася стаття? Ви можете залишити відгук або підписатися на RSS , щоб автоматично отримувати інформацію про нові статтях.

Коментарів поки що немає.

Ваш отзыв

Поділ на параграфи відбувається автоматично, адреса електронної пошти ніколи не буде опублікований, допустимий HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*

*