Александр Тарво - Использование NuMega DriverStudio для написания WDM-драйверов
- Название:Использование NuMega DriverStudio для написания WDM-драйверов
- Автор:
- Жанр:
- Издательство:неизвестно
- Год:неизвестен
- ISBN:нет данных
- Рейтинг:
- Избранное:Добавить в избранное
-
Отзывы:
-
Ваша оценка:
Александр Тарво - Использование NuMega DriverStudio для написания WDM-драйверов краткое содержание
Использование NuMega DriverStudio для написания WDM-драйверов - читать онлайн бесплатно полную версию (весь текст целиком)
Интервал:
Закладка:
//несколькими приложениями одновременно.
//Возвращаем ошибку.
status = I.PnpComplete(this, STATUS_INVALID_PARAMETER, IO_NO_INCREMENT);
else {
//Это устройство свободно. Устанавливаем флаг и возвращаем успех.
m_AlreadyUsed = false;
status = I.PnpComplete(this, STATUS_SUCCESS, IO_NO_INCREMENT);
}
return status;
}
NTSTATUS MyPrettyDevice::Close(KIrp I) {
NTSTATUS status;
//Пользователь закончил работу с устройством, теперь оно свободно.
//Сбрасываем флаг.
m_AlreadyUsed = false;
status = I.PnpComplete(this, STATUS_SUCCESS, IO_NO_INCREMENT);
return status;
}
Функция SerialRead вызывается, когда драйвер получает запрос на чтение. Это важная функция. Т.к. мы хотим, чтобы приложение пользователя могло читать и писать в память микросхемы, то именно сюда необходимо добавлять наш код. Все фрагменты кода, добавленные программистом, будут выделены жирным шрифтом:
//This code was added by the programmer
Фактически в данном методе мы должны прочитать содержимое памяти и передать его приложению пользователя. Но тут самое время вспомнить, что плата обменивается с памятью 4– байтными словами. Поэтому для операций с памятью следует применять метод ind/outd.
void XDSPdrvDevice::SerialRead(KIrp I) {
t << "Entering XDSPdrvDevice::SerialRead, " << I << EOL;
NTSTATUS status = STATUS_SUCCESS;
//Здесь мы получаем буфер пользователя. Он передается через Irp.
KMemory Mem(I.Mdl());
PUCHAR pBuffer = (PUCHAR) Mem.MapToSystemSpace();
//Теперь pBuffer – указатель на буфер пользователя.
//Здесь мы получаем число 4-байтных слов, которое должно быть прочитано. Оно также
//передается через Irp, как запрашиваемое количество байт для чтения.
ULONG dwTotalSize = I.ReadSize(CURRENT);
ULONG dwBytesRead = dwTotalSize;
//Здесь мы читаем заданное число байт из памяти устройства. Плата XDSP680 обменивается
//с памятью 4-байтными словами.Начальный адрес – 0, dwTotalSize 4-байтных слов будут
//прочитаны в буфер pBuffer.
m_MainMem.ind(0,(ULONG*)pBuffer,dwTotalSize);
//Возвращаем количество прочитанных слов
I.Information() = dwBytesRead;
I.Status() = status;
//Обработать следующий IRP-пакет.
PnpNextIrp(I);
}
Метод SerialWrite работает практически так же, только он записывает данные в память устройства, а не считывает их.
void XDSPdrvDevice::SerialWrite(KIrp I) {
t << "Entering XDSPdrvDevice::SerialWrite, " << I << EOL;
NTSTATUS status = STATUS_SUCCESS;
KMemory Mem(I.Mdl());
PUCHAR pBuffer = (PUCHAR) Mem.MapToSystemSpace();
ULONG dwTotalSize = I.WriteSize(CURRENT);
ULONG dwBytesSent = dwTotalSize;
m_MainMem.outd(0,(ULONG*)pBuffer,dwTotalSize);
I.Information() = dwBytesSent;
I.Status() = status;
PnpNextIrp(I);
}
Как мы упоминали ранее, для большинства драйверов устройств недостаточно функций чтения и записи. Мало-мальски сложное устройство требует еще и множества других операций: получить состояние, получить информацию об устройстве, как-то отконфигурировать его. Для выполнения этих задач служат функции управления вводом-выводом, IO Control; сокращенно — IOCTL. IOCTL предоставляет программисту возможность разработать практически неограниченное количество различных функций управления устройством.
И драйвер, и приложение пользователя различают, какую функцию управления устройством вызвать, при помощи IOCTL-кодов. Такой код представляет собой обыкновенное 32-разрядное число. Для удобства ему директивой #define задают какое-то понятное имя. Например, в нашем случае зададим IOCTL-код, при получении которого драйвер будет возвращать количество памяти "на борту" PCI-устройства.
#define XDSPDRV_IOCTL_GETMEMSIZE 0x800
Если при чтении драйверу посылается IRP-пакет со старшим кодом функции IRP_MJ_READ, при записи — IRP_MJ_WRITE, то при вызове функции DeviceIOControl для нашего устройства драйвер получает пакет со старшим кодом IRP_MJ_IOCONTROL и младшим — код самой IOCTL-функции. Метод DeviceControl вызывается при получении драйвером IRP со старшим кодом IRP_MJ_DEVICE_CONTROL. Она действует подобно методу StartIo. В зависимости от кода IOCTL производится вызов соответствующей функции.
NTSTATUS XDSPdrvDevice::DeviceControl(KIrp I) {
NTSTATUS status;
t << "Entering XDSPdrvDevice::Device Control, " << I << EOL;
switch (I.IoctlCode()) {
case XDSPDRV_IOCTL_GETMEMSIZE:
//Получен определенный нами IOCTL-код XDSPDRV_IOCTL_GETMEMSIZE.
//Вызвать соответствующий обработчик.
status = XDSPDRV_IOCTL_GETMEMSIZE_Handler(I);
break;
default:
//Этот код не определен.
status = STATUS_INVALID_PARAMETER;
break;
}
if (status == STATUS_PENDING)
// Если драйвер по каким-то причинам отложил обработку запроса, переменной status
//присваивается значение STATUS_PENDING. Этот код будет возвращен методом
//DeviceControl.
{
return status;
} else
//В противном случае завершаем обработку пакета.
{
return I.PnpComplete(this, status);
}
}
Метод XDSPDRV_IOCTL_GETMEMSIZE_Handler является обработчиком IOCTL–кода XDSPDRV_IOCTL_GETMEMSIZE. Получив этот код, драйвер возвращает общий объем памяти устройтсва. Шаблон метода сгенерирован DriverWizard, но программист должен написать практически весь его код.
NTSTATUS XDSPdrvDevice::XDSPDRV_IOCTL_GETMEMSIZE_Handler(KIrp I) {
NTSTATUS status = STATUS_SUCCESS;
t << "Entering XDSPdrvDevice::XDSPDRV_IOCTL_GETMEMSIZE_Handler, " << I << EOL;
//Количество памяти будет возвращено как число unsigned long. Поэтому определяем
//указатель на unsigned long.
unsigned long *buf;
//Получаем указатель на буфер пользователя
buf=(unsigned long*) I.UserBuffer();
//Записываем туда количество памяти нашего устройства. Получаем его при помощи
//метода Count объекта m_MainMem.
*buf=m_MainMem.Count();
//Длина возвращаемых нами данных равна длине числа unsigned long.
I.Information() = sizeof(unsigned long);
//Возвращаем STATUS_SUCCESS
return status;
}
Написание драйвера завершено. Возможно, у Вас сложилось впечатление, что DriverWizard может практически все и написание драйвера — очень простая задача. Но не следует забывать, что наш драйвер — всего-то простейшая демонстрационная программа, которая практически не выполняет никаких полезных действий. Написание реальных драйверов является гораздо более сложной задачей.
Если бы драйвер был написан с использованием пакета DDK, то он бы имел практически ту же структуру и почти тот же код (правда, не объектно-ориентированный). Но в таком случае весь драйвер пришлось бы писать вручную, а DriverWizard генерирует скелет драйвера автоматически. Это сильно облегчает процесс разработки драйвера, позволяя программисту не заботиться о написании скелета драйвера и предохраняя его от возможных ошибок.
2.4 Разработка dll-библиотеки для взаимодействия с драйвером
Интервал:
Закладка: