Драйвер клавиатуры, реализующий функции музыкального синтезатора на клавиатуре для Windows NT 5
Поможем в ✍️ написании учебной работы
Поможем с курсовой, контрольной, дипломной, рефератом, отчетом по практике, научно-исследовательской и любой другой работой

Драйвер клавиатуры, реализующий функции музыкального синтезатора на клавиатуре для Windows NT 5

 



Содержание

 

1. Введение

2. Конструкторский раздел

2.1 Постановка задачи

2.2 Архитектура Windows NT 5

2.3 Драйверная модель WDM

2.3.1 Функции загрузки/выгрузки драйвера

2.3.2 Функции обработки запросов/прерываний

2.4 Приоритеты выполнения программного кода

2.5 Стек клавиатуры

2.6 Kernel Streaming

2.7 Описание формата MIDI-данных

2.8 Выбор структуры программного обеспечения

2.8.1 Драйвер-фильтр

2.8.2 Получение доступа к аудиоустройству

2.8.3 Взаимодействие компонент программного обеспечения

2.9 Алгоритм работы драйвера-фильтра

2.9.1 Функции загрузки/выгрузки драйвера

2.9.2 Функции обработки пакетов IRP

2.9.3 Функции работы с аудио-устройством

2.9.4 Схема хранения музыкальных параметров клавиш

2.9.5 Разделение задачи на потоки

3. Технологический раздел

3.1 Выбор средств разработки программного обеспечения

3.2 Установка драйвера в системе

3.3 Сборка программного обеспечения

3.4 Использование библиотеки DirectKS

3.5 Отправление запросов из приложения в драйвер

3.6 Описание интерфейса пользователя

3.7 Тестирование программного обеспечения

4. Заключение

5. Список литературы

6. Приложения

6.1 Функции установки драйвера в системе

6.2 Изменения в библиотеке DirectKS



Введение

 

В современных вычислительных системах важнейшую роль играет подсистема ввода/вывода. Известно, что примерно 80% инструкций программы связаны с этой подсистемой. В связи с этим в настоящее время возрастает актуальность разработки программных систем, осуществляющих взаимодействие с периферийными устройствами посредством подсистемы ввода/вывода.

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

Упрощение работы с компьютером людям со слабым зрением.

Создание MIDI-синтезатора на клавиатуре.

Создание системы звуковой сигнализации.

В стандартную поставку ОС Microsoft Windows XP Professional входят компоненты, обеспечивающие специальные возможности операционной системы для пользователей с различными отклонениями в здоровье. Среди этих компонент следует отметить:

«Экранную клавиатуру», с помощью которой, в частности, можно озвучить выбор клавиш мышью.

Возможность озвучивания нажатий клавиш Num, Caps и Scroll Lock.

Характерной особенностью вышеперечисленных компонент является подача сигнала одной частоты независимо от нажатой клавиши и невозможность настройки параметров звучания. В этой связи представляется целесообразной разработка нового программного обеспечения, позволяющего организовать звуковое сопровождение клавиатуры в более полном объеме, в частности, настраивать выборочное озвучивание клавиш.



Конструкторский раздел

Постановка задачи

 

В соответствии с заданием на курсовую работу необходимо разработать программное обеспечение для решения следующих задач:

Вывод музыкальных нот с заданными параметрами при нажатии клавиш.

Возможность настройки пользователем параметров звучания клавиш.

Программное обеспечение состоит из двух взаимодействующих частей:

Драйвер-фильтр, реализующий первую задачу.

Программа настройки, реализующая вторую задачу.

Требования к программному обеспечению:

Драйвер-фильтр должен отслеживать нажатия всех клавиш клавиатуры PS/2 и генерировать музыкальные ноты с соответствующими клавише параметрами.

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

В качестве устройства для вывода музыкальных нот используется синтезатор MIDI, встроенный в звуковую карту Sound Blaster.

Приоритетным критерием при разработке продукта является производительность для драйвера-фильтра и удобство пользователя для программы настройки.

Программное обеспечение ориентировано на работу в ОС Windows NT 5. Функционирование программного обеспечения не должно влиять на работу других драйверов системы.



Архитектура Windows NT 5

 

Архитектура Windows NT 5 соответствует классическим представлениям о проектировании операционных систем. Наиболее распространены реализации данной ОС для платформы Intel x86 в одно- или многопроцессорных конфигурациях, однако существуют также версии для DEC Alpha и MIPS. Windows NT 5 использует защищённый режим центрального процессора, реализует механизмы виртуальной памяти и многозадачности.

Исполняемый код в Windows NT 5 имеет два уровня привилегий: код пользовательского режима и код режима ядра. Уровень привилегий накладывает определённые ограничения: в пользовательском режиме не могут выполняться привилегированные инструкции процессора, не разрешено обращение к защищённым страницам памяти. Эти ограничения накладываются для обеспечения безопасности работы системы. Пользовательское приложение не должно иметь возможность в результате ошибки или преднамеренно вносить изменения в критические таблицы операционной системы или в память других приложений. В частности, такие ограничения запрещают пользовательскому приложению напрямую управлять внешними устройствами, потому что каждое из них является разделяемым ресурсом.

В Windows NT 5 обеспечение обмена данными и управление доступом к внешнему устройству как к разделяемому ресурсу возлагается на его драйвер. Ввод и вывод в драйверах осуществляется посредством IRP-пакетов (Input/Output Request Packet). Запросы на ввод/вывод, посылаемые приложениями или другими драйверами, обрабатываются драйвером, после чего запрашивающей программе в том же пакете посылается статус завершения операции. Общий принцип взаимодействия проиллюстрирован на рис. 2.1.


Рис.2.1. Архитектура ввода/вывода Windows NT 5

 

Управление внешним устройством в общем случае сводится к заполнению его регистров необходимыми данными. Монопольный доступ драйвера к этим регистрам гарантируется операционной системой. Очевидно, что при данных обстоятельствах требуется, чтобы драйвер устройства выполнялся в режиме ядра.

Обобщённая классификация драйверов Windows NT 5 может быть представлена следующим образом:

Драйверы режима ядра:

Унаследованные драйверы

Драйверы файловой системы

Видеодрайверы

Драйверы PnP (Plug And Play):

Драйверы WDM

Драйверы пользовательского режима:

Драйверы виртуальных устройств

 




Драйверная модель WDM

Windows Driver Model (WDM) — это стандартная спецификация Microsoft для разработчиков драйверов устройств. Она поддерживается в операционных системах Windows 98/Me/2000/XP. Компания Microsoft требует, чтобы все новые драйверы под эти операционные системы создавались по этой спецификации. Для этого от них требуется чёткое следование структуре WDM, поддержка Plug and Play и управления электропитанием.

Драйверная модель WDM построена на организации и манипулировании слоями объектов физических устройств (PDO, Physical Device Object) и объектов функциональных устройств (FDO, Functional Device Object).

Объект PDO создается для каждого физически идентифицируемого элемента аппаратуры, подключенного к шине данных, и подразумевает ответственность за низкоуровневый контроль, достаточно общий для набора функций реализуемых этим аппаратным элементом. Объект PDO – это особая структура данных, создаваемая системой для взаимодействия программного и аппаратного обеспечения.

Объекты FDO подразумевает единицу логической функциональности устройства.

