Форум программистов «Весельчак У»
  *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

  • Рекомендуем проверить настройки временной зоны в вашем профиле (страница "Внешний вид форума", пункт "Часовой пояс:").
  • У нас больше нет рассылок. Если вам приходят письма от наших бывших рассылок mail.ru и subscribe.ru, то знайте, что это не мы рассылаем.
   Начало  
Наши сайты
Помощь Поиск Календарь Почта Войти Регистрация  
 
Страниц: [1]   Вниз
  Печать  
Автор Тема: Реализация Bus Master DMA в xapp1052 для SP605  (Прочитано 29362 раз)
0 Пользователей и 2 Гостей смотрят эту тему.
chaika_sv
Участник

ru
Offline Offline
Пол: Мужской

« : 30-07-2010 10:06 » 

Всем привет.

У меня есть устройство — ПЛИС (Xilinx® Spartan-6 FPGA SP605).
Нужно реализовать для него драйвер, работающий в режиме Bus Master DMA.
Как это примерно сделать описано в xapp1052. Скачал архив для этого xapp1052, установил оттуда прошивку, поставил драйвер, dll-библиотеку и приложение для тестирования. Приложение работает, всё ок.
Стал разбираться с реализацией драйвера и сразу возникло много вопросов..
В драйвере практически нет стандартных DMA-функций типа WdfDma* 
В частности, нет функции для определения профиля DMA-устройства (WdfDmaProfile*), нет функций для работы с DMA-транзакциями (WdfDmaTransaction*) и многих других, казалось бы, необходимых функций.

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

С уважением,
Сергей
Записан
Ochkarik
Модератор

ru
Offline Offline
Пол: Мужской

« Ответ #1 : 30-07-2010 10:40 » 

доброго!
выложите проект, посмотрим)

PS а с чего вы решили что функции типа WdfDma*  являются стандартными?))))
по мне, так стандартными -  являются функции типа IoGetDmaAdapter)))
PS засели вы с своем WDF... света белого не видите))) по минимуму - DMA это ж простейшая вещь.
Записан

RTFM уже хоть раз наконец!  RTFM :[ ну или хотя бы STFW...
chaika_sv
Участник

ru
Offline Offline
Пол: Мужской

« Ответ #2 : 30-07-2010 11:38 » new

Драйвер с исходниками прикрепил. Если надо, могу и dll с тестирующим приложением выложить.

С чего я решил про WdfDma* ? Ну так книжку умную читаю Орвик, Смит "WDF. Разработка драйверов" Улыбаюсь Там в разделе про DMA как раз описывается, как работать с функциями типа WdfDma*.
IoGetDmaAdapter - такой функции у себя не нашёл.

Ну вот что-то никак не могу DMA осилить А черт его знает...

* win32_driver.rar (38.93 Кб - загружено 1151 раз.)
Записан
Ochkarik
Модератор

ru
Offline Offline
Пол: Мужской

« Ответ #3 : 01-08-2010 12:32 » 

гы) смешно))) книжка то хорошая... но... это не отменяет всех остальных)
так что закройте книжку Пенни Орвик, хотя бы на некоторое время.... откройте книжку Вальтера Они, и будет вам счастье! ну и msdn конечно.....

это обычный драйвер WDM.... это НЕ-WDF.
WDF - это некая надстройка над WDM драйверами windows. попытка систематизации стандартных WDM функций, чтобы большая их часть  вызывалась по умолчанию, скрытно от программиста, если он о них ничего не хочет знать и читать) книга Орвик - об этой надстройке. и только! (то есть не только, но там все операции исключительно через WDF функции описаны)

теперь коротенько по коду.
процедура PnPStartDevice() - это вся инициализация всего необходимого во время работы PnP.
в том числе получение ресурсов: портов (ух ты! их вообще нет?), отображаемой памяти deviceExtension->MemoryStart[] и прерываний.
плата имеет прерывание: регистрируется IoConnectInterrupt() с обработчиком BDMA_Isr() :
в BDMA_Isr проверяется статусный бит в регистре платы (адрес pDevExt->MemoryStart[FPGA] + DCSR_OFFSET)    
if (~pDevExt->ReadDone && (regValue & 0x1000000))   // Read Complete
или
if (~pDevExt->WriteDone && (regValue & 0x100))      // Write Complete
по прерыванию ставятся в очередь DPC pDevExt->ReadIrp() и соответственно pDevExt->WriteIrp().
это как бы отложенные функции обработки прерывания на низком IRQL. на них вешают завершение IRP что ли?(видимо сразу от приложения?) лень глубоко разбираться... плата видимо выставляет прерывания по полному завершению DMA операции....

инициализация DMA контроллера винды находится в функции GetDMAInfo() - это вызов  IoGetDmaAdapter для чтения и записи

