LINUX.ORG.RU

Аналог OVERLAPPED структуры и DeviceIOControl

 , , ,


0

1

Доброго времени знатокам драйверописателям и не только.

Возможно-ли в Linux использовать отслеживание асинхронных событий от некоего девайса, которые не являются событиями от read()/write()?

Допустим, есть некое у-во, от которого мне нужно ловить некоторые события изменения его состояния, грубо - моргание светодиодика. Но мне это нужно делать не поллингом, а в event-based режиме.

В оффтопике я для этих целей могу сделать так:


// получаем хендл у-ва
HANDLE hDevice = ::CreateFile(..., FILE_FLAG_OVERLAPPED);

// создаем событие
OVERLAPPED ov = {0};
ov.hEvent = CreateEvent(...);

// переменная, которая отражает состояние LED
DWORD ledState = 0;

// запускаю отслеживание евента о смене состояния LED
::DeviceIOControl(
    hDevice, 
    LED_TRIGGERED_NOTIFY_ON, 
    NULL,
    0,
    &ledState,
    sizeof(ledState),
    NULL,
    &ov
    );

// где-то жду когда событие произойдет.
::WaitForSingleObject(
    ov.hEvent,
    INFINITE
);

// если оно произошло, то сбрасываю евент и 
// получаю состояние LED

::GetOverlappedResult(
    hDevice,
    &ov,
    &NumberOfBytesTransferred,
    FALSE
);

// ledState теперь содержит текущее состояние LED у-ва 
DWORD newLedState = ledState;

А как сделать подобное в Linux?


// получаю дескриптор у-ва
int fd = ::open();

// делаю ioctl
::ioctl(fd, LED_TRIGGERED_NOTIFY_ON, <но что сюда совать?? >)

// Жду события, но непонятно какой дескриптор сюда
// писать.. По идее нужен не дескриптор у-ва fd,
// а какой-то дескриптор события, но где его взять?
::select(<какой сюда дескриптор нужен???>)

Есть ли какие-то мысли по этому поводу?

Может быть, в ioctl() передавать некую пользовательскую структуру, типа:

// 
typedef struct {
    int efd; // дескриптор события, но как его создать?
    int ledState; // состояние LED
} led_state_t;

led_state_t ls = {0};

ls.efd = <надо как-то что-то создать??>

::ioсtl(fd, LED_TRIGGERED_NOTIFY_ON, &ls);

::select(ls.efd, ...);

// если дождались, то
int newLedState = ls.ledState;

Подскажите, знатоки, как в этом случае быть?

★★

Ответ на: комментарий от Stil

Блин, парни, ну так как это в Linux делается то?

kuzulis ★★
() автор топика

А как сделать подобное в Linux?

руками.

сделай ещё один chardev, отдельно для ивентов с самописным интерфейсом с картами и подругами. будет что-то вроде /dev/device0 и /dev/device_event0

nanoolinux ★★★★
()
Ответ на: комментарий от nanoolinux

Не, как-то некрасиво, не напосешься устройств... А если надо следить за переключением 100500 параметров?

Нашел что-то похожее тут:

http://stackoverflow.com/questions/13607730/writing-to-eventfd-from-kernel-mo...

Наверное буду делать как-то так:


int fd = ::open(...);

int efdLed1 = eventfd(...);
int efdLed2 = eventfd(...);

::ioctl(fd, IOCTL_REGISTER_EVENT, &efdLed1);
::ioctl(fd, IOCTL_REGISTER_EVENT, &efdLed2);

fd_set rfds;
FD_ZERO(&rfds);
FD_SET(efdLed1, &rfds);
FD_SET(efdLed2, &rfds);

int max_efd = (efdLed1 > efdLed2 ? efdLed1 : efdLed2) + 1;

int n = ::select(max_efd, &rfds, NULL, NULL, NULL);

