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

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

ru
Offline Offline

« : 17-06-2010 05:40 » 

Решаю всю ту же проблему, что была обозначена в конце темы https://forum.shelek.ru/index.php/topic,24622.0.html. А именно, мой USB-девайс при работе с драйвером, созданным в DS 3.2 пропускает данные на медленных машинах (PIV) и не пропускает данные на машине Core 2 Duo, при условии, если не запущены приложения, требующие значительных ресурсов (Opera). Отсюда сделал вывод, что пропуски могут проходить на уровне драйвера. Девайс работает следующим образом: подготовив данные для передачи по USB (заполнив FIFO UDP-модуля контроллера, отвечающего за работу с USB), ставит флаг готовности для передачи пакета данных размером 60 байт по USB. Далее ожидает, пока придет запрос от Хоста и уже после этого начинает передачу данных. Запросы формирует драйвер, который в свою очередь принимает их от приложения. Если я правильно понял, то в медленных или загруженных другими приложениями эти запросы могут задерживаться и в случае маленького буфера для запросов - теряться. Таким образом, какой смысл делать большой буфер для принимаемых драйвером от девайса данных, если при этом запросы на считывание этих данных приложением будут поступать с той же  частотой или реже?
Что вы посоветуете для решения подобной проблемы?
Заранее благодарен!
Записан
resource
Молодой специалист

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

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

Цитата: Bulat
не пропускает данные на машине Core 2 Duo, при условии, если не запущены приложения, требующие значительных ресурсов (Opera). Отсюда сделал вывод, что пропуски могут проходить на уровне драйвера.

Не могу понять логическую цепь, которая приводит к такому выводу.

Цитата: Bulat
в медленных или загруженных другими приложениями эти запросы могут задерживаться и в случае маленького буфера для запросов - теряться

Теряться не могут. Что касается задержек, то если драйвер работает на уровне меньше DISPATCH_LEVEL, то поток, в контексте которого выполняется код драйвера, ничем не лучше и не хуже любых других потоков.
Записан
Bulat
Участник

ru
Offline Offline

« Ответ #2 : 17-06-2010 09:02 » 

Цитата
Не могу понять логическую цепь, которая приводит к такому выводу.
Потому что само приложение простое - в одном потоке считывает данные из драйвера, в другом записывает в статический массив. С одним и тем же драйвером проверял на двух девайсах, одинаковых по функциональности,но реализованных на разных контроллерах, ситуация одинаковая - оба пропускают на машинах типа  PIV и не пропускают на Core 2Duo. Отсюда, методом исключения сделал вывод, что во всем виноват драйвер Улыбаюсь
Цитата
Теряться не могут. Что касается задержек, то если драйвер работает на уровне меньше DISPATCH_LEVEL, то поток, в контексте которого выполняется код драйвера, ничем не лучше и не хуже любых других потоков.
А где приоритет работы драйвера устанавливается?
Записан
resource
Молодой специалист

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

« Ответ #3 : 17-06-2010 11:45 » 

А что такое приоритет драйвера?

Есть серьезное подозрение, что "приоритет драйвера" тут вообще не при чем. Вот кривонаписанный драйвер или юзермодная часть, это запросто.
Записан
Bulat
Участник

ru
Offline Offline

« Ответ #4 : 18-06-2010 03:55 » 

1.Драйвер собиралв DS 3.2. Никаких своих изменений не вносил. Пошаговая сборка приведена в прикрепленном архиве.
2.В приложении я просто в отдельных потоках выполняю readfile и writefile в синхронном режиме:
Код:
UINT TransmitThread( LPVOID pParam )
{
    ...
    while(update == 0)
    {
        //считывание данных из статического массива в buf_w
        ...
        Success = WriteFile(PipeOut, &buf_w, 60, &nBytes, NULL);
        ...
     }
     return 0;   // thread completed successfully
     AfxEndThread(0, TRUE);
}
UINT ReceiveThread( LPVOID pParam )
{
    ....
    while(stop_r == 0)
    {
         ...
         RealRead = 0;
         ReadFile(PipeIn, &buf_r, 60, &RealRead, NULL);
         //запись данных в статический массива из buf_r
         ...
    }
    return 0;
    AfxEndThread(0, TRUE);
}

* скриншоты.zip (343.15 Кб - загружено 1052 раз.)
Записан
Ochkarik
Модератор

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

« Ответ #5 : 20-06-2010 09:46 » 

тут кстати не факт что драйвер виноват...
к сожалению не знаю как для USB реализована передача пакетов, но для 1394 было так.
1. устройство накапливает пакет, и тут же его выплевывает в шину и ждет подтверждения приема.
2. ПК автоматически принимает этот пакет в ISR (видимо), разбирает его, и ставит в очередь DPC драйверу. а так же генерит второй "системный" DPC, для отправки устройству пакета подтверждения приема.
3. драйвер обрабатывает DPC.
4. устройство получает подтверждение и только после этого!!! освобождает FIFO отправленного пакета, и может отправить следующий пакет(но тут возможны варианты, зависит от алгоритма железа)
5. в некоторых случаях в устройстве стоит FIFO на  несколько пакетов, чтобы повысить пропускную способность на малом отрезке времени. в противном случае -  нагрузка на fifo становится импульсной, что может привести к его переполнению.