далее функция StartDMAWrite() - собственно запуск DMA транзакии. (а так же StartDMARead)
при помощи  KeSynchronizeExecution() вызывается  SetIoTimerValue() синхронизировано с прерыванием... почитайте описание.
вначале видимо расчитывают объем ScatterGather-списка (фактически это список указателей на куски физической памяти куда будет осуществлена запись)
потом собственно размещают память списка страниц DMA GetScatterGatherList
 ну и оно начинает жужжать...
потом приходит прерывание, вызывается DPC для начального IRP... ну и... все это поступает приложению)

да. еще вам надо посмотреть реализацию вашей пользовательской ReadAdapterControl(). ее вызывает GetScatterGatherList. она собственно говорит плате о том что надо зажужжать, путем загрузки адреса ScatterGather списка в регистры платы pDevExt->MemoryStart[FPGA] + READ_ADDR_OFFSET

PS кстати спасибо за выложенный код. а то я ни одного нормального примера работы со ScatterGather-List как-то не видел))) правда и не интересовался давно...
« Последнее редактирование: 02-08-2010 19:26 от Ochkarik » Записан

RTFM уже хоть раз наконец!  RTFM :[ ну или хотя бы STFW...
chaika_sv
Участник

ru
Offline Offline
Пол: Мужской

« Ответ #4 : 04-08-2010 09:21 » 

Ochkarik, спасибо за подробный комментарий! Очень помогло, главное, что я научился отличать WDF от WDM и открыл для себя книжку Они))) В в чём-то разобрался, но вопросы остались.

Сначала опишу процесс DMA-передачи, как я его себе представляю.

1.
Где-нибудь в обработчике AddDevice выполняется IoGetDmaAdapter().
Функция возвращает указатель на объект адаптера. В качестве параметра функции получает структуру DEVICE_DESCRIPTION, описывающую DMA-характеристики устройства. Важно, что моё устройство может работать в режиме мастера шины (BusMaster) и не поддерживает аппаратный ScatterGather.
Также функция возвращает максимально допустимое для моего устройства количество регистров отображения (Map Registers) - ReadMapRegMax.

2.
Драйвер получает запрос на чтение данных IRP_MJ_READ.
В обработчике этого запроса происходит подготовка к выполнению DMA-передачи (у меня это функция StartDMARead())
 - запускается таймер функцией KeSynchronizeExecution(). Если через 3 секунды после его запуска драйвер не получит прерывания, означающего конец пересылки, то значит таймаут.
 - чистятся буфера функцией KeFlushIoBuffers()
 - считается необходимое кол-во регистров отображения макросом ADDRESS_AND_SIZE_TO_SPAN_PAGES(). Если регистров достаточно (не больше, чем ReadMapRegMax), то столько их и делаем. Если не достаточно, то в рамках этой передачи используем все регистры по максимуму, а остальное делаем в последующих передачах.
 - самое главное, формируется S/G list функцией GetScatterGatherList()
Если всё это прошло успешно, то IRP получает статус STATUS_PENDING, т.е. статус ожидания обработки.

3.
В функции GetScatterGatherList() в качестве одного из параметров указывается функция ReadAdapterControl(), которая вызывается каждый раз для обработки очередного элемента S/G list. А точнее она берёт очередной элемент и S/G list, смотрит его адрес и длину в памяти (MapTransfer тут где-то выполняется автоматом) и осуществляет его чтение или запись.
Тут внимание. В моём примере эта функция реализована как-то уж больно ущербно, мне кажется! Вот практически весь её ключевой код:
Цитата
   regAddr = (ULONG) pDevExt->MemoryStart[FPGA] + READ_ADDR_OFFSET;
   WRITE_REGISTER_ULONG ((PULONG) regAddr, pSGL->Elements[0].Address.LowPart);
и больше ничего..
Т.е. она возвращает по смещению READ_ADDR_OFFSET (в BAR0) адрес (физический, логический???) первого элемента S/G list.
В примере SCATGATH как-то она посерьёзнее выглядит.

4.
Драйвер получает прерывание (срабатывает обработчик BDMA_Isr).
Получение данного прерывания означает, что пересылка завершилась полностью (при условии, что прерывание пришло действительно от нашего устройства).
Далее включается как раз механизм отложенной обработки прерываний DPC. Там (в DpcForIrp) ничего особенного вроде не происходит. Просто корректно завершается Irp (и освобождается S/G List).


Вот так я всё это дело понимаю в общих чертах. Большая просьба поправить, если что-то не так.

Задам пока два больших вопроса, которые меня интересуют в первую очередь:
1. По пункту 3 (про ReadAdapterControl). Подскажите, что не так с реализацией функции ReadAdapterControl? Почему она такая куцая в приведённом мной примере?
2. Как с этим драйвером работать пользовательскому приложению? Ну например, выполняю я запрос на чтение.. из какого места мне потом нужно читать результат пользовательским приложением??

Заранее спасибо!
Записан
Ochkarik
Модератор

ru
Offline Offline
Пол: Мужской

« Ответ #5 : 04-08-2010 18:00 » 