Объектам FDO устройств разрешается окружать себя Объектами Фильтрами (FiDO, Filter Device objects) как показано на рис. 2.2. Соответственно, каждому FiDO объекту сопоставлен драйвер, который выполняет определенную работу. Объекты фильтры подразделяются на фильтры нижнего уровня и фильтры верхнего уровня. И тех и других может существовать произвольное количество. Они модифицируют процесс обработки запросов ввода/вывода. Объекты FDO и FiDO отличаются только в смысловом отношении. FDO объект и его драйвер считаются главными. Они обычно обеспечивают управление устройством, а объекты FiDO являются вспомогательными.

 

Рис. 2.2. Стек устройств

 

Все объекты FDO и FiDO позиционируют себя в стеке устройств. Порядок объектов в стеке определяет порядок обработки запросов ввода-вывода. Если необходимо перехватить и обработать запрос, непосредственно идущий от пользователя, то нужно устанавливать верхний фильтр. Если же нужно отслеживать обращение к портам ввода вывода, обрабатывать прерывания, то нужен нижний фильтр. Данная модель позволяет драйверу устанавливать callback процедуры. Когда запрос начинает обрабатываться, то он обрабатывается последовательно всеми драйверами стека устройства ( исключая ситуацию, когда какой-либо драйвер сам завершит обработку запроса ). После этого диспетчер ввода-вывода передает запрос callback процедуре каждого драйвера стека. Сначала запрос передается callback процедуре последнего драйвера который в стеке, потом процедуре предпоследнего драйвера и т.д. Callback процедуры нужны для того, чтобы обработать прочитанную из устройства информацию. Если фильтр обрабатывает запросы на чтение, то когда этот запрос поступит в драйвер информация еще не будет считана. Поэтому драйверу необходимо установить callback функцию. При ее вызове запрос уже будет содержать считанные данные.

Драйвер имеет следующие точки входа:

DriverEntry

DriverUnload

AddDevice

Функции для обработки пакетов IRP

ISR

Стек IRP пакета

Основное значение ячеек стека IRP пакета состоит в том, чтобы хранить функциональный код и параметры запроса на ввод/вывод. Для запроса, который адресован драйверу самого нижнего уровня, соответствующий IRP пакет имеет только одну ячейку стека. Для запроса, который послан драйверу верхнего уровня, Диспетчер ввода/вывода создает пакет IRP с несколькими стековыми ячейками – по одной для каждого FDO.

Каждая ячейка стека IRP содержит:

MajorFunction типа UCHAR – это код, описывающий назначение операции

MinorFunction типа UCHAR – это код, описывающий суб-код операции

DeviceObject типа PDEVICE _ OBJECT – это указатель на объект устройства, которому был адресован данный запрос IRP

FileObject типа PFILE _ OBJECT – файловый объект для данного запроса

Диспетчер ввода/вывода использует поле MajorFunction для того, чтобы извлечь из массива MajorFunction нужную для обработки запроса процедуру.

 

Рис. 2.4 Стек IRP-пакета

 

Функция обработки пакетов IRP _ MJ _ DEVICE _ CONTROL

Эта функция позволяет обрабатывать расширенные запросы от клиентов пользовательского режима. Такой запрос может быть сформирован посредством вызова функции DeviceIoControl. Каждый IOCTL запрос имеет свой код. Этот код передается как параметр функции DeviceIoControl. Код IOCTL – это 32-битное число.

Запросы IOCTL служат чаще всего для обмена данными между драйвером и приложением. Для передачи данных в Windows предусмотрены 4 способа:

METHOD_BUFFERED

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

METHOD_IN_DIRECT и METHOD_OUT_DIRECT

Необходимые страницы пользовательского буфера загружаются с диска в оперативную память и блокируются. Используются MDL-списки для доступа к буферу пользователя.

METHOD_NEITHER

При данном методе передачи не производится проверка доступности памяти, не выделяются промежуточные буфера и не создаются MDL. В пакете IRP передаются виртуальные адреса буферов в пространстве памяти инициатора запроса ввода/вывода.

Функция обработки пакетов IRP _ MJ _ READ

Данная функция должна обрабатывать запросы на чтение информации из устройства.

Функция обработки пакетов IRP _ MJ _ PNP

Данная функция должна обрабатывать запросы от менеджера PnP.

Функция обработки пакетов IRP _ MJ _ POWER

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

ISR

Данная точка входа вызовется когда произойдет прерывание, на которое зарегистрирована эта ISR функция. Вызов может произойти в любом контексте: как ядра, так и пользовательского процесса. Здесь драйвер может либо дожидаться следующего прерывания либо запросить отложенный вызов процедуры DPC (Deferred Procedure Call).

Стек клавиатуры

 

Физическую связь клавиатуры с шиной осуществляет микроконтроллер клавиатуры Intel 8042. На современных компьютерах он интегрирован в чипсет материнской платы. Этот контроллер может работать в двух режимах: AT-совместимом и PS/2-совместимом. Почти все клавиатуры уже давно являются PS/2-совместимыми. В PS/2-совместимом режиме микроконтроллер клавиатуры также связывает с шиной и PS/2-совместимую мышь. Данным микроконтроллером управляет функциональный драйвер i8042prt. Драйвер i8042prt создает два безымянных объекта «устройство» и подключает один к стеку клавиатуры, а другой к стеку мыши. Поверх драйвера i8042prt, точнее, поверх его устройств, располагаются именованные объекты «устройство» драйверов Kbdclass и Mouclass. Драйверы Kbdclass и Mouclass являются так называемыми драйверами класса и реализуют общую функциональность для всех типов клавиатур и мышей, т.е. для всего класса этих устройств. Оба эти драйвера устанавливаются как высокоуровневые драйверы.

Стек клавиатуры обрабатывает несколько типов запросов. В данной курсовой работе необходимо рассмотреть только IRP типа IRP_MJ_READ, которые несут с собой коды клавиш. Генератором этих IRP является поток необработанного ввода RawInputThread системного процесса csrcc.exe. Этот поток открывает объект «устройство» драйвера класса клавиатуры для эксклюзивного использования и направляет ему IRP типа IRP_MJ_READ. Получив IRP, драйвер Kbdclass отмечает его как ожидающий завершения (pending), ставит в очередь и возвращает STATUS_PENDING. Потоку необработанного ввода придется ждать завершения IRP. Подключаясь к стеку, драйвер Kbdclass регистрирует у драйвера i8042prt процедуру обратного вызова KeyboardClassServiceCallback, направляя ему IRP IOCTL_INTERNAL_KEYBOARD_CONNECT. Драйвер i8042prt тоже регистрирует у системы свою процедуру обработки прерывания (ISR) I8042KeyboardInterruptService, вызовом функции IoConnectInterrupt. Когда будет нажата или отпущена клавиша, контроллер клавиатуры выработает аппаратное прерывание. Его обработчик вызовет I8042KeyboardInterruptService, которая прочитает из внутренней очереди контроллера клавиатуры необходимые данные. Т.к. обработка аппаратного прерывания происходит на повышенном IRQL, ISR делает только самую неотложную работу и ставит в очередь вызов отложенной процедуры I8042KeyboardIsrDpc (DPC). DPC работает при IRQL = DISPATCH_LEVEL. Когда IRQL понизится до DISPATCH_LEVEL, система вызовет процедуру I8042KeyboardIsrDpc, которая вызовет зарегистрированную драйвером Kbdclass процедуру обратного вызова KeyboardClassServiceCallback (также выполняется на IRQL = DISPATCH_LEVEL). KeyboardClassServiceCallback извлечет из своей очереди ожидающий завершения IRP, заполнит структуру KEYBOARD_INPUT_DATA, несущую всю необходимую информацию о нажатиях/отпусканиях клавиш, и завершит IRP. Поток необработанного ввода пробуждается, обрабатывает полученную информацию и вновь посылает IRP типа IRP_MJ_READ драйверу класса, который опять ставится в очередь до следующего нажатия/отпускания клавиши. Таким образом, у стека клавиатуры всегда есть, по крайней мере, один, ожидающий завершения IRP, и находится он в очереди драйвера Kbdclass. Стек клавиатуры представлен на рис.2.5.