для 1394 существовал второй режим, когда подтверждения приема должен был отправить пользовательский драйвер.
1. устройство накапливает пакет, и тут же его выплевывает в шину и ждет подтверждения приема.
2. ПК автоматически принимает этот пакет в ISR (видимо), разбирает его, и ставит в очередь DPC драйверу. но НЕ генерит второй "системный" DPC, для отправки устройству пакета подтверждения приема.
3. драйвер обрабатывает DPC, в котором он обязан отослать пакет подтверждения (или то что он сочтет нужным)
4. устройство получает подтверждение и только после этого!!! освобождает FIFO отправленного пакета.
второй способ позволил мне избавиться от удвоения количества DPC на один переданный пакет, и немного уменьшить импульсную нагрузку на аппаратное FIFO.
в обоих случаях - скорость может быть ограничена большим временем отклика на принятый пакет.
кроме того выяснилось, что кривой драйвер CD-ROM на некоторых ПК тормозит очередь DPC на единицы  мс.

в вашем случае, возможно, проблема именно в размере FIFO самого устройства.
на медленных и загруженных ПК, комп может обрабатывает очередь DPC с задержками. из за этого может падает скорость.

теперь неплохо бы подтвердить эту идею... я к сожалению, каким то макаром, отключил счетчики производителсьности на своей машине. так что попробуйте посмотреть у себя.
панель управления-> администрирование-> счетчики производительности:
добавьте отслеживание количества DPC и соотнесите со скоростью вашего потока. если я прав то количество вызовов  будет соответствовать скорости потока деленной на 60 байт.

PS это я по аналогии с 1394 предлагаю сделать, но я не уверен что реализация USB сделана именнно так(времени разобраться - у меня совсем нема)
если кто-то знает точнее  - опровергайте!
Записан

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

ru
Offline Offline

« Ответ #6 : 16-08-2010 11:26 » 

Здравствуйте) По производственным причинам пришлось отложить этот вопрос. Извиняюь, что спустя такое время, опять его поднимаю, но он все еще остается для меня открытым.
По вашей рекомендации посмотрел счетчики производительности. Прикрепляю 2 скриншота. На изображении "Core2.jpg" приведен график, который снят на машине Core2Duo c 1Гб ОЗУ. То есть на графике отображен тот момент времени, когда мой девайс активно передавал принимал данные от ПК по USB. На картинке же "PIV_1,8HGz.jpg" приведен  график, снятый соответственно на машине PIV с 256 Мб ОЗУ. Видно, что характеристика "% времени DPC" имеет периодические всплески на машине PIV, в отличие от Core2. Именно на машине PIV моя тестовая программа для ПК фиксирует пропуски данных, принимаемых от девайса (возможно и передаваемых на девайс тоже), а на Core2 пропусков нет. Девайс работает с интерфейсом Arinc, со скоростью приема и передачи данных 100 кбит/с. Размер одного слова 4 байта. Для передачи по USB девайс разбивает принятое по Аринку слово на байты и формирует пакеты по 60 байт, затем отправляет их на ПК.


* Core2 (141.99 Кб - загружено 3165 раз.)
* PIV_1,8HGz (114.48 Кб - загружено 3071 раз.)
« Последнее редактирование: 04-07-2011 13:28 от Ochkarik » Записан
Ochkarik
Модератор

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

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

у вас на скриншотах самый важный параметр "поставлено в очередь DPC" зашкалил.
отмасштабируйте плс) он должен строго соответствовать скорости поступления ваших USB пакетов:100кбит/60 байт=208 DPC/сек. возможно в этом параметре будут различия... хотя, там DPC еще системные будут...
в любом случае любопытно взглянуть.

да, и введите контроль поступления прямо в драйвере - необходимо отловить кто виноват: драйвер или приложение.
« Последнее редактирование: 16-08-2010 12:51 от Ochkarik » Записан

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

ru
Offline Offline

« Ответ #8 : 17-08-2010 05:07 » 

Прикладываю отмаштабированные графики под сетчик "поставлено в очередь DPC/сек", а также график с системными DPC. И еще, мой девайс принимает из Аринк одновременно по 2-м каналам, каждый по 100 кбит/с, то есть реальный входной поток в по USB ПК составляет 200 кбит/с. Но всеравно  счетчик поставленных в очередь DPC слишком велик, причем на машине Core2 он больше. Напомню, что в процессе эксперимента на машине Core2 пропуски принимаемых от девайса данных не зафиксированы, а на PIV их 10-ки тысяч (за 1-2 мин наблюдений). При этом счетчики скорости записи и чтения данных  на моем тестовом приложении падают. Например, если скорость записи на мой девайс на машине core2 составляет ~14 кбайт/с, то на PIV около 12 кбайт/с, соответственно и скорость чтения из девайса 28 и 24 кбайт/с.
На графике, снятом на PIV наблюдаются значительные всплески в процессе работы (кроме тестового приложения и счетчика производительности ничего не запущено).
А как ввести контроль поступления прямо в драйвер?



* Core2_1.JPG (106.93 Кб - загружено 3226 раз.)
* PIV_1,8HGz_1.JPG (84.51 Кб - загружено 2883 раз.)
* Системный_Сore2.JPG (98.04 Кб - загружено 3155 раз.)
« Последнее редактирование: 04-07-2011 13:29 от Ochkarik » Записан
Ochkarik
Модератор

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

« Ответ #9 : 17-08-2010 10:19 » 