1. нет. в AddDevice еще рано инициализировать DMA. у вас же не Legacy а PnP драйвер! Прежде необходимо пройти IRP процедуры PnP в результате которой выяснить ресурсы железки. как-то порты IO память прерывания.
AddDevice - служит для создания вашего объекта (читай: структуры DevExt) устройства. по этому вызову драйвер узнает что в системе присутствует устройство данного класса (и может быть не одно).
последовательность такая DriverEntry->AddDevice->IRP_MJ_PNP(их там несколько IRP_MN_XXX вызывается)
последняя IRP_MN_PNP_START (или как ее там...)
а вот по MN_PNP_START - в принципе можно уже инициализировать. обычно в этот момент устройство должно быть готовым к рабочему режиму.

2. KeSynchronizeExecution- это не совсем таймер... по моему сама функция просто вызыват пользовательский код с защитой от паралельного выполнения с кодом прерывания. таймер там где то отдельно должен быть наверное)
 KeFlushIoBuffers не чистит никакие буфера, читайте внимательнее)

насчет того что не поддерживает аппаратный ScatterGather... я честно говоря с трудом  понимаю зачем его программно городить....единственно что ради того чтобы напрямую память IRP передавать без буферизации... или MJ_READ - всегда буферизируются? тогда вообще не понял.
можно забить на SG список, выделить сразу много непрерывной памяти и радоваться...
но если хотите - то да, реализация ReadAdapterControl явно не полная. надо? тогда вы должны по обработке первого элемента - ставить на выполнение следующий. думаю прямо в ISR можно...

из какого места читать? - да как обычно... обычный ReadFile. повнимательней посмотрите для какой памяти GetScatterGatherList() свой список генерит?)

в остальном вроде все правильно...) поищите на форуме про DMA и SG список... была тема в которой было словесное описание...

PS на самом деле... вся эта куча процедур нужна только для того чтобы память приложения без гемороя сразу драйверу отдавать...
 учитывая что он dd.ScatterGather = FALSE;... то фактически можно просто тупо записать ему физический адрес в регистр) без всей этой возни с GetScatterGatherList
« Последнее редактирование: 04-08-2010 18:10 от Ochkarik » Записан

RTFM уже хоть раз наконец!  RTFM :[ ну или хотя бы STFW...
chaika_sv
Участник

ru
Offline Offline
Пол: Мужской

« Ответ #6 : 05-08-2010 07:44 » 

Цитата
1. нет. в AddDevice еще рано инициализировать DMA.
а вот по MN_PNP_START - в принципе можно уже инициализировать.
Согласен. С этим разобрался.

С функциями KeSynchronizeExecution и KeFlushIoBuffers тоже, кажется, разобрался.

Что касается, необходимости использования SG. Вот Вы пишите:
Цитата
можно забить на SG список, выделить сразу много непрерывной памяти и радоваться...
Как я понимаю, проблема как раз в выделении большого объема непрерывной памяти. Ну т.е. можно, конечно, сделать большой буфер, и он будет логически выглядеть непрерывным, но в физической памяти он при этом может быть сильно фрагментирован. А каждый фрагмент в физической памяти - это организация отдельной дополнительной DMA-передачи, т.е. хлопотно всё это.

Цитата
реализация ReadAdapterControl явно не полная. надо? тогда вы должны по обработке первого элемента - ставить на выполнение следующий.
Буду пробовать. О результатах отпишусь.

Цитата
из какого места читать? - да как обычно... обычный ReadFile
Вот тут мне что-то совсем ничего не понятно))
В DLL у меня есть две функции: SetDMARead() и StartDMA(). Их надо запускать последовательно.
В SetDMARead() как раз делается обычный ReadFile(), но он ничего не возвращает.
В StartDMA() вызывается IOCTL_DMA_START().
После выполнения этих двух функций в BAR0 по смещению READ_ADDR_OFFSET появляется адрес (как я понял, физический) очередного элемента SG списка. Этот адрес туда записывается как раз в функцией драйвера ReadAdapterControl():
Код:
regAddr = (ULONG) pDevExt->MemoryStart[FPGA] + READ_ADDR_OFFSET;
WRITE_REGISTER_ULONG ((PULONG) regAddr, pSGL->Elements[i].Address.LowPart);
Пытаюсь в DLL обращаться по этому адресу и вижу ошибку:
Цитата
Инструкция по адресу YYY обратилась к памяти по адресу XXX. Память не может быть "read".
,
где XXX - это как раз тот самый адрес очередного элемента SG списка.

Цитата
поищите на форуме про DMA и SG список... была тема в которой было словесное описание...
Она?

Цитата
учитывая что он dd.ScatterGather = FALSE;... то фактически можно просто тупо записать ему физический адрес в регистр) без всей этой возни с GetScatterGatherList
Вот есть у меня в регистре физический адрес.. я приложением пользовательского режима (DLL или обычным приложением, не важно) могу прочитать содержимое этого регистра, т.е. могу прочитать этот адрес.
Глупый вопрос: физический адрес у меня есть.. как прочитать то, что находится по этому адресу? И чтобы не было ошибки типа:
Цитата
Инструкция по адресу YYY обратилась к памяти по адресу XXX. Память не может быть "read".
Записан
chaika_sv
Участник