Рис. 2.5. Стек клавиатуры

 


Kernel Streaming

 

Kernel streaming (KS) – это совокупность функций Windows NT 5, которые обрабатывают в режиме ядра потоковые данные, такие как аудио и видео-данные. WDM аудио-драйвер предоставляет системе свои музыкальные функции, как набор фильтров KS. Объект KS фильтра может расширить функции аудиоадаптера, если требуется дополнительная цифровая обработка аудио-потоков, которые идут через этот фильтр. Например, фильтр может преобразовывать форматы потоков, синтезировать или смешивать потоки.

Фильтр может содержать несколько пинов. Пин является точкой входа или выхода, через которую аудио-поток входит или покидает фильтр. У каждого пина есть определённый формат данных, и данные только этого формата могут проходить через пин.

Пины могут быть подсоединены к пинам других фильтров, что позволяет создавать графы фильтров. Для того чтобы быть частью графа аудио-фильтров, фильтр должен содержать не менее одного пина.

KS-фильтр – это объект ядра и к нему можно получить доступ используя HANDLE. Обращение к объекту пина производится также с использованием HANDLE. Данные входят во входные пины, проходят соответствующую обработку в узлах фильтра и выходят из выходящих пинов, как показано на рис. 2.6.

 

Рис.2.6. Схема аудио-фильтра

 

Драйвер-фильтр

Из анализа архитектуры Windows XP следует, что для доступа к информации, содержащей коды нажатых или отпущенных клавиш необходимо написать драйвер. Драйвер может получить доступ к кодам нажатых или отпущенных клавиш двумя способами. Либо перехватывая IRP пакеты от других драйверов, либо самостоятельно обрабатывая прерывания от клавиатуры. Предпочтительнее выбрать драйвер-фильтр верхнего уровня, поскольку информация, возвращаемая драйвером клавиатуры, хорошо документирована и описана в литературе. Разрабатываемый драйвер должен быть WDM-драйвером.

Разрабатываемый драйвер-фильтр устанавливается над фильтром Kbdclass. Так как IRP типа IRP_MJ_READ является фактически запросом на чтение данных, то когда он идет вниз по стеку, его буфер пуст. Прочитанные данные буфер будет содержать после завершения IRP. Чтобы эти данные увидеть, фильтр должен установить в свой блок стека IRP процедуру завершения. Место драйвера-фильтра в стеке клавиатуры представлено на рис.2.7.

 

Рис.2.7. Место драйвера-фильтра в стеке клавиатуры

 

DiverEntry

Заполнение массива MajorFunctions.

Регистрируется процедура обработки пакета на чтение, процедура обработки IOCTL-запросов, процедуры обработки запросов от менеджера PnP и менеджера питания. Остальные элементы массива заполняются адресом функции __MyFilterDispatchGeneral, которая пропускает пакеты ниже по стеку.

Регистрация процедуры AddDevice. В данной работе она называется MyFilterAddDevice.

Регистрация процедуры DriverUnload, называющейся MyFilterUnload.

AddDevice

В данной работе функция __MyFilterAddDevice создает одно функциональное устройство с именем \Device\ kbd _ filter. Происходит резервирование места для хранения адреса устройства, расположенного ниже в стеке драйверов. Это сделано для того, чтобы при разрушении стека драйверов передать запрос PnP на демонтаж нижестоящему драйверу. Созданное устройство подключается к стеку драйверов клавиатуры. Это делается с помощью функции IoAttachDeviceToDeviceStack. Это стандартная функция Windows, она принимает PDO и указатель на структуру подключаемого FDO. FDO занимает место в стеке драйверов сразу после объекта, находящегося в вершине стека. Теперь подключаемый FDO становится вершиной стека. Очередность загрузки драйверов описана в реестре Windows.

Для того чтобы пользовательское приложение смогло обратиться к драйверу для FDO должно быть зарегистрировано DOS имя. Используя это имя, приложение сможет послать драйверу IOCTL-запрос. Для регистрации такого имени создается строка-юникод со значением \DosDevices\ kbd _ filter и применяется функция IoCreateSymbolicLink. Ее параметрами является только что созданная строка и имя FDO, которое обслуживает наш драйвер. Теперь \DosDevices\ kbd _ filter - это DOS имя созданного FDO устройства.

В этой функции происходит также инициализация других объектов, используемых в работе драйвера-фильтра:

PinInit() – инициализация модуля, который работает с пином.

KeyMidiInit() – инициализация таблицы, в которой хранится информация о музыкальных параметрах клавиш.

Создание системного потока PlayThread, который отправляет музыкальные команды аудиоустройству.

Создание двух очередей, используемых в работе двух потоков.

Создание объектов синхронизации потоков .

DriverUnload

Поскольку данный фильтр является PnP драйвером, то на процедуру DriverUnload ничего не возложено.

 

Разделение задачи на потоки

Функция MyFilterReadComplete, которая получает буфер нажатых клавиш как массив структур KEYBOARD_INPUT_DATA, выполняется на IRQL <= DISPATCH_LEVEL.

Функция PinMidiNoteOn работает только при IRQL = PASSIVE_LEVEL.

Это значит, что функцию PinMidiNoteOn нельзя вызывать внутри функции MyFilterReadComplete.

В драйвере создан системный поток PlayThread, который отправляет музыкальные команды аудиоустройству. В основном потоке драйвера функция MyFilterReadComplete заполняет очередь нажатых клавиш. Далее эта функция сигнализирует о событии, что можно начинать воспроизведение нот. По этому событию пробуждается поток PlayThread. Возникает проблема работы двух потоков с разделяемым ресусрсом – очередью. Поэтому используется 2 очереди.

Технологический раздел

Компиляция

Для того, чтобы скомпилировать драйвер, необходим установленный DDK для Windows NT 5. С помощью консольной утилиты build, входящей в состав DDK, необходимо войти в папку DriverControl\driver и ввести команду build. Произойдёт компиляция драйвера. Драйвер kbd_filter.sys окажется в папке DriverControl\driver\objchk_wnet_x86\i386.

Для того, чтобы скомпилировать управляющую программу, необходимо чтобы был установлен пакет разработки Microsoft Visual Studio .Net. Результатом компиляции является файл DriverControl.exe в папке DriverControl/Release.

Сборка проекта

Чтобы собрать проект (драйвер и управляющая программа) необходимо скопировать в любую папку файл DriverControl.exe, создать в этой папке другую папку driver и поместить в неё файл kbd_filter.sys.

Установка

Для того чтобы установить или удалить драйвер необходимо открыть программу DriverControl.exe. Выбрать вкладку «Установка драйвера». Кнопки «Установить» и «Удалить» означают установку или удаление драйвера из системы (рис. 3.1). Для того, чтобы изменения вступали в силу, необходимо перезагружать компьютер.

