LInux 鋰電池驅動分析

鋰電池的驅動程序要實現以下五個功能:

1.可以自動檢測到當前給電池充電的是USB還是AC

2.組織過大的充電電流

3.壞電池檢測

4.死亡溫度的檢測

5.電池電壓的測量

 

當我們要寫一個鋰電池的驅動程序的時候,首先要知道內核提供給驅動的接口,就是當驅動掛載到內核上的時候,內核是怎麼知道驅動中的信息的,如何來驅動。而這個內核提供給驅動的接就是一個結構體power_supply.

struct power_supply {

const char *name;

enum power_supply_type type;

enum power_supply_property *properties;//聲明瞭電源的屬性

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);//得到電源的屬性

void (*external_power_changed)(struct power_supply *psy);

void (*set_charged)(struct power_supply *psy);

 



int use_for_apm;

 



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

};

 

內核主要通過get_property這個函數指針來獲得驅動中的有關電池的信息,而這個函數在內核中只給出了其聲明,我們在寫驅動的時候要自己實現這個函數,即講自己寫函數值給函數指針,當內核需要驅動中的電源的信息的時候,回調這個get_property即可。另外,我們寫驅動程序又要給用戶提供接口,內核中的提供給用戶的接口即sysfs,通過讀取其中的屬性就可以得到電源的信息。內核主要通過兩個文件power_supply_class.c和power_supply_core.c,我們調用其中的函數就可以把電源(電池,USB power supply或者AC power supply)的信息展現給用戶,有關電源的屬性寫在/sys/class /powersupply文件夾下。

 

這樣,按照內核提供的接口,驅動程序的書寫就很清晰了,結合鋰電池的驅動程序的源代碼,我們來看看驅動程序的執行過程。

 

當一個驅動被編譯好並被掛到內核上之後,會首先執行一個模塊的初始化函數,每個驅動都是統一的,在這裏是module_init(stmp3xxx_bat_init);它代表首先執行stmp3xxx_bat_init,在驅動裏它是這麼定義的:

static int __init stmp3xxx_bat_init(void)

{

return platform_driver_register(&stmp3xxx_batdrv);

}

這個函數執行platform_driver_register(&stmp3xxx_batdrv);並將返回值返回。而platform_driver_register()是一個內核函數,它在內核中如下定義:

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仍然是一個內核函數,、

//上面的函數的作用就是將device的driver轉變成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);

}

這個函數所完成的就是:首先將platform_driver的結構體變量 driver的bus域初始化,然後將platform_driver的driver的函數指針probe等初始化爲platform_driver的 probe(如何完成的請看上面代碼中給出的註釋).然後執行driver_register(&drv->driver)(我們一會再分析driver_register(&drv->driver))。

 

platform_driver_register()在我們的驅動中,它的參數是一個結構體指針&stmp3xxx_batdrv,在我們的驅動裏它是如下這麼定義的:

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,

},

};

 

下面講一下 driver_register(&drv->driver),在這裏我就不貼出其中的代碼了,它的過程比較複雜,可以用 Source Insight跟蹤其中的調用過程,在這裏我就大致的介紹一下它的主要過程,一些不重要的東西略掉,首先它會遍歷在BUS上的所有設備,通過比較設備的名字和驅動的名字來進行匹配,如果名字相同才能註冊成功,當註冊成功後接下來會調用platform_driver結構中probe函數指針,在這裏就是stmp3xxx_bat_probe,其函數原型 static int stmp3xxx_bat_probe(struct platform_device *pdev),而此時 stmp3xxx_bat_probe的參數就是我們在總線上找到的和驅動相匹配的設備,它是在驅動註冊的時候,找到和驅動匹配的設備後給pdev初始化的。

下面我們說一說stmp3xxx_bat_probe所完成的主要功能:獲取電源設備的中斷資源,代碼實現如下:

struct resource *vdd5v_irq;

info->vdd5v_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);

下面說一下resource,該元素存入了最爲重要的設備資源信息,例如設備的地址,中斷號等,其定義如下:

struct resource {

resource_size_t start;

resource_size_t end;

const char *name;

unsigned long flags;

struct resource *parent, *sibling, *child;

};
下面舉s3c2410平臺的i2c驅動作爲例子來說明:


static struct resource s3c_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,
         }
};

這裏定義了兩組resource,它描述了一個I2C設備的資源,第1組描述了這個I2C設備所佔用的總線地址範圍,IORESOURCE_MEM表示第1組描述的是內存類型的資源信息,第2組描述了這個I2C設備的中斷號,IORESOURCE_IRQ表示第2組描述的是中斷資源信息。設備驅動會根據flags來獲取相應的資源信息。

 

 

保存指向驅動特有信息的指針:platform_set_drvdata(pdev, info);

 

對電源進行初始化,代碼如下:

info->bat.name           = ”battery”;//名字

info->bat.type           = POWER_SUPPLY_TYPE_BATTERY;//類型

info->bat.properties     = stmp3xxx_bat_props;//屬性

info->bat.num_properties = ARRAY_SIZE(stmp3xxx_bat_props);//屬性的個數

info->bat.get_property   = stmp3xxx_bat_get_property;//得到屬性的函數

主要是實現一些給電源名字類型等賦初值,最主要的是將get_property函數指向我們寫好的可以得到電源的屬性的函數的起始地址,以便當內核需要用到驅動的信息的時候進行回調。

 

接下來初始化timer,mutex,代碼如下:

init_timer(&info->sm_timer);

info->sm_timer.data = (unsigned long)info;

info->sm_timer.function = state_machine_timer;

 

mutex_init(&info->sm_lock);

 

接下來將三種電源註冊,即把他們的屬性寫到sys文件系統裏,以使用戶空間可以得到有關電源的信息,以其中的一個爲例:

ret = power_supply_register(&pdev->dev, &info->bat);//將電池註冊

power_supply_register調用內核提供的函數device_create()和power_supply_create_attrs來實現電池的註冊。

這裏只寫出了驅動要完成的基本功能,至於如何完成的,即驅動與硬件之間的交互,對寄存器的操作,需要參考具體的硬件手冊。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章