创建了一个netlink用于给上层获取电池各类信息,如bat_current,charger_status等,详见bmd_ctrl_cmd_from_user函数
static int __init battery_init(void)
{
struct netlink_kernel_cfg cfg = {
.input = nl_data_handler,
};
int ret;
daemo_nl_sk = netlink_kernel_create(&init_net, NETLINK_FGD, &cfg);
bm_err("netlink_kernel_create protol= %d\n", NETLINK_FGD);
if (daemo_nl_sk == NULL) {
bm_err("netlink_kernel_create error\n");
return -1;
}
bm_err("netlink_kernel_create ok\n");
#ifdef CONFIG_OF
/* register battery_device by DTS */
#else
ret = platform_device_register(&battery_device);
#endif
ret = platform_driver_register(&battery_driver_probe);
ret = platform_driver_register(&battery_dts_driver_probe);
bm_err("[battery_init] Initialization : DONE\n");
return 0;
}
nl_data_handler
struct fgd_nl_msg_t {
unsigned int fgd_cmd;
unsigned int fgd_subcmd;
unsigned int fgd_subcmd_para1;
unsigned int fgd_data_len;
unsigned int fgd_ret_data_len;
char fgd_data[FGD_NL_MSG_MAX_LEN];
};
static struct sock *daemo_nl_sk;
static void nl_data_handler(struct sk_buff *skb) {
u32 pid;
kuid_t uid;
int seq;
void *data;
struct nlmsghdr *nlh;
struct fgd_nl_msg_t *fgd_msg, *fgd_ret_msg;
int size = 0;//拿出数据
nlh = (struct nlmsghdr *)skb->data;
pid = NETLINK_CREDS(skb)->pid;
uid = NETLINK_CREDS(skb)->uid;
seq = nlh->nlmsg_seq;
/*bm_debug("[Netlink] recv skb from user space uid:%d pid:%d seq:%d\n",uid,pid,seq); */
data = NLMSG_DATA(nlh);//取得消息的数据部分的首地
fgd_msg = (struct fgd_nl_msg_t *)data;
size = fgd_msg->fgd_ret_data_len + FGD_NL_MSG_T_HDR_LEN;
fgd_ret_msg = vmalloc(size);
if (!fgd_ret_msg) {
/* bm_err("Error: nl_data_handler() vmalloc fail!!!\n"); */
return;
}
memset(fgd_ret_msg, 0, size);
bmd_ctrl_cmd_from_user(data, fgd_ret_msg);//数据到fgd_ret_msg
nl_send_to_user(pid, seq, fgd_ret_msg);
vfree(fgd_ret_msg);
}
根据不同的fgd_cmd填充fgd_data以及fgd_data_len
bmd_ctrl_cmd_from_user
void bmd_ctrl_cmd_from_user(void *nl_data, struct fgd_nl_msg_t *ret_msg)
{
struct fgd_nl_msg_t *msg;
static int ptim_vbat, ptim_i;
msg = nl_data;
ret_msg->fgd_cmd = msg->fgd_cmd;
switch (msg->fgd_cmd) {
case FG_DAEMON_CMD_IS_BAT_PLUGOUT:
{
int is_bat_plugout = 0;
int bat_plugout_time = 0;
gauge_dev_get_boot_battery_plug_out_status(gauge_dev, &is_bat_plugout, &bat_plugout_time);
ret_msg->fgd_data_len += sizeof(is_bat_plugout);
memcpy(ret_msg->fgd_data, &is_bat_plugout, sizeof(is_bat_plugout));
bm_debug("[fg_res] BATTERY_METER_CMD_GET_BOOT_BATTERY_PLUG_STATUS = %d\n", is_bat_plugout);
}
break;
case FG_DAEMON_CMD_GET_FG_SHUTDOWN_COND:
{
unsigned int shutdown_cond = get_shutdown_cond();
ret_msg->fgd_data_len += sizeof(shutdown_cond);
memcpy(ret_msg->fgd_data, &shutdown_cond, sizeof(shutdown_cond));
bm_debug("[fg_res] shutdown_cond = %d\n", shutdown_cond);
}
break;
如在FG_DAEMON_CMD_GET_FG_SHUTDOWN_COND获取的是,是否满足关机条件
int get_shutdown_cond(void)
{
int ret = 0;
int vbat = pmic_get_battery_voltage();
if (sdc.shutdown_status.is_soc_zero_percent)
ret |= 1;
if (sdc.shutdown_status.is_uisoc_one_percent)
ret |= 1;
if (sdc.lowbatteryshutdown)
ret |= 1;
bm_err("get_shutdown_cond ret:%d %d %d %d vbat:%d\n",
ret, sdc.shutdown_status.is_soc_zero_percent,
sdc.shutdown_status.is_uisoc_one_percent,
sdc.lowbatteryshutdown, vbat);
return ret;
}
向user发送数据
static void nl_send_to_user(u32 pid, int seq, struct fgd_nl_msg_t *reply_msg)
{
struct sk_buff *skb;
struct nlmsghdr *nlh;
/* int size=sizeof(struct fgd_nl_msg_t); */
int size = reply_msg->fgd_data_len + FGD_NL_MSG_T_HDR_LEN;
int len = NLMSG_SPACE(size);
void *data;
int ret;
skb = alloc_skb(len, GFP_ATOMIC);//alloc skb
if (!skb)
return;
nlh = nlmsg_put(skb, pid, seq, 0, size, 0);//设置netlink消息头
data = NLMSG_DATA(nlh);//首地址赋予data
memcpy(data, reply_msg, size);//将要发送给user的msg赋予skb
NETLINK_CB(skb).portid = 0; /* from kernel */
NETLINK_CB(skb).dst_group = 0; /* unicast 单播。多播为5*/
ret = netlink_unicast(daemo_nl_sk, skb, pid, MSG_DONTWAIT);//send msg
if (ret < 0) {
bm_err("[Netlink] send failed %d\n", ret);
return;
}
/*bm_debug("[Netlink] reply_user: netlink_unicast- ret=%d\n", ret); */
}
一共注册了两个平台
#ifdef CONFIG_OF
static const struct of_device_id mtk_bat_of_match[] = {
{.compatible = "mediatek,bat_gm30",},
{},
};
MODULE_DEVICE_TABLE(of, mtk_bat_of_match);
#endi
static struct platform_driver battery_dts_driver_probe = {
.probe = battery_dts_probe,
.remove = NULL,
.shutdown = NULL,
.suspend = NULL,
.resume = NULL,
.driver = {
.name = "battery-dts",
#ifdef CONFIG_OF
.of_match_table = mtk_bat_of_match,
#endif
},
};
static struct platform_driver battery_driver_probe = {
.probe = battery_probe,
.remove = NULL,
.shutdown = NULL,
.suspend = battery_suspend,
.resume = battery_resume,
.driver = {
.name = "battery",
},
};
在fg_custom_init_from_dts中解析设备树
static int battery_dts_probe(struct platform_device *dev)
{
int ret = 0;
bm_err("******** battery_dts_probe!! ********\n");
battery_device.dev.of_node = dev->dev.of_node;
ret = platform_device_register(&battery_device);
if (ret) {
bm_err("****[battery_dts_probe] Unable to register device (%d)\n", ret);
return ret;
}
check_isevb_dtsi(dev);
fg_custom_init_from_dts(dev);
return 0;
}
从中可以得知目前mtk电池曲线是按照mah,voltage,resistance,percentage来读取的
switch (bat_id) {
case 0:
fg_custom_parse_table(np, "battery0_profile_t0",
fg_table_cust_data.fg_profile_t0);
fg_custom_parse_table(np, "battery0_profile_t1",
fg_table_cust_data.fg_profile_t1);
fg_custom_parse_table(np, "battery0_profile_t2",
fg_table_cust_data.fg_profile_t2);
fg_custom_parse_table(np, "battery0_profile_t3",
fg_table_cust_data.fg_profile_t3);
#ifdef CONFIG_MTK_ADDITIONAL_BATTERY_TABLE
fg_custom_parse_table(np, "battery0_profile_t4",
fg_table_cust_data.fg_profile_t4);
#endif
struct FUELGAUGE_PROFILE_STRUCT {
unsigned int mah;
unsigned short voltage
unsigned short resistance; /* Ohm*/
unsigned short resistance;
};
而在fg_custom_parse_table中实际用到的只有mah,voltage,resistance;
static void fg_custom_parse_table(const struct device_node *np,
const char *node_srting, struct FUELGAUGE_PROFILE_STRUCT *profile_struct)
{
int mah, voltage, resistance, idx, saddles;
struct FUELGAUGE_PROFILE_STRUCT *profile_p;
profile_p = profile_struct;
saddles = fg_table_cust_data.fg_profile_t0_size;
idx = 0;
bm_debug("fg_custom_parse_table: %s, %d\n", node_srting, saddles);
while (!of_property_read_u32_index(np, node_srting, idx, &mah)) {
idx++;
if (!of_property_read_u32_index(np, node_srting, idx, &voltage)) {
/*bm_err("fg_custom_parse_table: mah: %d, voltage: %d\n", mah, voltage);*/
/*bm_err("fg_custom_parse_table: mah: %d, voltage: %d\n", mah, voltage);*/
}
idx++;
if (!of_property_read_u32_index(np, node_srting, idx, &resistance)) {
bm_debug("fg_custom_parse_table: mah: %d, voltage: %d, resistance: %d\n",
mah, voltage, resistance);
}
profile_p->mah = mah;
profile_p->voltage = voltage;
profile_p->resistance = resistance;
/* dump parsing data */
#if 0
msleep(20);
bm_print(BM_LOG_CRTI, "__batt_meter_parse_table>> %s[%d]: <%d, %d>\n",
node_srting, (idx/2), profile_p->percentage, profile_p->voltage);
#endif
profile_p++;
if ((idx++) >= (saddles * 3))
break;
}