Using <Linux/Types.H> in User Programs, or <Stdint.H> in Driver Module Code...Does It Matter

Using linux/types.h in user programs, or stdint.h in driver module code...does it matter?

  1. 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.

  1. 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:

  1. I cleaned the build directory. Building on top of old builds caused failures with these files, not sure why.
  2. 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



Leave a reply



Submit