The driver of the lithium battery should implement the following five functions:
1. It can automatically detect whether the current battery is charged with USB or AC.
2. Organize excessive charging current
3. Bad battery Detection
4. Death Temperature Detection
5. Battery voltage measurement
When we want to write a lithium-ion driver, we first need to know the interface provided to the driver by the kernel, that is, when the driver is mounted to the kernel, how does the kernel know the information in the driver and how to control the driver. The driver interface provided by this kernel is a struct Power_Supply.
Struct Power_Supply {
Const char * Name;
Enum power_supply_type;
Enum power_supply_property * properties; // declares the power supply attribute.
Size_t num_properties;
Char ** supplied_to;
Size_t num_supplicants;
INT (* get_property) (struct Power_Supply * Psy,
Enum power_supply_property PSP,
Union power_supply_propval * val); // obtain the power attribute.
Void (* external_power_changed) (struct Power_Supply * Psy );
Void (* set_charged) (struct Power_Supply * Psy );
/* For APM emulation, think legacy userspace .*/
Int use_for_apm;
/* Private */
Struct device * dev;
Struct work_struct changed_work;
# 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;
# Endif
};
The kernel mainly uses the get_property function pointer to obtain information about the battery in the driver. This function only gives its declaration in the kernel. We need to implement this function ourselves when writing the driver, that is to say, the function written by yourself is assigned to the function pointer. When the kernel needs the power supply information in the driver, you can call back the get_property. In addition, we need to write the driver program and provide the user with an interface. The interface provided to the user in the kernel is sysfs, and the power information can be obtained by reading the attribute. The kernel mainly uses two files power_supply_class.c and power_supply_core.c. We can call the function to display the power supply (battery, USB power supply, or AC power supply) information to users, the power properties are written in the/sys/class/powersupply folder.
In this way, according to the interface provided by the kernel, the driver is clearly written. Combined with the source code of the lithium battery driver, let's take a look at the execution process of the driver.
After a driver is compiled and mounted to the kernel, it will first execute the initialization function of a module. Each driver is unified, where module_init (stmp3xxx_bat_init ); it indicates that stmp3xxx_bat_init is executed first, which is defined in the driver as follows:
Static int _ init stmp3xxx_bat_init (void)
{
Return platform_driver_register (& stmp3xxx_batdrv );
}
This function runs platform_driver_register (& stmp3xxx_batdrv) and returns the returned value. Platform_driver_register () is a kernel function defined in the kernel as follows:
Int platform_driver_register (struct platform_driver * DRV)
{
DRV-> driver. Bus = & platform_bus_type;
If (DRV-> probe)
DRV-> driver. Probe = platform_drv_probe; // platform_drv_probe is still a kernel function, And/* It is defined in the kernel as follows:
Static int platform_drv_probe (struct device * _ Dev)
{
Struct platform_driver * DRV = to_platform_driver (_ Dev-> driver );
Struct platform_device * Dev = to_platform_device (_ Dev );
Return DRV-> probe (Dev );
}*/
// The function above is used to convert the driver of the device into platform_driver.
If (DRV-> remove)
DRV-> driver. Remove = platform_drv_remove;
If (DRV-> shutdown)
DRV-> driver. Shutdown = platform_drv_shutdown;
Return driver_register (& DRV-> driver );
}
This function initializes the bus field of the Structure Variable driver of platform_driver, then, initialize the function pointer probe of the driver of platform_driver to the probe of platform_driver. (For details about how to complete the probe, see the comments in the above Code ). then execute driver_register (& DRV-> driver) (we will analyze driver_register (& DRV-> driver) later )).
Platform_driver_register () in our driver, its parameter is a struct pointer & stmp3xxx_batdrv, which is defined as follows in our driver:
Static struct platform_driver stmp3xxx_batdrv = {
. Probe = stmp3xxx_bat_probe,
. Remove = stmp3xxx_bat_remove,
. Shutdown = stmp3xxx_bat_shutdown,
. Suspend = stmp3xxx_bat_suspend,
. Resume = stmp3xxx_bat_resume,
. Driver = {
. Name = "stmp3xxx-battery ",
. Owner = this_module,
},
};
Let's talk about driver_register (& DRV-> driver). Here I will not post the code. The process is complicated. You can use source insight to track the call process, here I will give a general introduction to its main process. Some unimportant things are omitted. First, it will traverse all the devices on the bus, if the device name and driver name are the same, the system will call the probe function pointer in the platform_driver structure after the registration is successful. In this example, stmp3xxx_bat_probe is called, its function prototype is static int stmp3xxx_bat_probe (struct platform_device * pdev). At this time, the stmp3xxx_bat_probe parameter is the device we found on the bus that matches the driver. It is during driver registration, find the device that matches the driver and initialize it for pdev.
The following describes the main functions completed by stmp3xxx_bat_probe: Obtain the interrupted resources of the power supply device, and implement the Code as follows:
Struct resource * vdd5v_irq;
Info-> vdd5v_irq = platform_get_resource (pdev, ioresource_irq, 0 );
The following describes the resource. This element stores the most important device resource information, such as the device address and interrupt number. Its definition is as follows:
Struct resource {
Resource_size_t start;
Resource_size_t end;
Const char * Name;
Unsigned long flags;
Struct resource * parent, * sibling, * child;
};
The following describes the I2C driver of the S3C2410 platform as an example:
/* ARCH/ARM/mach-s3c2410/Devs. C */ /* I2C */ Static struct resource initi_i2c_resource [] = { [0] = { . Start = s3c24xx_pa_iic, . End = s3c24xx_pa_iic + s3c24xx_sz_iic-1, . Flags = ioresource_mem, }, [1] = { . Start = irq_iic, // s3c2410_irq (27) . End = irq_iic, . Flags = ioresource_irq, } }; |
Two groups of resources are defined here, which describe the resources of an I2C device, and 1st describe the BUS address range occupied by this I2C device, ioresource_mem indicates that the 1st group describes the memory-type resource information, the 2nd group describes the interrupt Number of the I2C device, and the ioresource_irq indicates that the 2nd group describes the interrupted resource information. The device driver obtains the corresponding resource information based on flags. |
The pointer to the specific driver information is saved: platform_set_drvdata (pdev, Info );
Initialize the power supply with the following code:
Info-> bat. Name = "battery"; // name
Info-> bat. type = power_supply_type_battery; // type
Info-> bat. properties = stmp3xxx_bat_props; // attribute
Info-> bat. num_properties = array_size (stmp3xxx_bat_props); // number of attributes
Info-> bat. get_property = stmp3xxx_bat_get_property; // gets the function of the attribute.
The main function is to assign initial values to the power supply name type. The main function is to direct the get_property function to the starting address of the function that can obtain the power supply attribute, so that the kernel calls back when the driver information is needed.
Next, initialize timer and mutex. The Code is as follows:
Init_timer (& info-> sm_timer );
Info-> sm_timer.data = (unsigned long) Info;
Info-> sm_timer.function = state_machine_timer;
Mutex_init (& info-> sm_lock );
Next, we will write the three types of power supply registrations to the SYS file system so that the user space can obtain information about the power supply. Take one of them as an example:
Ret = power_supply_register (& pdev-> Dev, & info-> BAT); // register the battery
Power_supply_register calls the device_create () and power_supply_create_attrs functions provided by the kernel to register the battery.
Only the basic functions to be completed by the driver are provided here. For details about how to do this, that is, the interaction between the driver and the hardware, and the operation of registers, You need to refer to the specific hardware manual.
This document still needs to be improved. I hope you will give more comments and I will continue to modify it.