ну что сказать.. явно на второй машине кто то много времени в DPC занимает. возможно чей то кривой драйвер...
хм... а попробуйте лишнее оборудование отключить в дереве устройств. CD-ром например... что там еще не нужного

контроль... ну точно так же как в приложении. у вас очередность пакетов как-то помечается?
Записан

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

ru
Offline Offline

« Ответ #10 : 17-08-2010 10:50 » 

так ведь драйвер одинаковый на обеих машинах.
Цитата
контроль... ну точно так же как в приложении.
Это вы имеете в виду выбрать Объект, при выборе счетчика? Там у меня в качестве объекта выбран "Процессор".
Цитата
у вас очередность пакетов как-то помечается?
Не совсем понял где они должны помечаться?)

У меня в девайсе при приеме данных от ПК используется две страницы FIFO буфера, то есть пока разбирается первый пакет в 60 байт, второй может приниматься по USB от ПК. А при передаче на ПК я пользуюсь одной страницей. То есть, заполняю FIFO, затем ставлю флаг на отправку и пока ПК не считает данные из FIFO моего девайса, я туда больше ничего записать не могу. Я рассчитываю на то, что скорость передачи данных по USB гораздо выше 100 кбит/с, поэтому ПК должен успеть считать все 60 байт и отправить подтверждение, пока девайс принимает по Аринку следующее 32-битное слово. Так как, 32-битное слово с 40 мкс паузой по каналу Аринк девайс принимает за 360 мкс. За эти 360 мкс по USB 2.0 можно переслать 4320 бит (12 Мбит/с), а реально нужно успеть отправить лишь 60 байт (480 бит).
Записан
Ochkarik
Модератор

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

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

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

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

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

ru
Offline Offline

« Ответ #12 : 18-08-2010 03:35 » 

меня смущает то, что на медленных машинах скорость записи на мой девайс ( PIV) около 12 кбайт/с, что на 2 кбайт/с ниже чем на Core2. То есть, "слабый" ПК посылает по USB с уже заниженной скоростью, поэтому я и смотрю в сторону драйвера...
Записан
Ochkarik
Модератор

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

« Ответ #13 : 19-08-2010 10:20 » new

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

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

ru
Offline Offline

« Ответ #14 : 26-08-2010 04:34 » 

Почему Driver studio не дает возможность использовать буферы данных для конечных точек при обработке запросов IRP_MJ_READ и IRP_MJ_WRITE и при попытке установки Bufferd method выдает сообщение о том, что только Direct method можно использовать с конечными точками (см. скриншот в прикрепленном файле)? Ведь мне как раз и необходимо буферизовать данные.

* DS.JPG (59.58 Кб - загружено 1128 раз.)
Записан
Ochkarik
Модератор

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

« Ответ #15 : 26-08-2010 11:47 » 

угу. тока это не совсем тот буфер)
это просто промежуточная операция копирования входных/выходных данных. с точки зрения быстродействия - ничего особо не изменится.
это НЕ FIFO!
размер буфера в точности равен размеру max(входных, выходных) данных IOCTL.
Записан

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

ru
Offline Offline

« Ответ #16 : 09-09-2010 10:46 » 

   Я еще раз хотел бы расписать все положения моего вопроса и, таким образом, корректнее перейти к интересующим меня вопросам.
Мое USB-устройство низкоскоростное (100 кбит/с - скорость приема и передачи). В самом устройстве буферы маленькие и нет возможности реализовать больший буфер. Поэтому для синхронизации с высокоскоростным протоколом USB нужно сформировать буфер данных на прием и передачу в драйвере устройства (подобный тому, что есть в драйвере USB-устройства, имитирующего com-порт).
У меня имеется драйвер, сгенрированный DS 3.2, c Direct методом записи и чтения. 
   Считывание данных из устройства в конечную точку Pipe0 осуществляется следующим образом:
Код:
NTSTATUS A5M2Pipe0Io(
    IN  PA5M2_DEVICE_EXTENSION   DeviceExtension,
    IN  PIRP                                Irp,
    IN  PUSBD_PIPE_INFORMATION              PipeInformation
    )
{
    PA5M2_IO_CONTEXT ioContext;
    NTSTATUS                    status;
    PUCHAR                      virtualAddress;
    ULONG                       totalLength;
    ULONG                       transferLength;
    PMDL                        mdl;
    PURB                        urb;
    PIO_STACK_LOCATION          irpStack;

    A5M2DebugPrint(DBG_IO, DBG_TRACE, __FUNCTION__"++. IRP %p", Irp);

    // we will use do{}while(FALSE); structure
    // for resources cleanup
    status = STATUS_SUCCESS;
    ioContext = NULL;
    mdl = NULL;

    do
    {
        Irp->IoStatus.Information = 0;

        // allocate io context to pass to completion routine
        ioContext = (PA5M2_IO_CONTEXT)ExAllocatePoolWithTag(
                                                    NonPagedPool,
                                                    sizeof(A5M2_IO_CONTEXT),
                                                    A5M2_POOL_TAG
                                                    );

        if (ioContext == NULL)
        {
            status = STATUS_INSUFFICIENT_RESOURCES;
            break;
        }

        if (Irp->MdlAddress != NULL)
        {
            totalLength = MmGetMdlByteCount(Irp->MdlAddress);
        }
        else
        {
            totalLength = 0;
        }

        if (totalLength == 0)
        {
            status = STATUS_SUCCESS;
            break;
        }

        virtualAddress = (PUCHAR)MmGetMdlVirtualAddress(Irp->MdlAddress);

        if (totalLength > USBD_DEFAULT_MAXIMUM_TRANSFER_SIZE)
        {
            transferLength = USBD_DEFAULT_MAXIMUM_TRANSFER_SIZE;
        }
        else
        {
            transferLength = totalLength;
        }

        mdl = IoAllocateMdl(virtualAddress, totalLength, FALSE, FALSE, NULL);
        if (mdl == NULL)
        {
            status = STATUS_INSUFFICIENT_RESOURCES;
            break;
        }

        IoBuildPartialMdl(Irp->MdlAddress, mdl, virtualAddress, transferLength);

        urb = (PURB)ExAllocatePoolWithTag(
                        NonPagedPool,
                        sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER),
                        A5M2_POOL_TAG
                        );

        if (urb == NULL)
        {
            status = STATUS_INSUFFICIENT_RESOURCES;
            break;
        }

        UsbBuildInterruptOrBulkTransferRequest(
            urb,
            sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER),
            PipeInformation->PipeHandle,
            NULL,
            mdl,
            transferLength,
            USBD_TRANSFER_DIRECTION_IN | USBD_SHORT_TRANSFER_OK,
            NULL
            );