В случае, если на компьютере не найдены нужные аудиоустройства, будет выведено сообщение «Невозможно открыть MIDI-пин». В этом случае драйвер установить можно будет, но назначить ноты для клавиши будет нельзя.

 

Рис.3.1. Установка драйвер

 

Управление

Для того чтобы можно было управлять драйвером, нужно выбрать в программе DrivcrControl.exe вкладку «Назначение звука». Выбирая кнопку, которая соответствует клавиши клавиатуры, можно для неё назначить канал, инструмент и ноту. Можно предварительно проверить звучание ноты, а потом сохранить музыкальную информацию в драйвере. Информация будет продублирована в файле C:\KeyMidi.dat.

 


Рис.3.2. Управление драйвером

 


Автозагрузка

Во время перезагрузки операционной системы все значения переменных, сохранённых в памяти драйвера, теряются. Т.е. драйверу необходимо ждать пользовательское приложение, которое отправит ему объект пина и заново назначит ноты для клавиш.

Для этого было создано простое приложение, которое открывает пин, отправляет его в драйвер, отправляет в драйвер информацию обо всех музыкальных параметрах, созранённых в файле C:\KeyMidi.dat.

Проект этого приложения находится в папке DriverControl под названием DriverAutoloader. Скомпилированную программу DriverAutoloader.exe можно найти в папке DriverControl/Release. Эту программу по желанию можно сохранить в папке Startup, приложения из которой загружаются во время загрузки ОС Windows.

 

Заключение

 

Исследована структура драйверов Windows

Определено место драйвера в стеке драйверов и выбрана структура программного обеспечения, состоящего из двух частей:

- верхнего драйвера-фильтра клавиатуры

- программы, которая осуществляет настройку параметров музыкального озвучивания клавиш и управляет работой драйвера посредством IOCTL-запросов

Изучены механизмы:

- встраивания драйвера-фильтра в стек драйверов

- взаимодействия компонент программного обеспечения через IOCTL-запросы

Разработаны механизмы:

- поиска доступа к аудиоустройству в режиме ядра

- синхронизации потоков, работающих на разных уровнях IRQL

Разработано программное обеспечение в соответствии с техническим заданием и проведено его тестирование.

Разработанный программный продукт полностью удовлетворяет поставленной задаче и осуществляет настраиваемое озвучивание нажатий клавиш на клавиатуре под управлением ОС Microsoft Windows NT 5.



Список литературы

 

1. Oney W. Programming the Microsoft Windows Driver Model.— Redmond, Washington: Microsoft Press., 1999.

2. Форум разработчиков MS Windows на сайте microsoft.com.

3. Рассылка, посвящённая разработке аудиодрайверов на сайте wdmaudiodev.de

4. Рассылка, посвящённая разработке драйверов для Windows на сайте osronline.com

5. Microsoft Windows Server 2003 DDK Documentation.

 



Приложения

В файле audfilter . cpp

Что заменить:

CKsAudRenPin*

CKsAudRenFilter::CreateRenderPin

(const WAVEFORMATEX* pwfx,

BOOL fLooped)

{TRACE_ENTER();

HRESULT hr = S_OK;

CKsAudRenPin* pPin = FindViablePin(pwfx);

if (!pPin)

{DebugPrintf(TRACE_NORMAL, TEXT("Could not find a Render pin that supports the given wave format"));

hr = E_FAIL;}

else

{hr = pPin->SetFormat(pwfx);

if (FAILED(hr))

{DebugPrintf(TRACE_ERROR, TEXT("Failed to set Render Pin format - the pin lied about its supported formats"));}}

if (SUCCEEDED(hr))

{hr = pPin->Instantiate(fLooped);

if (SUCCEEDED(hr))

{DebugPrintf(TRACE_LOW, TEXT("Successfully instantiated Render Pin. Handle = 0x%08x"), pPin->GetHandle());}

else

{DebugPrintf(TRACE_ERROR, TEXT("Failed to instantiate Render Pin"));}}

if (FAILED(hr))

{// Initialize pPin to NULL again

pPin = NULL;

// Try to intstantiate all the pins, one at a time

CKsPin *pKsPin;

LISTPOS listPosPin = m_listRenderSinkPins.GetHeadPosition();

while( !pPin && m_listRenderSinkPins.GetNext( listPosPin, &pKsPin ))

{CKsAudRenPin *pKsAudRenPin = (CKsAudRenPin *)pKsPin;

hr = pKsAudRenPin->SetFormat( pwfx );

if (SUCCEEDED(hr))

{hr = pKsAudRenPin->Instantiate(fLooped);}

if (SUCCEEDED(hr))

{// Save the pin in pPin

pPin = pKsAudRenPin;

break;}}}

if (FAILED(hr))

{// Don't delete the pin - it's still in m_listRenderPins

//delete pPin;

pPin = NULL;}

else

{// Remove the pin from the filter's list of pins

LISTPOS listPosPinNode = m_listPins.Find( pPin );

assert(listPosPinNode);

m_listPins.RemoveAt( listPosPinNode );

listPosPinNode = m_listRenderSinkPins.Find( pPin );

assert(listPosPinNode);

m_listRenderSinkPins.RemoveAt( listPosPinNode );}

TRACE_LEAVE_HRESULT(hr);

return pPin;}

Заменить на:

CKsAudRenPin*

CKsAudRenFilter::CreateRenderPin

(BOOL fLooped)

{TRACE_ENTER();

HRESULT hr = S_OK;

CKsAudRenPin * pPin = FindViablePin();

if(!pPin)

{DebugPrintf(TRACE_NORMAL, TEXT("Could not find a Render pin that supports the given wave format"));

hr = E_FAIL;}

if(SUCCEEDED(hr))

{hr = pPin->Instantiate(fLooped);

if (SUCCEEDED(hr))

{DebugPrintf(TRACE_LOW, TEXT("Successfully instantiated Render Pin. Handle = 0x%08x"), pPin->GetHandle());}

else

{DebugPrintf(TRACE_ERROR, TEXT("Failed to instantiate Render Pin"));}}

/*if(FAILED(hr))

{// Initialize pPin to NULL again

pPin = NULL;

// Try to intstantiate all the pins, one at a time

CKsPin * pKsPin;

LISTPOS listPosPin = m_listRenderSinkPins.GetHeadPosition();

while(!pPin && m_listRenderSinkPins.GetNext(listPosPin, &pKsPin))

{CKsAudRenPin * pKsAudRenPin = (CKsAudRenPin *)pKsPin;

hr = pKsAudRenPin->Instantiate(fLooped);

if(SUCCEEDED(hr))

{// Save the pin in pPin

pPin = pKsAudRenPin;

break;}}}*/

if(FAILED(hr))

{// Don't delete the pin - it's still in m_listRenderPins

//delete pPin;

pPin = NULL;}

else

{// Remove the pin from the filter's list of pins

LISTPOS listPosPinNode = m_listPins.Find(pPin);

assert(listPosPinNode);

m_listPins.RemoveAt(listPosPinNode);

listPosPinNode = m_listRenderSinkPins.Find(pPin);

assert(listPosPinNode);

m_listRenderSinkPins.RemoveAt(listPosPinNode);}

TRACE_LEAVE_HRESULT(hr);

return pPin;}

