引言
目前,各个OEM其实为了降低成本,将会用各自的渠道去购买更加物美价廉的外设。
这样,其实促进了各个厂商的竞争,而开发更加简单适配的驱动程序将会成为厂商必选项。
那么,开发一个驱动,将会有哪些点需要关注,i2c读写,interrupt实现等内容将会是本文的关注点。
这里以max8925为例,来进行一个分析。
驱动注册
在每个驱动想要被系统识别,肯定是需要进行一个声明。
module_platform_driver(max8925_power_driver);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Power supply driver for MAX8925");
MODULE_ALIAS("platform:max8925-power");
在上面的例子中,我们看到使用了module_platform_driver函数来进行注册。
/* module_platform_driver() - Helper macro for drivers that don't do
* anything special in module init/exit. This eliminates a lot of
* boilerplate. Each module may only use this macro once, and
* calling it replaces module_init() and module_exit()
*/
#define module_platform_driver(__platform_driver) \
module_driver(__platform_driver, platform_driver_register, \
platform_driver_unregister)
这里其实只是定义了一个宏,真正的使用是在module_driver中实现。
/**
* module_driver() - Helper macro for drivers that don't do anything
* special in module init/exit. This eliminates a lot of boilerplate.
* Each module may only use this macro once, and calling it replaces
* module_init() and module_exit().
*
* @__driver: driver name
* @__register: register function for this driver type
* @__unregister: unregister function for this driver type
* @...: Additional arguments to be passed to __register and __unregister.
*
* Use this macro to construct bus specific macros for registering
* drivers, and do not use it on its own.
*/
#define module_driver(__driver, __register, __unregister, ...) \
static int __init __driver##_init(void) \
{ \
return __register(&(__driver) , ##__VA_ARGS__); \
} \
module_init(__driver##_init); \
static void __exit __driver##_exit(void) \
{ \
__unregister(&(__driver) , ##__VA_ARGS__); \
} \
module_exit(__driver##_exit);
由上面的定义可知,module_platform_driver宏的作用就是定义指定名称的平台设备驱动注册函数和平台设备驱动注销函数。
并且在函数体内分别通过 __register函数和 __unregister() 注册和注销该平台设备驱动。
在本例中,注册的就是 max8925_power_driver了。
driver结构体实现
在本例中,max8925_power_driver 的结构体实现如下:
static struct platform_driver max8925_power_driver = {
.probe = max8925_power_probe,
.remove = max8925_power_remove,
.driver = {
.name = "max8925-power",
},
};
变量 | 成员变量 | 作用 |
---|---|---|
probe | max8925_power_probe | probe函数在设备驱动注册最后收尾工作,当设备的device 和其对应的driver 在总线上完成配对之后,系统就调用 platform设备的probe函数完成驱动注册最后工作 |
remove | max8925_power_remove | (1)卸载驱动的时候,remove调用 (2)设备移除的时候,与设备关联的驱动需要移除,remove调用 |
.name | max8925-power | 驱动的名字为max8925-power |
那么我们可以知道,当注册匹配时,就可以进入probe函数进行注册的完成。
probe函数的实现
该函数一般实现较长,我们先来看看代码片段。
static int max8925_power_probe(struct platform_device *pdev)
{
struct max8925_chip *chip = dev_get_drvdata(pdev->dev.parent);
struct power_supply_config psy_cfg = {}; /* Only for ac and usb */
struct max8925_power_pdata *pdata = NULL;
struct max8925_power_info *info;
int ret;
pdata = max8925_power_dt_init(pdev);
if (!pdata) {
dev_err(&pdev->dev, "platform data isn't assigned to "
"power supply\n");
return -EINVAL;
}
info = devm_kzalloc(&pdev->dev, sizeof(struct max8925_power_info),
GFP_KERNEL);
if (!info)
return -ENOMEM;
info->chip = chip;
info->gpm = chip->i2c;
info->adc = chip->adc;
platform_set_drvdata(pdev, info);
psy_cfg.supplied_to = pdata->supplied_to;
psy_cfg.num_supplicants = pdata->num_supplicants;
info->ac = power_supply_register(&pdev->dev, &ac_desc, &psy_cfg);
if (IS_ERR(info->ac)) {
ret = PTR_ERR(info->ac);
goto out;
}
info->ac->dev.parent = &pdev->dev;
info->usb = power_supply_register(&pdev->dev, &usb_desc, &psy_cfg);
if (IS_ERR(info->usb)) {
ret = PTR_ERR(info->usb);
goto out_unregister_ac;
}
info->usb->dev.parent = &pdev->dev;
info->battery = power_supply_register(&pdev->dev, &battery_desc, NULL);
if (IS_ERR(info->battery)) {
ret = PTR_ERR(info->battery);
goto out_unregister_usb;
}
info->battery->dev.parent = &pdev->dev;
info->batt_detect = pdata->batt_detect;
info->topoff_threshold = pdata->topoff_threshold;
info->fast_charge = pdata->fast_charge;
info->set_charger = pdata->set_charger;
info->no_temp_support = pdata->no_temp_support;
info->no_insert_detect = pdata->no_insert_detect;
max8925_init_charger(chip, info);
return 0;
out_unregister_usb:
power_supply_unregister(info->usb);
out_unregister_ac:
power_supply_unregister(info->ac);
out:
return ret;
}
因为这段是完成注册,所以我们来分析几个除了初始化以外其中的关键点。
max8925_power_dt_init
该函数其实是去传了一个platform_devices,这个的作用是什么呢?
- 在内核初始化时通过device_node转换为platform_device,这种是最新的实现方式,基于设备树,在内核初始化时将设备树中的节点转化为platform_device;
- 使用platform_device_register注册platform_device;
static struct max8925_power_pdata *
max8925_power_dt_init(struct platform_device *pdev)
{
struct device_node *nproot = pdev->dev.parent->of_node;
struct device_node *np;
int batt_detect;
int topoff_threshold;
int fast_charge;
int no_temp_support;
int no_insert_detect;
struct max8925_power_pdata *pdata;
if (!nproot)
return pdev->dev.platform_data;
np = of_get_child_by_name(nproot, "charger");
if (!np) {
dev_err(&pdev->dev, "failed to find charger node\n");
return NULL;
}
pdata = devm_kzalloc(&pdev->dev,
sizeof(struct max8925_power_pdata),
GFP_KERNEL);
if (!pdata)
goto ret;
of_property_read_u32(np, "topoff-threshold", &topoff_threshold);
of_property_read_u32(np, "batt-detect", &batt_detect);
of_property_read_u32(np, "fast-charge", &fast_charge);
of_property_read_u32(np, "no-insert-detect", &no_insert_detect);
of_property_read_u32(np, "no-temp-support", &no_temp_support);
pdata->batt_detect = batt_detect;
pdata->fast_charge = fast_charge;
pdata->topoff_threshold = topoff_threshold;
pdata->no_insert_detect = no_insert_detect;
pdata->no_temp_support = no_temp_support;
ret:
of_node_put(np);
return pdata;
}
这里的两个关键点:
- 在没有使用dts的kernel驱动中,会使用 pdev->dev.platform_data 来进行注册
- 在使用dts的的kernel驱动中,会去调用devm_kzalloc来获取注册。
代码实现分别为:
if (!nproot)
return pdev->dev.platform_data;
pdata = devm_kzalloc(&pdev->dev,
sizeof(struct max8925_power_pdata),
GFP_KERNEL);
这也是现在驱动越写越容易适配和调试的原因,会在实现时就考虑到多种情况。
注册后,如果成功,那么接下来就是platform_set_drvdata这个常用的函数了。
函数实现为:
static inline void platform_set_drvdata(struct platform_device *pdev,
void *data)
{
dev_set_drvdata(&pdev->dev, data);
}
继续看下这个封装的调用:
static inline void dev_set_drvdata(struct device *dev, void *data)
{
dev->driver_data = data;
}
就是把data的值赋值给了dev->driver_data,传进来的device其实是pdev,而pdev是总线设备,所以这些数据对整个驱动设备都是可见的。
因为我们试一个power设备,所以power_supply_register是必须要实现的。
定义一个struct power_supply 变量,并初始化必要字段后,调用power_supply_registe将其注册到kernel中。
info->ac = power_supply_register(&pdev->dev, &ac_desc, &psy_cfg);
然后就是一些具体功能的init。
max8925_init_charger(chip, info);
而在这个函数中,我们可以看到注册了很多的中端。
static int max8925_init_charger(struct max8925_chip *chip,
struct max8925_power_info *info)
{
int ret;
REQUEST_IRQ(MAX8925_IRQ_VCHG_DC_OVP, "ac-ovp");
if (!info->no_insert_detect) {
REQUEST_IRQ(MAX8925_IRQ_VCHG_DC_F, "ac-remove");
REQUEST_IRQ(MAX8925_IRQ_VCHG_DC_R, "ac-insert");
}
if (!info->no_temp_support) {
REQUEST_IRQ(MAX8925_IRQ_VCHG_THM_OK_R, "batt-temp-in-range");
REQUEST_IRQ(MAX8925_IRQ_VCHG_THM_OK_F, "batt-temp-out-range");
}
REQUEST_IRQ(MAX8925_IRQ_VCHG_SYSLOW_F, "vsys-high");
REQUEST_IRQ(MAX8925_IRQ_VCHG_SYSLOW_R, "vsys-low");
REQUEST_IRQ(MAX8925_IRQ_VCHG_RST, "charger-reset");
REQUEST_IRQ(MAX8925_IRQ_VCHG_DONE, "charger-done");
REQUEST_IRQ(MAX8925_IRQ_VCHG_TOPOFF, "charger-topoff");
REQUEST_IRQ(MAX8925_IRQ_VCHG_TMR_FAULT, "charger-timer-expire");
info->usb_online = 0;
info->bat_online = 0;
/* check for power - can miss interrupt at boot time */
if (start_measure(info, MEASURE_VCHG) * 2000 > 500000)
info->ac_online = 1;
else
info->ac_online = 0;
ret = max8925_reg_read(info->gpm, MAX8925_CHG_STATUS);
if (ret >= 0) {
/*
* If battery detection is enabled, ID pin of battery is
* connected to MBDET pin of MAX8925. It could be used to
* detect battery presence.
* Otherwise, we have to assume that battery is always on.
*/
if (info->batt_detect)
info->bat_online = (ret & MAX8925_CHG_MBDET) ? 0 : 1;
else
info->bat_online = 1;
if (ret & MAX8925_CHG_AC_RANGE_MASK)
info->ac_online = 1;
else
info->ac_online = 0;
}
/* disable charge */
max8925_set_bits(info->gpm, MAX8925_CHG_CNTL1, 1 << 7, 1 << 7);
/* set charging current in charge topoff mode */
max8925_set_bits(info->gpm, MAX8925_CHG_CNTL1, 3 << 5,
info->topoff_threshold << 5);
/* set charing current in fast charge mode */
max8925_set_bits(info->gpm, MAX8925_CHG_CNTL1, 7, info->fast_charge);
return 0;
}
我们在这边就不一一分析,而是只关注中断申请函数,REQUEST_IRQ。
中断的申请
仍然是以本例中的代码,我们展开分析。
REQUEST_IRQ(MAX8925_IRQ_VCHG_DC_OVP, "ac-ovp");
REQUEST_IRQ是一个宏定义,实现如下:
#define REQUEST_IRQ(_irq, _name) \
do { \
ret = request_threaded_irq(chip->irq_base + _irq, NULL, \
max8925_charger_handler, \
IRQF_ONESHOT, _name, info); \
if (ret) \
dev_err(chip->dev, "Failed to request IRQ #%d: %d\n", \
_irq, ret); \
} while (0)
request_threaded_irq申请的中断,handler不在中断上下文里执行。
而是在新创建的线程里执行。
这样,该handler非常像执行workqueue。
即拥有所有workqueue的特性,但是省掉了创建,初始化,调度workqueue的繁多步骤。
我们可以看到,在这个例子里面,注册的handler为max8925_charger_handler。
中断的实现
上面注册了max8925_charger_handler,那么当出现中断的时候,就会调用对应的handler。
static irqreturn_t max8925_charger_handler(int irq, void *data)
{
struct max8925_power_info *info = (struct max8925_power_info *)data;
struct max8925_chip *chip = info->chip;
switch (irq - chip->irq_base) {
case MAX8925_IRQ_VCHG_DC_R:
info->ac_online = 1;
__set_charger(info, 1);
dev_dbg(chip->dev, "Adapter inserted\n");
break;
case MAX8925_IRQ_VCHG_DC_F:
info->ac_online = 0;
__set_charger(info, 0);
dev_dbg(chip->dev, "Adapter removed\n");
break;
case MAX8925_IRQ_VCHG_THM_OK_F:
/* Battery is not ready yet */
dev_dbg(chip->dev, "Battery temperature is out of range\n");
case MAX8925_IRQ_VCHG_DC_OVP:
dev_dbg(chip->dev, "Error detection\n");
__set_charger(info, 0);
break;
case MAX8925_IRQ_VCHG_THM_OK_R:
/* Battery is ready now */
dev_dbg(chip->dev, "Battery temperature is in range\n");
break;
case MAX8925_IRQ_VCHG_SYSLOW_R:
/* VSYS is low */
dev_info(chip->dev, "Sys power is too low\n");
break;
case MAX8925_IRQ_VCHG_SYSLOW_F:
dev_dbg(chip->dev, "Sys power is above low threshold\n");
break;
case MAX8925_IRQ_VCHG_DONE:
__set_charger(info, 0);
dev_dbg(chip->dev, "Charging is done\n");
break;
case MAX8925_IRQ_VCHG_TOPOFF:
dev_dbg(chip->dev, "Charging in top-off mode\n");
break;
case MAX8925_IRQ_VCHG_TMR_FAULT:
__set_charger(info, 0);
dev_dbg(chip->dev, "Safe timer is expired\n");
break;
case MAX8925_IRQ_VCHG_RST:
__set_charger(info, 0);
dev_dbg(chip->dev, "Charger is reset\n");
break;
}
return IRQ_HANDLED;
}
在这个函数中,我们可以看到会有不同的中断进行对应的处理。
刚才我们举例的REQUEST_IRQ(MAX8925_IRQ_VCHG_DC_OVP, "ac-ovp");
其实也会有对应的case来完成。
case MAX8925_IRQ_VCHG_DC_OVP:
dev_dbg(chip->dev, "Error detection\n");
__set_charger(info, 0);
break;
以上,就是一个驱动的简单实现流程和分析。