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

В данной работе за пропускание пакетов вниз отвечает процедура __MyFilterDispatchGeneral. Она передает IRP пакет нижестоящему драйверу с помощью функции IoCallDriver. При этом нижестоящий драйвер должен считывать текущую ячейку IRP пакета. Это достигается за счет использования функции IoSkipCurrentIrpStackLocation.

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

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

NTSTATUS PinInit()

В этой функции происходит инициализация полей заголовка IRP-пакета и MIDI-данных, которые используются во время отправления музыкальных команд в пин. Далее в функциях PinMidiNoteOn и PinMidiNoteOff используется инициализированный заголовок.

Инициализация заголовка IRP-пакета происходит следующим образом:

typedef __declspec(align(16)) struct _MIDI_DATA

{KSMUSICFORMAT InstrumentFormat; // include <ksmedia.h>

union

{UCHAR InstrumentByte[4];

UCHAR NoteOffByte[4];};

KSMUSICFORMAT NoteFormat;

UCHAR NoteOnByte[4];

} MIDI_DATA, * PMIDI_DATA;

MIDI_DATA PinMidiData;

KSSTREAM_HEADER PinWriteHeader; // include <ks.h>

RtlZeroMemory(&PinWriteHeader, sizeof(PinWriteHeader));

// 12 байт

PinMidiData.InstrumentFormat.TimeDeltaMs = 0;

PinMidiData.InstrumentFormat.ByteCount = 3;

PinMidiData.InstrumentByte[0] = 0x00;

PinMidiData.InstrumentByte[1] = 0x00;

PinMidiData.InstrumentByte[2] = 0x00;

PinMidiData.InstrumentByte[3] = 0x00;

// ещё 12 байт

PinMidiData.NoteFormat.TimeDeltaMs = 0;

PinMidiData.NoteFormat.ByteCount = 3;

PinMidiData.NoteOnByte[0] = 0x00;

PinMidiData.NoteOnByte[1] = 0x00;

PinMidiData.NoteOnByte[2] = 0x00;

PinMidiData.NoteOnByte[3] = 0x00;

PinWriteHeader.Size = sizeof(PinWriteHeader);

PinWriteHeader.TypeSpecificFlags = 0;

PinWriteHeader.PresentationTime.Time = 0;

PinWriteHeader.PresentationTime.Numerator = 1;

PinWriteHeader.PresentationTime.Denominator = 1;

PinWriteHeader.Duration = 0;

PinWriteHeader.FrameExtent = 24; // всего 24 байта

PinWriteHeader.DataUsed = 24; // всего 24 байта

PinWriteHeader.Data = &PinMidiData;

NTSTATUS PinOpenStream(IN HANDLE UserPin)

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

UserPin - HANDLE того пина, который содержится в буфере IRP-пакета.

NTSTATUS PinIsOpenedStream()

Возвращает STATUS_SUCCESS если пин открыт.

NTSTATUS PinFree()

Здесь происходит вызов функции ObDereferenceObject, которая уменьшает число ссылок на объект пина.

NTSTATUS PinSetState(IN KSSTATE State)

Устанавливает состояние пина в State (KSSTATE_RUN, KSSTATE_PAUSE или KSSTATE_STOP). Перед тем, как воспроизводить ноты, необходимо установить состояние пина в KSSTATE_RUN. Функция работает только при IRQL = PASSIVE_LEVEL.

NTSTATUS PinWriteData(IN KSSTREAM_HEADER * Pheader)

Отправляет заголовок в открытый пин посредством IOCTL_KS_WRITE_STREAM.

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

NTSTATUS PinMidiNoteOn(IN UCHAR Channel,

IN UCHAR Instrument, IN UCHAR Note)

Если пин открыт, то отправляет команду на воспроизведение ноты Note с использованием инструмента Instrument в канале Channel.

Модификация инициализированного заголовка:

PinMidiData.InstrumentByte[0] = 0xC0 | Channel;

PinMidiData.InstrumentByte[1] = Instrument;

PinMidiData.NoteOnByte[0] = 0x90 | Channel;

PinMidiData.NoteOnByte[1] = Note;

PinMidiData.NoteOnByte[2] = 0x7F;

// 24 байта отправляем, т.к. в одном