Что заменить :

CKsAudRenPin*

CKsAudRenFilter::FindViablePin

(const WAVEFORMATEX* pwfx)

{TRACE_ENTER();

assert( pwfx );

CKsPin* pNode;

LISTPOS listPos = m_listRenderSinkPins.GetHeadPosition();

while(m_listRenderSinkPins.GetNext( listPos, &pNode ))

{CKsAudRenPin* pPin = (CKsAudRenPin*)pNode;

// To only look at non-digital output pins, check that pPin->IsVolumeSupported() is TRUE,

// as digital output pins don't have volume controls associated with them.

if( pPin->IsFormatSupported( pwfx ) )

{// This should be a valid pin

TRACE_LEAVE();

return pPin;}}

TRACE_LEAVE();

return NULL;}

Заменить на :

CKsAudRenPin*

CKsAudRenFilter::FindViablePin

(){TRACE_ENTER();

CKsPin * pNode;

LISTPOS listPos = m_listRenderSinkPins.GetHeadPosition();

while(m_listRenderSinkPins.GetNext( listPos, &pNode ))

{CKsAudRenPin * pPin = (CKsAudRenPin*)pNode;

// To only look at non-digital output pins, check that pPin->IsVolumeSupported() is TRUE,

// as digital output pins don't have volume controls associated with them.

if(pPin->IsFormatSupported())

{/ This should be a valid pin

TRACE_LEAVE();

return pPin;}}

TRACE_LEAVE();

return NULL;}

В файле audfilter . h

Что заменить:

CKsAudRenPin* CreateRenderPin(const WAVEFORMATEX* pwfx, BOOL fLooped);

Заменить на :

CKsAudRenPin * CreateRenderPin(BOOL fLooped);

Что заменить :

CKsAudRenPin* FindViablePin(const WAVEFORMATEX* pwfx);

Заменить на :

CKsAudRenPin * FindViablePin();

В файле audpin.cpp

Что заменить :

BOOL CKsAudPin::IsFormatSupported(const WAVEFORMATEX* pwfx)

{TRACE_ENTER();

LISTPOS listPosRange = m_listDataRange.GetHeadPosition();

KSDATARANGE_AUDIO* pKSDATARANGE_AUDIO;

while( m_listDataRange.GetNext( listPosRange, &pKSDATARANGE_AUDIO ) )

{if( KSDATAFORMAT_TYPE_WILDCARD == pKSDATARANGE_AUDIO->DataRange.MajorFormat

|| KSDATAFORMAT_TYPE_AUDIO == pKSDATARANGE_AUDIO->DataRange.MajorFormat )

{// Set the format to search for

GUID guidFormat = {DEFINE_WAVEFORMATEX_GUID(pwfx->wFormatTag)};

// If this is a WaveFormatExtensible structure, then use its defined SubFormat

if( WAVE_FORMAT_EXTENSIBLE == pwfx->wFormatTag )

{guidFormat = ((WAVEFORMATEXTENSIBLE *)pwfx)->SubFormat;}

if( KSDATAFORMAT_SUBTYPE_WILDCARD == pKSDATARANGE_AUDIO->DataRange.SubFormat

|| guidFormat == pKSDATARANGE_AUDIO->DataRange.SubFormat )

{if( KSDATAFORMAT_SPECIFIER_WILDCARD == pKSDATARANGE_AUDIO->DataRange.Specifier

|| KSDATAFORMAT_SPECIFIER_WAVEFORMATEX == pKSDATARANGE_AUDIO->DataRange.Specifier )

{if( pKSDATARANGE_AUDIO->MaximumChannels >= pwfx->nChannels

&& pKSDATARANGE_AUDIO->MinimumBitsPerSample <= pwfx->wBitsPerSample

&& pKSDATARANGE_AUDIO->MaximumBitsPerSample >= pwfx->wBitsPerSample

&& pKSDATARANGE_AUDIO->MinimumSampleFrequency <= pwfx->nSamplesPerSec

&& pKSDATARANGE_AUDIO->MaximumSampleFrequency >= pwfx->nSamplesPerSec )

{// This should be a valid pin

TRACE_LEAVE();

return TRUE;}}}}}

TRACE_LEAVE();

return FALSE;}

Заменить на :

BOOL CKsAudPin::IsFormatSupported()

{TRACE_ENTER();

LISTPOS listPosRange = m_listDataRange.GetHeadPosition();

KSDATARANGE_MUSIC * pKSDATARANGE_MUSIC;

while(m_listDataRange.GetNext(listPosRange, &pKSDATARANGE_MUSIC))

{if(KSDATAFORMAT_TYPE_WILDCARD == pKSDATARANGE_MUSIC->DataRange.MajorFormat

||KSDATAFORMAT_TYPE_MUSIC == pKSDATARANGE_MUSIC->DataRange.MajorFormat)

{if(KSDATAFORMAT_SUBTYPE_WILDCARD == pKSDATARANGE_MUSIC->DataRange.SubFormat

||KSDATAFORMAT_SUBTYPE_MIDI == pKSDATARANGE_MUSIC->DataRange.SubFormat)

{if(KSDATAFORMAT_SPECIFIER_WILDCARD == pKSDATARANGE_MUSIC->DataRange.Specifier

||KSDATAFORMAT_SPECIFIER_NONE == pKSDATARANGE_MUSIC->DataRange.Specifier)

{if(KSMUSIC_TECHNOLOGY_SWSYNTH == pKSDATARANGE_MUSIC->Technology)

{// This should be a valid pin

TRACE_LEAVE();

return TRUE;}}}}}

TRACE_LEAVE();

return FALSE;}

Что заменить :

HRESULT CKsAudPin::Init()

