Using linux/types.h in user programs, or stdint.h in driver module code...does it matter?
- Is it a bad idea to use linux/*.h includes in user-space code?
Yes, usually. The typical situation is that you should be using the C-library headers (in this case, stdint.h
and friends), and interface with the C library though those user-space types, and let the library handle talking with the kernel through kernel types.
You're not in a typical situation though. In your case, you're writing the driver library. So you should be presenting an interface to userspace using stdint.h
, but using the linux/*.h
headers when you interface to your kernel driver.
So the answer is no, in your case.
- Is it a bad idea to use stdint.h in kernel-space code?
Most definitely yes.
See also: http://lwn.net/Articles/113349/
When to use stdint.h's scalars in driver code
1. fixed with vs. fundamental types
Fixed width types are sometimes difficultly to use. E.g. printf()
specifiers for int32_t
are PRIi32
and require splitting of the format string:
printk("foo=" PRIi32 ", bar=" PRIi32 "\n", foo, bar);
Fixed width types should/must be used when hardware is accessed directly; e.g. when writing DMA descriptors. But for simple register accesses, writel()
or readl()
functions can be used which work with fundamental types.
As a rule of thumb, when a certain memory layout is assumed (like the __attribute__((__aligned__(16)))
in your example, fixed width types should be used.
Signed fixed width types (int32_t x,y
in your example) might need double checking, whether their representation matches the hardware expectations.
NOTE that in your example, the second structure is architecture dependent because of
uint32_t ptr; // convert to void* in order to use it
Writing such thing in common C would be uintptr_t ptr
and in the kernel it is common to write
unsigned long ptr;
Alternatively, dma_addr_t
might be a better type.
2. uint32_t
vs. __u32
More than 10 years ago, Linus Torvalds objected against uint32_t
because at this time, non-C99 compilers were common and using such types in (exported) linux headers would pollute the namespace.
But now, uint32_t
and similar types are available everywhere (you can not compile the kernel with a non-C99 compiler) and kernel header export has been improved significantly, so these arguments are gone.
It is a matter of personal preference whether to use standard types or typedef'ed variants (which are framework dependent and differ between them).
3. uint_fastX_t
and variants
They are not used in the kernel and I would avoid them. They combine disadvantages of uint32_t
(difficult usage) and int
(variable width).
4. __le32
vs. __u32
Use the endian types when specification explicitly requires them (e.g. in network protocol implementations). This makes it easy to detect wrong usage (e.g. assignments like endian_variable = native_variable
).
Do not use them e.g. for filling processor structures (e.g. DMA descriptors); some processors can run both in little and big endian mode and native datatypes are usually the right way to write such infomration.
Identifying kernel space in the preprocessor?
From reading this, it seems that an existing constant used for this purpose is __KERNEL__
.
#ifndef __KERNEL__
#include <stdint.h>
#else
#include <linux/types.h>
#endif
How to share data-structure definition between kernel modules and user-application?
What you are creating is a character device. The kernel documentation includes a specific section, the Linux driver implementer's guide, you should also read. Specifically, the ioctl based interfaces section, which also describes some of the considerations necessary (regarding alignment and 64-bit fields).
As to header files, see KernelHeaders article at kernelnewbies.org.
I would like to have the structure defined into only one file, having it defined in multiple files seems to be quite error-prone if i plan on changing it in the future.
No. You do specify the headers in two separate files: one for use in-kernel, and the other for use by userspace.
Kernel-userspace interface should be stable. You should take care to design your data structure so that you can extend it if necessary; preferably by adding some padding reserved for future use and required to be initialized to zero, and/or a version number at the beginning of the structure. Later versions must support all previous versions of the structure as well. Even if it is only a "toy" or "experimental" device driver, it is best to learn to do it right from the get go. This stuff is much, much harder to learn to "add afterwards"; I'm talking from deep experience here.
As a character device, you should also be prepared for the driver to be compiled on other architectures besides the one you are developing on. Even byte order ("endianness") can vary, although all Linux architectures are currently either ILP32 or LP64.
Also remember that there are several hardware architectures, including x86-64, that support both 64-bit and 32-bit userspace. So, even if you believe your driver will ever be used on x86-64, you cannot really assume the userspace is 64-bit (and not 32-bit). Look at existing code to see how it is done right; I recommend using e.g. bootlin's elixir to browse the Linux kernel sources.
Kernel-side header file should use __s8
, __u8
, __s16
, __u16
, __s32
, __u32
, __s64
, or __u64
. For pointers, use __u64
, and u64_to_user_ptr()
.
Userspace-side header file should use <stdint.h>
types (int8_t
, uint8_t
, int16_t
, uint16_t
, int32_t
, uint32_t
, int64_t
, uint64_t
) and uint64_t
for pointers. Use a cast via (uintptr_t)
for the conversion, i.e. ptr = (void *)(uintptr_t)u64;
and u64 = (uintptr_t)ptr
.
Ensure all members are naturally aligned. This means that an N-bit member is preceded by k×N bits of other members, where k is either zero or a positive integer. Thus, if your structure needs an unsigned and a signed 8-bit integer (one for version, one for foo), an 16-bit signed integer (bar), and a 64-bit unsigned integer (baz), considering the version should be first, you'll probably want
struct kernel_side {
__u8 version;
__s8 foo;
__u16 bar;
__u32 padding;
__u64 baz;
};
struct userspace_side {
uint8_t version;
int8_t foo;
uint16_t bar;
uint32_t padding;
uint64_t baz;
};
You can also have character arrays and such, but do note that a single ioctl data block is limited to 8191 bytes or less in length.
If you spend some time designing your interface structures, you'll find that careful design will avoid annoying issues like compat_ support (making them just simple wrappers). Personally, I end up creating a test version with a userspace test program to see what works best, and only then decide on the data structures.
Compilation error when linking global array of structs
Here's what ended up working for me:
- I cleaned the build directory. Building on top of old builds caused failures with these files, not sure why.
- As @IanAbbott mentioned, I moved the MODULE_DEVICE_TABLE() macro to be within the same file as the i2c_device_id table definition. As Ian and Fabio mentioned the macro needs to see the size of the table and cannot see that with just the extern. I didn't want to have future coders needing to define the size with every new board so I chose to keep them together. Here's how that will look:
somedriver_cardSpecific.c
#include <linux/module.h>
#include <linux/i2c.h>
#include "somedriver.h"
struct i2c_device_id somedriver_idtable[] = {
{ "somedevice_1", 0 },
{ "somedevice_2", 1 },
{ },
};
MODULE_DEVICE_TABLE(i2c, somedriver_idtable);
somedriver.h
#include <linux/i2c.h>
extern struct i2c_device_id somedriver_idtable[];
Driver still works fine. I hope that helps someone who comes across same problem.
Related Topics
Is There a Limit on Number of Tcp/Ip Connections Between MAChines on Linux
How to Remove All White Spaces from a Given Text File
How to Read Websocket Response in Linux Shell
Is There Any Modern Review of Solutions to the 10000 Client/Sec Problem
How to Find Ports Opened by Process Id in Linux
Linux Process in Background - "Stopped" in Jobs
Truncating the First 100Mb of a File in Linux
How to Execute Mips Assembly Programs on an X86 Linux
Find and Replace a Particular Term in Multiple Files
What Is the *Nix Command to View a User's Default Login Shell
Unix: Differencebetween Source and Export
How to Find Files Modified in Last X Minutes (Find -Mmin Does Not Work as Expected)
Maximum Number of Files/Directories on Linux
How Use Qt in Visual Studio Code
What's the Meaning of a ! Before a Command in the Shell
Smbclient - Send All Files in Directory
Ocr - Getting Text from Image Using Tesseract 3.0 and Imagemagick 6.6.5