mirror of
https://github.com/microsoft/WSL.git
synced 2025-07-03 07:23:20 +00:00

Many Microsoft employees have contributed to the Windows Subsystem for Linux, this commit is the result of their work since 2016. The entire history of the Windows Subsystem for Linux can't be shared here, but here's an overview of WSL's history after it moved to it own repository in 2021: Number of commits on the main branch: 2930 Number of contributors: 31 Head over https://github.com/microsoft/WSL/releases for a more detailed history of the features added to WSL since 2021.
1052 lines
27 KiB
C
1052 lines
27 KiB
C
/*++
|
|
|
|
Copyright (c) Microsoft. All rights reserved.
|
|
|
|
Module Name:
|
|
|
|
Template.c
|
|
|
|
Abstract:
|
|
|
|
This file is a ttys test.
|
|
|
|
--*/
|
|
|
|
#include "lxtcommon.h"
|
|
#include "unittests.h"
|
|
#include <sys/ioctl.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/sysmacros.h>
|
|
#include <fcntl.h>
|
|
#include <termios.h>
|
|
#include <stdarg.h>
|
|
#include <stdio.h>
|
|
#include <poll.h>
|
|
|
|
#define LXT_NAME "Ttys"
|
|
|
|
#define LXT_TTYS_LARGE_BUFFER_SIZE (1 * 1024)
|
|
#define LXT_TTYS_DEFAULT "/dev/ttyS1"
|
|
#define LXT_TTYS_DEFAULT_MINOR (LXT_TTYS_DEV_OFFSET + 1)
|
|
#define LXT_TTYS_DEFAULT2 "/dev/ttyS2"
|
|
#define LXT_TTYS_DEFAULT2_MINOR (LXT_TTYS_DEV_OFFSET + 2)
|
|
#define LXT_TTYS_FORMAT "/dev/ttyS%d"
|
|
#define LXT_TTYS_MAX 192
|
|
#define LXT_TTYS_DEV_MODE (S_IFCHR | 0660)
|
|
#define LXT_TTYS_DEV_MAJOR 4
|
|
#define LXT_TTYS_DEV_OFFSET 64
|
|
|
|
LXT_VARIATION_HANDLER TtysBasicOps;
|
|
LXT_VARIATION_HANDLER TtysTermiosBaudParity;
|
|
LXT_VARIATION_HANDLER TtysWrite;
|
|
LXT_VARIATION_HANDLER TtysWindowSize;
|
|
LXT_VARIATION_HANDLER TtysTermiosFlowControl;
|
|
LXT_VARIATION_HANDLER TtysWriteRead;
|
|
LXT_VARIATION_HANDLER TtysModemIoctls;
|
|
|
|
//
|
|
// Global constants
|
|
//
|
|
// TtysWriteRead requires two connected serial ports for testing.
|
|
//
|
|
|
|
static const LXT_VARIATION g_LxtVariations[] = {
|
|
{"Ttys basic operations", TtysBasicOps},
|
|
{"Ttys termios - baud rate and parity", TtysTermiosBaudParity},
|
|
{"Ttys write", TtysWrite},
|
|
{"Ttys window size", TtysWindowSize},
|
|
/* {"Ttys write read", TtysWriteRead}, */
|
|
{"Ttys termios - flow control", TtysTermiosFlowControl},
|
|
{"Ttys modem ioctls", TtysModemIoctls}};
|
|
|
|
int TtysTestEntry(int Argc, char* Argv[])
|
|
|
|
/*++
|
|
--*/
|
|
|
|
{
|
|
|
|
LXT_ARGS Args;
|
|
int Result;
|
|
|
|
LxtCheckResult(LxtInitialize(Argc, Argv, &Args, LXT_NAME));
|
|
LxtCheckResult(LxtRunVariations(&Args, g_LxtVariations, LXT_COUNT_OF(g_LxtVariations)));
|
|
|
|
ErrorExit:
|
|
LxtUninitialize();
|
|
return !LXT_SUCCESS(Result);
|
|
}
|
|
|
|
int TtysBasicOps(PLXT_ARGS Args)
|
|
|
|
/*++
|
|
--*/
|
|
|
|
{
|
|
|
|
char Buffer[64];
|
|
dev_t Device;
|
|
int Fd;
|
|
char Path[32];
|
|
int Index;
|
|
int Result;
|
|
|
|
for (Index = 0; Index < LXT_TTYS_MAX; ++Index)
|
|
{
|
|
snprintf(Path, sizeof(Path), LXT_TTYS_FORMAT, Index);
|
|
unlink(Path);
|
|
Device = makedev(LXT_TTYS_DEV_MAJOR, Index + LXT_TTYS_DEV_OFFSET);
|
|
LxtCheckErrno(mknod(Path, LXT_TTYS_DEV_MODE, Device));
|
|
Fd = open(Path, O_RDWR | O_NONBLOCK, 0);
|
|
if (Fd != -1)
|
|
{
|
|
read(Fd, Buffer, sizeof(Buffer));
|
|
write(Fd, Buffer, sizeof(Buffer));
|
|
fsync(Fd);
|
|
tcflush(Fd, TCIOFLUSH);
|
|
LxtClose(Fd);
|
|
}
|
|
}
|
|
|
|
Result = LXT_RESULT_SUCCESS;
|
|
|
|
ErrorExit:
|
|
return Result;
|
|
}
|
|
|
|
int TtysTermiosBaudParity(PLXT_ARGS Args)
|
|
|
|
/*++
|
|
--*/
|
|
|
|
{
|
|
|
|
int BaudRate;
|
|
int BaudRates[] = {B1200, B9600, B9600};
|
|
int Cflag;
|
|
dev_t Device;
|
|
int Iflag;
|
|
int Index;
|
|
int Fd;
|
|
int Result;
|
|
struct termios Termios = {0};
|
|
struct termios TermiosNew = {0};
|
|
|
|
Fd = -1;
|
|
Result = LXT_RESULT_FAILURE;
|
|
|
|
//
|
|
// Check the default termios values.
|
|
//
|
|
// N.B. Ignore termios fields that may differ.
|
|
//
|
|
|
|
Device = makedev(LXT_TTYS_DEV_MAJOR, LXT_TTYS_DEFAULT_MINOR);
|
|
mknod(LXT_TTYS_DEFAULT, LXT_TTYS_DEV_MODE, Device);
|
|
Fd = open(LXT_TTYS_DEFAULT, O_RDWR, 0);
|
|
if (Fd == -1)
|
|
{
|
|
if (errno == EIO)
|
|
{
|
|
LxtLogInfo("Skipping test %d", errno);
|
|
Result = LXT_RESULT_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
LxtLogError("Unexpected error %d", errno);
|
|
}
|
|
|
|
goto ErrorExit;
|
|
}
|
|
|
|
LxtCheckErrno(tcgetattr(Fd, &Termios));
|
|
LxtCheckEqual(Termios.c_oflag, 05, "%d");
|
|
LxtCheckEqual(Termios.c_lflag, 0105063, "%d");
|
|
LxtCheckEqual(Termios.c_line, 0, "%d");
|
|
LxtCheckEqual(Termios.c_cc[0], 3, "%d");
|
|
LxtCheckEqual(Termios.c_cc[1], 28, "%d");
|
|
LxtCheckEqual(Termios.c_cc[2], 127, "%d");
|
|
LxtCheckEqual(Termios.c_cc[3], 21, "%d");
|
|
LxtCheckEqual(Termios.c_cc[5], 0, "%d");
|
|
LxtCheckEqual(Termios.c_cc[6], 1, "%d");
|
|
LxtCheckEqual(Termios.c_cc[7], 0, "%d");
|
|
LxtCheckEqual(Termios.c_cc[10], 26, "%d");
|
|
LxtCheckEqual(Termios.c_cc[11], 0, "%d");
|
|
LxtCheckEqual(Termios.c_cc[12], 18, "%d");
|
|
LxtCheckEqual(Termios.c_cc[13], 15, "%d");
|
|
LxtCheckEqual(Termios.c_cc[14], 23, "%d");
|
|
LxtCheckEqual(Termios.c_cc[15], 22, "%d");
|
|
LxtCheckEqual(Termios.c_cc[16], 0, "%d");
|
|
|
|
//
|
|
// Set and check the baud rate.
|
|
//
|
|
|
|
BaudRate = cfgetispeed(&Termios);
|
|
for (Index = 0; Index < LXT_COUNT_OF(BaudRates); ++Index)
|
|
{
|
|
cfsetspeed(&Termios, BaudRates[Index]);
|
|
LxtCheckErrno(tcsetattr(Fd, TCSANOW, &Termios));
|
|
LxtCheckErrno(tcgetattr(Fd, &TermiosNew));
|
|
LxtCheckMemoryEqual(&Termios, &TermiosNew, sizeof(Termios));
|
|
LxtClose(Fd);
|
|
Fd = -1;
|
|
Fd = open(LXT_TTYS_DEFAULT, O_RDWR, 0);
|
|
LxtCheckErrno(tcgetattr(Fd, &TermiosNew));
|
|
LxtCheckMemoryEqual(&Termios, &TermiosNew, sizeof(Termios));
|
|
}
|
|
|
|
//
|
|
// Reset to the original.
|
|
//
|
|
|
|
cfsetspeed(&Termios, BaudRate);
|
|
LxtCheckErrno(tcsetattr(Fd, TCSANOW, &Termios));
|
|
LxtCheckErrno(tcgetattr(Fd, &TermiosNew));
|
|
LxtCheckMemoryEqual(&Termios, &TermiosNew, sizeof(Termios));
|
|
LxtClose(Fd);
|
|
Fd = -1;
|
|
Fd = open(LXT_TTYS_DEFAULT, O_RDWR, 0);
|
|
LxtCheckErrno(tcgetattr(Fd, &TermiosNew));
|
|
LxtCheckMemoryEqual(&Termios, &TermiosNew, sizeof(Termios));
|
|
|
|
//
|
|
// Set and check the parity enable and type bits
|
|
//
|
|
// N.B. NT does not support setting input and output parity independently,
|
|
// but Linux does.
|
|
//
|
|
|
|
Cflag = Termios.c_cflag;
|
|
Iflag = Termios.c_iflag;
|
|
|
|
//
|
|
// No parity, 8 bits
|
|
//
|
|
|
|
Termios.c_iflag &= ~INPCK;
|
|
Termios.c_cflag &= ~PARENB;
|
|
Termios.c_cflag &= ~CSTOPB;
|
|
Termios.c_cflag &= ~CMSPAR;
|
|
Termios.c_cflag &= ~CSIZE;
|
|
Termios.c_cflag |= CS8;
|
|
LxtCheckErrno(tcsetattr(Fd, TCSANOW, &Termios));
|
|
LxtCheckErrno(tcgetattr(Fd, &TermiosNew));
|
|
LxtCheckMemoryEqual(&Termios, &TermiosNew, sizeof(Termios));
|
|
LxtClose(Fd);
|
|
Fd = -1;
|
|
Fd = open(LXT_TTYS_DEFAULT, O_RDWR, 0);
|
|
LxtCheckErrno(tcgetattr(Fd, &TermiosNew));
|
|
LxtCheckMemoryEqual(&Termios, &TermiosNew, sizeof(Termios));
|
|
|
|
//
|
|
// Even parity, 7 bits
|
|
//
|
|
|
|
Termios.c_iflag |= INPCK;
|
|
Termios.c_cflag |= PARENB;
|
|
Termios.c_cflag &= ~PARODD;
|
|
Termios.c_cflag &= ~CMSPAR;
|
|
Termios.c_cflag &= ~CSTOPB;
|
|
Termios.c_cflag &= ~CSIZE;
|
|
Termios.c_cflag |= CS7;
|
|
LxtCheckErrno(tcsetattr(Fd, TCSANOW, &Termios));
|
|
LxtCheckErrno(tcgetattr(Fd, &TermiosNew));
|
|
LxtCheckMemoryEqual(&Termios, &TermiosNew, sizeof(Termios));
|
|
LxtClose(Fd);
|
|
Fd = -1;
|
|
Fd = open(LXT_TTYS_DEFAULT, O_RDWR, 0);
|
|
LxtCheckErrno(tcgetattr(Fd, &TermiosNew));
|
|
LxtCheckMemoryEqual(&Termios, &TermiosNew, sizeof(Termios));
|
|
|
|
//
|
|
// Odd parity, 7 bits
|
|
//
|
|
|
|
Termios.c_iflag |= INPCK;
|
|
Termios.c_cflag |= PARENB;
|
|
Termios.c_cflag |= PARODD;
|
|
Termios.c_cflag &= ~CMSPAR;
|
|
Termios.c_cflag &= ~CSTOPB;
|
|
Termios.c_cflag &= ~CSIZE;
|
|
Termios.c_cflag |= CS7;
|
|
LxtCheckErrno(tcsetattr(Fd, TCSANOW, &Termios));
|
|
LxtCheckErrno(tcgetattr(Fd, &TermiosNew));
|
|
LxtCheckMemoryEqual(&Termios, &TermiosNew, sizeof(Termios));
|
|
LxtClose(Fd);
|
|
Fd = -1;
|
|
Fd = open(LXT_TTYS_DEFAULT, O_RDWR, 0);
|
|
LxtCheckErrno(tcgetattr(Fd, &TermiosNew));
|
|
LxtCheckMemoryEqual(&Termios, &TermiosNew, sizeof(Termios));
|
|
|
|
//
|
|
// Even parity, 5 bits
|
|
//
|
|
|
|
Termios.c_iflag |= INPCK;
|
|
Termios.c_cflag |= PARENB;
|
|
Termios.c_cflag &= ~PARODD;
|
|
Termios.c_cflag &= ~CMSPAR;
|
|
Termios.c_cflag &= ~CSTOPB;
|
|
Termios.c_cflag &= ~CSIZE;
|
|
Termios.c_cflag |= CS5;
|
|
LxtCheckErrno(tcsetattr(Fd, TCSANOW, &Termios));
|
|
LxtCheckErrno(tcgetattr(Fd, &TermiosNew));
|
|
LxtCheckMemoryEqual(&Termios, &TermiosNew, sizeof(Termios));
|
|
LxtClose(Fd);
|
|
Fd = -1;
|
|
Fd = open(LXT_TTYS_DEFAULT, O_RDWR, 0);
|
|
LxtCheckErrno(tcgetattr(Fd, &TermiosNew));
|
|
LxtCheckMemoryEqual(&Termios, &TermiosNew, sizeof(Termios));
|
|
|
|
//
|
|
// Odd parity, 5 bits, with stop bit
|
|
//
|
|
|
|
Termios.c_iflag |= INPCK;
|
|
Termios.c_cflag |= PARENB;
|
|
Termios.c_cflag |= PARODD;
|
|
Termios.c_cflag &= ~CMSPAR;
|
|
Termios.c_cflag |= CSTOPB;
|
|
Termios.c_cflag &= ~CSIZE;
|
|
Termios.c_cflag |= CS5;
|
|
LxtCheckErrno(tcsetattr(Fd, TCSANOW, &Termios));
|
|
LxtCheckErrno(tcgetattr(Fd, &TermiosNew));
|
|
LxtCheckMemoryEqual(&Termios, &TermiosNew, sizeof(Termios));
|
|
LxtClose(Fd);
|
|
Fd = -1;
|
|
Fd = open(LXT_TTYS_DEFAULT, O_RDWR, 0);
|
|
LxtCheckErrno(tcgetattr(Fd, &TermiosNew));
|
|
LxtCheckMemoryEqual(&Termios, &TermiosNew, sizeof(Termios));
|
|
|
|
//
|
|
// Space parity, 7 bits
|
|
//
|
|
|
|
Termios.c_iflag |= INPCK;
|
|
Termios.c_cflag |= PARENB;
|
|
Termios.c_cflag &= ~PARODD;
|
|
Termios.c_cflag |= CMSPAR;
|
|
Termios.c_cflag &= ~CSTOPB;
|
|
Termios.c_cflag &= ~CSIZE;
|
|
Termios.c_cflag |= CS7;
|
|
LxtCheckErrno(tcsetattr(Fd, TCSANOW, &Termios));
|
|
LxtCheckErrno(tcgetattr(Fd, &TermiosNew));
|
|
LxtCheckMemoryEqual(&Termios, &TermiosNew, sizeof(Termios));
|
|
LxtClose(Fd);
|
|
Fd = -1;
|
|
Fd = open(LXT_TTYS_DEFAULT, O_RDWR, 0);
|
|
LxtCheckErrno(tcgetattr(Fd, &TermiosNew));
|
|
LxtCheckMemoryEqual(&Termios, &TermiosNew, sizeof(Termios));
|
|
|
|
//
|
|
// Mark parity, 7 bits
|
|
//
|
|
|
|
Termios.c_iflag |= INPCK;
|
|
Termios.c_cflag |= PARENB;
|
|
Termios.c_cflag |= PARODD;
|
|
Termios.c_cflag |= CMSPAR;
|
|
Termios.c_cflag &= ~CSTOPB;
|
|
Termios.c_cflag &= ~CSIZE;
|
|
Termios.c_cflag |= CS7;
|
|
LxtCheckErrno(tcsetattr(Fd, TCSANOW, &Termios));
|
|
LxtCheckErrno(tcgetattr(Fd, &TermiosNew));
|
|
LxtCheckMemoryEqual(&Termios, &TermiosNew, sizeof(Termios));
|
|
LxtClose(Fd);
|
|
Fd = -1;
|
|
Fd = open(LXT_TTYS_DEFAULT, O_RDWR, 0);
|
|
LxtCheckErrno(tcgetattr(Fd, &TermiosNew));
|
|
LxtCheckMemoryEqual(&Termios, &TermiosNew, sizeof(Termios));
|
|
|
|
//
|
|
// Reset to the original
|
|
//
|
|
|
|
Termios.c_cflag = Cflag;
|
|
Termios.c_iflag = Iflag;
|
|
LxtCheckErrno(tcsetattr(Fd, TCSANOW, &Termios));
|
|
LxtCheckErrno(tcgetattr(Fd, &TermiosNew));
|
|
LxtCheckMemoryEqual(&Termios, &TermiosNew, sizeof(Termios));
|
|
LxtClose(Fd);
|
|
Fd = -1;
|
|
Fd = open(LXT_TTYS_DEFAULT, O_RDWR, 0);
|
|
LxtCheckErrno(tcgetattr(Fd, &TermiosNew));
|
|
LxtCheckMemoryEqual(&Termios, &TermiosNew, sizeof(Termios));
|
|
|
|
Result = LXT_RESULT_SUCCESS;
|
|
|
|
ErrorExit:
|
|
if (Fd != -1)
|
|
{
|
|
LxtClose(Fd);
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
int TtysWrite(PLXT_ARGS Args)
|
|
|
|
/*++
|
|
--*/
|
|
|
|
{
|
|
|
|
char Buffer[64];
|
|
int BytesWritten;
|
|
dev_t Device;
|
|
int Fd;
|
|
char Path[32];
|
|
struct pollfd PollFd;
|
|
int Index;
|
|
int Result;
|
|
|
|
//
|
|
// Test non-blocking write paths.
|
|
//
|
|
// N.B. Blocking can hang if there is no reader.
|
|
//
|
|
|
|
Fd = -1;
|
|
Device = makedev(LXT_TTYS_DEV_MAJOR, LXT_TTYS_DEFAULT_MINOR);
|
|
mknod(LXT_TTYS_DEFAULT, LXT_TTYS_DEV_MODE, Device);
|
|
Fd = open(LXT_TTYS_DEFAULT, O_RDWR | O_NONBLOCK, 0);
|
|
if (Fd == -1)
|
|
{
|
|
if (errno == EIO)
|
|
{
|
|
LxtLogInfo("Skipping test %d", errno);
|
|
Result = LXT_RESULT_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
LxtLogError("Unexpected error %d", errno);
|
|
}
|
|
|
|
goto ErrorExit;
|
|
}
|
|
|
|
memset(&PollFd, 0, sizeof(PollFd));
|
|
PollFd.fd = Fd;
|
|
PollFd.events = POLLIN | POLLOUT | POLLHUP;
|
|
LxtCheckErrno(poll(&PollFd, 1, 0));
|
|
LxtCheckEqual(PollFd.revents & POLLOUT, POLLOUT, "%d");
|
|
|
|
errno = 0;
|
|
BytesWritten = write(Fd, Buffer, sizeof(Buffer));
|
|
if ((BytesWritten != sizeof(Buffer)) && (errno != EAGAIN))
|
|
{
|
|
LxtLogError("Unexpected BytesWritten %d, %d", BytesWritten, errno);
|
|
Result = LXT_RESULT_FAILURE;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
Result = LXT_RESULT_SUCCESS;
|
|
|
|
ErrorExit:
|
|
if (Fd != -1)
|
|
{
|
|
LxtClose(Fd);
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
int TtysWindowSize(PLXT_ARGS Args)
|
|
|
|
/*++
|
|
--*/
|
|
|
|
{
|
|
|
|
dev_t Device;
|
|
int Fd;
|
|
char Path[32];
|
|
int Result;
|
|
struct winsize WindowSize;
|
|
struct winsize WindowSizeNew;
|
|
|
|
//
|
|
// Test setting and getting the window size of a serial device.
|
|
//
|
|
|
|
Fd = -1;
|
|
Device = makedev(LXT_TTYS_DEV_MAJOR, LXT_TTYS_DEFAULT_MINOR);
|
|
mknod(LXT_TTYS_DEFAULT, LXT_TTYS_DEV_MODE, Device);
|
|
Fd = open(LXT_TTYS_DEFAULT, O_RDWR, 0);
|
|
if (Fd == -1)
|
|
{
|
|
if (errno == EIO)
|
|
{
|
|
LxtLogInfo("Skipping test %d", errno);
|
|
Result = LXT_RESULT_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
LxtLogError("Unexpected error %d", errno);
|
|
}
|
|
|
|
goto ErrorExit;
|
|
}
|
|
|
|
LxtCheckErrno(ioctl(Fd, TIOCGWINSZ, &WindowSize));
|
|
LxtLogInfo("%d, %d", WindowSize.ws_row, WindowSize.ws_col);
|
|
WindowSizeNew = WindowSize;
|
|
WindowSizeNew.ws_row += 1;
|
|
WindowSizeNew.ws_col += 1;
|
|
LxtCheckErrno(ioctl(Fd, TIOCSWINSZ, &WindowSizeNew));
|
|
LxtCheckErrno(ioctl(Fd, TIOCGWINSZ, &WindowSize));
|
|
LxtCheckMemoryEqual(&WindowSize, &WindowSizeNew, sizeof(WindowSize));
|
|
memset(&WindowSizeNew, 0, sizeof(WindowSizeNew));
|
|
LxtCheckErrno(ioctl(Fd, TIOCSWINSZ, &WindowSizeNew));
|
|
LxtCheckErrno(ioctl(Fd, TIOCGWINSZ, &WindowSize));
|
|
LxtCheckMemoryEqual(&WindowSize, &WindowSizeNew, sizeof(WindowSize));
|
|
|
|
Result = LXT_RESULT_SUCCESS;
|
|
|
|
ErrorExit:
|
|
if (Fd != -1)
|
|
{
|
|
LxtClose(Fd);
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
int TtysTermiosFlowControl(PLXT_ARGS Args)
|
|
|
|
/*++
|
|
--*/
|
|
|
|
{
|
|
|
|
int Cflag;
|
|
dev_t Device;
|
|
int Iflag;
|
|
int Index;
|
|
int Fd;
|
|
int Result;
|
|
struct termios Termios = {0};
|
|
struct termios TermiosNew = {0};
|
|
char VStart;
|
|
char VStop;
|
|
|
|
Fd = -1;
|
|
Result = LXT_RESULT_FAILURE;
|
|
|
|
//
|
|
// Check the default termios values.
|
|
//
|
|
// N.B. Ignore the termios settings that may differ..
|
|
//
|
|
|
|
Device = makedev(LXT_TTYS_DEV_MAJOR, LXT_TTYS_DEFAULT_MINOR);
|
|
mknod(LXT_TTYS_DEFAULT, LXT_TTYS_DEV_MODE, Device);
|
|
Fd = open(LXT_TTYS_DEFAULT, O_RDWR | O_NONBLOCK, 0);
|
|
if (Fd == -1)
|
|
{
|
|
if (errno == EIO)
|
|
{
|
|
LxtLogInfo("Skipping test %d", errno);
|
|
Result = LXT_RESULT_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
LxtLogError("Unexpected error %d", errno);
|
|
}
|
|
|
|
goto ErrorExit;
|
|
}
|
|
|
|
//
|
|
// Set and check the parity enable and type bits
|
|
//
|
|
// N.B. NT does not support setting input and output parity independently,
|
|
// but Linux does.
|
|
//
|
|
|
|
LxtCheckErrno(tcgetattr(Fd, &Termios));
|
|
Cflag = Termios.c_cflag;
|
|
Iflag = Termios.c_iflag;
|
|
VStart = Termios.c_cc[VSTART];
|
|
VStop = Termios.c_cc[VSTOP];
|
|
|
|
//
|
|
// ixon and ixoff set
|
|
//
|
|
|
|
Termios.c_iflag |= IXON;
|
|
Termios.c_iflag |= IXOFF;
|
|
LxtCheckErrno(tcsetattr(Fd, TCSANOW, &Termios));
|
|
LxtCheckErrno(tcgetattr(Fd, &TermiosNew));
|
|
LxtCheckMemoryEqual(&Termios, &TermiosNew, sizeof(Termios));
|
|
LxtClose(Fd);
|
|
Fd = -1;
|
|
Fd = open(LXT_TTYS_DEFAULT, O_RDWR | O_NONBLOCK, 0);
|
|
LxtCheckErrno(tcgetattr(Fd, &TermiosNew));
|
|
LxtCheckMemoryEqual(&Termios, &TermiosNew, sizeof(Termios));
|
|
|
|
//
|
|
// ixon and ixoff not set
|
|
//
|
|
|
|
Termios.c_iflag &= ~IXON;
|
|
Termios.c_iflag &= ~IXOFF;
|
|
LxtCheckErrno(tcsetattr(Fd, TCSANOW, &Termios));
|
|
LxtCheckErrno(tcgetattr(Fd, &TermiosNew));
|
|
LxtCheckMemoryEqual(&Termios, &TermiosNew, sizeof(Termios));
|
|
LxtClose(Fd);
|
|
Fd = -1;
|
|
Fd = open(LXT_TTYS_DEFAULT, O_RDWR | O_NONBLOCK, 0);
|
|
LxtCheckErrno(tcgetattr(Fd, &TermiosNew));
|
|
LxtCheckMemoryEqual(&Termios, &TermiosNew, sizeof(Termios));
|
|
|
|
//
|
|
// crtscts set
|
|
//
|
|
|
|
Termios.c_cflag |= CRTSCTS;
|
|
LxtCheckErrno(tcsetattr(Fd, TCSANOW, &Termios));
|
|
LxtCheckErrno(tcgetattr(Fd, &TermiosNew));
|
|
LxtCheckMemoryEqual(&Termios, &TermiosNew, sizeof(Termios));
|
|
LxtClose(Fd);
|
|
Fd = -1;
|
|
Fd = open(LXT_TTYS_DEFAULT, O_RDWR | O_NONBLOCK, 0);
|
|
LxtCheckErrno(tcgetattr(Fd, &TermiosNew));
|
|
LxtCheckMemoryEqual(&Termios, &TermiosNew, sizeof(Termios));
|
|
|
|
//
|
|
// crtscts not set
|
|
//
|
|
|
|
Termios.c_cflag &= ~CRTSCTS;
|
|
LxtCheckErrno(tcsetattr(Fd, TCSANOW, &Termios));
|
|
LxtCheckErrno(tcgetattr(Fd, &TermiosNew));
|
|
LxtCheckMemoryEqual(&Termios, &TermiosNew, sizeof(Termios));
|
|
LxtClose(Fd);
|
|
Fd = -1;
|
|
Fd = open(LXT_TTYS_DEFAULT, O_RDWR | O_NONBLOCK, 0);
|
|
LxtCheckErrno(tcgetattr(Fd, &TermiosNew));
|
|
LxtCheckMemoryEqual(&Termios, &TermiosNew, sizeof(Termios));
|
|
|
|
//
|
|
// clocal not set
|
|
//
|
|
|
|
LxtLogInfo("Clearing clocal");
|
|
Termios.c_cflag &= ~CLOCAL;
|
|
LxtCheckErrno(tcsetattr(Fd, TCSANOW, &Termios));
|
|
LxtCheckErrno(tcgetattr(Fd, &TermiosNew));
|
|
LxtCheckMemoryEqual(&Termios, &TermiosNew, sizeof(Termios));
|
|
LxtClose(Fd);
|
|
Fd = -1;
|
|
Fd = open(LXT_TTYS_DEFAULT, O_RDWR | O_NONBLOCK, 0);
|
|
LxtCheckErrno(tcgetattr(Fd, &TermiosNew));
|
|
LxtCheckMemoryEqual(&Termios, &TermiosNew, sizeof(Termios));
|
|
|
|
//
|
|
// clocal set
|
|
//
|
|
|
|
Termios.c_cflag |= CLOCAL;
|
|
LxtCheckErrno(tcsetattr(Fd, TCSANOW, &Termios));
|
|
LxtCheckErrno(tcgetattr(Fd, &TermiosNew));
|
|
LxtCheckMemoryEqual(&Termios, &TermiosNew, sizeof(Termios));
|
|
LxtClose(Fd);
|
|
Fd = -1;
|
|
Fd = open(LXT_TTYS_DEFAULT, O_RDWR | O_NONBLOCK, 0);
|
|
LxtCheckErrno(tcgetattr(Fd, &TermiosNew));
|
|
LxtCheckMemoryEqual(&Termios, &TermiosNew, sizeof(Termios));
|
|
|
|
//
|
|
// Flip vstart and vstop
|
|
//
|
|
|
|
LxtLogInfo("Updating vstart and vstop");
|
|
Termios.c_cc[VSTART] = VStop;
|
|
Termios.c_cc[VSTOP] = VStart;
|
|
LxtCheckErrno(tcsetattr(Fd, TCSANOW, &Termios));
|
|
LxtCheckErrno(tcgetattr(Fd, &TermiosNew));
|
|
LxtCheckMemoryEqual(&Termios, &TermiosNew, sizeof(Termios));
|
|
LxtClose(Fd);
|
|
Fd = -1;
|
|
Fd = open(LXT_TTYS_DEFAULT, O_RDWR | O_NONBLOCK, 0);
|
|
LxtCheckErrno(tcgetattr(Fd, &TermiosNew));
|
|
LxtCheckMemoryEqual(&Termios, &TermiosNew, sizeof(Termios));
|
|
|
|
//
|
|
// Reset to the original
|
|
//
|
|
|
|
LxtLogInfo("Resetting...");
|
|
Termios.c_cflag = Cflag;
|
|
Termios.c_iflag = Iflag;
|
|
Termios.c_cc[VSTART] = VStart;
|
|
Termios.c_cc[VSTOP] = VStop;
|
|
LxtCheckErrno(tcsetattr(Fd, TCSANOW, &Termios));
|
|
LxtCheckErrno(tcgetattr(Fd, &TermiosNew));
|
|
LxtCheckMemoryEqual(&Termios, &TermiosNew, sizeof(Termios));
|
|
LxtClose(Fd);
|
|
Fd = -1;
|
|
Fd = open(LXT_TTYS_DEFAULT, O_RDWR | O_NONBLOCK, 0);
|
|
LxtCheckErrno(tcgetattr(Fd, &TermiosNew));
|
|
LxtCheckMemoryEqual(&Termios, &TermiosNew, sizeof(Termios));
|
|
|
|
Result = LXT_RESULT_SUCCESS;
|
|
|
|
ErrorExit:
|
|
if (Fd != -1)
|
|
{
|
|
LxtClose(Fd);
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
int TtysWriteReadTransfer(PLXT_ARGS Args, int FdRead, int FdWrite)
|
|
|
|
/*++
|
|
--*/
|
|
|
|
{
|
|
|
|
int BytesRead;
|
|
int BytesWritten;
|
|
int BytesTotal;
|
|
pid_t ChildPid;
|
|
int Index;
|
|
char* RecvBuffer;
|
|
int Result;
|
|
char* SendBuffer;
|
|
|
|
ChildPid = -1;
|
|
RecvBuffer = NULL;
|
|
SendBuffer = NULL;
|
|
|
|
//
|
|
// Allocate a buffer with known data to transfer and a buffer to recieve
|
|
// the data.
|
|
//
|
|
|
|
SendBuffer = LxtAlloc(LXT_TTYS_LARGE_BUFFER_SIZE);
|
|
if (SendBuffer == NULL)
|
|
{
|
|
Result = LXT_RESULT_FAILURE;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
for (Index = 0; Index < LXT_TTYS_LARGE_BUFFER_SIZE; ++Index)
|
|
{
|
|
SendBuffer[Index] = (char)Index;
|
|
}
|
|
|
|
RecvBuffer = LxtAlloc(LXT_TTYS_LARGE_BUFFER_SIZE);
|
|
if (RecvBuffer == NULL)
|
|
{
|
|
Result = LXT_RESULT_FAILURE;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
memset(RecvBuffer, 0, LXT_TTYS_LARGE_BUFFER_SIZE);
|
|
|
|
//
|
|
// Send and recv the data.
|
|
//
|
|
|
|
LxtCheckErrno(ChildPid = fork());
|
|
if (ChildPid == 0)
|
|
{
|
|
for (BytesTotal = 0; BytesTotal < LXT_TTYS_LARGE_BUFFER_SIZE; BytesTotal += BytesWritten)
|
|
{
|
|
BytesWritten = write(FdWrite, &SendBuffer[BytesTotal], LXT_TTYS_LARGE_BUFFER_SIZE - BytesTotal);
|
|
|
|
if (BytesWritten == -1)
|
|
{
|
|
if ((errno == EAGAIN) || (errno == EINTR))
|
|
{
|
|
BytesWritten = 0;
|
|
continue;
|
|
}
|
|
|
|
Result = LXT_RESULT_FAILURE;
|
|
LxtLogError("Write failed with %d", errno);
|
|
goto ErrorExit;
|
|
}
|
|
}
|
|
|
|
_exit(0);
|
|
}
|
|
|
|
for (BytesTotal = 0; BytesTotal < LXT_TTYS_LARGE_BUFFER_SIZE; BytesTotal += BytesRead)
|
|
{
|
|
BytesRead = read(FdRead, &RecvBuffer[BytesTotal], LXT_TTYS_LARGE_BUFFER_SIZE - BytesTotal);
|
|
|
|
if (BytesRead == -1)
|
|
{
|
|
if ((errno == EAGAIN) || (errno == EINTR))
|
|
{
|
|
BytesRead = 0;
|
|
continue;
|
|
}
|
|
|
|
Result = LXT_RESULT_FAILURE;
|
|
LxtLogError("Read failed with %d", errno);
|
|
goto ErrorExit;
|
|
}
|
|
}
|
|
|
|
LxtWaitPidPoll(ChildPid, 0);
|
|
for (Index = 0; Index < LXT_TTYS_LARGE_BUFFER_SIZE; ++Index)
|
|
{
|
|
if (RecvBuffer[Index] != (char)Index)
|
|
{
|
|
Result = LXT_RESULT_FAILURE;
|
|
LxtLogError("Mismatch at index %d: %d != %d", Index, (int)(RecvBuffer[Index]), (int)((char)Index));
|
|
|
|
goto ErrorExit;
|
|
}
|
|
}
|
|
|
|
Result = 0;
|
|
|
|
ErrorExit:
|
|
if (ChildPid == 0)
|
|
{
|
|
_exit(Result);
|
|
}
|
|
|
|
if (RecvBuffer != NULL)
|
|
{
|
|
LxtFree(RecvBuffer);
|
|
}
|
|
|
|
if (SendBuffer != NULL)
|
|
{
|
|
LxtFree(SendBuffer);
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
int TtysWriteReadTermios(PLXT_ARGS Args, int FdRead, int FdWrite)
|
|
|
|
/*++
|
|
--*/
|
|
|
|
{
|
|
|
|
struct termios FdReadTermios;
|
|
int Fds[2];
|
|
struct termios FdWriteTermios;
|
|
int Index;
|
|
struct termios Termios;
|
|
int Result;
|
|
|
|
//
|
|
// Set the termios structure for transferring raw data.
|
|
//
|
|
|
|
Fds[0] = FdRead;
|
|
Fds[1] = FdWrite;
|
|
for (Index = 0; Index < LXT_COUNT_OF(Fds); ++Index)
|
|
{
|
|
LxtCheckErrno(tcgetattr(Fds[Index], &Termios));
|
|
Termios.c_iflag = 0;
|
|
Termios.c_oflag = 0;
|
|
Termios.c_cflag = B115200 | CREAD | CS8;
|
|
Termios.c_lflag = 0;
|
|
LxtCheckErrno(tcsetattr(Fds[Index], TCSANOW, &Termios));
|
|
}
|
|
|
|
memset(&FdReadTermios, 0, sizeof(FdReadTermios));
|
|
LxtCheckErrno(tcgetattr(FdRead, &FdReadTermios));
|
|
memset(&FdWriteTermios, 0, sizeof(FdWriteTermios));
|
|
LxtCheckErrno(tcgetattr(FdWrite, &FdWriteTermios));
|
|
LxtCheckMemoryEqual(&FdReadTermios, &FdWriteTermios, sizeof(FdWriteTermios));
|
|
|
|
Result = 0;
|
|
|
|
ErrorExit:
|
|
return Result;
|
|
}
|
|
|
|
int TtysWriteRead(PLXT_ARGS Args)
|
|
|
|
/*++
|
|
--*/
|
|
|
|
{
|
|
|
|
int BytesWritten;
|
|
dev_t Device;
|
|
int FdRead;
|
|
int FdWrite;
|
|
char Path[32];
|
|
int Result;
|
|
|
|
FdRead = -1;
|
|
FdWrite = -1;
|
|
|
|
Device = makedev(LXT_TTYS_DEV_MAJOR, LXT_TTYS_DEFAULT_MINOR);
|
|
mknod(LXT_TTYS_DEFAULT, LXT_TTYS_DEV_MODE, Device);
|
|
LxtCheckErrno(FdRead = open(LXT_TTYS_DEFAULT, O_RDWR, 0));
|
|
Device = makedev(LXT_TTYS_DEV_MAJOR, LXT_TTYS_DEFAULT2_MINOR);
|
|
mknod(LXT_TTYS_DEFAULT2, LXT_TTYS_DEV_MODE, Device);
|
|
LxtCheckErrno(FdWrite = open(LXT_TTYS_DEFAULT2, O_RDWR, 0));
|
|
LxtCheckResult(TtysWriteReadTermios(Args, FdRead, FdWrite));
|
|
|
|
//
|
|
// Transfer data with blocking IO
|
|
//
|
|
|
|
LxtCheckResult(TtysWriteReadTransfer(Args, FdRead, FdWrite));
|
|
|
|
//
|
|
// Transfer again with non-blocking IO.
|
|
//
|
|
|
|
Result = LXT_RESULT_SUCCESS;
|
|
|
|
ErrorExit:
|
|
if (FdRead != -1)
|
|
{
|
|
LxtClose(FdRead);
|
|
}
|
|
|
|
if (FdWrite != -1)
|
|
{
|
|
LxtClose(FdWrite);
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
int TtysModemIoctls(PLXT_ARGS Args)
|
|
|
|
/*++
|
|
--*/
|
|
|
|
{
|
|
|
|
dev_t Device;
|
|
int Fd;
|
|
int ModemSettings;
|
|
int ModemSettingsOrig;
|
|
int Result;
|
|
struct termios Termios;
|
|
struct termios TermiosOrig;
|
|
|
|
Fd = -1;
|
|
ModemSettingsOrig = -1;
|
|
Result = LXT_RESULT_FAILURE;
|
|
|
|
//
|
|
// Check the default modem settings
|
|
//
|
|
|
|
Device = makedev(LXT_TTYS_DEV_MAJOR, LXT_TTYS_DEFAULT_MINOR);
|
|
mknod(LXT_TTYS_DEFAULT, LXT_TTYS_DEV_MODE, Device);
|
|
Fd = open(LXT_TTYS_DEFAULT, O_RDWR | O_NONBLOCK, 0);
|
|
if (Fd == -1)
|
|
{
|
|
if (errno == EIO)
|
|
{
|
|
LxtLogInfo("Skipping test %d", errno);
|
|
Result = LXT_RESULT_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
LxtLogError("Unexpected error %d", errno);
|
|
}
|
|
|
|
goto ErrorExit;
|
|
}
|
|
|
|
LxtCheckResult(ioctl(Fd, TIOCMGET, &ModemSettingsOrig));
|
|
LxtLogInfo("ModemSettingsOrig: %d", ModemSettingsOrig);
|
|
ModemSettings = ModemSettingsOrig;
|
|
LxtCheckResult(ioctl(Fd, TIOCMSET, &ModemSettings));
|
|
LxtCheckResult(ioctl(Fd, TIOCMGET, &ModemSettings));
|
|
LxtCheckEqual(ModemSettings, ModemSettingsOrig, "%d");
|
|
|
|
//
|
|
// Check that invalid settings are ignored
|
|
//
|
|
|
|
ModemSettings = -1;
|
|
LxtCheckResult(ioctl(Fd, TIOCMSET, &ModemSettings));
|
|
LxtCheckResult(ioctl(Fd, TIOCMGET, &ModemSettings));
|
|
LxtCheckNotEqual(ModemSettings, ModemSettingsOrig, "%d");
|
|
ModemSettings = -1;
|
|
LxtCheckResult(ioctl(Fd, TIOCMBIC, &ModemSettings));
|
|
LxtCheckResult(ioctl(Fd, TIOCMGET, &ModemSettings));
|
|
LxtCheckEqual(ModemSettings, 0, "%d");
|
|
ModemSettings = -1;
|
|
LxtCheckResult(ioctl(Fd, TIOCMBIS, &ModemSettings));
|
|
LxtCheckResult(ioctl(Fd, TIOCMGET, &ModemSettings));
|
|
LxtCheckNotEqual(ModemSettings, ModemSettingsOrig, "%d");
|
|
|
|
//
|
|
// Recheck the settings after closing and reopening.
|
|
//
|
|
|
|
LxtClose(Fd);
|
|
Fd = -1;
|
|
LxtCheckErrno(Fd = open(LXT_TTYS_DEFAULT, O_RDWR | O_NONBLOCK, 0));
|
|
LxtCheckResult(ioctl(Fd, TIOCMGET, &ModemSettings));
|
|
LxtCheckNotEqual(ModemSettings, ModemSettingsOrig, "%d");
|
|
|
|
//
|
|
// Check DTR
|
|
//
|
|
// N.B. Some serial drivers start up with DTR on native Linux.
|
|
//
|
|
|
|
ModemSettings = ModemSettingsOrig & ~TIOCM_DTR;
|
|
LxtCheckResult(ioctl(Fd, TIOCMSET, &ModemSettings));
|
|
LxtCheckResult(ioctl(Fd, TIOCMGET, &ModemSettings));
|
|
LxtCheckEqual(ModemSettings, ModemSettingsOrig & ~TIOCM_DTR, "%d");
|
|
LxtClose(Fd);
|
|
Fd = -1;
|
|
LxtCheckErrno(Fd = open(LXT_TTYS_DEFAULT, O_RDWR | O_NONBLOCK, 0));
|
|
|
|
ModemSettings = TIOCM_DTR;
|
|
LxtCheckResult(ioctl(Fd, TIOCMBIS, &ModemSettings));
|
|
LxtCheckResult(ioctl(Fd, TIOCMGET, &ModemSettings));
|
|
LxtCheckEqual(ModemSettings, ModemSettingsOrig | TIOCM_DTR, "%d");
|
|
LxtClose(Fd);
|
|
Fd = -1;
|
|
LxtCheckErrno(Fd = open(LXT_TTYS_DEFAULT, O_RDWR | O_NONBLOCK, 0));
|
|
LxtCheckResult(ioctl(Fd, TIOCMGET, &ModemSettings));
|
|
LxtCheckEqual(ModemSettings, ModemSettingsOrig | TIOCM_DTR, "%d");
|
|
|
|
//
|
|
// Check that changing RTS doesn't impact termios
|
|
//
|
|
|
|
LxtCheckErrno(tcgetattr(Fd, &TermiosOrig));
|
|
ModemSettings = TIOCM_RTS;
|
|
LxtCheckResult(ioctl(Fd, TIOCMBIS, &ModemSettings));
|
|
LxtCheckErrno(tcgetattr(Fd, &Termios));
|
|
LxtCheckMemoryEqual(&TermiosOrig, &Termios, sizeof(Termios));
|
|
ModemSettings = TIOCM_RTS;
|
|
LxtCheckResult(ioctl(Fd, TIOCMBIC, &ModemSettings));
|
|
LxtCheckErrno(tcgetattr(Fd, &Termios));
|
|
LxtCheckMemoryEqual(&TermiosOrig, &Termios, sizeof(Termios));
|
|
|
|
Result = LXT_RESULT_SUCCESS;
|
|
|
|
ErrorExit:
|
|
if (Fd != -1)
|
|
{
|
|
if (ModemSettingsOrig != -1)
|
|
{
|
|
LxtCheckResult(ioctl(Fd, TIOCMSET, &ModemSettingsOrig));
|
|
}
|
|
|
|
LxtClose(Fd);
|
|
}
|
|
|
|
return Result;
|
|
}
|