{TRACE_ENTER();

HRESULT hr = S_OK;

BOOL fViablePin = FALSE;

// Make sure at least one interface is standard streaming

if (SUCCEEDED(hr))

{fViablePin = FALSE;

for(ULONG i = 0; i < m_Descriptor.cInterfaces && !fViablePin; i++)

{fViablePin =

fViablePin ||

IsEqualGUIDAligned(m_Descriptor.pInterfaces[i].Set, KSINTERFACESETID_Standard) &&

(m_Descriptor.pInterfaces[i].Id == KSINTERFACE_STANDARD_STREAMING) ;}

if (!fViablePin)

{DebugPrintf(TRACE_ERROR, TEXT("No standard streaming interfaces on the pin"));

hr = E_FAIL;}}

// Make sure at least one medium is standard streaming

if (SUCCEEDED(hr))

{fViablePin = FALSE;

for(ULONG i = 0; i < m_Descriptor.cInterfaces && !fViablePin; i++)

{fViablePin =

fViablePin ||

IsEqualGUIDAligned(m_Descriptor.pMediums[i].Set, KSMEDIUMSETID_Standard) &&

(m_Descriptor.pMediums[i].Id == KSMEDIUM_STANDARD_DEVIO) ;}

if (!fViablePin)

{DebugPrintf(TRACE_ERROR, TEXT("No standard streaming mediums on the pin"));

hr = E_FAIL;}}

// Make sure at least one datarange supports audio

if (SUCCEEDED(hr))

{fViablePin = FALSE;

PKSDATARANGE pDataRange = m_Descriptor.pDataRanges;

for(ULONG i = 0; i < m_Descriptor.cDataRanges; i++)

{// SubType should either be compatible with WAVEFORMATEX or

// it should be WILDCARD

fViablePin =

fViablePin ||

IS_VALID_WAVEFORMATEX_GUID(&pDataRange->SubFormat) ||

IsEqualGUIDAligned(pDataRange->SubFormat, KSDATAFORMAT_SUBTYPE_PCM) ||

IsEqualGUIDAligned(pDataRange->SubFormat, KSDATAFORMAT_SUBTYPE_WILDCARD);

if (fViablePin && IsEqualGUIDAligned(pDataRange->MajorFormat, KSDATAFORMAT_TYPE_AUDIO))

{// Copy the data range into the pin

PKSDATARANGE_AUDIO pCopyDataRangeAudio = new KSDATARANGE_AUDIO;

if( pCopyDataRangeAudio )

{PKSDATARANGE_AUDIO pDataRangeAudio = (PKSDATARANGE_AUDIO)pDataRange;

CopyMemory( pCopyDataRangeAudio, pDataRangeAudio, sizeof(KSDATARANGE_AUDIO) );

if (NULL == m_listDataRange.AddTail( pCopyDataRangeAudio ))

{delete pCopyDataRangeAudio;

pCopyDataRangeAudio = NULL;

DebugPrintf(TRACE_ERROR, TEXT("Unable to allocate list entry to save datarange in"));

hr = E_OUTOFMEMORY;}}

else

{DebugPrintf(TRACE_ERROR, TEXT("Unable to allocate memory to save datarange in"));

hr = E_OUTOFMEMORY;}}

pDataRange = (PKSDATARANGE)( ((PBYTE)pDataRange) + pDataRange->FormatSize);}

if (!fViablePin)

{DebugPrintf(TRACE_ERROR, TEXT("No audio dataranges on the pin"));

hr = E_FAIL;}}

TRACE_LEAVE_HRESULT(hr);

return hr;}

Заменить на :

HRESULT CKsAudPin::Init()

{TRACE_ENTER();

HRESULT hr = S_OK;

BOOL fViablePin = FALSE;

// Make sure at least one interface is standard streaming

if (SUCCEEDED(hr))

{fViablePin = FALSE;

for(ULONG i = 0; i < m_Descriptor.cInterfaces && !fViablePin; i++)

{fViablePin =

fViablePin ||

IsEqualGUIDAligned(m_Descriptor.pInterfaces[i].Set, KSINTERFACESETID_Standard) &&

(m_Descriptor.pInterfaces[i].Id == KSINTERFACE_STANDARD_STREAMING) ;}

if (!fViablePin)

{DebugPrintf(TRACE_ERROR, TEXT("No standard streaming interfaces on the pin"));

hr = E_FAIL;}}

// Make sure at least one medium is standard streaming

if (SUCCEEDED(hr))

{fViablePin = FALSE;

for(ULONG i = 0; i < m_Descriptor.cInterfaces && !fViablePin; i++)

{fViablePin =

fViablePin ||

IsEqualGUIDAligned(m_Descriptor.pMediums[i].Set, KSMEDIUMSETID_Standard) &&

(m_Descriptor.pMediums[i].Id == KSMEDIUM_STANDARD_DEVIO) ;}

if (!fViablePin)

{DebugPrintf(TRACE_ERROR, TEXT("No standard streaming mediums on the pin"));

hr = E_FAIL;}}

// Make sure at least one datarange supports audio

if (SUCCEEDED(hr))

{fViablePin = FALSE;

PKSDATARANGE pDataRange = m_Descriptor.pDataRanges;

for(ULONG i = 0; i < m_Descriptor.cDataRanges; i++)

{fViablePin = fViablePin ||

IsEqualGUIDAligned(pDataRange->SubFormat, KSDATAFORMAT_SUBTYPE_MIDI) ||

IsEqualGUIDAligned(pDataRange->SubFormat, KSDATAFORMAT_SUBTYPE_WILDCARD);

if (fViablePin && IsEqualGUIDAligned(pDataRange->MajorFormat, KSDATAFORMAT_TYPE_MUSIC))

{// Copy the data range into the pin

PKSDATARANGE_MUSIC pCopyDataRangeAudio = new KSDATARANGE_MUSIC;

if( pCopyDataRangeAudio )

{PKSDATARANGE_MUSIC pDataRangeAudio = (PKSDATARANGE_MUSIC)pDataRange;

CopyMemory( pCopyDataRangeAudio, pDataRangeAudio, sizeof(KSDATARANGE_MUSIC) );

if (NULL == m_listDataRange.AddTail( pCopyDataRangeAudio ))

{delete pCopyDataRangeAudio;

pCopyDataRangeAudio = NULL;

DebugPrintf(TRACE_ERROR, TEXT("Unable to allocate list entry to save datarange in"));

hr = E_OUTOFMEMORY;}}

else

{DebugPrintf(TRACE_ERROR, TEXT("Unable to allocate memory to save datarange in"));

hr = E_OUTOFMEMORY;}}

pDataRange = (PKSDATARANGE)( ((PBYTE)pDataRange) + pDataRange->FormatSize);}

if (!fViablePin)

{DebugPrintf(TRACE_ERROR, TEXT("No audio dataranges on the pin"));

hr = E_FAIL;}}

TRACE_LEAVE_HRESULT(hr);

return hr;}

Что заменить :

CKsAudPin::~CKsAudPin(void)

{TRACE_ENTER();

KSDATARANGE_AUDIO *pKSDATARANGE_AUDIO;

CKsNode *pKsNode;

// Clear datarange list

while( m_listDataRange.RemoveHead(&pKSDATARANGE_AUDIO) )

{delete pKSDATARANGE_AUDIO;}

// Clear the node list

while( m_listNodes.RemoveHead(&pKsNode) )

{delete pKsNode;}

delete[] (BYTE *)m_pWaveFormatEx;

TRACE_LEAVE();

return;}

Заменить на :

CKsAudPin::~CKsAudPin(void)

{TRACE_ENTER();

KSDATARANGE_MUSIC * pKSDATARANGE_MUSIC;

CKsNode * pKsNode;

// Clear datarange list

while(m_listDataRange.RemoveHead(&pKSDATARANGE_MUSIC))

{delete pKSDATARANGE_MUSIC;}

// Clear the node list

while(m_listNodes.RemoveHead(&pKsNode))

{delete pKsNode;}

TRACE_LEAVE();

return;}

Что заменить :

CKsAudPin::CKsAudPin

(CKsAudFilter* pFilter,

ULONG nId,

HRESULT* phr

) : CKsPin(pFilter, nId, phr),

m_pAudFilter(pFilter),

m_pWaveFormatEx(NULL),

m_pksDataFormatWfx(NULL)