if (n < 0) {
    perror("select failed");
} else if (n == 0) {
    puts("TIMEOUT");
} else {
    if (FD_ISSET(efdLed1, &input)) {
        // do read
        int val_led1;
        int r = read(efdLed1, &val_led1, sizeof(val_led1));
        // do next processing
    }

    if (FD_ISSET(efdLed2, &input)) {
        // do read
        int val_led2;
        int r = read(efdLed1, &val_led2, sizeof(val_led2));
        // do next processing
    }
}



Как-то так, думаю должно прокатить..
kuzulis ★★
() автор топика
Ответ на: комментарий от kuzulis

Упс, нет, так нельзя делать: вызов read() для eventfd должен читать только некий внутренний 8-ми байтный счетчик...

Получается, что да - нужно будет создавать некое у-во в

/dev/mydevice_event0

Вот блин..

kuzulis ★★
() автор топика

Но мне это нужно делать не поллингом, а в event-based режиме.

Это тебе, конечно, не нужно. Однако, если тебя заставляют делать именно так, кто мешает тебе просто посылать сигнал указаному в ioctl процессу?

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

Это тебе, конечно, не нужно.

Нужно. Мой пример с неким у-вом с LED's - это просто пример, реально этого устройства не будет.

Однако, если тебя заставляют делать именно так, кто мешает тебе просто посылать сигнал указаному в ioctl процессу?

Реальный проект, который я задумываю делать - это некий кросс-платформенный «шпион» за состоянием выбранного виртуального (созданного мною-же) последовательного порта.

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

Т.е. этими событиями являются изменение DTR/RTS и т.п. линий, а также моменты переконфигурирования termios (смена baudrate, parity и т.п.).

Поэтому я не могу посылать никаких сигналов процессу и т.п., а должен просто «тупо» открыть это /dev/my_control0 у-во, сунуть ему ioctl() для того, чтобы приаттачиться к исследуемому виртуальному порту /dev/my_virt_tty0, и перехватывать все его события через /dev/my_control0.

kuzulis ★★
() автор топика
Ответ на: комментарий от kuzulis

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

kuzulis ★★
() автор топика
Ответ на: комментарий от kuzulis

Реальный проект, который я задумываю делать

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

tailgunner ★★★★★
()
Ответ на: комментарий от kuzulis

Но, как понимаю, Linux убог в этом плане

Но, как я понимаю, ты зеленый нуб или просто тролль.

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

Т.е. тебя никто не заставляет работать без poll?

Как бы да, никто не заставляет. Но все дело в том, что проект кроссплатформенный, и GUI и всю обвязку предполагается делать на Qt. А там, кроме как QSocketNotifier (который использует select()) больше нечем ловить евенты от дескриптора.

Тогда расскажи подробнее, чем тебя не устроил eventfd - тем, что из него читается счетчик, а ты хотел бы состояние устрйства?

Именно! Нужно по некоторому событию получать некое изменение состояние устройсва! Тем более, что состоянием устройства может быть и некая структура, которую необходимо возвратить.

Но, как я понимаю, ты зеленый нуб или просто тролль.

Скорее первое, поэтому и ищу помощи. :)

Но в оффтопике с этим делом попроще...

kuzulis ★★
() автор топика
Ответ на: комментарий от kuzulis

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

зачем такое могло понадобиться?

nanoolinux ★★★★
()
Ответ на: комментарий от kuzulis

Во-первых, если тебя интересуют только последовательные порты, начни с чтения документации по tty/pty. Во-вторых, для сообщения о событиях устройства есть куча возможностей:

1) eventfd на событие, за которым следует read или ioctl на устройство;

2) драйвер устройства может создавать несколько специальных файлов, на каждом из которых определен poll и read (и read возвращает какие-то данные о событии);

3) можно создать специальный файл с poll и read, через который в пользовательскую программу будут поступать записи о разных событиях.

tailgunner ★★★★★
()
Ответ на: комментарий от nanoolinux

зачем такое могло понадобиться?

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

kuzulis ★★
() автор топика
Ответ на: комментарий от kuzulis

у меня есть подозрения, что для этого можно использовать pty/pts без написания чего-либо для kernel space. но я не стану утверждать, ибо не сильно в теме.