ru
Offline Offline
Пол: Мужской

« Ответ #7 : 05-08-2010 12:05 » 

Стал смотреть, как реализована функция, аналогичная моей ReadAdapterControl(), в примере SCATGATH (там она называется DmaExecutionRoutine()).
Вот код этой самой DmaExecutionRoutine():
Код:
VOID DmaExecutionRoutine(PDEVICE_OBJECT fdo, PIRP junk, PSCATTER_GATHER_LIST sglist, PDEVICE_EXTENSION pdx)
{ // DmaExecutionRoutine
PIRP Irp = GetCurrentIrp(&pdx->dqReadWrite);
BOOLEAN isread = IoGetCurrentIrpStackLocation(Irp)->MajorFunction == IRP_MJ_READ;
pdx->sglist = sglist; // save for deallocation in DPC routine
pdx->isg = 0; // index of current scatter gather element
pdx->sgdone = 0; // 0 elements processed so far
pdx->busy = TRUE; // indicate device is busy

// Invoke StartNextTransfer as a synch critical section routine to begin the
// first stage of a possibly multi-stage transfer

if (!KeSynchronizeExecution(pdx->InterruptObject, (PKSYNCHRONIZE_ROUTINE) StartNextTransfer, pdx))
IoRequestDpc(fdo, NULL, NULL); // nothing to do
} // DmaExecutionRoutine

Тут всё понятно. В функции KeSynchronizeExecution() вызывается функция StartNextTransfer().
Привожу код StartNextTransfer():
Код:
BOOLEAN StartNextTransfer(PDEVICE_EXTENSION pdx)
{ // StartNextTransfer
PSCATTER_GATHER_LIST sglist = pdx->sglist;
ULONG i = pdx->isg += pdx->sgdone;
ULONG n = sglist->NumberOfElements - i; // # left to do
if (!n)
return FALSE; // request is now finished
if (n > MAXSG)
n = MAXSG; // only do as many as hardware understands
pdx->sgdone = n; // save count for next interrupt

// TODO Program the hardware to transfer data using the address/length pairs
// sglist->Elements[i].Address and .Length, for the next "n" elements

return TRUE;
} // StartNextTransfer


Ключевой момент тут:
Код:
	// TODO Program the hardware to transfer data using the address/length pairs
// sglist->Elements[i].Address and .Length, for the next "n" elements
т.е. самую основную часть нам предлагают реализовать самостоятельно...

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

PS Пример SCATGATH прикрепил на всякий случай.

* SCATGATH.rar (11.38 Кб - загружено 1016 раз.)
« Последнее редактирование: 05-08-2010 12:07 от chaika_sv » Записан
Ochkarik
Модератор

ru
Offline Offline
Пол: Мужской

« Ответ #8 : 05-08-2010 18:24 » 

1. приложение(ring-3) может работать только с виртуальными адресами. и никак иначе. и только драйвер(ring-0) может сказать какие физические адреса имеет участок памяти. на то есть специальные функции...
драйвер же может так же выделить довольно большой участок и физически непрерывной памяти - тоже функции есть. объем - в зависимости от дефрагментации памяти - как повезет. при свежей системе -  речь идет о десятках мегабайт. так что в некоторых случаях можно обойтись...

2. теперь внимательно:
обработка запроса чтения:
   case   IRP_MJ_READ:
                   ..тратата..
         status = StartDMARead(DeviceObject, Irp);
<<второй параметр что? пральна! указатель на текущий запрос IRP (со всеми его буферами)

сама функция StartDMARead:
....тратата...

   KeFlushIoBuffers(pIrp->MdlAddress,
                FALSE,                        // Write DMA (from the OS perspective)
                TRUE);                        // DMA Transfer indicated   
чей буфер мы сбрасываем? пральна! того самого IRP!

   mapRegsNeeded = ADDRESS_AND_SIZE_TO_SPAN_PAGES(MmGetMdlVirtualAddress(pIrp->MdlAddress),
                                       pDevExt->ReadTotalLength - pDevExt->ReadOffset);
размер SG для чего считаем?

   baseVa = MmGetMdlVirtualAddress(pIrp->MdlAddress + pDevExt->ReadOffset);
чей MDL  тут получают?

      status = pDevExt->pReadAdapter->DmaOperations->GetScatterGatherList(pDevExt->pReadAdapter,
                                                   pDeviceObject,
                                                   pIrp->MdlAddress,
                                                   baseVa,
                                                   pDevExt->ReadLength,
                                                   ReadAdapterControl,
                                                   pIrp,
                                                   TRUE)// transfer to device from buffer
- дальше надо? Ага

