I'm developing an application which needs to interact with Video4Linux abstraction. The application is developed in C#, using the mono framework.
The problem I'm facing is that I can't P/Invoke the ioctl
system call. Or, more precisely, I can P/Invoke it, but it crashes badly.
The extern declaration is the following:
[DllImport("libc", EntryPoint = "ioctl", SetLastError = true)]
private extern static int KernelIoCtrl(int fd, int request, IntPtr data);
So far so good.
The actual routine that uses the KernelIoCtrl
is the following:
protected virtual int Control(IoSpecification request, object data)
{
GCHandle dataHandle;
IntPtr dataPointer = IntPtr.Zero;
try {
// Pin I/O control data
if (data != null) {
dataHandle = GCHandle.Alloc(data, GCHandleType.Pinned);
dataPointer = dataHandle.AddrOfPinnedObject();
}
// Perform I/O control
int result = KernelIoCtrl(mFileDescriptor, request.RequestCode, dataPointer);
int errno = Marshal.GetLastWin32Error();
// Throw exception on errors
if (errno != (int)ErrNumber.NoError)
throw new System.ComponentModel.Win32Exception(errno);
return (result);
} finally {
if (dataPointer != IntPtr.Zero)
dataHandle.Free();
}
}
All the above code seems good. The class IoSpecification
is used for computing the I/O request code following the header specification (basically it follows the _IOC
macro declared at /usr/include/linux/asm/ioctl.h
.
The data
parameter is a structure, declared as follow:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct Capability
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 16)]
public string Driver;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public string Device;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public string BusInfo;
public UInt32 Version;
public CapabilityFlags Capabilities;
[MarshalAs(UnmanagedType.ByValArray, SizeConst=4)]
public UInt32[] Reserved;
}
which should mimic the following structure (declared at /usr/include/linux/videodev2.h
):
struct v4l2_capability {
__u8 driver[16]; /* i.e. "bttv" */
__u8 card[32]; /* i.e. "Hauppauge WinTV" */
__u8 bus_info[32]; /* "PCI:" + pci_name(pci_dev) */
__u32 version; /* should use KERNEL_VERSION() */
__u32 capabilities; /* Device capabilities */
__u32 reserved[4];
};
Before having the crash, there was a problem in the IOCTL request code computation, and the KernelIoCtrl
was working as expected (returning an errno
equals to EINVAL). When I corrected the bug (and indeed having the correct IOCTRL request code), the call has started to cause a crash.
In conclusion, it seems there's a problem in the structure marshalling, but I cannot see what it's going wrong.
I fear that the problem is the variable argument list, because the ioctl routine is declared as follow (taken from man):
int ioctl(int d, int request, ...);
But I saw lot of code declaring the above routine as int ioctl(int d, int request, void*);
, and I can ensure that the specific IOCTRL request takes only one argument.
See Question&Answers more detail:
os