Асинхронная последовательная связь в неканоническом (raw) режиме и генерация SIGIO в linux/osx
Для начала у меня проблемы с тем, чтобы мое последовательное устройство генерировало SIGIO, когда данные готовы к чтению.
Я пытаюсь написать простой последовательный интерфейс для связи с микро с помощью адаптера USB-последовательный порт. Я хотел бы, чтобы этот интерфейс был похож на тот, который вы могли бы найти в микро (управляемом прерыванием, используя обратные вызовы), и поэтому я решил пойти по пути перехвата сигнала SIGIO вместо использования отдельного потока или постоянного неблокирующего последовательного читает. Вот мой код инициализации (большая его часть будет выглядеть знакомой из различных асинхронных последовательных обучающих программ):
int mcs = TIOCM_RTS;
ttyDev = open(tty, O_RDWR | O_NOCTTY | O_NONBLOCK);
if (!ttyDev)
{
printf("couldn't open serial device");
return NULL;
}
//create signal handler
byteAction.sa_handler = byteCallback;
sigemptyset(&byteAction.sa_mask); //sa_mask = 0
byteAction.sa_flags = SA_RESTART;
sigaction(SIGPOLL, &byteAction, NULL);
//Allow process to detect SIGIO
fcntl(ttyDev, F_SETOWN, getpid());
fcntl(ttyDev, F_SETFL, FASYNC); //make async
fcntl(ttyDev, F_SETFL, FNDELAY); //non blocking reads
tcgetattr(ttyDev, &oldtio); //backup current settings
newtio.c_cflag = getBaud(baudrate) | CS8 | CLOCAL | CREAD;
newtio.c_cflag &= ~CRTSCTS; //disable hw flow control
newtio.c_iflag &= ~(IXON | IXOFF | IXANY); //disable flow control
newtio.c_iflag |= IGNPAR; //ignore parity
newtio.c_oflag = 0;
newtio.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); //raw mode
newtio.c_cc[VMIN] = 1;
newtio.c_cc[VTIME] = 0;
cfsetspeed(&newtio, getBaud(baudrate));
tcflush(ttyDev, TCIFLUSH);
tcsetattr(ttyDev, TCSANOW, &newtio);
//clear RTS
ioctl(ttyDev, TIOCMBIC, &mcs);
byteCallback - мой обработчик сигнала. Я уже проверил, что он работает, отправив процессу сигнал вручную. Микро, с которым я общаюсь, не поддерживает управление потоком, поэтому оно отключено. Я подтвердил, что могу читать и писать с устройства, используя стандартные системные вызовы чтения / записи.
Проблема в том, что когда данные поступают, обработчик сигнала не вызывается. Я подозреваю, что это потому, что SIGIO генерируется только при получении символа CR или конца строки. Поскольку это полностью необработанный режим, и такой символ не отправляется, SIGIO не генерируется. На данный момент я пробовал это только на OSX, но в идеале тот же код будет работать и на Linux.
Любая помощь будет оценена. Я искал довольно много, и люди всегда заканчивали тем, что предлагали просто использовать select() или другой метод. Это стало почти проблемой, и я пока не готов сдаваться.
Благодарю.
1 ответ
Поколение SIGIO
определенно не имеет ничего общего с CR/NL. Ваша проблема в том, что сразу после установки FASYNC
флаг, вы очистили его:
fcntl(ttyDev, F_SETFL, FASYNC); //make async
fcntl(ttyDev, F_SETFL, FNDELAY); //non blocking reads AND CLEAR FASYNC FLAG
Что вы должны делать, это:
fcntl(ttyDev, F_SETFL, O_ASYNC|O_NONBLOCK);
или предпочтительно (чтобы избежать очистки любых других флагов, которые, возможно, уже были установлены):
fcntl(ttyDev, F_SETFL, O_ASYNC|O_NONBLOCK|fcntl(ttyDev, F_GETFL));
Обратите внимание, что я также взял на себя смелость заменить странные нестандартные имена флагов, которые вы использовали, на соответствующие стандартные имена.:-)