...

То есть, создается mdl и туда записываются данные принятые от устройства. Если мне вводить свой буфер, тогда функция UsbBuildInterruptOrBulkTransferRequest  будет выглядеть следующим образом:

Код:
            UsbBuildInterruptOrBulkTransferRequest(
            urb,
            sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER),
            PipeInformation->PipeHandle,
            MyBuffer,                                                              //буфер для хранения принятых данных                                                                             
            NULL,
            transferLength,
            USBD_TRANSFER_DIRECTION_IN | USBD_SHORT_TRANSFER_OK,
            NULL
            );
   Так вот, объем данных, который будет записан в MyBuffer будет равен transferLength, то есть равен объему данных, запрашиваемых приложением. Мне же необходимо, чтобы считывание данных из устройства проходило постоянно, независимо от прихода запросов от приложения и , чтобы эти данные накапливались в MyBuffer, а при приходе запроса от приложения часть данных определяемая  transferLength копировалась бы в созданную ранее mdl.
   Насколько я понимаю, функция A5M2Pipe0Io вызывается при каждом запросе на чтение, пришедшем  от приложения. Получается для моего случая нужно писать отдельную процедуру, которая многократно выполняет функцию UsbBuildInterruptOrBulkTransferRequest и производит запись в MyBuffer, а  A5M2Pipe0Io будет переписывать данные из MyBuffer в mdl, но без выполнения функции UsbBuildInterruptOrBulkTransferRequest?
Записан
Ochkarik
Модератор

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

« Ответ #17 : 09-09-2010 11:39 » 

да, все верно. направление ввода DIRECTION только не перепутайте) это может быть отдельная нитка в драйвере.

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

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

ru
Offline Offline

« Ответ #18 : 13-09-2010 10:03 » 

Начал реализовывать, описанное выше, в коде и столкнулся с рядом вопросов.
1.Где инициализировать и запускать процедуру, которая будет непрерывно считывать данные из девайса в буфер?
Я решил проинициализировать ее в заголовочном файле А5М2.h, в котором у меня проинициализированы все  точки входа в драйвер.
Код:
VOID A5M2RcvData(
     IN  PA5M2_DEVICE_EXTENSION   DeviceExtension,
     IN  PIRP                                Irp,
     IN  PUSBD_PIPE_INFORMATION              PipeInformation
Запускаю же в точке входа A5M2AddDevice
Код:
A5M2RcvData(
IoContext->DeviceExtension,
IoContext->Irp,
IoContext->PipeInformation
);
2. Где создать буфер, в котором будут накапливаться принятые от девайса данные?
Решил проинициализировать его также в заголовочном файле А5М2.h в структуре
Код:
_A5M2_DEVICE_EXTENSION { ...
PVOID MyBuffer[1000];}

3. Как осуществить непрерывный прием данных из девайса в буфер?
Функцию A5M2RcvData разместил в файле usb.c, там же, где и функция A5M2Pipe0Io.
Код:
//++++++++++++++++++Функция непрерывного приема данных из девайса++++++++++++
VOID A5M2RcvData(
     IN  PA5M2_DEVICE_EXTENSION   DeviceExtension,
     IN  PIRP                                Irp,
     IN  PUSBD_PIPE_INFORMATION              PipeInformation
    )
{
    PA5M2_IO_CONTEXT ioContext;
    ULONG                       transferLength;
    PURB                        urb;

transferLength = 60;

urb = (PURB)ExAllocatePoolWithTag(
                        NonPagedPool,
                        sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER),
                        A5M2_POOL_TAG
                        );

if (urb == NULL)
              {
                 status = STATUS_INSUFFICIENT_RESOURCES;
                 break;
              }


                  UsbBuildInterruptOrBulkTransferRequest(
                  urb,
                  sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER),
                  PipeInformation->PipeHandle,
                  &deviceExtension->MyBuffer,
                  NULL,
                  transferLength,
                  USBD_TRANSFER_DIRECTION_IN | USBD_SHORT_TRANSFER_OK,
                  NULL
                  );

    // save io parameters in our io context
                  ioContext->DeviceExtension = DeviceExtension;
                  ioContext->Urb = urb;

    return;
}
А как ее непрерывно вызывать? не в поток же ставить с циклом while(1)..

