Kernel Data Types

The kernel uses specific data types for portability and clarity. Understanding these is crucial for writing correct driver code.

Fixed-Width Integer Types

Always use fixed-width types when size matters (hardware registers, protocols):

#include <linux/types.h>

u8   val8;    /* Unsigned 8-bit  (0 to 255) */
u16  val16;   /* Unsigned 16-bit (0 to 65535) */
u32  val32;   /* Unsigned 32-bit */
u64  val64;   /* Unsigned 64-bit */

s8   sval8;   /* Signed 8-bit  (-128 to 127) */
s16  sval16;  /* Signed 16-bit */
s32  sval32;  /* Signed 32-bit */
s64  sval64;  /* Signed 64-bit */

When to Use Fixed-Width Types

/* Hardware register access - ALWAYS use fixed-width */
u32 reg_value = readl(base + CONTROL_REG);

/* Protocol data structures */
struct my_header {
        u8  version;
        u8  flags;
        u16 length;
        u32 sequence;
} __packed;

/* Loop counters, local variables - use int */
int i;
for (i = 0; i < count; i++)
        ...
size_t     len;    /* Size of objects (unsigned) */
ssize_t    ret;    /* Size or error (signed) */
loff_t     offset; /* File offset (64-bit) */

Usage Example

static ssize_t my_read(struct file *file, char __user *buf,
                       size_t count, loff_t *ppos)
{
        size_t remaining = data_len - *ppos;
        size_t to_copy = min(count, remaining);

        if (copy_to_user(buf, data + *ppos, to_copy))
                return -EFAULT;

        *ppos += to_copy;
        return to_copy;  /* ssize_t return: count or negative errno */
}

Physical and DMA Addresses

#include <linux/types.h>

phys_addr_t   phys;    /* Physical address */
dma_addr_t    dma;     /* DMA address */
resource_size_t res;   /* Resource size/address */

Example: DMA Allocation

void *virt;
dma_addr_t dma_handle;

virt = dma_alloc_coherent(dev, size, &dma_handle, GFP_KERNEL);
if (!virt)
        return -ENOMEM;

/* dma_handle is the address to give to hardware */
writel(dma_handle, base + DMA_ADDR_REG);

Boolean Type

#include <linux/types.h>

bool enabled = true;
bool valid = false;

if (enabled)
        do_something();

Pointer Annotations

__user - User Space Pointer

/* Never dereference directly! */
char __user *user_buf;

/* Use accessor functions */
if (copy_from_user(kernel_buf, user_buf, len))
        return -EFAULT;

__iomem - I/O Memory Pointer

void __iomem *base;

base = ioremap(phys_addr, size);
if (!base)
        return -ENOMEM;

/* Use I/O accessors */
u32 val = readl(base + OFFSET);
writel(new_val, base + OFFSET);

__percpu - Per-CPU Data

DEFINE_PER_CPU(int, my_counter);

/* Access current CPU's copy */
int val = get_cpu_var(my_counter);
put_cpu_var(my_counter);

Endianness

Hardware may use different byte order than CPU:

Little-endian (x86, ARM default):
0x12345678 stored as: 78 56 34 12

Big-endian (network byte order):
0x12345678 stored as: 12 34 56 78

Conversion Functions

#include <linux/byteorder/generic.h>

/* CPU to little-endian */
u32 le = cpu_to_le32(value);
u16 le = cpu_to_le16(value);

/* Little-endian to CPU */
u32 cpu = le32_to_cpu(le_value);
u16 cpu = le16_to_cpu(le_value);

/* CPU to big-endian */
u32 be = cpu_to_be32(value);

/* Big-endian to CPU */
u32 cpu = be32_to_cpu(be_value);

Annotated Types

#include <linux/types.h>

__le16  le_val16;  /* Little-endian 16-bit */
__le32  le_val32;  /* Little-endian 32-bit */
__le64  le_val64;  /* Little-endian 64-bit */

__be16  be_val16;  /* Big-endian 16-bit */
__be32  be_val32;  /* Big-endian 32-bit */
__be64  be_val64;  /* Big-endian 64-bit */

Example: Hardware Register Structure

struct device_registers {
        __le32 control;    /* Little-endian hardware */
        __le32 status;
        __le32 data;
} __packed;

static void write_control(struct device_registers __iomem *regs, u32 val)
{
        /* Convert CPU to little-endian, then write */
        writel(val, &regs->control);  /* writel handles conversion */
}

static u32 read_status(struct device_registers __iomem *regs)
{
        /* readl reads and converts to CPU endianness */
        return readl(&regs->status);
}

readl() and writel() handle endianness automatically for memory-mapped I/O on most architectures.

Structure Packing

__packed Attribute

Remove padding between structure members:

/* Without __packed: may have padding */
struct normal {
        u8  a;      /* 1 byte */
                    /* 3 bytes padding */
        u32 b;      /* 4 bytes */
};                  /* Total: 8 bytes */

/* With __packed: no padding */
struct packed {
        u8  a;      /* 1 byte */
        u32 b;      /* 4 bytes */
} __packed;         /* Total: 5 bytes */

__aligned Attribute

struct aligned_data {
        u32 value;
} __aligned(64);  /* 64-byte alignment */

Atomic Types

For concurrent access without locks:

#include <linux/atomic.h>

atomic_t counter = ATOMIC_INIT(0);

atomic_inc(&counter);          /* counter++ */
atomic_dec(&counter);          /* counter-- */
int val = atomic_read(&counter);
atomic_set(&counter, 10);

/* For 64-bit atomic operations */
atomic64_t big_counter = ATOMIC64_INIT(0);

Time Types

#include <linux/time.h>
#include <linux/ktime.h>

ktime_t now = ktime_get();           /* High-resolution time */
time64_t seconds = ktime_get_real_seconds();  /* Wall clock */

unsigned long jiffies_val = jiffies; /* Tick counter */

Error Pointers

Encode errors in pointer values:

#include <linux/err.h>

void *ptr;

ptr = some_allocation();
if (IS_ERR(ptr))
        return PTR_ERR(ptr);  /* Extract error code */

/* Create error pointer */
return ERR_PTR(-ENOMEM);

Summary

Use Case Type
Hardware registers u8, u16, u32, u64
Sizes and counts size_t, ssize_t
File offsets loff_t
Physical addresses phys_addr_t
DMA addresses dma_addr_t
User pointers __user annotation
I/O memory __iomem annotation
Little-endian __le16, __le32 with cpu_to_le*
Big-endian __be16, __be32 with cpu_to_be*

Next

Learn about error handling patterns used throughout the kernel.


Back to top

Linux Driver Development Guide is a community resource for learning kernel driver development. Not affiliated with the Linux Foundation. Content provided for educational purposes.

This site uses Just the Docs, a documentation theme for Jekyll.