nanoolinux ★★★★
()
Ответ на: комментарий от tailgunner

Во-первых, если тебя интересуют только последовательные порты, начни с чтения документации по tty/pty

Причем тут это?

Во-вторых, для сообщения о событиях устройства есть куча возможностей:

1) eventfd на событие, за которым следует read или ioctl на устройство;

Вот про это и речь веду. Получается, что это наиболее приемлемый результат. Особенно при использовании ioctl() для чтения состояния. Просто я не подумал, что им можно пользоваться после срабатывания события.

Значит идея такая:

1. Создаю некое /dev/my_control у-во.

2. Открываю его:

inf control_fd = open(/dev/my_control, ...);

3. Создаю виртуальную пару связанных последовательных портов:

struct virt_pair_info vpi;

...
...

::ioctl(control_fd, IOCTL_CREATE_PAIR, &vpi);

4. Регистрирую евент и аттачусь к любому из виртуальных девайсов моей пары для получения событий.

struct attath_info {
    int efd;
    int virt_dev_id;
} ai;

ai.efd = eventfd(...);
ai.virt_dev_id = некий ID у-ва к которому надо подкл.

::ioctl(control_fd, IOCTL_ATTATH_TO, &ai);

5. Через select() или др. вызов мониторю появление евента

int n = ::select(ai.efd + 1, &rfds, NULL, NULL, NULL);

6. Если событие произошло, то читаю данные, которые ассоциированы с этим событием.

struct my_event_data {} ed;

::ioctl(control_fd, IOCTL_GET_EVENT_DATA, &ed);

Примерно такая суть? Я правильно понял?

kuzulis ★★
() автор топика
Ответ на: комментарий от nanoolinux

что для этого можно использовать pty/pts без написания чего-либо для kernel space

Нет, этого недостаточно.

kuzulis ★★
() автор топика
Ответ на: комментарий от tailgunner

Но я всё равно считаю, что pty/tty даст тебе всё, что нужно.

Скорее не получится с ними, т.к. я не смогу перехватить никаких данных и событий между /dev/pty и /dev/tty

kuzulis ★★
() автор топика
Ответ на: комментарий от kuzulis

Скорее не получится с ними, т.к. я не смогу перехватить никаких данных и событий между /dev/pty и /dev/tty

Мде. Ты бы хоть почитал о них.

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

Мде. Ты бы хоть почитал о них.

Почитал. И в упор не вижу как это сделать при помощи /dev/pty и /dev/tty. Ткни носом.

У меня вкрались сомнения в то, что ты сам это знаешь, пока что слышу одну «воду» от тебя.

Как говорят «не прокати за балабола».

kuzulis ★★
() автор топика
Ответ на: комментарий от kuzulis

И в упор не вижу как это сделать при помощи /dev/pty и /dev/tty.

Я не знаю, что и зачем ты хочешь сделать. Но вот это утверждение:

kuzulis> я не смогу перехватить никаких данных и событий между /dev/pty и /dev/tty

гарантированно ложно.

Ткни носом.

Advanced Programming in the UNIX® Environment, Second Edition, Chapter 19. Pseudo Terminals

Как говорят «не прокати за балабола».

Мне безразлично, кем ты меня считаешь.

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

Я не знаю, что и зачем ты хочешь сделать. Но вот это утверждение:

kuzulis> я не смогу перехватить никаких данных и событий между /dev/pty и /dev/tty

гарантированно ложно.

Где же оно ложно то? Нужно перехватить вызовы read(), write(), ioctl() к /dev/ttyX девайсу.

Девайсы /dev/pty и /dev/tty обеспечивают только между собой связанную пару, это то-же самое, что и работать с реальными ttyS0, ttyS1, которые соединены шнурком.

В оффтопике это делается просто, но в Linux - это жесткач, которому нет решения!

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

В оБщем, тут тупик...

kuzulis ★★
() автор топика

посмотри как сделан

jpnevulator - Just another serial sniffer

(драйверов в нем нет)

sergej ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.