串行套接字 - 忽略在非规范模式下收到的 POLLHUP?
Serial socket - ignore POLLHUP received in non-canonical mode?
我有一个 Raspberry Pi 通过 UART 连接到微控制器。 RPI 上的代码正在尝试读取传入的非规范 UART 数据,但随机接收到 POLLHUP
。我已经能够通过关闭并重新打开文件来恢复,但这并不理想。
有没有办法在 Linux 中禁用 termios 的断开连接检测行为?我不确定为什么首先提出 POLLHUP
。我怀疑尽管我调用了 cfmakeraw()
,但某些控制字符仍在被解释。电缆不太可能是问题,因为规范的调试输出工作正常(不可否认,在不同的引脚上,但相同的波特率和相同类型的电缆)。
示例代码,设置:
bool UartSocket::setup()
{
int fd = ::open("/dev/serial0", O_RDWR | O_NOCTTY | O_NONBLOCK);
if (fd == 0)
{
return false;
}
struct termios portSettings;
::memset(&portSettings, 0, sizeof(portSettings));
if (m_rSyscalls.tcgetattr(fd, &portSettings) != 0)
{
m_logger.error("tcgetattr() failed, errno = %d.", errno);
return false;
}
m_rSyscalls.cfsetispeed(&portSettings, 115200);
m_rSyscalls.cfsetospeed(&portSettings, 115200);
cfmakeraw(&portSettings);
// Fiddling with more settings out of desperation
portSettings.c_iflag &= ~IGNBRK; // disable break processing
portSettings.c_lflag &= ~ICANON;
portSettings.c_cc[VEOF] = 0;
if (m_rSyscalls.tcsetattr(fd, TCSANOW, &portSettings) != 0)
{
m_logger.error("tcsetattr() failed, errno = %d.", errno);
return false;
}
// Prepare to poll on recv() calls
m_pollfd.fd = fd;
m_pollfd.events = POLLIN;
return true;
}
示例代码,Rx:
ssize_t UartSocket::recv(char* buf, size_t maxRead)
{
ssize_t readResult = -1;
int pollResult = ::poll(&m_pollfd, 1, 1000);
if (pollResult > 0)
{
if (m_pollfd.revents & POLLERR)
{
int error = 0;
socklen_t errlen = sizeof(error);
if (getsockopt(
fd,
SOL_SOCKET,
SO_ERROR,
static_cast<void*>(&error),
&errlen))
{
m_logger.error(
"getsockopt failed when trying to diagnose an error.");
}
m_logger.error(
"Error on uart %s. Error = %d, len = %u.",
m_rConfig.getPath().c_str(),
error,
errlen);
return -1;
}
if (m_pollfd.revents & POLLIN)
{
readResult = ::read( //
fd,
buf,
maxRead);
m_logger.info("readResult = %d.", readResult);
if (readResult > 0)
{
// Party, we are happy
return readResult;
}
else if (readResult == 0)
{
// empty read..no-op
m_logger.dump("Got an empty UART read.");
}
else
{
if (errno == EAGAIN)
{
// No data was available to read; do nothing.
readResult = 0;
m_logger.dump("Got an empty UART read.");
}
else
{
m_logger.error(
"Failure reading uart %s, errno = %d.",
m_rConfig.getPath().c_str(),
errno);
}
}
}
// We wait for the buffer to empty before handling any hangups
if ((m_pollfd.revents & POLLHUP) && (readResult == 0))
{
m_logger.error("Hangup on uart %s.", m_rConfig.getPath().c_str());
reopen(); // closes the fd, reopens it and repeats the termios setup
}
}
else if (pollResult == 0)
{
// No data was available to read; do nothing.
readResult = 0;
m_logger.dump("Got an empty UART poll.");
}
else
{
m_logger.error("Failure polling uart 0, errno = %d.", errno);
readResult = -1;
}
return readResult;
}
TL;DR:上面的代码有一个分支,它通过关闭和重新打开串行设备来处理 POLLHUP
。我正在与一个发送原始字节的设备交谈,如果 Linux 中的 termios 不会使文件描述符在 POLLHUP 的情况下不可用,我会更喜欢它。理想情况下,如果它是控制字符,他们也应该完全忽略导致这种情况的任何控制字符。有办法吗?
POLLHUP
问题已通过正确设置波特率得到解决。
我的原始代码调用了 cfsetispeed(&portSettings, 115200);
。这是错误的,需要传递 B115200
。 B115200
是一个常数,通常解析为不可预测的东西 (example)。
我建议不要从我的代码中复制,而是使用 this example 进行基本的原始 tty 设置。
我有一个 Raspberry Pi 通过 UART 连接到微控制器。 RPI 上的代码正在尝试读取传入的非规范 UART 数据,但随机接收到 POLLHUP
。我已经能够通过关闭并重新打开文件来恢复,但这并不理想。
有没有办法在 Linux 中禁用 termios 的断开连接检测行为?我不确定为什么首先提出 POLLHUP
。我怀疑尽管我调用了 cfmakeraw()
,但某些控制字符仍在被解释。电缆不太可能是问题,因为规范的调试输出工作正常(不可否认,在不同的引脚上,但相同的波特率和相同类型的电缆)。
示例代码,设置:
bool UartSocket::setup()
{
int fd = ::open("/dev/serial0", O_RDWR | O_NOCTTY | O_NONBLOCK);
if (fd == 0)
{
return false;
}
struct termios portSettings;
::memset(&portSettings, 0, sizeof(portSettings));
if (m_rSyscalls.tcgetattr(fd, &portSettings) != 0)
{
m_logger.error("tcgetattr() failed, errno = %d.", errno);
return false;
}
m_rSyscalls.cfsetispeed(&portSettings, 115200);
m_rSyscalls.cfsetospeed(&portSettings, 115200);
cfmakeraw(&portSettings);
// Fiddling with more settings out of desperation
portSettings.c_iflag &= ~IGNBRK; // disable break processing
portSettings.c_lflag &= ~ICANON;
portSettings.c_cc[VEOF] = 0;
if (m_rSyscalls.tcsetattr(fd, TCSANOW, &portSettings) != 0)
{
m_logger.error("tcsetattr() failed, errno = %d.", errno);
return false;
}
// Prepare to poll on recv() calls
m_pollfd.fd = fd;
m_pollfd.events = POLLIN;
return true;
}
示例代码,Rx:
ssize_t UartSocket::recv(char* buf, size_t maxRead)
{
ssize_t readResult = -1;
int pollResult = ::poll(&m_pollfd, 1, 1000);
if (pollResult > 0)
{
if (m_pollfd.revents & POLLERR)
{
int error = 0;
socklen_t errlen = sizeof(error);
if (getsockopt(
fd,
SOL_SOCKET,
SO_ERROR,
static_cast<void*>(&error),
&errlen))
{
m_logger.error(
"getsockopt failed when trying to diagnose an error.");
}
m_logger.error(
"Error on uart %s. Error = %d, len = %u.",
m_rConfig.getPath().c_str(),
error,
errlen);
return -1;
}
if (m_pollfd.revents & POLLIN)
{
readResult = ::read( //
fd,
buf,
maxRead);
m_logger.info("readResult = %d.", readResult);
if (readResult > 0)
{
// Party, we are happy
return readResult;
}
else if (readResult == 0)
{
// empty read..no-op
m_logger.dump("Got an empty UART read.");
}
else
{
if (errno == EAGAIN)
{
// No data was available to read; do nothing.
readResult = 0;
m_logger.dump("Got an empty UART read.");
}
else
{
m_logger.error(
"Failure reading uart %s, errno = %d.",
m_rConfig.getPath().c_str(),
errno);
}
}
}
// We wait for the buffer to empty before handling any hangups
if ((m_pollfd.revents & POLLHUP) && (readResult == 0))
{
m_logger.error("Hangup on uart %s.", m_rConfig.getPath().c_str());
reopen(); // closes the fd, reopens it and repeats the termios setup
}
}
else if (pollResult == 0)
{
// No data was available to read; do nothing.
readResult = 0;
m_logger.dump("Got an empty UART poll.");
}
else
{
m_logger.error("Failure polling uart 0, errno = %d.", errno);
readResult = -1;
}
return readResult;
}
TL;DR:上面的代码有一个分支,它通过关闭和重新打开串行设备来处理 POLLHUP
。我正在与一个发送原始字节的设备交谈,如果 Linux 中的 termios 不会使文件描述符在 POLLHUP 的情况下不可用,我会更喜欢它。理想情况下,如果它是控制字符,他们也应该完全忽略导致这种情况的任何控制字符。有办法吗?
POLLHUP
问题已通过正确设置波特率得到解决。
我的原始代码调用了 cfsetispeed(&portSettings, 115200);
。这是错误的,需要传递 B115200
。 B115200
是一个常数,通常解析为不可预测的东西 (example)。
我建议不要从我的代码中复制,而是使用 this example 进行基本的原始 tty 设置。