4. Как переписать данные из MyBuffer в mdl, который был создан в A5M2Pipe0Io? В предыдущем посте я приводил код A5M2Pipe0Io и там принятые от девайса данные писались в mdl с помощью функции UsbBuildInterruptOrBulkTransferRequest. А как переписывать в моем случае?

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

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

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

1. там где вам удобно) я не совсем понял при чем тут вообще h-файл?)
вы перед программированием сначала продумайте, как оно у вас в теории работать должно. по шагам. или в виде блок схемы. и только потом задавайте себе вопросы. если вы правильно будете формулировать вопрос - в нем будет содержаться процентов 80 необходимого вам ответа.

2.
_A5M2_DEVICE_EXTENSION { ...
PVOID    MyBuffer[1000];}
-это не инициализация а объявление переменной, чтоб вы знали.

3. вы так не шутите))) оно конечно while(1) в конечном счете будет, но вы сначала подймайте
еще раз посмотрите на алгоритм и код которым принимается ОДИН пакет. представьте его целиком в одной процедуре.
не забудьте про возможности асинхронного приема. и еще раз подумайте)

4. для копирования данных обычно используют memcpy() Ага

PS пардон за уклончивые ответы, но  намеки гораздо полезнее)

Записан

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

ru
Offline Offline

« Ответ #20 : 16-09-2010 09:55 » 

1.
Цитата
3. вы так не шутите))) оно конечно while(1) в конечном счете будет, но вы сначала подймайте
еще раз посмотрите на алгоритм и код которым принимается ОДИН пакет. представьте его целиком в одной процедуре.
не забудьте про возможности асинхронного приема. и еще раз подумайте)

Приходит запрос от приложения IRP_MJ_READ, вызывается обработчик A5M2ReadDispatch, в котором пришедший IRP ставится в очередь. Затем с помощью функции A5M2ReadQueueStartIo достается из очереди этот запрос и помещается в функцию  A5M2Pipe0Io, которая в свою очередь считывает данные из конечной точки в mdl-буфер. И так все операции с драйвером происодят через процедуры, вызов которых инициируется IRP запрсами. А моя функция должна работать непрерывно. Инициализироваться она может при первом запросе IRP_MJ_READ, но потом ей же нужно непрерывно опрашивать конечную точку. В пользовательских приложениях такая задача обычно решается с помощью потока. А в моем случае поток нельзя использовать?

2.
Цитата
для копирования данных обычно используют memcpy()
Не совсем понятно как пользоваться этой функцией для mdl-буфера. memcpy(PMDL mdl, PVOID MyBuffer, length)? Простите за такие вопросы, просто первый раз с mdl-сталкиваюсь.

Цитата
PS пардон за уклончивые ответы, но  намеки гораздо полезнее)
Я понима Улыбаюсь Спасибо большое за помощь!
Записан
jur
Помогающий

lt
Offline Offline

« Ответ #21 : 16-09-2010 10:34 » 

3. Как осуществить непрерывный прием данных из девайса в буфер?
Функцию A5M2RcvData разместил в файле usb.c, там же, где и функция A5M2Pipe0Io.
А как ее непрерывно вызывать? не в поток же ставить с циклом while(1)..

По-моему тут без вариантов:

1. Сделать пользовательский поток для приема поступающих данных.
2. В нем ожидать 2-х ивентов:
    - прекращение работы и выход из потока;
    - получение очередного пакета из устройства;
3. Принятые пакеты буферизировать, откуда основное приложение сможет их прочитать (не забыть про закрытые секции записи/считывания пакетов в/из буфера).

Тут требуется, чтобы драйвер устанавливал ивент по приему пакета, как это делает, к примеру, драйвер CyUSB.sys фирмы Cypress (я работаю с чипом CY7C68013A). А далее основное приложение может забирать принятые пакеты когда захочет.

P.S. Даже на дохлейшей enbedded-материнке с процессором класса P-III достигается скорость передачи по USB 2.0 порядка 35 МБайт/сек. Правда, при этом процессор забивается конкретно. На меньших скоростях проблем вовсе нет.
Записан

MPEG-4 - в массы!
Ochkarik
Модератор

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

« Ответ #22 : 16-09-2010 20:19 » 

вариантов на самом деле гораздо больше. главное найти оптимальный.
1. постановка вопроса, что нужно реализовать: непрерывный опрос конечной точки.
что для этого нужно?
 как только завершится первый запрос - немедленно ставить второй.
ранее - это выполняло приложение: посылало запрос драйверу, тот ставил запрос в очередь, приходил пакет от устройства, запрос удовлетворялся, управление возвращалось драйверу(фактически драйвер ждал  WFSO WaiteForSingleObject), драйвер завершал запрос запрос приложения, приложение получало управление и все повторялось заново.
основные накладные расходы - на каждой итерации ожидание события.

как избежать: исключить из очереди эти два ожидания.
    как исключить - например осуществить очередь на уровне драйвера USB - ставить в очередь USB запросов - сразу несколько. и обеспечить непрерывное поддержание нескольких необработанных запросов.

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

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

PS создание потока в драйвере - PsCreateSystemThread().
еще посмотрите в сторону IoQueueWorkItem  - можно попробовать использовать их.
обязательно разберитесь, что такое DPC процедуры и зачем они нужны.
и самое важное - посмотрите как реализуются асинхронные процедуры в драйвере и как они работают. посмотрите как реализована синхронная процедура ожидания пакета от USB драйвера. нумега обычно генерит функцию синхронного вызова - посмотрите как она реализована. может быть это натолкнет вас на разные идеи)

 jur, как видите варианты все же есть) ну по крайней мере мне так кажется)