{TRACE_ENTER();

HRESULT hr = *phr;

if (SUCCEEDED(hr))

{hr = m_listDataRange.Initialize(1);

if (FAILED(hr))

DebugPrintf(TRACE_ERROR,TEXT("Failed to Initialize m_listDataRange"));}

if (SUCCEEDED(hr))

{hr = m_listNodes.Initialize(1);

if (FAILED(hr))

DebugPrintf(TRACE_ERROR,TEXT("Failed to Initialize m_listNodes"));}

// create a KSPIN_CONNECT structure to describe a waveformatex pin

if (SUCCEEDED(hr))

{m_cbPinCreateSize = sizeof(KSPIN_CONNECT) + sizeof(KSDATAFORMAT_WAVEFORMATEX);

m_pksPinCreate = (PKSPIN_CONNECT)new BYTE[m_cbPinCreateSize];

if (!m_pksPinCreate)

{DebugPrintf(TRACE_ERROR,TEXT("Failed to allocate m_pksPinCreate"));

hr = E_OUTOFMEMORY;}}

if (SUCCEEDED(hr))

{m_pksPinCreate->Interface.Set = KSINTERFACESETID_Standard;

m_pksPinCreate->Interface.Id = KSINTERFACE_STANDARD_STREAMING;

m_pksPinCreate->Interface.Flags = 0;

m_pksPinCreate->Medium.Set = KSMEDIUMSETID_Standard;

m_pksPinCreate->Medium.Id = KSMEDIUM_TYPE_ANYINSTANCE;

m_pksPinCreate->Medium.Flags = 0;

m_pksPinCreate->PinId = nId;

m_pksPinCreate->PinToHandle = NULL;

m_pksPinCreate->Priority.PriorityClass = KSPRIORITY_NORMAL;

m_pksPinCreate->Priority.PrioritySubClass = 1;

// point m_pksDataFormatWfx to just after the pConnect struct

PKSDATAFORMAT_WAVEFORMATEX pksDataFormatWfx = (PKSDATAFORMAT_WAVEFORMATEX)(m_pksPinCreate + 1);

// set up format for KSDATAFORMAT_WAVEFORMATEX

pksDataFormatWfx->DataFormat.FormatSize = sizeof(KSDATAFORMAT_WAVEFORMATEX);

pksDataFormatWfx->DataFormat.Flags = 0;

pksDataFormatWfx->DataFormat.Reserved = 0;

pksDataFormatWfx->DataFormat.MajorFormat = KSDATAFORMAT_TYPE_AUDIO;

pksDataFormatWfx->DataFormat.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;

pksDataFormatWfx->DataFormat.Specifier = KSDATAFORMAT_SPECIFIER_WAVEFORMATEX;

m_pksDataFormatWfx = pksDataFormatWfx;}

// Initialize the Pin;

if (SUCCEEDED(hr))

{hr = CKsAudPin::Init();}

TRACE_LEAVE_HRESULT(hr);

*phr = hr;

return;}

Заменить на :

CKsAudPin::CKsAudPin

(CKsAudFilter* pFilter,

ULONG nId,

HRESULT* phr

) : CKsPin(pFilter, nId, phr),

m_pAudFilter(pFilter),

m_pksDataFormat(NULL)

{TRACE_ENTER();

HRESULT hr = *phr;

if (SUCCEEDED(hr))

{hr = m_listDataRange.Initialize(1);

if (FAILED(hr))

DebugPrintf(TRACE_ERROR,TEXT("Failed to Initialize m_listDataRange"));}

if (SUCCEEDED(hr))

{hr = m_listNodes.Initialize(1);

if (FAILED(hr))

DebugPrintf(TRACE_ERROR,TEXT("Failed to Initialize m_listNodes"));}

// create a KSPIN_CONNECT structure to describe a waveformatex pin

if (SUCCEEDED(hr))

{m_cbPinCreateSize = sizeof(KSPIN_CONNECT) + sizeof(KSDATAFORMAT);

m_pksPinCreate = (PKSPIN_CONNECT)new BYTE[m_cbPinCreateSize];

if (!m_pksPinCreate)

{DebugPrintf(TRACE_ERROR,TEXT("Failed to allocate m_pksPinCreate"));

hr = E_OUTOFMEMORY;}}

if (SUCCEEDED(hr))

{m_pksPinCreate->Interface.Set = KSINTERFACESETID_Standard;

m_pksPinCreate->Interface.Id = KSINTERFACE_STANDARD_STREAMING;

m_pksPinCreate->Interface.Flags = 0;

m_pksPinCreate->Medium.Set = KSMEDIUMSETID_Standard;

m_pksPinCreate->Medium.Id = KSMEDIUM_TYPE_ANYINSTANCE;

m_pksPinCreate->Medium.Flags = 0;

m_pksPinCreate->PinId = nId;

m_pksPinCreate->PinToHandle = NULL;

m_pksPinCreate->Priority.PriorityClass = KSPRIORITY_NORMAL;

m_pksPinCreate->Priority.PrioritySubClass = 1;

// point m_pksDataFormat to just after the pConnect struct

PKSDATAFORMAT pksDataFormat = (PKSDATAFORMAT)(m_pksPinCreate + 1);

// set up format for KSDATAFORMAT

pksDataFormat->FormatSize = sizeof(KSDATAFORMAT);

pksDataFormat->Flags = 0;

pksDataFormat->SampleSize = 0;

pksDataFormat->Reserved = 0;

pksDataFormat->MajorFormat = KSDATAFORMAT_TYPE_MUSIC;

pksDataFormat->SubFormat = KSDATAFORMAT_SUBTYPE_MIDI;

pksDataFormat->Specifier = KSDATAFORMAT_SPECIFIER_NONE;

m_pksDataFormat = pksDataFormat;}

// Initialize the Pin;

if (SUCCEEDED(hr))

{hr = CKsAudPin::Init();}

TRACE_LEAVE_HRESULT(hr);

*phr = hr;

return;}

В файле audpin . h

Что заменить :

TList<KSDATARANGE_AUDIO> m_listDataRange;

Заменить на :

TList<KSDATARANGE_MUSIC> m_listDataRange;

Что заменить :

BOOL IsFormatSupported(const WAVEFORMATEX* pwfx);

Заменить на:

BOOL IsFormatSupported();

Что заменить:

KSDATAFORMAT_WAVEFORMATEX* m_pksDataFormatWfx;

Заменить на :

KSDATAFORMAT * m_pksDataFormat;

Драйвер клавиатуры, реализующий функции музыкального синтезатора на клавиатуре для Windows NT 5

 



Содержание

 

1. Введение

2. Конструкторский раздел

2.1 Постановка задачи

2.2 Архитектура Windows NT 5

2.3 Драйверная модель WDM

2.3.1 Функции загрузки/выгрузки драйвера

2.3.2 Функции обработки запросов/прерываний

2.4 Приоритеты выполнения программного кода

2.5 Стек клавиатуры

2.6 Kernel Streaming

2.7 Описание формата MIDI-данных

2.8 Выбор структуры программного обеспечения

2.8.1 Драйвер-фильтр

2.8.2 Получение доступа к аудиоустройству

2.8.3 Взаимодействие компонент программного обеспечения

2.9 Алгоритм работы драйвера-фильтра

2.9.1 Функции загрузки/выгрузки драйвера

2.9.2 Функции обработки пакетов IRP

2.9.3 Функции работы с аудио-устройством

2.9.4 Схема хранения музыкальных параметров клавиш

2.9.5 Разделение задачи на потоки

3. Технологический раздел

3.1 Выбор средств разработки программного обеспечения

3.2 Установка драйвера в системе

3.3 Сборка программного обеспечения

3.4 Использование библиотеки DirectKS

3.5 Отправление запросов из приложения в драйвер

3.6 Описание интерфейса пользователя

3.7 Тестирование программного обеспечения

4. Заключение

5. Список литературы

6. Приложения

6.1 Функции установки драйвера в системе

6.2 Изменения в библиотеке DirectKS



Введение

 

