Repository-Metriken
- Stars
- (17.886 Stars)
- PR-Merge-Metriken
- (Durchschn. Merge 12T 11h) (661 gemergte PRs in 30 T)
Beschreibung
Description
Trying to open a System.IO.Ports.SerialPort for 250000 baud under Linux gives an argument exception. Even if it didn't the underlying code would still fail to properly open that rate.
This is a common serial rate for many 3D printers, such as all Lulzbot printers.
Bot said to add an Area, but didn't say where. So here it is: @adamsitnik
Reproduction Steps
Running on any version of .NET (fails in 5 and 6) on a Raspberry Pi with the latest 32-bit Rasberry Pi OS, attempt to open a serial port:
var port port = new SerialPort("/dev/ttyACM0", 250000, Parity.None, 8, StopBits.One);
Expected behavior
I expect it to open and be usable.
Actual behavior
An exception is thrown.
Regression?
No response
Known Workarounds
I have a known-good, working workaround. It's ugly, but works. First, open the port with a supported rate, like 115200. then run this code:
[DllImport("libc", SetLastError = true)]
private static extern int ioctl(IntPtr fd, uint request, int[] data);
private void SetNonStandardBaudRateIfRequired()
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)
&& _baudRate > 115200)
{
// due to limitations/behavior of Linux driver, we need to work around setting the baud rate above 115200
var stream = m_port.GetType()
.GetField("_internalSerialStream", BindingFlags.NonPublic | BindingFlags.Instance)
.GetValue(m_port);
var handle = (SafeHandle)stream.GetType()
.GetField("_handle", BindingFlags.NonPublic | BindingFlags.Instance)
.GetValue(stream);
uint TCGETS2 = 0x802C542A;
uint TCSETS2 = 0x402C542B;
var BOTHER = 0x1000; // 0o010000;
var CBAUD = 0x100F; // 0o010017;
var buffer = new int[64];
// get the current value
var fd = handle.DangerousGetHandle();
ioctl(fd, TCGETS2, buffer);
buffer[2] &= ~CBAUD; // turn off the standard baud flag
buffer[2] |= BOTHER; // turn on the "other baud rate" flag
buffer[9] = buffer[10] = _baudRate; // set the same rate in both directions
Console.WriteLine($"BAUD RATE WORKAROUND");
// write the new value
var result = ioctl(fd, TCSETS2, buffer);
if (result != 0)
{
// failure
Console.WriteLine($"IOCTL FAILED: {Marshal.GetLastWin32Error()}");
return;
}
// verify
ioctl(fd, TCGETS2, buffer);
if (buffer[9] != _baudRate || buffer[10] != _baudRate)
{
// failure
Console.WriteLine($"IOCTL FAILED: read != write");
return;
}
}
}
I told you it was ugly.
NOTE that this code is unsetting the CBAUD flag and then setting the BOTHER flag, which the BCL does not do. Simply allowing 250000 baud through is not enough, the logic above must be applied.
NOTE 2 the above is not the right way to do it in the BCL. The proper fix should be done in C in the PAL, somewhere in this method:
I would have done a fork and PR, but honestly I don't have the native toolchain set up, and that feels like a huge headache. Anyone with a little C knowledge can port the above into that file.
Configuration
.NET 5.0 Raspberry Pi 4 ARM
pi@raspberrypi:~/marlin $ cat /etc/os-release
PRETTY_NAME="Raspbian GNU/Linux 11 (bullseye)"
NAME="Raspbian GNU/Linux"
VERSION_ID="11"
VERSION="11 (bullseye)"
VERSION_CODENAME=bullseye
This is not specific to this configuration. I'm willing to bet it will fail on any Linux kernel.
Other information
Fix and where it belongs are detailed above