As far as I know, ioctl()
is used to expose an "extended" system call interface to userspace applications. Rather than adding thousands of system calls that are unique to specific drivers, ioctl()
is used to provide extensible driver-specific functions through a single system call.
This seems clear enough. However, I'm trying to compile my first application that uses an ioctl()
call, and I'm starting to doubt my understanding.
Specifically, I want to make an ioctl()
call to "sanitize" an eMMC device. Taking a look at /usr/include/linux/mmc/ioctl.h
(or in kernel source at include/uapi/linux/mmc/ioctl.h
), I can see this structure:
struct mmc_ioc_cmd {
// Most fields omitted
int write_flag;
__u32 opcode;
__u32 arg;
};
From userspace, I don't have any issues including this header and passing this structure into my ioctl()
calls.
So this is what my final sanitize snippet looks like:
int sanitize(int fd)
{
struct mmc_ioc_cmd command;
memset(&command, 0, sizeof(command));
command.write_flag = 1;
command.opcode = MMC_SWITCH;
command.arg = EXT_CSD_SANITIZE_START << 16;
return ioctl(fd, MMC_IOC_CMD, &command);
}
My problem is that MMC_SWITCH
and EXT_CSD_SANITIZE_START
are both defined in kernel headers. Specifically, in my kernel source, they're both found at include/linux/mmc/mmc.h
.
Everything I've seen on the internet says to not include headers from the kernel source when building userspace projects. If that's the case, how can you reasonably use the MMC ioctl()
? The kernel exposes the structure to pass into ioctl()
, but it seems like you can only use the structure by filling it in with "hidden" constants hidden in the kernel source.
My current solution is to copy the necessary constants from the kernel headers to my own project, but this feels dirty to me.
Am I misunderstanding the use-case for ioctl()
? Is this a design oversight?
The MMC_IOC_CMD
ioctl, and the corresponding mmc_ioc_cmd
structure, are part of the Linux userspace API, and therefore are defined in the uapi
headers that are installed into /usr/include
.
The value that you put into the opcode
field gets sent directly to the device. The kernel does not really care what it is, and cannot guarantee what opcodes the device supports, or how it behaves for any specific opcode. Therefore, opcodes like MMC_SWITCH
are not part of the API.
As far as I can see, you are supposed to get the opcodes from the relevant MMC standards.
(This is not really a good reason to keep these symbols out of the user-space API; copying the kernel header is much easier than manually transcribing the values from the standard. And the kernel actually has a special case for handling EXT_CSD_SANITIZE_START
through this ioctl.)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With