3. как я уже говорил, в приложении с физическими адресами вам делать нечего... это чисто для отладки посмотреть. над п.п.2 думайте)
Записан

RTFM уже хоть раз наконец!  RTFM :[ ну или хотя бы STFW...
chaika_sv
Участник

ru
Offline Offline
Пол: Мужской

« Ответ #9 : 06-08-2010 06:05 » 

Сейчас стал с этим делом внимательнее разбираться и сразу возник вот какой вопрос. Смотрим GetScatterGatherList() из функции StartDMARead():

Код:
      status = pDevExt->pReadAdapter->DmaOperations->GetScatterGatherList(pDevExt->pReadAdapter, 
                                                   pDeviceObject,
                                                   pIrp->MdlAddress,
                                                   baseVa,
                                                   pDevExt->ReadLength,
                                                   ReadAdapterControl,
                                                   pIrp,
                                                   TRUE)// transfer to device from buffer
           

Последний параметр называется WriteToDevice и равен TRUE.
Вопрос. Почему TRUE, а не FALSE? Ведь у нас же операция чтения и нужна пересылка из устройства в буфер!

А так спасибо за ответ, буду разбираться дальше..
Записан
chaika_sv
Участник

ru
Offline Offline
Пол: Мужской

« Ответ #10 : 06-08-2010 06:55 » 

Не удержусь, сразу же ещё вопрос задам Улыбаюсь

Стал внимательно читать Using Scatter/Gather DMA и обратил внимание на это:
Цитата
A driver can use GetScatterGatherList whether or not its device supports scatter/gather DMA. For a device that does not support scatter/gather DMA, the scatter/gather list will contain only one element.
Перевожу на русский
Цитата
Драйвер может использовать функцию GetScatterGatherList вне зависимости от того поддерживает ли устройство аппаратный scatter/gather DMA или нет. Для устройств, которые не поддерживают аппаратный scatter/gather DMA, scatter/gather list будет содержать только ОДИН элемент.
Серьёзно что ли?? Улыбаюсь
Так может быть тогда ReadAdapterControl() в моём примере нормально реализован? Ещё раз вот она вся реализация, как есть у меня в примере:

Код:
VOID
ReadAdapterControl(
  IN PDEVICE_OBJECT pDevObj,
  IN PIRP Unsed_with_Driver_Queuing,
  IN PSCATTER_GATHER_LIST pSGL,
  IN PVOID pContext)
{
PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION) pDevObj->DeviceExtension;
PIRP pIrp = (PIRP) pContext;
ULONG regAddr; // Memory-mapped Device register for the DMA transfer address

LogMsg(pDevObj, L"ReadAdapterControl: BMDMA Read using System S/G");
KdPrint(("s3_1000.sys: ReadAdapterControl - BMDMA Read using System S/G\n"));

// Save the Scatter/Gather list pointer for release later.
pDevExt->pReadSGL = pSGL;

// Program the logical transfer address into our Bus Master device.
regAddr = (ULONG) pDevExt->MemoryStart[FPGA] + READ_ADDR_OFFSET;
WRITE_REGISTER_ULONG ((PULONG) regAddr, pSGL->Elements[0].Address.LowPart);

// LIMITATION:  The driver relies on the application to provide the programming information for the device
// in one step, and then kick off the DMA engine via PIO when all operations have been programmed.  This
// means that the driver cannot know at this point if it should wait for a simultaneous write transfer to
// complete before starting the engine again to continue with the read operations.  This effectively
// restricts simultaneous read/write operations to a size that can be performed with a single set of map
// registers.  Without a redesign of the driver/application interaction, transfers requiring multiple
// DMA passes can only be Read or Write, but not both.  This restriction must be enforced by the
// application, since the driver cannot check for this scenario.
if (pDevExt->ReadOffset > 0)
{
// Maintain statistics for current transfer - storage for these needs to be added to the device
// extension.
// Perform Initiator Reset
// Release Initiator Reset
// Start DMA Engine
}

// We're done with the adapter channel object, but we must keep the map registers around for the
// device to use for the transfer.  The return code signals this intent to the I/O Manager
KdPrint(("s3_1000.sys: ReadAdapterControl completed\n"));
return;

}

Т.е. получается, вовсе не надо смотреть в цикле ВСЕ элементы S/G списка, т.к. элемент всего один pSGL->Elements[0]! Правильно я понимаю, как думаете?

Единственное, что надо тут ещё сделать (этого как раз нет в моей реализации ReadAdapterControl) - это скопировать содержимое единственного элемента S/G списка в буфер IRP-запроса, так? Адрес буфера IRP-запроса можно наверно получить так:

Код:
MmGetMdlVirtualAddress(pIrp->MdlAddress + pDevExt->ReadOffset)

Правильно? Скромно так...
Записан
Ochkarik
Модератор

ru
Offline Offline
Пол: Мужской

« Ответ #11 : 06-08-2010 19:01 » 

