edit SideBar

Main / Drivers

Linux Devices

How the Linux system sees drivers... e.g. every USB device is driven by a USB module that works with the USB subsystem, but the device itself shows up in the system as a char device (a USB serial port, say), a block device (a USB memory card reader), or a network device (a USB Ethernet interface).

A key difference between character and block devices is that character devices can't use seek and don't have a concept of size. MTD is a new-ish type of device that is a translation of a block device.


cat /proc/devices gives you a list of the drivers present, with major numbers on the left side.

The kernel exports information about open network sockets. The relevant /proc/net/ files for IPv4 are: tcp, udp, and raw (are these the "types" of sockets?). A process' open sockets are listed under /proc/[pid]/fd/.

What if /proc/modules is empty and lsmod returns nothing?

This means the kernel modules are compiled in directly and support for inserting modules may be disabled.


/sys/class is a kernel IF similar to /proc; when you read data from procfs or sysfs, it's really an info request from kernel - data is formatted and returned. The /sys/class entries are created at startup when subsystems register with kobject core. They then begin to discover objects. A device class describes a functional type of device. No 1:1 mapping of class objects and physical devices, as each device may have multiple objects to perform different functions. There may be exposed parameters to control or describe the class object. Both sysfs and procfs are RAM file systems.

If you do an ls on /sys/class, you get a list of device types. For USB devices, these seem to match "subsystem" names.

ls /sys/class/
bdi    gpio	    mdio_bus  net  regulator	scsi_host   vc
block  i2c-adapter  mem       pps  rtc		sound	    vtconsole
bsg    i2c-dev	    misc      ptp  scsi_device	spi_master  watchdog
dma    input	    mmc_host  pwm  scsi_disk	tty


For the Boundary Devices kernel build for the Nitrogen, you are looking at /sys/class/backlight/pwm-backlight.0/ or /sys/class/backlight/backlight-<id>/
You can read the alias of the module at /sys/devices/platform/pwm-backlight.2/backlight/pwm-backlight.2/device/modalias and this is defined in the module .c file with the MODULE_ALIAS("string") instruction at the bottom of the file.

Latency discussion: “Accessing procfs or sysfs files does not entail an I/O bottleneck because they are not real files -- they are kernel interfaces. So no, accessing this stuff through "the file layer" does not affect performance. This is a not uncommon misconception in linux systems programming, I think. Programmers can be squeamish about system calls that aren't well, system calls, and paranoid that opening a file will be somehow slower. Of course, file I/O in the ABI is just system calls anyway. What makes a normal (disk) file read slow is not the calls to open, read, write, whatever, it's the hardware bottleneck.”

udev and libudev

Great walkthrough of sysfs and udev here: and intro to udevadm here:

Obsoleted at some point (at least by 2020 and Ubuntu 20) by libsystemd and sd-device.

Note that older versions of libudev (pre-2012/2013?) had a blocking version of udev_monitor_receive_device() so this required using select() to check a FD for data before using it, but this didn't really work for devices just connecting only and not xmitting. They later changed the function to non-blocking.

Older versions of libudev also suffered from memory leaks, although it's not clear where exactly this occurred nor when it/if was fixed. This was seen by me using kernel 3.14 and attempting to use the monitor and devnode path finding tools.

To set up new udev rules for something like sending signals to a running application, here is a good note on the difference between /etc/udev/rules.d/ and /lib/udev/rules.d/ (use the former).

Kernel 2.6.31+ net driver update

In kernel 2.6.31 the function pointers to net_device operations disappeared from the net_device structure, and were put into a new net_device_ops structure which is a const member of the new copy of net_device. To update, add a kernel version conditional net_device_ops struct, e.g.