Записан

RTFM уже хоть раз наконец!  RTFM :[ ну или хотя бы STFW...
jur
Помогающий

lt
Offline Offline

« Ответ #23 : 17-09-2010 07:28 » 

Довольно интересная дискуссия, не так ли? :-)

вариантов на самом деле гораздо больше. главное найти оптимальный.

Безусловно! Только я продолжаю думать, что оптимальный вариант (точнее, алгоритм) - один :-) Сейчас поясню, что я имею ввиду.

1. постановка вопроса, что нужно реализовать: непрерывный опрос конечной точки.
что для этого нужно?
 как-только завершится первый запрос - немедленно ставить второй.
ранее - это выполняло приложение: посылало запрос драйверу, тот ставил запрос в очередь, приходил пакет от устройства, запрос удовлетворялся, управление возвращалось драйверу(фактически драйвер ждал  WFSO WaiteForSingleObject), драйвер завершал запрос запрос приложения, приложение получало управление и все повторялось заново.
основные накладные расходы - на каждой итерации ожидание события.

Вот это оно и есть. Как ни крути, а без отдельного потока не обойтись. И вот почему.

Приложение выполняется, делает какую-то работу, наверное, обслуживает интерфейс пользователя, выполняет какую-то обработку данных, вывод результатов и т.п. Таким образом получается, что висеть и ждать очередного пакета, видимо, не стоит (ведь при этом все приложение остановится). Значит имеет смысл просто спрашивать у потока приема пакетов: "Эй, друг! Пакеты есть? Нет? Ну ладно, зайду в другой раз.". Если обработка завершилась, а новых пакетов еще нет, то можно и WFSO, или прицепиться к системному таймеру, или еще что-то, не нагружающее процессор.

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

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

Совершенно верно. Именно так я и поступаю. У меня по USB поступают пакеты размером 512 байт. Период их поступления варьируется от 40 до 350 микросекунд. Т.к. скорость поступления пакетов достаточно высока (имею ввиду Win XP, которая отнюдь не ОС РВ), я обращаюсь к драйверу с запросом сразу нескольких пакетов в пачке. Эмпирическим путем я установил, что загрузка процессора разко падает, когда число пакетов в пачке возрастает от 1 до 8. Дальнейшее падение загрузки заметно замедляется, поэтому я остановился на 8 пакетах. И еще важный момент: в потоке приема я запускаю сразу несколько запросов к драйверу (круговая очередь на 4 запроса). Пока принимаю одну порцию - драйвер USB нижнего уровня получает следующую.

jur, как видите варианты все же есть) ну по крайней мере мне так кажется)

:-)

P.S. Да, забыл сказать. При таком алгоритме действий, какой я применяю, с драйвером вообще ничего делать не надо :-) Я использую драйвер CyUSB.sys как есть, без модификаций (каковые я и не смог бы сделать, ввиду отсутствия исходных текстов драйвера, т.к. фирма Cypress их не предоставляет).

« Последнее редактирование: 17-09-2010 07:37 от jur » Записан

MPEG-4 - в массы!
Bulat
Участник

ru
Offline Offline

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

Цитата
я обращаюсь к драйверу с запросом сразу нескольких пакетов в пачке

Цитата
P.S. Да, забыл сказать. При таком алгоритме действий, какой я применяю, с драйвером вообще ничего делать не надо Улыбаюсь Я использую драйвер CyUSB.sys как есть, без модификаций (каковые я и не смог бы сделать, ввиду отсутствия исходных текстов драйвера, т.к. фирма Cypress их не предоставляет).

 Получается сразу несколько запросов из приложения делаете, посредством ф-ии readfile? Или вы имеете в виду запрос от драйвера верхнего уровня (которым занимаюсь я) к драйверу нижнего уровня?
А ваш драйвер CyUSB.sys можно будет попробовать с моим чипом at91sam7s? Например, родной атмелевский драйвер даже с многопоточным приложением работать не может, сразу экран смерти появляется.
 
Записан
Ochkarik
Модератор

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

« Ответ #25 : 17-09-2010 10:18 » 

jur, это я просто прозрачно так намекал, что можно нитку получения данных реализовать в драйвере, а можно вынести ее в приложение) - уже два варианта как минимум.
мне, например, удобнее было бы все в драйвере делать - так как я свои силы знаю, и сколько времени у меня уйдет на это - тоже.
кому то, покажется проще запускать такую нить в приложении, и не тратить много времени на изучение драйверов...
собственно именно на это я и намекал все время)

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

RTFM уже хоть раз наконец!  RTFM :[ ну или хотя бы STFW...
jur
Помогающий

lt
Offline Offline

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

Цитата
P.S. Да, забыл сказать. При таком алгоритме действий, какой я применяю, с драйвером вообще ничего делать не надо :-) Я использую драйвер CyUSB.sys как есть, без модификаций (каковые я и не смог бы сделать, ввиду отсутствия исходных текстов драйвера, т.к. фирма Cypress их не предоставляет).

 Получается сразу несколько запросов из приложения делаете, посредством ф-ии readfile? Или вы имеете в виду запрос от драйвера верхнего уровня (которым занимаюсь я) к драйверу нижнего уровня?

