Friday, June 16, 2017

Linux device model

The Linux device model

Linux device model is a general abstraction, describing the structure of the system. It is used within the kernel to support a wide variety of tasks, including:
  • Power management and system shutdown
  • Communications with user space - sysfs is part of the device model.
  • Hot pluggable devices - handling plug/unplug events and communication with user space.
  • Device classes - Each device is associated to some class which describes its type/functionality. Many parts of system may check which types of devices are available by looking at their classes.
  • Object life cycles - dealing with object life cycles, their relationships to each other, and their representation in user space.

Top-down presentation of the device model

If you look at the Linux device model design from the top, you will see:
Buses, Frameworks, Classes, Devices, Device Drivers, where all of them have a sysfs representation.

Bus

A bus is a channel between the processor and one or more devices, for example - USB, PCI, SPI, MMC, ISA. Buses primarily exist to gather similar devices together and coordinate initialization, shutdown and power management.

There are two types of drivers attached to the bus:
  • Adapter drivers: main chip that manage the bus, for example: USB controllers, I2C adapters, etc.
  • Device drivers : external chip devices, for example: USB devices, I2C devices, PCI devices, etc.
All devices are connected via a bus, which should be able to detect when they are plugged and manage them.

Framework

In Linux, many device drivers are not implemented directly as a character drivers, they are implemented under a specific type of framework.
A framework implements a common parts of drivers which are of the same type (framebuffer, V4L, serial, etc...), and from the user space they are still seen as a character drivers.

Framework example - framebuffer framework

If you write an LCD driver, which usually gets its images from a framebuffer, you should use the framebuffer framework.

You will need to impliment a set of framebuffer specific operations defined by the struct fb_ops, and register the new frame buffer. This will create the character device that can be used by userspace applications with a generic framebuffer API.


#include <include/linux/fb.h>

static int __devinit xxxfb_probe(struct pci_dev *dev, 
                        const struct pci_device_id *ent)
{
    struct fb_info *info;
    ...
    info = framebuffer_alloc(sizeof(struct xxx_par), device);
    ...
    info­>fbops = &xxxfb_ops;
    ...
    if (register_framebuffer(info) < 0)
        return ­EINVAL;
    ...
}

 

Device

A device is a specific piece of hardware, an IC, a collection of an ICs or could represent something that has no real physical equivalent at all.
The compelling reason to use a "device" to represent some "thing" seems to be the interfaces, like sysfs representation, power management, resource management, etc.
All devices are connected via a bus.

Platform device

On embedded systems, devices are often not connected through a bus but directly to the CPU. We can not detect this kind of devices automatically and provide a proper driver for them. To fit this kind of devices into device model, we create them as platform devices which are connected to a special platform bus.

Class

A class is a "device interface" in object oriented manner, it exposes a common API, while the real implementation which is different for each device, is in hidden in devices logic.
For example for a 'led' class, exposes blinking, flashing, and brightness functionality. The class requires an underlying device to be available (such as a TCA6507 or GPIO...), which must support fully or partially functions exposed by the class. A class may provide more then one interface outside, for example the gpio class provides both the "gpiochip" devices and the individual "gpio" devices.

Device driver

Device drivers talk directly to hardware, and may be associated to many devices, because same driver may be compatible with many similar ICs.
A device driver defines a table with a list of device identifier it is able to manage:


static const struct pci_device_id rhine_pci_tbl[] = {
{ 0x1106, 0x3043, PCI_ANY_ID, PCI_ANY_ID, },    /* VT86C100A */
{ 0x1106, 0x3053, PCI_ANY_ID, PCI_ANY_ID, },    /* VT6105M */
{ }     /* terminate list */
};
MODULE_DEVICE_TABLE(pci, rhine_pci_tbl);


This list is passed to the bus, so it can locate that driver, for a compatible device.

sysfs

sysfs is a virtual file system which is mounted on /sys and offers a mechanism to export device model information to the user space.
A special locations inside sysfs which should be mentioned are:
  • /sys/bus - contain the list of all buses
  • /sys/devices - contains a list of all devices
  • /sys/class - enumerate devices by class (for example: net, input, block)
 You may use the systool utility to list devices by bus, class and topology.