В современных вычислительных системах важнейшую роль играет подсистема ввода/вывода. Известно, что примерно 80% инструкций программы связаны с этой подсистемой. В связи с этим в настоящее время возрастает актуальность разработки программных систем, осуществляющих взаимодействие с периферийными устройствами посредством подсистемы ввода/вывода.

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

Упрощение работы с компьютером людям со слабым зрением.

Создание MIDI-синтезатора на клавиатуре.

Создание системы звуковой сигнализации.

В стандартную поставку ОС Microsoft Windows XP Professional входят компоненты, обеспечивающие специальные возможности операционной системы для пользователей с различными отклонениями в здоровье. Среди этих компонент следует отметить:

«Экранную клавиатуру», с помощью которой, в частности, можно озвучить выбор клавиш мышью.

Возможность озвучивания нажатий клавиш Num, Caps и Scroll Lock.

Характерной особенностью вышеперечисленных компонент является подача сигнала одной частоты независимо от нажатой клавиши и невозможность настройки параметров звучания. В этой связи представляется целесообразной разработка нового программного обеспечения, позволяющего организовать звуковое сопровождение клавиатуры в более полном объеме, в частности, настраивать выборочное озвучивание клавиш.



Конструкторский раздел

Постановка задачи

 

В соответствии с заданием на курсовую работу необходимо разработать программное обеспечение для решения следующих задач:

Вывод музыкальных нот с заданными параметрами при нажатии клавиш.

Возможность настройки пользователем параметров звучания клавиш.

Программное обеспечение состоит из двух взаимодействующих частей:

Драйвер-фильтр, реализующий первую задачу.

Программа настройки, реализующая вторую задачу.

Требования к программному обеспечению:

Драйвер-фильтр должен отслеживать нажатия всех клавиш клавиатуры PS/2 и генерировать музыкальные ноты с соответствующими клавише параметрами.

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

В качестве устройства для вывода музыкальных нот используется синтезатор MIDI, встроенный в звуковую карту Sound Blaster.

Приоритетным критерием при разработке продукта является производительность для драйвера-фильтра и удобство пользователя для программы настройки.

Программное обеспечение ориентировано на работу в ОС Windows NT 5. Функционирование программного обеспечения не должно влиять на работу других драйверов системы.



Архитектура Windows NT 5

 

Архитектура Windows NT 5 соответствует классическим представлениям о проектировании операционных систем. Наиболее распространены реализации данной ОС для платформы Intel x86 в одно- или многопроцессорных конфигурациях, однако существуют также версии для DEC Alpha и MIPS. Windows NT 5 использует защищённый режим центрального процессора, реализует механизмы виртуальной памяти и многозадачности.

Исполняемый код в Windows NT 5 имеет два уровня привилегий: код пользовательского режима и код режима ядра. Уровень привилегий накладывает определённые ограничения: в пользовательском режиме не могут выполняться привилегированные инструкции процессора, не разрешено обращение к защищённым страницам памяти. Эти ограничения накладываются для обеспечения безопасности работы системы. Пользовательское приложение не должно иметь возможность в результате ошибки или преднамеренно вносить изменения в критические таблицы операционной системы или в память других приложений. В частности, такие ограничения запрещают пользовательскому приложению напрямую управлять внешними устройствами, потому что каждое из них является разделяемым ресурсом.

В Windows NT 5 обеспечение обмена данными и управление доступом к внешнему устройству как к разделяемому ресурсу возлагается на его драйвер. Ввод и вывод в драйверах осуществляется посредством IRP-пакетов (Input/Output Request Packet). Запросы на ввод/вывод, посылаемые приложениями или другими драйверами, обрабатываются драйвером, после чего запрашивающей программе в том же пакете посылается статус завершения операции. Общий принцип взаимодействия проиллюстрирован на рис. 2.1.


Рис.2.1. Архитектура ввода/вывода Windows NT 5

 

Управление внешним устройством в общем случае сводится к заполнению его регистров необходимыми данными. Монопольный доступ драйвера к этим регистрам гарантируется операционной системой. Очевидно, что при данных обстоятельствах требуется, чтобы драйвер устройства выполнялся в режиме ядра.

Обобщённая классификация драйверов Windows NT 5 может быть представлена следующим образом:

Драйверы режима ядра:

Унаследованные драйверы

Драйверы файловой системы

Видеодрайверы

Драйверы PnP (Plug And Play):

Драйверы WDM

Драйверы пользовательского режима:

Драйверы виртуальных устройств

 




Драйверная модель WDM

Windows Driver Model (WDM) — это стандартная спецификация Microsoft для разработчиков драйверов устройств. Она поддерживается в операционных системах Windows 98/Me/2000/XP. Компания Microsoft требует, чтобы все новые драйверы под эти операционные системы создавались по этой спецификации. Для этого от них требуется чёткое следование структуре WDM, поддержка Plug and Play и управления электропитанием.

Драйверная модель WDM построена на организации и манипулировании слоями объектов физических устройств (PDO, Physical Device Object) и объектов функциональных устройств (FDO, Functional Device Object).

Объект PDO создается для каждого физически идентифицируемого элемента аппаратуры, подключенного к шине данных, и подразумевает ответственность за низкоуровневый контроль, достаточно общий для набора функций реализуемых этим аппаратным элементом. Объект PDO – это особая структура данных, создаваемая системой для взаимодействия программного и аппаратного обеспечения.

Объекты FDO подразумевает единицу логической функциональности устройства.

Объектам FDO устройств разрешается окружать себя Объектами Фильтрами (FiDO, Filter Device objects) как показано на рис. 2.2. Соответственно, каждому FiDO объекту сопоставлен драйвер, который выполняет определенную работу. Объекты фильтры подразделяются на фильтры нижнего уровня и фильтры верхнего уровня. И тех и других может существовать произвольное количество. Они модифицируют процесс обработки запросов ввода/вывода. Объекты FDO и FiDO отличаются только в смысловом отношении. FDO объект и его драйвер считаются главными. Они обычно обеспечивают управление устройством, а объекты FiDO являются вспомогательными.

 

Рис. 2.2. Стек устройств

 

Все объекты FDO и FiDO позиционируют себя в стеке устройств. Порядок объектов в стеке определяет порядок обработки запросов ввода-вывода. Если необходимо перехватить и обработать запрос, непосредственно идущий от пользователя, то нужно устанавливать верхний фильтр. Если же нужно отслеживать обращение к портам ввода вывода, обрабатывать прерывания, то нужен нижний фильтр. Данная модель позволяет драйверу устанавливать callback процедуры. Когда запрос начинает обрабатываться, то он обрабатывается последовательно всеми драйверами стека устройства ( исключая ситуацию, когда какой-либо драйвер сам завершит обработку запроса ). После этого диспетчер ввода-вывода передает запрос callback процедуре каждого драйвера стека. Сначала запрос передается callback процедуре последнего драйвера который в стеке, потом процедуре предпоследнего драйвера и т.д. Callback процедуры нужны для того, чтобы обработать прочитанную из устройства информацию. Если фильтр обрабатывает запросы на чтение, то когда этот запрос поступит в драйвер информация еще не будет считана. Поэтому драйверу необходимо установить callback функцию. При ее вызове запрос уже будет содержать считанные данные.

Драйвер имеет следующие точки входа:

DriverEntry

DriverUnload

AddDevice

Функции для обработки пакетов IRP

ISR

Дата: 2019-12-22, просмотров: 302.