PinWriteHeader.FrameExtent = 24; // пакете 2 команды: устанавливаем

PinWriteHeader.DataUsed = 24; // инструмент и отправляем ноту

PinWriteData(&PinWriteHeader);

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

NTSTATUS PinMidiNoteOff(IN UCHAR Channel, IN UCHAR Note)

Выключает ноту Note в канале Channel.

Модификация инициализированного заголовка:

PinMidiData.NoteOffByte[0] = 0x80 | Channel;

PinMidiData.NoteOffByte[1] = Note;

PinWriteHeader.FrameExtent = 12; // 12 байт шлём, т.к. в одном пакете

PinWriteHeader.DataUsed = 12; // 1 команда: выключаем ноту

PinWriteData(&PinWriteHeader);

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

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

Во время нажатия клавиши клавиатура генерирует скан-код и флаг (эта информация находится в структуре KEYBOARD_INPUT_DATA).

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

Для нескольких клавиш может быть сгенерирован один и тот же скан-код, например для левого и правого Shift. Их возможно различить только по флагу. Не более чем трём клавишам может сооответствовать один скан-код. Поэтому скан-код нельзя использовать как индекс массива, в котором хранятся музыкальные данные только для одной клавиши - эти музыкальные данные будут использоваться для нескольких клавиш с одинаковым скан-кодом.

Тогда следует для каждой пары (скан-код, флаг) создать новый скан-код клавиши такой, чтобы для каждой клавиши существовал единственный скан-код. Позиция - пусть так называется новый скан-код.

Схема реализована в модуле keys.

Схема такова:

typedef struct _KEY_SCANS_TABLE

{UCHAR Usage;

UCHAR ScanMake;

UCHAR ScanE0;

UCHAR ScanE1;

} KEY_SCANS_TABLE, * PKEY_SCANS_TABLE;

typedef struct _KEY_MIDI_TABLE

{UCHAR Used;

UCHAR Channel;

UCHAR Instrument;

UCHAR Note;

} KEY_MIDI_TABLE, * PKEY_MIDI_TABLE;

Создаётся массив структур KEY_SCANS_TABLE, где индекс массива - реальный скан-код, генерируемый клавиатурой, а в полях ScanMake, ScanE0, ScanE1 хранится позиция клавиши, соответствующая тому или иному значению флага клавиши, генерируемого клавиатурой. В поле Usage число единичных бит равно числу установленных клавиш, соответствующих одному скан-коду.

Создаётся массив структур KEY_MIDI_TABLE, где индекс массива - позиция клавиши, в полях структуры содержится музыкальная информация о клавише:

Used - используется в схеме или нет. Если используется: нажата или отпущена.

Channel - канал, в котором звучит нота. Существует 16 каналов - 0..15.

Instrument - инструмент ноты. Существует 128 инструментов - 0..127.

Note - нота. Существует 128 нот для каждого инструмента - 0..127.

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

На обычной клавиатуре находится 104 клавиши. Поэтому в данной курсовой работе используется 104 позиции (0..103).

Функции установки и выборки музыкальной информации

NTSTATUS KeyMidiInit()

Выделяет память для двух таблиц и заполняет их нулями.

NTSTATUS KeyMidiFree()

Освобождает память, выделенную для таблиц.

NTSTATUS KeyMidiSetNote(IN UCHAR ScanCode, IN UCHAR Flag,

IN UCHAR Position,

IN UCHAR Channel, IN UCHAR Instrument,

IN UCHAR Note, IN UCHAR Used)

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

NTSTATUS KeyMidiGetPosition(IN UCHAR ScanCode, IN UCHAR Flag,

OUT PUCHAR Position)

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

NTSTATUS KeyMidiGetNote(IN UCHAR Position, PUCHAR Channel,

OUT PUCHAR Instrument, OUT PUCHAR Note,

OUT PUCHAR Used)

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

NTSTATUS KeyMidiSetUsed(IN UCHAR Position, IN UCHAR Used)

Используя позицию клавиши, устанавливает состояние клавиши KEY_RELEASED (отпущена) или KEY_PRESSED (нажата).

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

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

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

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

Страницы: 1, 2, 3, 4, 5, 6, 7, 8, 9



2012 © Все права защищены
При использовании материалов активная ссылка на источник обязательна.