Bottom-up presentation of the device model

kobject

The kobject is the fundamental structure that holds the device model together. Tasks which are handled by kobject include:
  • Reference counting of objects - used for receiving a notification if no one is using the object anymore.
  • Sysfs representation - kobjects used to create a representation of kernel objects in sysfs.
  • Hot plug event handling - handles the generation of events that notify user space about the comings and goings of hardware on the system.
The purpose of kobject is extending kernel objects providing their capabilities to object which contains them. Kernel object are represented as structure which embed kobject structure inside them.
 
When a kernel object which is a structure contains a kobject pointer, it actually being extended, because, now it has an access to all kobject capabilities when needed.

In sysfs, each directory is represented as a kobject, and all files in that directory are 'attributes' of that kobject.

ktype

A ktype is the type of the object which embeds a kobject. The ktype controls what happens to the kobject when its destroyed and also its default representation in sysfs.

ktype is represented by the structure below:

struct kobj_type {
        void (*release)(struct kobject *);
        struct sysfs_ops        * sysfs_ops;
        struct attribute        ** default_attrs;
};

default_attrs - each file in sysfs is considered an attribute and is related to some kobject.
sys_ops -
struct sysfs_ops {
        ssize_t (*show)(kobj, struct attribute *attr, char *buf);
        ssize_t (*store)(kobj, struct attribute *attr, const char *, size_t);
}; 

These are pointers to user specific handlers, allowing to handle data write/read
to/from kobject's attribures.

kset

A kset is a container type of a collection of a kobjects which of the same type.
When you see a sysfs directory full of other directories, generally each of those directories corresponds to a kobject in the same kset.

subsystem

A subsystem is a representation for a high-level portion of the kernel as a whole.
Subsystems tend to correspond to toplevel directories in the sysfs hierarchy.

$ ls /sys
block  bus  class  devices  firmware  module

Their names in the source tend to end in _subsys (produced by the macro decl_subsys()).

You can find in the source:  system_subsys, block_subsys, bus_subsys, class_subsys, devices_subsys, firmware_subsys, class_obj_subsys, acpi_subsys, edd_subsys, vars_subsys, efi_subsys, cdev_subsys, module_subsys, power_subsys, pci_hotplug_slots_subsys.

Some are not visible in sysfs.

Each kset is contained by a subsystem. A subsystem handles a special semaphore which is used to letting only one process at a time adding/removing kobjects to its kset.

struct subsystem {
        struct kset             kset;
        struct rw_semaphore     rwsem;
};

So, now after reading all these, check out kobject and kset structs:

struct kobject {
       const char              *name;
       struct kobject          *parent;
       struct kset             *kset;
       struct kobj_type        *ktype;
       struct kref             kref;

       ...
};

name - each kobject has a name, this will be the directory name on sysfs. 
parent -  a pointer used to position the object in the sysfs hierarchy.
kref -  One of the key functions of a kobject is to serve as a reference counter for the object in which it is embedded. As long as references to the object exist, it must continue to exist.


struct kset {
      struct subsystem *subsys;
      struct kobj_type *ktype;
      struct list_head list;
      struct kobject kobj;
      struct kset_hotplug_ops *hotplug_ops;
};

hotplug_ops - Ksets can support the "hotplugging" of kobjects and influence how uevent events are reported to user space.


A Kset contain its own kobject, which is managed by kset core code automatically, and thats why it is also a directory in sysfs.

The relation between a kset and kobjects which belong to that kset:


A kset can be used by the kernel to track "all block devices" or "all PCI device drivers.", because they are represented by kobjects related to a subsystems.

After a kobject has been registered with the kobject core, you need to announce to the world that it has been created using:

int kobject_uevent (kobj, KOBJ_ADD);

This will notify the device manager at user space (udev) to handle the new changes.
When kobject is removed, kobject_uevent(kobj, KOBJ_REMOVE) will be called automatically.

Thats it for today...

 

No comments:

Post a Comment