Android8.1使用的是kernel4.4,驱动中自带的cw2015没有dc_det功能,因此需要将之前Android6.0,kernel3.1中使用的cw2015驱动尝试移植到8.1中。
代码拷贝过来后果然很多报错。编译过程中,包括rk_bat,rk_ac,rk_usb均会报错power_supply 没有成员 type,properties等
static int cw_bat_probe(struct i2c_client *client,
const struct i2c_device_id *id)
中,cw_bat->rk_bat.type = POWER_SUPPLY_TYPE_BATTERY;
cw_bat->rk_bat.properties = rk_battery_properties;
cw_bat->rk_bat.num_properties = ARRAY_SIZE(rk_battery_properties);
cw_bat->rk_bat.get_property = rk_battery_get_property;
ret = power_supply_register(&client->dev, &cw_bat->rk_bat);
很显然是内核的接口变了, 查看include/linux/power_supply.h中struct power_supply的定义:
struct power_supply {
const struct power_supply_desc *desc;
char **supplied_to;
size_t num_supplicants;
char **supplied_from;
size_t num_supplies;
struct device_node *of_node;
/* Driver private data */
void *drv_data;
/* private */
struct device dev;
struct work_struct changed_work;
struct delayed_work deferred_register_work;
spinlock_t changed_lock;
bool changed;
bool initialized;
atomic_t use_cnt;
#ifdef CONFIG_THERMAL
struct thermal_zone_device *tzd;
struct thermal_cooling_device *tcd;
#endif
#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
};
的确是没有这些成员了,不过看一眼便知,这些成员都封装在power_supply_desc 这个结构体中:
struct power_supply_desc {
const char *name;
enum power_supply_type type;
enum power_supply_property *properties;
size_t num_properties;
/*
* Functions for drivers implementing power supply class.
* These shouldn't be called directly by other drivers for accessing
* this power supply. Instead use power_supply_*() functions (for
* example power_supply_get_property()).
*/
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);
/*
* property_is_writeable() will be called during registration
* of power supply. If this happens during device probe then it must
* not access internal data of device (because probe did not end).
*/
int (*property_is_writeable)(struct power_supply *psy,
enum power_supply_property psp);
void (*external_power_changed)(struct power_supply *psy);
void (*set_charged)(struct power_supply *psy);
/*
* Set if thermal zone should not be created for this power supply.
* For example for virtual supplies forwarding calls to actual
* sensors or other supplies.
*/
bool no_thermal;
/* For APM emulation, think legacy userspace. */
int use_for_apm;
};
如此来看,只需要在代码中多加一个指向成员就可以了吗?然而这个desc在power_supply中是const的,如此修改编译器只会告诉你desc是只读的,不允许定义后修改,那么只剩下一种方案了,就是在power_supply初始化之后,
定义一个power_supply_desc指针,将需要初始化的内容填充好,然后把这个指针传给power_supply中的desc。这种方法还有两种方案,一种是在初始化的过程中动态的去申请和传递,另一种是在初始化之前,直接静态定义好
接口,初始化之后直接传递,后者写起来比较方便,但是直接定义静态的const数据不太好,还是动态申请吧:
struct power_supply_desc *rk_bat_desc;
rk_bat_desc = devm_kzalloc(&client->dev, sizeof(*rk_bat_desc), GFP_KERNEL);
if (!rk_bat_desc)
return -ENOMEM;
rk_bat_desc->name = "rk-bat";
rk_bat_desc->type = POWER_SUPPLY_TYPE_BATTERY;
rk_bat_desc->properties = rk_battery_properties;
rk_bat_desc->num_properties = ARRAY_SIZE(rk_battery_properties);
rk_bat_desc->get_property = rk_battery_get_property;
其中,注册的接口也变化了:
extern struct power_supply *__must_check
power_supply_register(struct device *parent,
const struct power_supply_desc *desc,
const struct power_supply_config *cfg);
第二个参数接受的是 power_supply_desc, 第三个这是power_supply_config,所以还需要构建一个cfg指针,cfg需要接受一个device作为 drv_data,
struct power_supply_config psy_cfg = { .drv_data = cw_bat, };
同时返回一个 struct power_supply, 旧接口返回的是一个int错误码,可以判断是否注册成功,新的接口可以把返回值赋给cw_bat->rk_bat:
cw_bat->rk_bat = power_supply_register(&client->dev, rk_bat_desc, &psy_cfg);
后,在这个函数中,还会报错: get_gadget_connect_flag()未定义,也是新内核中usb的接口变化了,搜一下代码发现这个方法只用于初始化一个值,直接替换成0即可。
static int get_usb_charge_state(struct cw_battery *cw_bat)
{
int charge_time;
int time_from_boot;
struct timespec ts;
int gadget_status = get_gadget_connect_flag();
int usb_status = dwc_vbus_status();
get_monotonic_boottime(&ts);
time_from_boot = ts.tv_sec;
if (cw_bat->charger_init_mode) {
if (usb_status == 1 || usb_status == 2) {
cw_bat->charger_init_mode = 0;
} else if (time_from_boot < 8) {
usb_status = cw_bat->charger_init_mode;
} else if (strstr(saved_command_line, "charger")) {
cw_bat->charger_init_mode = dwc_otg_check_dpdm();
usb_status = cw_bat->charger_init_mode;
}
}
#ifdef NO_STANDARD_AC_BIG_CHARGE_MODE
if (cw_bat->usb_online == 1) {
charge_time =
time_from_boot - cw_bat->sleep_time_charge_start -
cw_bat->run_time_charge_start;
if (charge_time > 3) {
if (gadget_status == 0 && dwc_vbus_status() == 1)
usb_status = 2;
}
}
#endif
dev_dbg(&cw_bat->client->dev,
"%s usb_status=[%d],cw_bat->charger_mode=[%d],cw_bat->gadget_status=[%d], cw_bat->charger_init_mode = [%d]\n",
__func__, usb_status, cw_bat->charger_mode, gadget_status,
cw_bat->charger_init_mode);
return usb_status;
}
再次编译,成功通过。