Change from: http://www.wowotech.net/pm_subsystem/psy_class_overview.html
0. Documents involved
framwork
drivers\power\power_supply_core.c
drivers\power\power_supply_sysfs.c
drivers\power\power_supply_leds.c
PSY drv
drivers\power\qpnp-charger.c // "battery"
drivers\power\qpnp-bms.c // "bms"
drivers\usb\otg\msm_otg.c // "usb"
Important function
power_supply_register // Register psy drv with core
power_supply_changed // The psy device state change calls this function in core, which calls back the power_supply-> external_power_change function in drv
1. Preface
power supply class provides a unified framework for writing power supply (PSY) drivers. Its functions include:
1) To abstract the commonness of PSY devices and provide a unified API for user space.
2) Provide a simple and unified way to write the underlying PSY driver. At the same time, it encapsulates and implements common logic. Driver engineers only need to concentrate on hardware-related parts.
Note: In fact, all classes (such as input subsystem) have such ideas as abstract generality, unified interface and shielding details. We introduced the "Linux Device Model (7)_Class". This paper introduces the power supply class and takes it as an example to further understand the significance and usage of the class in the device model.
2. Design Ideas
First of all, to answer a question: the purpose of device driver in kernel is to manage devices and provide them to user-space programs. So what does kernel manage for PSY devices? What does a user space program use?
In fact, PSY equipment is a special case, its purpose is very simple, is to supply power for the system. If you only consider this goal, you don't need any drivers, but the situation is slightly more complicated because:
1) PSY devices may be battery devices, which are very common in embedded systems. This will lead to a number of issues such as power detection, charging management and so on.
At this point, PSY driver needs to manage the following things: battery type detection; battery power detection; charging status detection; and so on. User-space programs need to display the detected results to users.
2) There may be multiple PSY devices in the system, and these devices may also have cascade relationship. For example, in some tablets, there may be three power supply devices, DC-charger, USB-charger and battery, among which DC-charger and USB-charger may supply battery power and battery power to the system.
At this point, PSY driver needs to manage things including: access to external power supply equipment connection status; charging status; and so on. Similarly, user-space programs need to display this information to users.
Then, the generality has been summarized: the main function of PSY driver is to collect all kinds of state information from user space programs. Therefore, the core idea of power supply class is:
These state information are abstracted as "properties". Because the type of state information is limited, the number of properties is also limited.
PSY driver only needs to be responsible for: what are the "attributes" of the PSY device; what are the "values" of these "attributes"; when the "attribute values" change, notify the power supply class.
power supply class is responsible for providing user space with attributes and their values supported by a PSY device in the form of sysfs, and broadcasting them to user space programs in the form of uevent when attribute values change.
In addition, power supply class es also assist in handling PSY cascades (described in detail later).
3. Software Architecture and API Integration
3.1 Software Architecture
power supply class is located in drivers/power/directory and consists of three parts (refer to the software architecture shown below):
1) power supply core, which is used to abstract core data structure and realize common logic. Located in drivers/power/power_supply_core.c.
2) power supply sysfs, realize sysfs and uevent functions. Located in drivers/power/power_supply_sysfs.c.
3) power supply leds, based on linux led class, provides a general implementation of PSY device status indication. Located in drivers/power/power_suppply_leds.c.
Finally, driver engineers can implement specific PSY drivers based on power supply class, mainly dealing with platform-related and hardware-related logic. These drivers are located in drivers/power / directory.
3.2 Core Data Structure
1)struct power_supply
Strct power_supply is the core data structure of power supply class, which is used to abstract PSY devices. Its definition is as follows:
/* include/linux/power_supply.h */
struct power_supply {
const char *name; // The name of the PSY
enum power_supply_type type; // The PSY type, enumeration type, including battery, USB charger and so on
/* ★ sys/class/power_supply/xxx/ The following node attributes
The PSY has a list of attributes, enumeration type
*/
enum power_supply_property *properties;
size_t num_properties; // Number of attributes
/* An array of strings saves a list of PSY powered by the PSY, thus organizing the PSY into cascading PSY chains.
These "powered" PSY s are called supplicant s.
*/
char **supplied_to;
size_t num_supplicants;
/*
An array of strings that holds a list of PSY supplies to the PSY, also known as supply.
From another direction, organize cascading relationships between PSY
*/
char **supplied_from;
size_t num_supplies;
struct device_node *of_node;
/* ★core It calls set callback to set various property actions of psy, power_supply_set_online, power_supply_set_current_limit.
(sys Callback of Nodes
PSY driver Two callback functions that need to be implemented to get / set property values
*/
int (*get_property)(struct power_supply *psy,
enum power_supply_property psp,
union power_supply_propval *val);
int (*set_property)(struct power_supply *psy,
enum power_supply_property psp,
const union power_supply_propval *val);
int (*property_is_writeable)(struct power_supply *psy,
enum power_supply_property psp);
/* ★(power_supply_core.c Callback function)
When a PSY device has a supply PSY and the attributes of the supply PSY change (e.g. online, offline),
power_supply_core.c The callback function is called and the PSY driver is notified so that it can be processed accordingly.
*/
void (*external_power_changed)(struct power_supply *psy);
/*
The application scenario of this callback function is a bit strange: the external module notifies the PSY driver that the state of the PSY device has changed.
I have changed my ignorance. I hope you don't encounter it in practice. Otherwise, it's too entangled.
*/
void (*set_charged)(struct power_supply *psy);
/* For APM emulation, think legacy userspace. */
int use_for_apm;
/* private */
struct device *dev;
/*
A workqueue for dealing with state changes, the main idea is: when the state of the PSY changes,
Start a workqueue, query and notify all supplicants
*/
struct work_struct changed_work;
spinlock_t changed_lock;
bool changed;
/*
A workqueue for dealing with state changes, the main idea is: when the state of the PSY changes,
Start a workqueue, query and notify all supplicants
*/
#ifdef CONFIG_THERMAL
struct thermal_zone_device *tzd;
struct thermal_cooling_device *tcd;
#endif
/*If CONFIG_LED S_TRIGGERS is configured, the interface of linux led class is called to register the corresponding LED device for PSY status indication.*/
#ifdef CONFIG_LEDS_TRIGGERS
struct led_trigger *charging_full_trig;
char *charging_full_trig_name;
struct led_trigger *charging_trig;
char *charging_trig_name;
struct led_trigger *full_trig;
char *full_trig_name;
struct led_trigger *online_trig;
char *online_trig_name;
struct led_trigger *charging_blink_full_solid_trig;
char *charging_blink_full_solid_trig_name;
#endif
};
2) PSY type
The PSY type is defined by enum power_supply_type:
enum power_supply_type {
POWER_SUPPLY_TYPE_UNKNOWN = 0,
POWER_SUPPLY_TYPE_BATTERY, // Batteries, Embedded Devices and Commonly Used Power Supply Forms for Handheld Intelligent Devices
POWER_SUPPLY_TYPE_UPS, // Uninterruptible power supply equipment, through the AC and battery connection, under normal circumstances by AC power supply, while charging to the battery. When AC power is cut off, the battery supplies emergency power. Usually used for servers and other devices;
POWER_SUPPLY_TYPE_MAINS, // Main power supply devices, such as adapters for laptops, are characterized by the ability to supply power separately, and when they are powered off, they are supplied by auxiliary power supply devices (such as battery).
/*USB Type of power supply, the difference lies in the limitation of charging current, specified by USB Battery Charge Spec, can refer to the specifications of the USB organization. */
POWER_SUPPLY_TYPE_USB, /* Standard Downstream Port */
POWER_SUPPLY_TYPE_USB_DCP, /* Dedicated Charging Port */
POWER_SUPPLY_TYPE_USB_CDP, /* Charging Downstream Port */
POWER_SUPPLY_TYPE_USB_ACA, /* Accessory Charger Adapters */
};
3) PSY attributes
power supply class abstracts all PSY attributes in the form of enum power_supply_property. PSY driver can select some of them according to the actual situation of the device.
enum power_supply_property {
/* Properties of type `int' */
POWER_SUPPLY_PROP_STATUS = 0,
POWER_SUPPLY_PROP_CHARGE_TYPE,
POWER_SUPPLY_PROP_HEALTH,
POWER_SUPPLY_PROP_PRESENT,
POWER_SUPPLY_PROP_ONLINE,
POWER_SUPPLY_PROP_AUTHENTIC,
POWER_SUPPLY_PROP_TECHNOLOGY,
POWER_SUPPLY_PROP_CYCLE_COUNT,
POWER_SUPPLY_PROP_VOLTAGE_MAX,
POWER_SUPPLY_PROP_VOLTAGE_MIN,
POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
......
};
Status: Mainly the charging status, including "Unknown", "Charging", "Discharging", "Not charging", "Full".
charge_type: Charging type, including: "Unknown", "N/A", "Trickle", "Fast".
health: "health" includes "Unknown", "Good", "Overheat", "Dead", "Overvoltage" and so on.
Techgnology: The technologies used include "Unknown", "NiMH", "Li-ion", "Li-poly", "LiFe", "NiCd", "LiMn".
Capability: Volume Value
Type: The PSY type (battry, usb), which is special, is stored in the "psy - > type" variable, not in the properties array.
3.3 API for specific PSY driver
The primary task of power supply class is to provide PSY driver with a unified driver programming interface, which mainly includes:
1) PSY register/unregister
power_supply_register(struct device *parent, struct power_supply *psy);
power_supply_register_no_ws(struct device *parent, struct power_supply *psy);
power_supply_unregister(struct power_supply *psy);
The difference between power_supply_register and power_supply_register_no_ws is that PSY registered by power_supply_register has the ability of wakeup system, but power_supply_register_no_ws does not.
2) Notify power supply core when PSY status changes
power_supply_changed(struct power_supply *psy);
When PSY driver detects that some attribute values of the device have changed, it calls the interface and notifies the power supply core that the power supply core will perform the following actions:
If the PSY is the power supply of other PSY, call the external_power_changed callback function of these PSY and inform them (what these PSY do is decided by their own logic);
b. If CONFIG_led S_TRIGGERS is configured, power_supply_update_leds is called to update the led state related to the PSY.
Send notifier to drivers who are concerned about the status of PSY devices.
In a uniform format, uevent is sent to user space (that is the charm of class in device model. The external interface is provided by class core, which can save the workload of driver and ensure the consistency of interface).
3) Other Miscellaneous Interfaces
// Get the PSY pointer by name
power_supply_get_by_name(const char *name);
// From DTS, the corresponding dePSY pointer is resolved
power_supply_get_by_phandle(struct device_node *np, const char *property);
// Query if you are powered by other PSY
power_supply_am_i_supplied(struct power_supply *psy);
// Call the set_charged callback for the specified PSY
power_supply_set_battery_charged(struct power_supply *psy);
// Query whether the system has a valid PSY or is in an online state. If not, it may be a desktop system.
power_supply_is_system_supplied(void);
// Under the sysfs directory of the specified device (usually the PSY device) (/sys/devices/xxx/), create a symbolic link for the specified PSY (/sys/devices/xxx/powers).
power_supply_powers(struct power_supply *psy, struct device *dev);
3.4 API provided to other driver s to receive PSY status change notifier
1: extern int power_supply_reg_notifier(struct notifier_block *nb);
2: extern void power_supply_unreg_notifier(struct notifier_block *nb);
After registering notifier through notifier registration interface, the state of any PSY device in the system changes, and the power_supply_changed interface is called. power supply core is the listener who notifier is notified.
3.5 API for User Space Programs
power supply class provides interfaces to user space in two forms.
1) uevent (refer specifically to "Linux Device Model (3)_Uevent"), in the form of "name = value", report the values of all properties in the following format:
POWER_SUPPLY_NAME=xxx /* power supply name */
POWER_SUPPLY_xxx1=xxx /* property = value */
POWER_SUPPLY_xxx2=xxx
…
uevent is typically sent when a PSY device is added to the kernel, or when the PSY property changes (see the introduction in 3.3).
2)sysfs
power supply class defines a considerable number of default attributes in power_supply_sysfs.c. (see below). If a PSY device has an attribute, the attributes corresponding to the attribute will be reflected in sysfs (generally in "/sys/class/power_supply/xxx/".
/* Must be in the same order as POWER_SUPPLY_PROP_* */
static struct device_attribute power_supply_attrs[] = {
/* Properties of type `int' */
POWER_SUPPLY_ATTR(status),
POWER_SUPPLY_ATTR(charge_type),
POWER_SUPPLY_ATTR(health),
POWER_SUPPLY_ATTR(present),
POWER_SUPPLY_ATTR(online),
POWER_SUPPLY_ATTR(authentic),
POWER_SUPPLY_ATTR(technology),
POWER_SUPPLY_ATTR(cycle_count),
POWER_SUPPLY_ATTR(voltage_max),
POWER_SUPPLY_ATTR(voltage_min),
POWER_SUPPLY_ATTR(voltage_max_design),
POWER_SUPPLY_ATTR(voltage_min_design),
POWER_SUPPLY_ATTR(voltage_now),
POWER_SUPPLY_ATTR(voltage_avg),
POWER_SUPPLY_ATTR(voltage_ocv),
POWER_SUPPLY_ATTR(voltage_boot),
POWER_SUPPLY_ATTR(current_max),
POWER_SUPPLY_ATTR(current_now),
POWER_SUPPLY_ATTR(current_avg),
POWER_SUPPLY_ATTR(current_boot),
POWER_SUPPLY_ATTR(power_now),
POWER_SUPPLY_ATTR(power_avg),
POWER_SUPPLY_ATTR(charge_full_design),
POWER_SUPPLY_ATTR(charge_empty_design),
POWER_SUPPLY_ATTR(charge_full),
POWER_SUPPLY_ATTR(charge_empty),
POWER_SUPPLY_ATTR(charge_now),
POWER_SUPPLY_ATTR(charge_avg),
POWER_SUPPLY_ATTR(charge_counter),
POWER_SUPPLY_ATTR(constant_charge_current),
POWER_SUPPLY_ATTR(constant_charge_current_max),
POWER_SUPPLY_ATTR(constant_charge_voltage),
POWER_SUPPLY_ATTR(constant_charge_voltage_max),
POWER_SUPPLY_ATTR(charge_control_limit),
POWER_SUPPLY_ATTR(charge_control_limit_max),
POWER_SUPPLY_ATTR(input_current_limit),
POWER_SUPPLY_ATTR(energy_full_design),
POWER_SUPPLY_ATTR(energy_empty_design),
POWER_SUPPLY_ATTR(energy_full),
POWER_SUPPLY_ATTR(energy_empty),
POWER_SUPPLY_ATTR(energy_now),
POWER_SUPPLY_ATTR(energy_avg),
POWER_SUPPLY_ATTR(capacity),
POWER_SUPPLY_ATTR(capacity_alert_min),
POWER_SUPPLY_ATTR(capacity_alert_max),
POWER_SUPPLY_ATTR(capacity_level),
POWER_SUPPLY_ATTR(temp),
POWER_SUPPLY_ATTR(temp_max),
POWER_SUPPLY_ATTR(temp_min),
POWER_SUPPLY_ATTR(temp_alert_min),
POWER_SUPPLY_ATTR(temp_alert_max),
POWER_SUPPLY_ATTR(temp_ambient),
POWER_SUPPLY_ATTR(temp_ambient_alert_min),
POWER_SUPPLY_ATTR(temp_ambient_alert_max),
POWER_SUPPLY_ATTR(time_to_empty_now),
POWER_SUPPLY_ATTR(time_to_empty_avg),
POWER_SUPPLY_ATTR(time_to_full_now),
POWER_SUPPLY_ATTR(time_to_full_avg),
POWER_SUPPLY_ATTR(type),
POWER_SUPPLY_ATTR(scope),
POWER_SUPPLY_ATTR(charge_term_current),
POWER_SUPPLY_ATTR(calibrate),
/* Properties of type `const char *' */
POWER_SUPPLY_ATTR(model_name),
POWER_SUPPLY_ATTR(manufacturer),
POWER_SUPPLY_ATTR(serial_number),
};
The specific meaning is not elaborated here.
4. How to write PSY driver based on power supply class
Finally, from the point of view of PSY driver, how to write driver based on power supply class is explained.
1) According to the hardware spec, determine what features the PSY device has, and correspond them to the property defined in enum power_supply_property.
2) Realize the get/set interface of these properties according to the actual situation.
3) Define a struct power_supply variable, initialize the necessary fields, call power_supply_register or power_supply_register_no_ws, and register it in the kernel.
4) According to the actual situation, start the monitoring logic for the change of device attributes, such as interruption, polling, etc., and call power_supply_changed to notify power supply core when the change occurs.
Maybe you will laugh and say it simply. Indeed, the same principle: framework can only provide us with a good mechanism, convenient way, and so on, but what equipment to do, only the device driver is the clearest, can never be lazy ah!