User Tools

Site Tools


drivers

Main steps when implementing a character driver:

  1. implement operations corresponding to system calls from an application
  2. define a file_operations structure associated to the implemented functions
  3. reserve a ser of major and minor numbers for the driver
  4. tell the kernel to associate the reserved major and minor to the defined file operations

Kernel APIs

These are some useful general-purpose kernel APIs:

  • from <linux/string.h>, memory-related (memset, memcpy, …), string-related (strcpy, strcat, …)
  • from <linux/kernel.h>, string to int conversion (simple_strtoul, …) and other string functions (sprintf, …)
  • from <linux/list.h>, linked-list facilities (struct list_head, list_add(), …)

Kernel memory allocation

  • kmalloc is the primary allocator in the Linux kernel, for objects from 8 bytes to 128KB. The allocated area is guaranteed to be physically contiguous.
  • vmalloc provides virtually contiguous memory zones that may not be physically contiguous. The resulting virtual addresses are higher than the top of physical memory. vmalloc cannot be used when the real physical address is needed, e.g. for DMA, and cannot be used at interrupt time. Allocation of large areas is possible, since physical memory fragmentation is not an issue.
  • kzalloc
  • kcalloc
  • dma_alloc_coherent
  • dma_alloc_sg
  • contiguous memory algorithms (CMA, ION)

Debugging features:

  • Kmemcheck
  • Kmemleak

MMIO. I/O memory load/store instructions work with virtual addresses, therefore drivers need to have the proper virtual address. This is configured with ioremap(), iounmap(). MMIO devices are accessed with (read[bwl], write[bwl]) or without endianness conversion (raw_read[bwl], raw_write[bwl]).

An issue with I/O memory accesses is memory reordering, which may require memory barriers (rmb(), wmb(), mw()).

User space memory handling

User-space applications can access physical addresses directly through /dev/mem.

  • Process heap (sbrk, brk)
  • Direct/Abstract (mmap)
  • Allocators (malloc, calloc, realloc, free)

Modules

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>

static int myparam = 1;
module_param(myparam, int, 0);

static int __init modname_init(void)
{
}

static void __exit modname_exit(void)
{
}

module_init(modname_init);
module_exit(modname_exit);
MODULE_LICENSE(¨GPL¨);
MODULE_DESCRIPTION(¨blah blah¨);
MODULE_AUTHOR(¨me¨);

Functions and variables have to be explicitly exported by the kernel to be visible from a module:

EXPORT_SYMBOL(symname);

A kernel module will only run in a kernel that matches the version of the kernel headers it was compiled against. Example of makefile for an out-of-tree module:

ifneq ($(KERNELRELEASE),)
obj-m := hello.o
else
KDIR := /path/kernel/sources     # if not full sources, at least headers directory
all:
    $(MAKE) -C $(KDIR) M=`pwd` modules
endif

Sleeping

/* dynamic declaration */
wait_queue_head_t queue;
init_waitqueue_head(&queue);
/* blocking */
wait_event_interruptible(queue, condition);
/* unblocking - if condition is still false, nothing happens;
   if the condition is true, the process in the wait queue is
   awakaned and its state set to TASK_RUNNING */
wake_up_interruptible(&queue);

Interrupts

Top half and botton half processing:

  • Top half. The real interrupt handler. Schedules a bottom half to handle it.
  • Bottom half, implemented the available work deferring mechanisms:
    • SoftIRQs, executed with all i/r enabled. They run in interrupt context. A softirq handler can run simultaneously on multiple CPUs. They are executed once all i/r handlers have completed, before the scheduler resumes (sleeping not allowed). HI and TASKLET softirqs are used to execute tasklets.
    • Tasklets, executed with all i/r enabled. They are built on top of softirqs. They run in interrupt context. A taklet is guaranteed to execute on a single CPU at a time. Implemented as a function, and easily usable by individual device drivers. The interrupt handler can schedule the execution of a tasklet with tasklet_schedule() or tasklet_hi_schedule().
    • Workqueues, not limited to handling interrupts. They run in process context (kernel thread). All i/r enabled, and sleeping is allowed. A workqueue is registered with INIT_WORK and triggered with queue_work().

Interrupt handler constraints

  • It is not possible to transfer data to and from user space.
  • Execution is managed by the CPU, not by the scheduler (actions that may sleep are not possible).
  • All interrupts disabled since 2.6.36. Care should be taked not to block interrupts for too long.

Interrupt handler taks

  • acknowledge i/r to the device
  • act on data from/to the device
  • wake up any waiting process (wake_up_interruptible)

Threaded interrupts

Threaded interrupts are executed inside a thread (allows to block inside the handler). There is support for interrupt handler execution priority.

UIO allows the handling from interrupt in user space.

Concurrency

The kernel lock validator is an useful tool to detect violations of locking rules during system life. Alternatives to locking are the use of lock-free algorithms (e.g. read copy update, RCU) or atomic operations (.e.g. atomic_set/atomic_read).

Semaphores

Mutexes

DEFINE_MUTEX(mname);
mutex_lock(&mname);
mutex_trylock(&mname);
mutex_is_locked(&mname);
mutex_unlock(&mname);

Spinlocks

Busy waiting locks, therefore usable inside interrupt handlers or critical sections in process context (that don´t want to sleep). Spinlocks cause kernel preemption to be disabled on the CPU executing them.

DEFINE_SPINLOCK(sname);
spin_lock(&sname);
... critical code ...
spin_unlock(&sname);

Debugging

Userspace:

  • printf
  • strace
  • ltrace
  • valgrind
  • gdb

Userspace drivers

drivers.txt · Last modified: by admin

Donate Powered by PHP Valid HTML5 Valid CSS Driven by DokuWiki