static const struct net_device_ops foobar_ops = {
    .ndo_init               = 0,
    .ndo_uninit             = 0,
    .ndo_open               = foobar_open,
    .ndo_stop               = foobar_close,
    .ndo_start_xmit         = foobar_xmit,
    .ndo_set_multicast_list = 0,
    .ndo_set_mac_address    = 0,
    .ndo_do_ioctl           = foobar_ioctl,
    .ndo_change_mtu         = 0,
    .ndo_get_stats          = foobar_get_stats,
    .ndo_tx_timeout         = 0,

The dev-> function pointer assignments from __init net_init can then be removed.

Tell me about IOCTL

ioctl is one of the standard driver file operations, along with read/write, etc. An ioctl allows you to extend the capabilities of the driver with driver specific operations.


This is the first of 16 ioctl commands that can be implemented by each driver for its own private use. All the network ioctl commands are defined in sockios.h. (From O'Reilly)

Note that there was a change in the file operations struct somewhere between kernel 2.6.x and 3.0.x, now you must distinguish between
long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
long (*compat_ioctl) (struct file *, unsigned int, unsigned long);

i.e. when filling out your _ioctl function, these can be referenced as offsets from the original:


What should my _ioctl look like?

//this code lives in the driver
//don't forget the .ioctl = foobar_ioctl in the operations struct

static int foobar_ioctl(struct net_device* netdev, struct ifreq __user* ifr, int cmd )
    MY_DATA_TYPE* foobar_priv;
    immap_t* immap;

    foobar_priv = (MY_DATA_TYPE*) netdev_priv(netdev);
    immap = (immap_t*)foobar_priv->some_kernel_space_guy;

    switch (cmd)
    case FOO_IOCTL_ROCK:
        printk( KERN_ERR "%s: rock \n", netdev->name);
        //do some rock stuff      
    case FOO_IOCTL_ROLL:
        printk( KERN_ERR "%s: roll %s\n", netdev->name);        
        //do some roll stuff
    return 0;

This function can be called from your user space application when you've included
#include <sys/ioctl.h> /* ioctl */

General notes

Adding a driver to your system means registering it with the kernel. This is synonymous with assigning it a major number during the module's initialization. You do this by using the register_chrdev function, defined by linux/fs.h.

Access registers or any physical mem through /dev/mem. The /dev/mem and /dev/kmem character special files provide access to a pseudo device driver that allows read and write access to system memory or I/O address space. Typically, these special files are used by operating system utilities and commands (such as sar, iostat, and vmstat) to obtain status and statistical information about the system.

Basically, on embedded systems most of the drivers will not be built as modules, instead they are statically built into the kernel. To get to know all the drivers, you need to go through the /sys directory.

To enable dev_dbg messages in a specific kernel file: #define DEBUG before including <linux/device.h>
dev_err on the other hand goes straight to dmesg

Platform devices cannot be detected dynamically, so they are statically defined: direct instantiation of platform device structures, as done on ARM using a device tree, as done on PowerPC

Notes on some output files and flags for kernel module compiles:

Refresher for kernel modules vs. user programs:

Power Management

The Linux .config file has CONFIG_SUSPEND and CONFIG_PM parameters but the control for these is fuzzy or nonexistent in some instances. In the defconfig, you can turn off CONFIG_PM_RUNTIME but what this accomplishes is questionable, if anything at all. Some of this stuff may be designed to be inaccessible to the kernel builder for some reason.

At least some parameters for the display devices can be read in /sys/class/graphics/ (for the i.MX6 kernel look at fb0 for the frame buffer) and in /sys/class/backlight (for the i.MX6 kernel look at pwm-backlight.0). You can turn the display on and off by echoing a 1 or 0 to /sys/class/graphics/fb0/blank.

Kernel Modules

Primer on kernel modules and user space

Building a kernel module

If the Makefile is as simple as

obj-m += hello.o

you can build with make hello.o

But, if you need to link to the kernel for system functions, you'll need the extras that add make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
The build folder is just a sym link to the kernel source that lives in /usr/src

You need to use the same gcc compiler version for your module that was used to compile the kernel itself on the system on which you'll be running your module. You can find out which version with cat /proc/version.

KM programming guides:

To search for a module that may be statically built into the kernel instead of loadable:

grep <module-name> /lib/modules/`uname -r`/modules.builtin

ARM ioremap

NAND flash stuff meant for the flash page


GPIO interaction is possible using the /sys/class/gpio interface. To give access, you are supposed to be able to echo the number of the desired gpio port to export. This is supposed to create a gpioX listing to use.

What a PowerPC SPI device driver might look like

#include <linux/module.h>       /* Needed by all modules */
#include <linux/kernel.h>       /* Needed for KERN_INFO */
#include <linux/init.h>         /* Needed for the macros */
#include <linux/proc_fs.h>      /* Needed for /proc entry */
#include <asm/cpm2.h>			/* memory */
#include "myfoo.h"

// map immr and parameter ram 

/* SPI Controller mode register definitions 
/* SPIE register values */
/* SPIM register values */
/* SPCOM start bit */

//function decs

// essential values to initialize

/* RX and TX buffers */

// parameter RAM here

int spi_init(void)

int spi_exit(void)

int spi_rx(unsigned char* buf, unsigned int length)

int spi_tx(unsigned char* buf, unsigned int length)

static int foo_read_proc( char *page, char **start, off_t off, int count, int *eof, void *data ) 

static int __init foo_start(void) 

static void __exit foo_end(void) 


Page last modified on May 14, 2024, at 07:22 PM