С этим драйвером можно работать, как с любым другим, но удобнее использовать его CyAPI. По этому вопросу у нас было довольно большое обсуждение в ветке "CY7C680013A Киньте ссылкой на софт и лит-ру" на форуме ELECTRONIX.ru. Идея реализации приема в пользовательской программе почерпнута из примера Streamer из Cypress'овской USB DevStudio (распространяется бесплатно). Вкратце суть в том, что очередь заданий запускается вот таким образом (QUEUE_SIZE - размер очереди, у меня 4):

  // Queue-up the first batch of transfer requests
  for (int i = 0; i < QUEUE_SIZE; i++)
    contexts = pUsDataInEndpt->BeginDataXfer(buffers, len, &inOvLap);


А вообще этот драйвер (точнее, его API) позволяет осуществлять передачу данных двумя способами: синхронно и асинхронно. Синхронный способ проще (всего один вызов), но намного медленнее асинхронного. Им хорошо пользоваться для каких-нибудь редких, однократных передач, например, передача команд по ендпойнте управления.

Асинхронный метод использует 3 функции:

BeginDataXfer, WaitForXfer и FinishDataXfer. Первая стартует запрос к драйверу. Вторая ожидает выполнения трансакции. Третья завершает трансакцию. Вот их я и использую для быстрой передачи пакетов. (В том обсуждении можно посмотреть скриншоты примера Streamer со значениями скорости передачи, пост #45 и другие.)

А ваш драйвер CyUSB.sys можно будет попробовать с моим чипом at91sam7s? Например, родной атмелевский драйвер даже с многопоточным приложением работать не может, сразу экран смерти появляется.

Ну вообще-то это не мой драйвер, иначе я мог бы зело гордиться собой :-) А с другими микросхемами я не пробовал. Подозреваю, что будет работать без проблем, т.к. из устройства USB драйвер считывает только дескрипторы. А в них можно написать, что угодно.

А BSOD'а я с этим драйвером не видел. Точнее, в ходе начальных экспериментов, когда я еще и железо насиловал, BSOD пару раз видел. Но это было давно и возникало из-за аппаратно-программных заморочек.

jur, это я просто прозрачно так намекал, что можно нитку получения данных реализовать в драйвере, а можно вынести ее в приложение)

Тогда понятно, а я сразу не допёр :-)
Записан

MPEG-4 - в массы!
Bulat
Участник

ru
Offline Offline

« Ответ #27 : 21-09-2010 05:39 » 

jur, это я просто прозрачно так намекал, что можно нитку получения данных реализовать в драйвере, а можно вынести ее в приложение) - уже два варианта как минимум.
1.То есть, это два равноценных решения? По скорости работы буфер в драйвере не будет быстрее аналогичного буфера в приложении?
2. Вопрос непросредственно по реализации такой очереди в потоке чтения и записи приложения.
Изначально потоки записи и чтения в моем приложении работали в синхронном режиме:
Код:
//Поток записи
UINT TransmitThread( LPVOID pParam )
{
   update = 0;
   //непрерывная запись, пока не установится флаг update
   while(update == 0)
   {
      //заполняем буфер размером 60 байт для записи
      ...
      Success = WriteFile(PipeOut, &buf_w, 60, &nBytes, NULL);
      WR_spd += nBytes;
      nBytes=0;
    }
    return 0;   // thread completed successfully
    AfxEndThread(0, TRUE);
}

//Поток чтения
UINT ReceiveThread( LPVOID pParam )
{
   stop_r = 0;
   //
   while(stop_r == 0)
   {
      RealRead = 0;
      ReadFile(PipeIn, &buf_r, 60, &RealRead, NULL);
      RD_spd += RealRead;
      RealRead=0;
      //Разбираем принятый буфер
      ...
    }
}
Если делать прием и передачу в асинхр режиме и если writefile или readfile возвращают 0, то необходимо WaitForSingleObject какой то таймаут, что уже ведет к задержке выполнения потока. Но я пока пытаюсь реализовать простой асинхронный режим без буферизации.
Привожу пример потоков чтения и записи в асинхр режиме:
Код:
//Поток записи
UINT TransmitThread( LPVOID pParam )
{
   update = 0;
   //непрерывная запись, пока не установится флаг update
   while(update == 0)
   {
      //заполняем буфер размером 60 байт для записи
      ...
      Success = WriteFile(PipeOut, &buf_w, 60, &nBytes, &gOverLapped_Write);
      if(!Success)
      {
DWORD flag = WaitForSingleObject(hEvent_Write,200);
if(flag==WAIT_OBJECT_0) GetOverlappedResult(PipeOut,&gOverLapped_Write,&nBytes,TRUE);
if(flag==WAIT_TIMEOUT) Sleep(1);
      }
      WR_spd += nBytes;
      nBytes=0;
    }
    return 0;   // thread completed successfully
    AfxEndThread(0, TRUE);
}

