- Brief introduction
- The 2.6 kernel device model provides a general abstract description of the system structure to support a variety of different tasks
- Power Management and system shutdown
- Communicating with user space
- Hot-swappable devices
- Device type
- Object life cycle
- Kobject, Kset and subsystems
- Kobject is the basic structure of the constituent equipment model
- The reference count of the object
- Sysfs expression
- Data Structure Association
- Hot Swap Event Handling
- Kobject Basic Knowledge
- <linux/kobject.h>
- Embedded Kobject
- Kernel code rarely creates a separate Kobject object, Kobject is used to control access to large domain-related objects
- Initialization of the Kobject
- First set Kobject to 0, usually using memset
- void Kobject_init (struct kobject *kobj);
- int kobject_set_name (struct kobject *kobj, const char *format, ...);
- Ktype, Kset, and parent
- Operation on Reference count
- struct Kobject *kobjct_get (struct kobject *kobj);
- void Kobject_put (struct kobject *kobj);
- Release function and Kobject type
- void My_object_readse (struct kobject *kobj)
- struct My_object *mine = container_of (kobj, struct my_object, kobj);
- Kfree (mine);
- struct Kobj_type
- void (*release) (struct kobject *);
- struct Sysfs_ops *sysfs_ops;
- struct attribute **default_attrs;
- struct Kobj_type *get_ktype (struct kobject *kobj);
- Kobject hierarchies, Kset, and subsystems
- The kernel uses the KOBJECT structure to connect individual objects together to form a hierarchical architecture, with two separate mechanisms for connecting: the parent pointer and the Kset
- In the parent member of the KOBJECT structure, a pointer to another kobject structure is saved, which represents the nodes in the upper layer of the hierarchy
- Kset
- A Kset is a collection of kobject that embed the same type of structure
- Focus and set of object of interest in kset structure
- You can think of Kset as the top-level container class for Kobject.
- Kset always appears in the SYSFS.
- First point the Kset member of Kobject to the destination Kset, and then add the kobject using the following function
- int Kobject_add (struct kobject *kobj);
- extern int Kobject_register (struct kobject *kobj);
- A simple combination of kobject_init and Kobject_add
- void Kobject_del (struct kobject *kobj);
- Kset holds its child nodes in a standard kernel-linked list
- Operations on the Kset
- void Kset_init (struct kset *kset);
- int Kset_add (struct kset *kset);
- int Kset_register (struct kset *kset);
- void Kset_unregister (struct kset *kset);
- Subsystem
- Subsystems are usually displayed in the top level of the SYSFS hierarchy
- Subsystems in the kernel
- Block_subsys (/sys/block for block devices)
- Devices_subsys (/sys/devices, the core of the device hierarchy)
- Specific subsystems known to the kernel for various buses
- Decl_subsys (name, struct kobj_type *type, struct kset_hotplug_ops *hotplug_ops);
- void Subsysstem_init (struct subsystem *subsys);
- int Subsystem_register (struct subsystem *subsys);
- void Subsystem_unregister (struct subsystem *subsys);
- struct subsystem *subsys_get (struct subsystem *subsys);
- void Subsys_put (struct subsystem *subsys);
- Low-level SYSFS operation
- Kobject is the mechanism behind the SYSFS virtual file system, and for each directory in the SYSFS, there is a corresponding kobject in the kernel.
- <linux/sysfs.h>
- SYSFS entrance
- Kobject in Sysfs is always a directory
- The name of the distribution valve to Kobject is the directory name in SYSFS
- Sysfs the location of the entry in the directory corresponds to the parent pointer of the Kobject
- Default Properties
- Kobject default properties are saved in the KOBJ_TYPE structure
- Default_attrs members save a list of properties
- Sysfs_ops provides a way to implement these properties
- struct attribute
- Char *name
- struct Module *owner
- mode_t mode
- Read Only: S_irugo
- Writable: S_IWUSR
- <linux/stat.h>
- struct SYSFS_OPS
- ssize_t (*show) (struct kobject * kobj, struct attribute *attr, char *buffer);
- ssize_t (*store) (struct kobject *kobj, struct attribute *attr, const char *buffer, size_t size);
- Non-default properties
- int sysfs_create_file (struct kobject *kobj, struct attribute *attr);
- int sysfs_remove_file (struct kobject *kobj, struct attribute *attr);
- Binary properties
- struct Bin_attribute
- struct attribute attr;
- size_t size;
- ssize_t (*read) (struct kobject *kobj, char *buffer, loff_t POS, size_t size);
- ssize_t (*write) (struct kobject *kobj, char *buffer, loff_t POS, size_t size);
- int sysfs_create_bin_file (struct kobject *kobj, struct bin_attribute *attr);
- int sysfs_remove_bin_file (struct kobject *kobj, struct bin_attribute *attr);
- Symbolic Links
- int Sysfs_create_link (struct kobject *kobj, struct kobject *target, char *name);
- void Sysfs_remove_link (struct kobject *kobj, char *name);
- The generation of hot-swappable events
- A hot plug event is a notification that is sent from the kernel space to the user space, which indicates that the system configuration has changed
- This event occurs regardless of whether kobject is created or deleted
- Hot swap operation
- struct KSET_HOTPLUG_OPS
- Int (*filter) (struct kset *kset, struct kobject *kobj);
- char * (*name) (struct kset *kset, struct kobject *kobj);
- Int (*hotplug) (struct kset *kset, struct kobject *kobj, char **envp, int num_envp, char *buffer, int buffer_size);
- The filter function is called when the kernel is about to generate an event for the specified Kobject, and if 0 is returned, no event is generated
- Bus, device, and driver
- Bus
- A bus is a channel between a processor and one or more devices
- <linux/device.h>
- struct BUS_TYPE
- Char *name;
- struct subsystem Subsys;
- struct Kset drivers;
- struct Kset devices;
- Int (*match) (struct device *dev, struct device_driver *drv);
- struct device * (*add) (struct device *parent, char *bus_id);
- Int (*hotplug) (struct device *dev, char **envp, int num_envp, char *buffer, int buffer_size);
- Each bus has its own subsystem
- A bus consists of two kset, each representing the bus driver and all the devices plugged into the bus
- Registration of the bus
- Example
- struct Bus_type Ldd_bus_type = {. Name= "LDd",. Match=ldd_match,. Hotplug=ldd_hotplug,};
- ret = Bus_register (&ldd_bus_type);
- if (res) return ret;
- void Bus_unregister (struct bus_type *bus);
- Bus method
- The match function is called one or more times when a new device on a bus or a new driver is added
- The HotPlug method allows the bus to add environment variables before a hot-swap event is generated for the user space
- When real hardware is called, the match function typically makes some kind of comparison between the hardware ID provided by the device and the ID that the driver supports.
- Iterations of the device and driver
- int Bus_for_each_dev (struct bus_type *bus, struct device *start, void *data, int (*FN) (struct device *, void *));
- int bus_for_each_drv (struct bus_type *bus, struct device_driver *start, void *data, int (*FN) (struct device_driver *, void *));
- Bus properties
- <linux/device.h>
- struct Bus_attribute
- struct attribute attr;
- ssize_t (*show) (struct bus_type *bus, char *buf);
- ssize_t (*store) (struct bus_type *bus, const char *BUF, size_t count);
- BUS_ATTR (name, mode, show, store);
- int bus_create_file (struct bus_type *bus, struct bus_attribute *attr);
- void Bus_remove_file (struct bus_tyep *bus, struct bus_attribute *attr);
- Equipment
- struct device
- struct device *parent;
- struct Kobject kobj;
- Char Bus_id[bus_id_size];
- struct Bus_type *bus;
- struct Device_driver *driver;
- void *driver_data
- void (*release) (struct device *dev);
- Device Registration
- int device_register (struct device *dev);
- void Device_unregister (struct device *dev);
- Device properties
- struct Device_attribute
- struct attribute attr;
- ssize_t (*show) (struct device *dev, char *buf);
- ssize_t (*store) (struct device *dev, const char *BUF, size_t count);
- DEVICE_ATTR (name, mode, show, store);
- int device_create_file (struct device *device, struct device_attribute *entry);
- void Device_remove_file (struct device *dev, struct device_attribute *attr);
- Embedding of device structures
- The device structure contains information about the equipment model core used to simulate the system.
- Typically, the underlying driver does not know the device structure
- Device drivers
- struct Device_driver
- Char *name;
- struct Bus_type *bus;
- struct Kobject kobj;
- struct List_head devices;
- Int (*probe) (struct device *dev);
- Int (*remove) (struct device *dev);
- void (*shutdown) (struct device *dev);
- Probe is a function used to query whether a particular device exists
- Call the Remove function when the device is removed from the system
- Call the shutdown function to shut down the device while shutting down the machine
- int Driver_register (struct device_driver *drv);
- void Driver_unregister (struct device_driver *drv);
- struct Driver_attribute
- struct attribute attr;
- ssize_t (*show) (struct device_driver *drv, char *buf);
- ssize_t (*store) (struct device_driver *drv, const char *BUF, size_t count);
- int driver_create_file (struct device_driver *drv, struct driver_attribute *attr);
- void Driver_remove_file (struct device_driver *drv, struct driver_attribute *attr);
- Embedding of driver structures
- Device_driver structures are often included in high-level and summarized-related structures
- Class
- Class is a high-level view of a device that abstracts the implementation details of the lower layer
- Almost all classes are displayed in the/sys/class directory
- /sys/class/net
- /sys/class/input
- /sys/class/tty
- /sys/block
- Class_simple interface
- Create the class itself
- struct class_simple *class_simple_create (struct module *owner, char *name);
- Destroying a simple class
- void Class_simple_destroy (struct class_simple *cs);
- Add a device to a simple class
- struct Class_device *class_simple_device_add (struct class_simple *cs, dev_t devnum, struct device *device, const char *FMT , ...);
- int Class_simple_set_hotplug (struct class_simple *cs, int (*hotplug) (struct class_device *dev, char **envp, int num_envp, Crah *buffer, int buffer_size));
- void Class_simple_device_remove (dev_t dev);
- The Complete class interface
- Management class
- struct class
- char *name;
- struct Class_attribute *class_attrs;
- struct Class_device_attribute *class_dev_attrs;
- Int (*hotplug) (struct class_device *dev, char **envp, int num_envp, char *buffer, int buffer_size);
- void (*release) (struct class_device *dev);
- void (*class_release) (struct class *class);
- int class_register (struct class *cls);
- void Class_unregister (struct class *cls);
- struct Class_attribute
- struct attribute attr;
- ssize_t (*show) (struct class *cls, char *buf);
- ssize (*store) (struct class *cls, const char *BUF, size_t count);
- class_attr (name, mode, show, store);
- int Class_create_file (struct class *cls, const struct class_attribute *attr);
- void Class_remove_file (struct class *cls, const struct class_attribute *attr);
- class device
- strict class_device
- struct kobject kobj;
- struct class *class;
- struct device *dev;
- void *class_data;
- Char class_id[bus_id_size];
- int class_device_register (struct class_device *cd);
- void Class_device_unregister (struct class_device *cd);
- int Class_device_renmae (struct class_device *cd, char *new_name);
- struct Class_device_attribute
- struct attribute attr;
- ssize_t (*show) (struct class_device *cls, char *buf);
- ssize_t (*store) (struct class_device *cls, const char *BUF, size_t count);
- class_device_attr (name, mode, show, store);
- int class_deivce_create_file (struct class_device *cls, const struct class_device_attribute *attr);
- void Class_device_remove_file (struct class_device *cls, const struct class_device_attribute *attr);
- Class interface
- struct Class_interface
- struct class *class;
- Int (*add) (struct class_device *cd);
- void (*remove) (struct class_device *cd);
- int Class_interface_register (struct class_interface *intf);
- void Class_interface_unregister (struct class_interface *intf);
- Integration of each link
- Add a Device
- The PCI subsystem declares a BUS_TYPE structure, called Pci_bus_type
- struct Bus_type Pci_bus_type
- . Name = "PCI",
- . match = Pci_bus_match,
- . HotPlug = Pci_hotplug,
- . Suspend = Pci_device_suspend,
- . Resume = Pci_device_resume,
- . Dev_attrs = Pci_dev_attrs,
- After registering, you will create a SYSFS directory/SYS/BUS/PCI that contains two directories: devices and drivers
- All PCI drivers must define a PCI_DRIVER structure variable, which contains a device_driver structure that will be initialized when the PCI driver is registered.
- Drv->driver.name = drv->name;
- Drv->driver.bus = &pci_bus_type;
- Drv->driver.probe = Pci_device_probe;
- Drv->driver.remove = Pci_device_remove;
- Drv->driver.kobj.ktype = &pci_deiver_kobj_type;
- Error = Driver_register (&drv->driver);
- When a PCI device is found, the PCI core creates a struct variable of type Pci_dev in memory
- struct PCI_DEV
- unsigned int devfn;
- unsigned short vendor;
- unsigned short device;
- unsigned short Subsystem_vendor;
- unsigned short subsystem_device;
- unsigned int class;
- struct Pci_driver *driver;
- struct device dev;
- When initializing, the parent variable of the device structure variable is set to the bus device where the PCI device is located
- Device_register (&dev->dev);
- In the Device_register function, the driver core registers the device with the Kobject core Kobject
- The device will then be added to all the device linked tables associated with the bus, traverse the list, and invoke the match function of the bus for each driver, specifying the device
- If the matching work completes successfully, the function returns 1 to the driver core, the driver core points to the driver in the driver pointer in the device structure, and then calls the probe function specified in the DEVICE_DRIVER structure
- Remove device
- Call the Pci_remove_bus_device function
- This function does some PCI-related cleanup work, and then calls the Device_unregister function using a pointer to the device structure in Pci_dev
- In the Device_unregister function, the driver core simply removes the symbolic link from the driver of the bound device to the Sysfs file, removes the device from the internal device chain list, and invokes the Kobject structure pointer in the device structure as a parameter kobject_ del function
- The Kobject_del function removes the Kobject reference to the device, and if the reference is the last one, call the release function of the PCI device
- Adding drivers
- When the Pci_register_driver function is called, a PCI driver is added to the PCI core heart
- The function simply initializes the DEVICE_DRIVER structure contained in the PCI_DRIVER structure
- The PCI core calls the Driver_register function within the driver core with the Device_driver structure pointer contained in the PCI_DRIVER structure as a parameter
- The Driver_reigster function initializes the locks in several device_driver, and then calls the Bus_add_driver function, which operates as follows
- Finding the bus associated with the driver
- Create a driver's Sysfs directory based on the name of the driver and the associated bus
- Get the lock inside the bus, then traverse all the devices registered with the bus and call the match function for those devices
- Remove Driver
- Call the Pci_unregister_driver function
- The function uses the DEVICE_DRIVER structure contained in the PCI_DRIVER structure as a parameter to invoke the driver core function Driver_unregister
- The Driver_unregister function clears the Sysfs property that belongs to the driver in the Sysfs tree
- Traverse all devices that belong to the driver and call the release function for it
- Processing firmware
- Putting the firmware code into the driver expands the driver code, making the firmware upgrade difficult and causing the license issue to be easy
- Do not put drivers that contain firmware into the kernel, or include them in a Linux distribution
- Kernel Firmware Interface
- <linux/firmware.h>
- int request_firmware (const struct firmware **fw, char *nam E, struct device *device);
- struct firmware
- void release_firmware (struct firmware *fw);
- int request_firmware_nowait (struct module *module, char *name, struct device *device, void *context, void (*cont) (cons t struct firmware *fw, void *context));
- Working principle
- Firmware subsystem works with SYSFS and hot-swap mechanisms
- When Request_firmware is called, a directory is created under/sys/class/firmware that uses the device name as its directory name, which contains three properties
- Loading
- Set to 1 by the user-space process responsible for loading the firmware
- Data
- is a binary property that is used to receive firmware data
- Device
- This property is a symbolic link to the corresponding entry under/sys/devices
- Once the SYSFS portal is created, the kernel generates a hot plug event for the device, and the environment passed to the hot-plug handler includes a firmware variable, which is set to the name provided to Request_firmware
- The handler locates the firmware file, copies the firmware file to the kernel using the provided properties, and if the firmware file cannot be found, the handler sets the loading property to-1
- If a firmware request cannot be serviced within 10 seconds, the kernel discards the effort and returns an error state to the driver, which can be changed by modifying the Sysfs property/sys/class/firmware/timeout
- The firmware of the device cannot be released without the manufacturer's permission
Linux Device Drivers chapter 14th Linux Equipment model