какой копировать?! да господи, SG список для этого буфера из IRP и создается! я уж и так намекал и так... и как только не намекал)
вы устройству, в качестве памяти КУДА ему писать/читать - подсовываете память приложения!  напрямую. pIrp->MdlAddress - это что по вашему?

насчет правильности. что такое DMA? устройству говорят куда и сколько писать. и оно пишет.
если запрос состоит из не-непрерывного куска - что делать? ваше устройство само с одного на другой - перескочить не может. значит надо заставить устройство записать эти два куска последовательно. сначала указать на первый, а когда завершит - указать  потом второй.  ReadAdapterControl() - указывает ему на первый элемент, вопрос - кто укажет на второй? понятно, что тот, кто знает когда завершилась первая транзакция. - а это известно в прерывании. там и надо стартовать следующую транзакцию.
Цитата
   // Determine the number of map registers required.  This driver currently does not support
   // a DMA transfer that exceeds the available map registers, so if that happens we treat it
   // as an error and punt the transfer. 
   // In order to handle this with a non S/G DMA device, the driver must split up
   // a single large transaction into N smaller transactions (that fit in the map register space), and
   // then program & start the DMA device once for each of the N transfers.  The driver keeps track of
   // how much data is transfered each time.

насчет TRUE - похоже на багу. во втором примере:
Код:
	BOOLEAN isread = stack->MajorFunction == IRP_MJ_READ;

NTSTATUS status = (*pdx->AdapterObject->DmaOperations->GetScatterGatherList)
(pdx->AdapterObject, fdo, mdl, vaddr, nbytes,
(PDRIVER_LIST_CONTROL) DmaExecutionRoutine, pdx, !isread);
« Последнее редактирование: 06-08-2010 19:07 от Ochkarik » Записан

RTFM уже хоть раз наконец!  RTFM :[ ну или хотя бы STFW...
chaika_sv
Участник

ru
Offline Offline
Пол: Мужской

« Ответ #12 : 09-08-2010 06:20 » 

Цитата
я уж и так намекал и так... и как только не намекал)
Не ругайтесь Скромно так... Сейчас соображу..

Ещё раз по порядку.
pIrp->MdlAddress - это указатель на структуру, которая представляет исходный (от верхнеуровневого приложения) буфер, так? Ну т.е. в конечном счёте в этом буфере всё должно оказаться.
Ключевой для меня момент. Получается, что после выполнения функции GetScatterGatherList() этот исходный буфер (pIrp->MdlAddress) должен быть уже заполненным??
Записан
Ochkarik
Модератор

ru
Offline Offline
Пол: Мужской

« Ответ #13 : 09-08-2010 09:19 » 

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

если из платы - соответственно он будет заполнен платой.
валидно содержимое будет - на момент выставления очередного прерывания (или нескольких - по размеру буфера)
Записан

RTFM уже хоть раз наконец!  RTFM :[ ну или хотя бы STFW...
chaika_sv
Участник

ru
Offline Offline
Пол: Мужской

« Ответ #14 : 09-08-2010 10:03 » 

Рассмотрим, например, операцию чтения из платы (ReadFile).
Вот я делаю GetScatterGatherList
Код:
GetScatterGatherList(pDevExt->pReadAdapter, 
pDeviceObject,
pIrp->MdlAddress,
baseVa,
pDevExt->ReadLength,
ReadAdapterControl,
pIrp,
FALSE)   //   transfer to buffer from device

После выполнения этой функции исходный буфер, на который ссылается структура MDL, оказывается заполнен инфой из платы, так?
Спрашиваю, потому что в моём случае функция ReadFile возвращает пустой буфер почему-то.. и ошибку STATUS_PENDING (хотя почему такая ошибка появляется я разобрался. Это нормально).
Записан
Ochkarik
Модератор

ru
Offline Offline
Пол: Мужской

« Ответ #15 : 09-08-2010 15:39 » 

пендинг, потому что оверлапед надо делать)
тут два варианта.. либо плата не пишет, либо вы что то неправильно сделали)))
Записан

RTFM уже хоть раз наконец!  RTFM :[ ну или хотя бы STFW...
chaika_sv
Участник

ru
Offline Offline
Пол: Мужской

« Ответ #16 : 09-08-2010 16:06 » 

Цитата
пендинг, потому что оверлапед надо делать)
Оверлапед сделан. Да и дело вовсе не в нём.

Вот кусок кода обработки IRP_MJ_READ из моего драйвера:

Код:
	
// тратататата

if(
status = pDevExt->pReadAdapter->DmaOperations->GetScatterGatherList(pDevExt->pReadAdapter,
pDeviceObject,
pIrp->MdlAddress,
baseVa,
pDevExt->ReadLength,
ReadAdapterControl,
pIrp,
FALSE)// transfer to buffer from device
!= STATUS_SUCCESS)
{
LogMsg(pDeviceObject, L"StartDMARead: (Read) GetScatterGatherList failed");
KdPrint(("s3_1000.sys: StartDMARead - GetScatterGatherList failed\n"));

pIrp->IoStatus.Status = status;
            pIrp->IoStatus.Information = 0;
           
            // Complete the request now
            IoCompleteRequest(pIrp, IO_NO_INCREMENT);
return status;
}
else
status = STATUS_PENDING;