//Поток чтения
UINT ReceiveThread( LPVOID pParam )
{
   stop_r = 0;
   //
   while(stop_r == 0)
   {
      RealRead = 0;
      if(!Success)
      {
      Success = ReadFile(PipeIn, &buf_r, 60, &RealRead, &gOverLapped_Read);
        DWORD flag = WaitForSingleObject(hEvent_Read,200);
if(flag==WAIT_OBJECT_0) GetOverlappedResult(PipeIn,&gOverLapped_Read,&RealRead,TRUE);
if(flag==WAIT_TIMEOUT) Sleep(1);
      }   
      RD_spd += RealRead;
      RealRead=0;
      //Разбираем принятый буфер
      ...
    }
}
В таком исполнении асинхр. ввод/вывод ничем от синхронного не отличается. Приложение также пропускает данные, в случае запуска сторонних приложении или запуска на старой машине.
Если делать очередь из запросов readfile и writefile, то всеравно при каждом выполнении readfile и writefile нужно дожидаться их исполнения, а потом уже выполнять новые функции readfile и writefile. Или я что-то неправильно понимаю? Здесь была моя ладья...
Записан
jur
Помогающий

lt
Offline Offline

« Ответ #28 : 21-09-2010 07:33 » 

1.То есть, это два равноценных решения? По скорости работы буфер в драйвере не будет быстрее аналогичного буфера в приложении?

По-моему - один черт :-) IMHO, тут весь вопрос не в скорости, а в приоритете процесса. А скорость приема данных - 30-40 и более МБ/сек даже на слабой машине.

2. Вопрос непросредственно по реализации такой очереди в потоке чтения и записи приложения.
Изначально потоки записи и чтения в моем приложении работали в синхронном режиме:

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

Если делать прием и передачу в асинхр режиме и если writefile или readfile возвращают 0, то необходимо WaitForSingleObject какой то таймаут, что уже ведет к задержке выполнения потока. Но я пока пытаюсь реализовать простой асинхронный режим без буферизации.

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

Код:
Завожу массив евентов:

  HANDLE   hEventArray[QUEUE_SIZE+1];

Первым, наиболее приоритетным, ставлю ивент закрытия потока, который выставляет главная программа по завершению работы (или смене режима).

Далее в вечном цикле ожидаю события:

    nEvent = WaitForMultipleObjects(QUEUE_SIZE+1, hEventArray, FALSE, INFINITE);
    if(nEvent == 0) { // Shutdown event
      sprintf_s(s,"%s: Shutdown event\r\n",module);
      ::OutputDebugString(s);
      break;
    }
    else if(nEvent > 0 && nEvent <= QUEUE_SIZE) { // US-data event
      // тут производится прием данных из драйвера
      recv_index = nEvent-1;
      result = false;
      rLen   = len;  // Reset this each time through because
                     // FinishDataXfer may modify it
      // Wait for the xfer to complete.
      if (!pUsDataInEndpt->WaitForXfer(&inOvLap[recv_index], ENDPT_TIMEOUT)) {
        pUsDataInEndpt->Abort();
        // Wait for the stalled command to complete
        WaitForSingleObject(inOvLap[recv_index].hEvent,INFINITE);
      }
      // Must always call FinishDataXfer to release memory of contexts[i]
      result = pUsDataInEndpt->FinishDataXfer(buffers[recv_index], rLen, &inOvLap[recv_index], contexts[recv_index]);
      ResetEvent(inOvLap[recv_index].hEvent);
      // Prepare read data
      ...
      // Re-submit this request to keep the queue full
      contexts[recv_index] = pUsDataInEndpt->BeginDataXfer(buffers[recv_index], len, &inOvLap[recv_index]);
    }
    else {
      sprintf_s(s,"%s: unknown event 0x%08X\r\n",module,nEvent);
      ::OutputDebugString(s);
    }

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

В таком исполнении асинхр. ввод/вывод ничем от синхронного не отличается. Приложение также пропускает данные, в случае запуска сторонних приложении или запуска на старой машине.

Пропуск данных в обычной Винде - вполне закономерная вещь. Она ведь не ОС РВ. Но минимизировать этот эффект можно. Как я сказал, сделать поток чтения высокоприоритетным, запрашивать данные у драйвера посредством очереди запросов, постоянно поддерживая ее наполненной, и запрашивать у драйвера не по одному пакету, а сразу порцию из 8-16-32 пакетов. В моем приборе пропуск пакетов иногда происходит, но достаточно редко, только если параллельно запустить какую-нить процессорожрущую программу. К примеру, в момент запуска Интернет Эксплорера может пропасть несколько пакетов. А так прием происходит надежно, без сбоев.

Если делать очередь из запросов readfile и writefile, то всеравно при каждом выполнении readfile и writefile нужно дожидаться их исполнения, а потом уже выполнять новые функции readfile и writefile. Или я что-то неправильно понимаю? :-/

Тут дело в том, чтобы запустить не один readfile/writefile, а сразу целую кучу. Тогда потоку некогда будет расслабляться :-)

Ну а если в процессе приема данных обязательно нужно еще и выполнять какую-то другую работу, особенно связанную с регулярным запуском ресурсоемких программ, то это тяжелый случай... Тогда нужно, IMHO, спускаться на более низкий уровень, поближе в системному драйверу USB, который максимально приближен к аппаратным прерываниям, и который работает с высоким приоритетом и первым получает пакеты. Этого я еще не умею :-)
Записан

MPEG-4 - в массы!
Ochkarik
Модератор

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

« Ответ #29 : 21-09-2010 07:46 » 

jur, тогда уж и REALTIME_PRIORITY_CLASS процессу имеет смысл назначить...
а вообще я в приоритеты "не верю") по моему ИМХО, для правильно распланированной программы - никакие приоритеты не нужны)
Записан

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

Powered by SMF 1.1.21 | SMF © 2015, Simple Machines