KeLowerIrql(oldIrql);
KdPrint(("s3_1000.sys: StartDMARead completed\n"));

    return status;

Т.е. если GetScatterGatherList() завершается успешно, то обработчик IRP_MJ_READ как раз возвращает STATUS_PENDING.
А STATUS_SUCCESS присваивается уже позже в DPC.

А так уж не знаю, на что и думать. Сейчас вот пытаюсь разобраться с отладчиком, чтобы выводить строки KdPrint(). Может что увижу.
Записан
Ochkarik
Модератор

ru
Offline Offline
Пол: Мужской

« Ответ #17 : 09-08-2010 17:12 » 

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

RTFM уже хоть раз наконец!  RTFM :[ ну или хотя бы STFW...
chaika_sv
Участник

ru
Offline Offline
Пол: Мужской

« Ответ #18 : 09-08-2010 18:32 » 

Ну как работал.. к нему прилагалась dll и приложение на VB для тестирования.
Приложение очень простое: задаёшь два параметра (кол-во TLP-пакетов и их размер) и нажимаешь кнопку Старт, после чего что-то там жужжало и приложение говорило, что скорость чтения/записи N Мбит/с. Ну т.е. вроде как работало. Стал разбираться, как сделать, чтобы что-то реальное записать/прочитать и вот упёрся:(
Записан
Ochkarik
Модератор

ru
Offline Offline
Пол: Мужской

« Ответ #19 : 09-08-2010 18:43 » 

стоп, тогда перед тем как разбираться, попробуйте эту самую dll и sys скомпилить... и чтобы результат был аналогичным)
а так ошибки могут быть в любом месте...
Записан

RTFM уже хоть раз наконец!  RTFM :[ ну или хотя бы STFW...
chaika_sv
Участник

ru
Offline Offline
Пол: Мужской

« Ответ #20 : 11-08-2010 05:18 » 

Да я и в dll разобрался вполне. Менял её, перекомпилировал.
ReadFile, например, вызывается как раз в dll, и я прямо из этой dll вывожу в файл содержимое исходного буфера.

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

В любом случае, спасибо большое за помощь.
« Последнее редактирование: 11-08-2010 05:20 от chaika_sv » Записан
chaika_sv
Участник

ru
Offline Offline
Пол: Мужской

« Ответ #21 : 12-08-2010 05:51 » 

И снова я:)

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

Устройство работает только с регистрами в BAR0. Может читать и устанавливать значения этих регистров.
Т.е., например, об инициации процедуры DMA-чтения устройство узнаёт, когда видит что изменился соответствующий статусный регистр в BAR0.
И если надо прочитать инфу из устройства, то устройство начинает на каждом такте своей работы заполнять один из регистров в BAR0 новым куском (32 бита) считываемых данных.

Интересно и как это вяжется с DMA-передачей?.. вопросов ещё прибавилось Здесь была моя ладья...
Записан
Ochkarik
Модератор

ru
Offline Offline
Пол: Мужской

« Ответ #22 : 12-08-2010 15:28 » 

И снова я:)
И если надо прочитать инфу из устройства, то устройство начинает на каждом такте своей работы заполнять один из регистров в BAR0 новым куском (32 бита) считываемых данных.
что то странное... DMA это что значит - устройство имеет возможность записи данных непосредственно в физическую память ПК.
собственно существуют грубо четыре вида ресурсов (для x86):
1. порты ввода/вывода
2. прерывания
3. отображаемая память (физически память находится на плате, но она проецируется в адресное пространство ПК)
4. прямой доступ к памяти (DMA) - я уже написал.

все эти ресурсы так или иначе через BAR-регистры проходят... но объяснение вашего железячника - какое то странноватое... может у вас как раз  отображаемая память стоит? но насколько я помню это не DMA .... я правда с ней очень давно работал, и  под win95, поэтому плохо помню.

PS а драйвер то вы пробовали перекомпилировать? это именно он с той библиотекой в комплекте идет?
PPS до кучи вот тут посмотрите, мельком глянул -  вроде толковая дискуссия была)
 Жжешь это оказывается автор тоже вы)))))))))))))
« Последнее редактирование: 12-08-2010 15:37 от Ochkarik » Записан

RTFM уже хоть раз наконец!  RTFM :[ ну или хотя бы STFW...
chaika_sv
Участник

ru
Offline Offline
Пол: Мужской

« Ответ #23 : 13-08-2010 09:11 » 

Цитата
может у вас как раз  отображаемая память стоит?
Ну в общем-то так и есть. Как я понимаю, у меня отображаемая память, которая SRAM.
Место и объём этой области памяти описаны в регистре BAR0.
Хоть я и говорю, что в окне памяти BAR0 у меня находятся регистры, через которые вся работа строится, на самом деле, это не регистры в прямом смысле этого слова:) Скажем так, это ячейки в отображаемой памяти, которые я называю регистрами. Например, и я, и ПЛИС знаем, что по смещению +1C в окне памяти BAR0 находится размер TLP-пакета. Ну вот я и называю ячейку по смещению +1C "регистром размера TLP-пакета".
Как Вы правильно заметили, с DMA всё это дело никоим образом не связано.

Поэтому я тоже не понял объяснений моего железячника и склоняюсь к мысли, что он сам не особо разобрался в вопросе. Дело в том, что прошивка устройства, которую мы сейчас используем, написана не им, а взята из примера. Он в ней наверно особо и не разбирался (надеюсь, он этот форум не читает  Краснею ). Думаю, что устройство действительно на каждом такте своей работы выдаёт 4 байта данных. Эти данные, с одной стороны, помещаются в элемент S/G-списка, а с другой (то, о чём он говорил) - помещаются в "регистр" окна BAR0 (наверно это делается чисто для отладки какой-нибудь).

Цитата
PS а драйвер то вы пробовали перекомпилировать? это именно он с той библиотекой в комплекте идет?
Да, в одном комплекте были: прошивка для устройства, драйвер, dll и приложение верхнего уровня.
Драйвер я уже неоднократно перекомпилировал (например, чтобы выводить отладочные строки).

Цитата
PPS до кучи вот тут посмотрите, мельком глянул -  вроде толковая дискуссия была)
  это оказывается автор тоже вы)))))))))))))
:yes:похоже я со своими проблемами весь интернет на уши поднял Скромно так...
Записан
Ochkarik
Модератор

ru
Offline Offline
Пол: Мужской

« Ответ #24 : 13-08-2010 19:14 » 

а если у вас отображаемая память тогда я уже совсем ничего не понимаю... причем тут вообще ScatterGatherList?Не понял
и при чем тут прерывание??? это ересь какая то.... берете эту память и тупо мапируете приложению MmMapLockedPagesSpecifyCache.

ааа!!!  глянул описание корки в первом сообщении....
 кажется начинаю понимать... речь шла не о DMA ПК а о том что в виртексе реализован свой DMA-мастер контроллер?! и что его можно запрограммировать на пересылку данных из своей SRAM по заданному адресу, в том числе если этот адрес будет физически находится даже не в памяти ПК, а например, на соседней PCIe плате... так наверное? только тогда это действительно DMA режим.... и тогда то, что там стоит SRAM - абсолютно не важно. вместо нее может быть просто блок который выдает просто данные.
а отображаемая память - она никак мастером быть не может. при обращении к ней из ПК - сам ПК является инициатором чтения/записи, и только так.

посмотрите что за ресурсы и сколько плата(драйвер) занимает в диспечере устройств?
« Последнее редактирование: 13-08-2010 19:23 от Ochkarik » Записан

RTFM уже хоть раз наконец!  RTFM :[ ну или хотя бы STFW...
chaika_sv
Участник

ru
Offline Offline
Пол: Мужской

« Ответ #25 : 23-08-2010 10:44 » 

Прошу прощения, что так долго ответа не было от меня.

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

В общем, чтение оказалось записью, а запись - чтением. Давненько уже в этой теме обращал внимание на это
Сейчас стал с этим делом внимательнее разбираться и сразу возник вот какой вопрос. Смотрим GetScatterGatherList() из функции StartDMARead():

Код:
      status = pDevExt->pReadAdapter->DmaOperations->GetScatterGatherList(pDevExt->pReadAdapter, 
                                                   pDeviceObject,
                                                   pIrp->MdlAddress,
                                                   baseVa,
                                                   pDevExt->ReadLength,
                                                   ReadAdapterControl,
                                                   pIrp,
                                                   TRUE)// transfer to device from buffer
           

Последний параметр называется WriteToDevice и равен TRUE.
Вопрос. Почему TRUE, а не FALSE? Ведь у нас же операция чтения и нужна пересылка из устройства в буфер!

А так спасибо за ответ, буду разбираться дальше..

Тогда решил, что это баг. Оказалось, не баг.
Чтобы прочитать данные из устройства, надо из программы верхнего уровня (dll, например) вызывать WriteFile(), и он в буфере всё вернёт.

Такие дела. Спасибо за помощь!
Записан
Ochkarik
Модератор

ru
Offline Offline
Пол: Мужской

« Ответ #26 : 23-08-2010 11:07 » 

ну... сказать честно, у меня тоже в последний раз такое впечатление сложилось))) особенно когда я на описание корки взглянул)
Записан

RTFM уже хоть раз наконец!  RTFM :[ ну или хотя бы STFW...
Страниц: [1]   Вверх
  Печать  
 

Powered by SMF 1.1.21 | SMF © 2015, Simple Machines